369001824e
* feat: 添加文件下载功能,支持多 Terminal Backend 实现基于 FileProvider 抽象的文件下载能力,支持 local、Docker、SSH、 Singularity 四种 backend。 主要变更: - 新增 FileProvider 接口及四种后端实现(含 SSH 命令注入防护) - 新增 GET /api/hermes/download 下载路由(含 MIME 类型检测) - 前端 Markdown 文件链接拦截下载 + 附件下载按钮 - 中英文 i18n 翻译 - 更新 README、CLAUDE.md 和设计文档 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: 添加文件浏览器与下载功能,支持目录浏览、文件编辑、预览和上传 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * build: add prepare script so 'npm install git+url' auto-builds dist/ Allows installing this package directly from git without a pre-built dist/. When cloned via npm, prepare runs 'npm run build' if dist/ is missing, producing the artifacts declared in the files[] field before packing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use clipboard fallback for non-secure HTTP contexts navigator.clipboard is undefined on HTTP intranet deployments (only available in secure contexts). The previous synchronous calls threw silently and the success toast still fired, making 'copy' actions appear broken. - Add packages/client/src/utils/clipboard.ts with execCommand fallback via a hidden textarea - Use the helper in FileContextMenu (copy file path), CodexLoginModal (copy user code), NousLoginModal (copy user code), ChatPanel (copy session id) - Each call now awaits the result and shows success/failure based on the actual outcome Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * i18n: backfill files/download translations for de, es, fr, ja, ko, pt Add nav.files, files.* (39 keys), and download.* (9 keys) so the file browser UI is fully localized in these six locales instead of falling back to English. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(files): close preview when navigating or affected file changes Opening a preview and then navigating directories, deleting the previewed file, or renaming it left the preview pane stuck on stale content because previewFile was never cleared. - stores/hermes/files.ts: - fetchEntries clears previewFile on path change (in-place refresh keeps the preview). - deleteEntry / renameEntry clear preview/editor state when the affected entry matches the previewed/edited file or its parent. - Add isAffected(target, changed, isDir) helper. - components/hermes/files/FilePreview.vue: replace the misleading common.cancel close button with a dedicated files.closePreview key plus an X icon and quaternary style. - i18n: add files.closePreview to all 8 locales. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: 清理已完成功能的计划与设计文档 文件浏览器与文件下载功能均已被上游合并,对应的开发计划 与设计稿不再需要在 fork 中保留: - plans/2025-07-20-file-browser.md - plans/2026-04-20-file-download.md - specs/2025-07-20-file-browser-design.md - specs/2026-04-20-file-download-design.md 清理后本 fork 与 upstream/main 代码层面完全对齐。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: 添加 thinking 块分离与折叠展示设计稿(#164) 针对上游 issue #164,设计 assistant 消息中 <think>/<thinking>/<reasoning> 标签的识别、分离与可折叠展示方案。 关键决策(经 rubber-duck 审查修订): - 不修改 Message.content 与持久化字段,确保 localStorage 向前兼容 - 耗时摘要改为纯运行时派生(store 内 Map),避免刷新/重连丢失 - 首版即实现代码块保护,避免误识别 - 流结束时未闭合标签降级为正文,防止吞答案 - 解析 computed 与 duration interval 分离,规避性能风险 - 解析器放置 packages/client/src/utils/ 避免反向依赖 - 显式不支持同名嵌套(罕见场景文档化) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: 添加 thinking 块分离与折叠实施计划(#164) 12 Task TDD 计划: - Task 1-7:utils/thinking-parser.ts 纯函数模块 + 单元测试 - Task 8-9:chat store thinkingObservation Map 接入 SSE - Task 10:8 语言 i18n 新增 6 条 key - Task 11:MessageItem.vue 渲染折叠 UI + SCSS - Task 12:构建/测试/手动验证/推送 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): 首个闭合 <think> 标签拆分 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(thinking-parser): 覆盖多段/变体标签/大小写/空输入 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(thinking-parser): 流式 pending 与终止态降级 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): 代码块保护避免误识别伪标签 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(thinking-parser): 同名嵌套与 chunk 边界行为 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): countThinkingChars 辅助函数 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(thinking-parser): detectThinkingBoundary 边界检测 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat-store): 新增 thinkingObservation 运行时 Map Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat-store): message.delta 写入 thinking 边界 + switchSession 清理 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * i18n: 新增 thinking 块 6 条 key(8 语言) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat): MessageItem 渲染 thinking 折叠区 - 复用 tool-line 风格 chevron - 两条响应链:parse computed + duration interval - 流式+pending 强制展开 - show_reasoning 控制默认态 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(chat): 支持思考块实时流式与历史展示 - 扩展 Message 接口增加 reasoning 字段,mapHermesMessages 从 HermesMessage.reasoning 透传历史会话的思考内容。 - RunEvent 类型新增 text 字段,chat store 处理三个新 SSE 事件: reasoning.delta / thinking.delta / reasoning.available。 - 思考时长观察:仅在 reasoning.delta 累积时记录起始时间戳, reasoning.available 时记录结束时间戳;无实时 delta 时不显示时长。 - MessageItem 采用双源渲染(reasoning 字段优先,<think> 标签作 fallback),duration > 0 才展示耗时。 - 新增 3 条单测覆盖三个 SSE 事件;测试 32/32 通过。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(chat): reasoning 块不再短暂展示正文 根因:上游 hermes-agent run_agent.py:11275 在每次模型响应结束时用 assistant content[:500] 作为 reasoning.available 的 preview 负载, 致使 Web UI 把正文写入 last.reasoning,思考块短暂显示正文直到会话 轮询/刷新从 session DB 读回正确的 reasoning 字段。 修复: - reasoning.available 事件不再写入 last.reasoning,仅用于标记计时 结束(noteReasoningEnd);真实推理由 reasoning.delta 或会话 DB 提供 - 新增 scrubBuggyReasoningInCache:hydration 时治愈 localStorage 里 已被污染的 assistant 消息(reasoning == content 或前缀时丢弃) - 两个 cache 加载入口(loadSessions / switchSession)均接入 scrubber 测试:新增 4 条单测,全套 280/280 通过。 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
662 lines
26 KiB
TypeScript
662 lines
26 KiB
TypeScript
export default {
|
||
// Login
|
||
login: {
|
||
title: 'Hermes Web UI',
|
||
description: 'Entrez votre jeton d\'acces pour continuer. Retrouvez-le dans les journaux de demarrage du serveur.',
|
||
placeholder: 'Jeton d\'acces',
|
||
submit: 'Connexion',
|
||
tokenRequired: 'Veuillez entrer votre jeton d\'acces',
|
||
invalidToken: 'Jeton invalide',
|
||
connectionFailed: 'Impossible de se connecter au serveur',
|
||
passwordLogin: 'Mot de passe',
|
||
tokenLogin: 'Jeton',
|
||
usernamePlaceholder: 'Nom d\'utilisateur',
|
||
passwordPlaceholder: 'Mot de passe',
|
||
credentialsRequired: 'Veuillez entrer le nom d\'utilisateur et le mot de passe',
|
||
invalidCredentials: 'Nom d\'utilisateur ou mot de passe incorrect',
|
||
passwordMismatch: 'Les mots de passe ne correspondent pas',
|
||
passwordTooShort: 'Le mot de passe doit contenir au moins 6 caracteres',
|
||
setupSuccess: 'Login par mot de passe configure avec succes',
|
||
passwordChanged: 'Mot de passe change avec succes',
|
||
passwordRemoved: 'Login par mot de passe supprime',
|
||
setupPassword: 'Configurer le login par mot de passe',
|
||
changePassword: 'Changer le mot de passe',
|
||
changeUsername: 'Changer le nom d\'utilisateur',
|
||
removePasswordLogin: 'Supprimer',
|
||
username: 'Nom d\'utilisateur',
|
||
currentPassword: 'Mot de passe actuel',
|
||
newPassword: 'Nouveau mot de passe',
|
||
confirmPassword: 'Confirmer le mot de passe',
|
||
newUsername: 'Nouveau nom d\'utilisateur',
|
||
usernameChanged: 'Nom d\'utilisateur change avec succes',
|
||
usernameTooShort: 'Le nom d\'utilisateur doit contenir au moins 2 caracteres',
|
||
setupDescription: 'Configurez un nom d\'utilisateur et un mot de passe pour un login rapide. Le jeton d\'acces reste disponible.',
|
||
removeConfirm: 'Voulez-vous vraiment supprimer le login par mot de passe? Vous devrez utiliser le jeton d\'acces.',
|
||
passwordLoginNotConfigured: 'Login par mot de passe non configure',
|
||
passwordLoginConfigured: 'Login par mot de passe active ({username})',
|
||
},
|
||
|
||
// Common
|
||
common: {
|
||
loading: 'Chargement...',
|
||
cancel: 'Annuler',
|
||
retry: 'Réessayer',
|
||
delete: 'Supprimer',
|
||
edit: 'Modifier',
|
||
save: 'Enregistrer',
|
||
saved: 'Enregistre',
|
||
update: 'Mettre a jour',
|
||
create: 'Creer',
|
||
saveFailed: 'Echec de l\'enregistrement',
|
||
deleteFailed: 'Echec de la suppression',
|
||
ok: 'OK',
|
||
copied: 'Copie',
|
||
copy: 'Copier',
|
||
noData: 'Aucune donnee',
|
||
fetch: 'Recuperer',
|
||
add: 'Ajouter',
|
||
enable: 'Activer',
|
||
disable: 'Desactiver',
|
||
configured: 'Configure',
|
||
notConfigured: 'Non configure',
|
||
confirm: 'Confirmer',
|
||
expand: 'Developper',
|
||
collapse: 'Reduire',
|
||
},
|
||
|
||
// Sidebar
|
||
sidebar: {
|
||
chat: 'Discussion',
|
||
jobs: 'Taches planifiees',
|
||
models: 'Modeles',
|
||
profiles: 'Profils',
|
||
skills: 'Competences',
|
||
memory: 'Memoire',
|
||
logs: 'Journaux',
|
||
usage: 'Utilisation',
|
||
channels: 'Canaux',
|
||
terminal: 'Terminal',
|
||
files: 'Fichiers',
|
||
groupChat: 'Chat de groupe',
|
||
groupConversation: 'Conversation',
|
||
settings: 'Parametres',
|
||
connected: 'Connecte',
|
||
disconnected: 'Deconnecte',
|
||
updateTip: 'Executez "hermes-web-ui update" dans le terminal pour mettre a jour',
|
||
updateVersion: 'Mettre a jour vers v{version}',
|
||
updating: 'Mise a jour...',
|
||
updateSuccess: 'Mise a jour terminee, veuillez redemarrer le serveur',
|
||
updateFailed: 'Echec de la mise a jour',
|
||
logout: 'Deconnexion',
|
||
nodeVersionWarning: 'Node.js v{version} detecte. Veuillez passer a la version 23 ou ulterieure.',
|
||
changelog: 'Journal des modifications',
|
||
noChangelog: 'Aucun journal disponible',
|
||
},
|
||
|
||
// Chat
|
||
chat: {
|
||
contextRemaining: 'restant',
|
||
emptyState: 'Demarrer une conversation avec Hermes Agent',
|
||
inputPlaceholder: 'Tapez un message... (Entree pour envoyer, Shift+Entree pour un saut de ligne)',
|
||
attachFiles: 'Joindre des fichiers',
|
||
stop: 'Arreter',
|
||
send: 'Envoyer',
|
||
contextUsed: 'Contexte utilise :',
|
||
sessions: 'Sessions',
|
||
noSessions: 'Aucune session',
|
||
newChat: 'Nouvelle discussion',
|
||
deleteSession: 'Supprimer cette session ?',
|
||
sessionDeleted: 'Session supprimee',
|
||
rename: 'Renommer',
|
||
pin: 'Épingler',
|
||
unpin: 'Désépingler',
|
||
pinned: 'Épinglés',
|
||
chatMode: 'Chat',
|
||
liveMode: 'Direct',
|
||
liveSessions: 'Sessions en direct',
|
||
recentBadge: 'Récent',
|
||
linkedSessions: '{count} sessions liées',
|
||
noVisibleMessages: 'Aucun message visible par l’humain.',
|
||
monitorRoleUser: 'Utilisateur',
|
||
monitorRoleAssistant: 'Assistant',
|
||
copySessionId: "Copier l'ID de session",
|
||
renamed: 'Renomme',
|
||
renameFailed: 'Echec du renommage',
|
||
renameSession: 'Renommer la session',
|
||
enterNewTitle: 'Entrez un nouveau titre',
|
||
other: 'Autre',
|
||
runFailed: 'Echec de l\'execution',
|
||
error: 'Erreur',
|
||
tool: 'Outil',
|
||
arguments: 'Arguments',
|
||
result: 'Resultat',
|
||
truncated: '... (tronque)',
|
||
thinkingLabel: 'Raisonnement',
|
||
thinkingInProgress: 'En réflexion…',
|
||
thinkingShow: 'Afficher le raisonnement',
|
||
thinkingHide: 'Masquer le raisonnement',
|
||
thinkingDuration: 'Observé {duration}',
|
||
thinkingChars: '{count} caractères',
|
||
},
|
||
|
||
// Jobs
|
||
jobs: {
|
||
title: 'Taches planifiees',
|
||
createJob: 'Creer une tache',
|
||
editJob: 'Modifier la tache',
|
||
noJobs: 'Aucune tache planifiee. Creez-en une pour commencer.',
|
||
name: 'Nom',
|
||
namePlaceholder: 'Nom de la tache',
|
||
schedule: 'Planification (expression Cron)',
|
||
schedulePlaceholder: 'ex. 0 9 * * *',
|
||
quickPresets: 'Presets rapides',
|
||
selectPreset: 'Selectionner un preset...',
|
||
presetEveryMinute: 'Chaque minute',
|
||
presetEvery5Min: 'Toutes les 5 minutes',
|
||
presetEveryHour: 'Chaque heure',
|
||
presetEveryDay: 'Tous les jours a 00:00',
|
||
presetEveryDay9: 'Tous les jours a 09:00',
|
||
presetEveryMonday: 'Chaque lundi a 09:00',
|
||
presetEveryMonth: 'Le 1er de chaque mois a 09:00',
|
||
prompt: 'Invite',
|
||
promptPlaceholder: 'L\'invite a executer',
|
||
deliverTarget: 'Cible de livraison',
|
||
origin: 'Origine',
|
||
local: 'Local',
|
||
repeatCount: 'Nombre de repetitions (facultatif)',
|
||
repeatPlaceholder: 'Laisser vide pour infini',
|
||
jobCreated: 'Tache creee',
|
||
jobUpdated: 'Tache mise a jour',
|
||
nameRequired: 'Le nom est requis',
|
||
scheduleRequired: 'La planification est requise',
|
||
loadFailed: 'Echec du chargement de la tache',
|
||
jobPaused: 'Tache en pause',
|
||
jobResumed: 'Tache reprise',
|
||
jobTriggered: 'Tache declenchee',
|
||
jobDeleted: 'Tache supprimee',
|
||
status: {
|
||
running: 'En cours',
|
||
paused: 'En pause',
|
||
disabled: 'Desactivee',
|
||
scheduled: 'Planifiee',
|
||
},
|
||
info: {
|
||
schedule: 'Planification',
|
||
lastRun: 'Derniere execution',
|
||
nextRun: 'Prochaine execution',
|
||
deliver: 'Livraison',
|
||
repeat: 'Repetition',
|
||
},
|
||
action: {
|
||
pause: 'Pause',
|
||
pauseJob: 'Mettre en pause',
|
||
resume: 'Reprendre',
|
||
resumeJob: 'Reprendre la tache',
|
||
runNow: 'Executer maintenant',
|
||
triggerImmediately: 'Declencher immediatement',
|
||
},
|
||
},
|
||
|
||
// Skills
|
||
skills: {
|
||
title: 'Competences',
|
||
searchPlaceholder: 'Rechercher des competences...',
|
||
noMatch: 'Aucune competence ne correspond a votre recherche',
|
||
noSkills: 'Aucune competence trouvee',
|
||
backTo: 'Retour a',
|
||
attachedFiles: 'Fichiers joints',
|
||
loadFailed: 'Echec du chargement de la competence',
|
||
fileLoadFailed: 'Echec du chargement du fichier',
|
||
toggleFailed: 'Echec de l\'activation/desactivation de la competence',
|
||
},
|
||
|
||
// Memory
|
||
memory: {
|
||
title: 'Memoire',
|
||
refresh: 'Actualiser',
|
||
loadFailed: 'Echec du chargement de la memoire',
|
||
myNotes: 'Mes notes',
|
||
noNotes: 'Aucune note pour l\'instant.',
|
||
notesPlaceholder: 'Ecrivez vos notes...',
|
||
userProfile: 'Profil utilisateur',
|
||
noProfile: 'Aucun profil pour l\'instant.',
|
||
profilePlaceholder: 'Ecrivez votre profil...',
|
||
soul: 'Ame',
|
||
noSoul: 'Aucune configuration d\'ame pour l\'instant.',
|
||
soulPlaceholder: 'Ecrivez la configuration de l\'ame...',
|
||
},
|
||
|
||
// Models
|
||
models: {
|
||
title: 'Modeles',
|
||
addProvider: 'Ajouter un fournisseur',
|
||
providerType: 'Type de fournisseur',
|
||
preset: 'Preset',
|
||
custom: 'Personnalise',
|
||
selectProvider: 'Selectionner un fournisseur',
|
||
chooseProvider: 'Choisir un fournisseur...',
|
||
name: 'Nom',
|
||
autoGeneratedName: 'Genere automatiquement a partir de l\'URL de base',
|
||
baseUrl: 'URL de base',
|
||
baseUrlPlaceholder: 'ex. https://api.example.com/v1',
|
||
apiKey: 'Cle API',
|
||
apiKeyPlaceholder: 'sk-...',
|
||
defaultModel: 'Modele par defaut',
|
||
selectOrInput: 'Sélectionner ou saisir un modèle...',
|
||
selectModel: 'Selectionner un modele...',
|
||
providerAdded: 'Fournisseur ajoute',
|
||
providerDeleted: 'Fournisseur supprime',
|
||
deleteProvider: 'Supprimer le fournisseur',
|
||
deleteConfirm: 'Etes-vous sur de vouloir supprimer "{name}" ?',
|
||
codexLoginTitle: 'Connexion OpenAI Codex',
|
||
codexWaiting: 'Entrez ce code sur la page d\'autorisation pour vous connecter :',
|
||
codexCopyCode: 'Code copié',
|
||
codexOpenLink: 'Ouvrir la page d\'autorisation',
|
||
codexApproved: 'Connexion réussie',
|
||
codexExpired: 'L\'autorisation a expiré. Veuillez réessayer.',
|
||
nousLoginTitle: 'Connexion Nous Portal',
|
||
nousWaiting: 'Entrez ce code sur la page d\'autorisation:',
|
||
nousCopyCode: 'Code copié',
|
||
nousOpenLink: 'Ouvrir la page d\'autorisation',
|
||
nousApproved: 'Connexion réussie',
|
||
nousDenied: 'Autorisation refusée',
|
||
nousExpired: 'Autorisation expirée',
|
||
customBadge: 'PERSONNALISÉ',
|
||
customModelPlaceholder: 'Nom du modèle personnalisé',
|
||
customModelHint: 'Entrée pour charger',
|
||
noProviders: 'Aucun fournisseur trouve. Ajoutez un fournisseur personnalise pour commencer.',
|
||
builtIn: 'Integre',
|
||
customType: 'Personnalise',
|
||
provider: 'Fournisseur',
|
||
contextLength: 'Longueur du contexte',
|
||
contextLengthPlaceholder: 'ex. 200000 (facultatif)',
|
||
local: 'Local ({host})',
|
||
selectProviderRequired: 'Veuillez selectionner un fournisseur',
|
||
baseUrlRequired: 'L\'URL de base est requise',
|
||
apiKeyRequired: 'La cle API est requise',
|
||
modelRequired: 'Le modele par defaut est requis',
|
||
enterBaseUrl: 'Veuillez d\'abord entrer l\'URL de base',
|
||
unexpectedFormat: 'Format de reponse inattendu',
|
||
foundModels: '{count} modeles trouves',
|
||
fetchFailed: 'Echec de la recuperation des modeles',
|
||
},
|
||
|
||
// Profiles
|
||
profiles: {
|
||
title: 'Profils',
|
||
create: 'Creer un profil',
|
||
import: 'Importer',
|
||
export: 'Exporter',
|
||
rename: 'Renommer',
|
||
delete: 'Supprimer',
|
||
switchTo: 'Passer a',
|
||
switchConfirm: 'Le passage au profil "{name}" redemarrera la passerelle. Continuer ?',
|
||
switchSuccess: 'Profil "{name}" actif',
|
||
switchFailed: 'Echec du changement de profil. La passerelle peut necessiter un redemarrage manuel.',
|
||
createSuccess: 'Profil "{name}" cree',
|
||
createFailed: 'Echec de la creation du profil',
|
||
renameSuccess: 'Profil renomme',
|
||
renameFailed: 'Echec du renommage du profil',
|
||
deleteConfirm: 'Etes-vous sur de vouloir supprimer le profil "{name}" ?',
|
||
deleteSuccess: 'Profil supprime',
|
||
deleteFailed: 'Echec de la suppression du profil',
|
||
exportSuccess: 'Profil exporte',
|
||
exportFailed: 'Echec de l\'exportation du profil',
|
||
importSuccess: 'Profil importe',
|
||
importFailed: 'Echec de l\'importation du profil',
|
||
importSelectFile: 'Selectionner un fichier d\'archive',
|
||
importInvalidFile: 'Veuillez selectionner une archive valide (.tar.gz, .tgz, .gz, .zip)',
|
||
name: 'Nom du profil',
|
||
namePlaceholder: 'Lettres, chiffres et tirets uniquement',
|
||
newName: 'Nouveau nom',
|
||
newNamePlaceholder: 'Entrez un nouveau nom',
|
||
cloneFromCurrent: 'Cloner depuis le profil actuel',
|
||
archivePath: 'Chemin de l\'archive',
|
||
archivePathPlaceholder: 'Chemin serveur du fichier d\'archive',
|
||
importName: 'Nom du profil (facultatif)',
|
||
importNamePlaceholder: 'Laisser vide pour utiliser le nom de l\'archive',
|
||
active: 'Actif',
|
||
model: 'Modele',
|
||
gateway: 'Passerelle',
|
||
alias: 'Alias',
|
||
provider: 'Fournisseur',
|
||
path: 'Chemin',
|
||
skills: 'Competences',
|
||
hasEnv: 'A un .env',
|
||
hasSoulMd: 'A un soul.md',
|
||
noProfiles: 'Aucun profil trouve. Creez-en un pour commencer.',
|
||
},
|
||
|
||
// Logs
|
||
logs: {
|
||
title: 'Journaux',
|
||
all: 'Tout',
|
||
searchPlaceholder: 'Rechercher...',
|
||
refresh: 'Actualiser',
|
||
noEntries: 'Aucune entree de journal',
|
||
},
|
||
|
||
// Settings
|
||
settings: {
|
||
title: 'Parametres',
|
||
saved: 'Enregistre',
|
||
saveFailed: 'Echec de l\'enregistrement',
|
||
tabs: {
|
||
display: 'Affichage',
|
||
account: 'Compte',
|
||
agent: 'Agent',
|
||
memory: 'Memoire',
|
||
session: 'Session',
|
||
privacy: 'Confidentialite',
|
||
apiServer: 'Serveur API',
|
||
},
|
||
display: {
|
||
streaming: 'Reponses en continu',
|
||
streamingHint: 'Afficher les reponses de l\'IA en temps reel',
|
||
compact: 'Mode compact',
|
||
compactHint: 'Reduire l\'espacement des messages',
|
||
showReasoning: 'Afficher le raisonnement',
|
||
showReasoningHint: 'Afficher le processus de reflexion du modele',
|
||
showCost: 'Afficher le cout',
|
||
showCostHint: 'Afficher l\'utilisation des jetons dans les reponses',
|
||
inlineDiffs: 'Diffs en ligne',
|
||
inlineDiffsHint: 'Afficher les changements de code en ligne',
|
||
bellOnComplete: 'Son de fin',
|
||
bellOnCompleteHint: 'Jouer un son lorsque l\'IA a termine',
|
||
busyInputMode: 'Mode saisie active',
|
||
busyInputModeHint: 'Permettre la saisie pendant le traitement de l\'IA',
|
||
theme: 'Theme',
|
||
themeHint: 'Choisir clair, sombre ou suivre les preferences du systeme',
|
||
themeLight: 'Clair',
|
||
themeDark: 'Sombre',
|
||
themeSystem: 'Systeme',
|
||
},
|
||
agent: {
|
||
maxTurns: 'Tours maximum',
|
||
maxTurnsHint: 'Nombre maximum de tours d\'interaction par conversation',
|
||
gatewayTimeout: 'Delai d\'attente de la passerelle',
|
||
gatewayTimeoutHint: 'Delai d\'attente de la requete en secondes',
|
||
restartDrainTimeout: 'Delai de vidange au redemarrage',
|
||
restartDrainTimeoutHint: 'Delai de vidange avant redemarrage en secondes',
|
||
toolEnforcement: 'Application des outils',
|
||
toolEnforcementHint: 'Controler le mode d\'execution des appels d\'outils',
|
||
auto: 'Automatique',
|
||
always: 'Toujours',
|
||
never: 'Jamais',
|
||
},
|
||
memory: {
|
||
enabled: 'Activer la memoire',
|
||
enabledHint: 'Permettre a l\'IA de memoriser le contexte de conversation',
|
||
userProfile: 'Profil utilisateur',
|
||
userProfileHint: 'Permettre a l\'IA de memoriser les preferences utilisateur',
|
||
charLimit: 'Limite de caracteres de la memoire',
|
||
charLimitHint: 'Nombre maximum de caracteres pour MEMORY.md',
|
||
userCharLimit: 'Limite de caracteres du profil utilisateur',
|
||
userCharLimitHint: 'Nombre maximum de caracteres pour USER.md',
|
||
},
|
||
session: {
|
||
mode: 'Mode de reinitialisation',
|
||
modeHint: 'Condition de declenchement de la reinitialisation de session',
|
||
modeBoth: 'Inactivite + Planifie',
|
||
modeIdle: 'Inactivite uniquement',
|
||
modeHourly: 'Planifie uniquement',
|
||
idleMinutes: 'Delai d\'inactivite',
|
||
idleMinutesHint: 'Temps d\'attente avant reinitialisation automatique (minutes)',
|
||
atHour: 'Heure de reinitialisation planifiee',
|
||
humanOnly: 'Afficher uniquement les sessions humaines',
|
||
humanOnlyHint: 'Masquer par défaut le bruit des sous-agents et du moniteur de session',
|
||
liveMonitorHumanOnly: 'Moniteur live : n’afficher que les sessions humaines',
|
||
liveMonitorHumanOnlyHint: 'Masquer par défaut le bruit des sous-agents et du moniteur de session dans le moniteur live',
|
||
atHourHint: 'Reinitialiser la session a cette heure chaque jour',
|
||
},
|
||
privacy: {
|
||
redactPii: 'Masquer les DPI',
|
||
redactPiiHint: 'Detecter et masquer automatiquement les informations sensibles (mots de passe, cles, etc.)',
|
||
},
|
||
apiServer: {
|
||
enable: 'Activer',
|
||
enableHint: 'Activer le serveur API',
|
||
host: 'Hote',
|
||
hostHint: 'Adresse d\'ecoute',
|
||
port: 'Port',
|
||
portHint: 'Port d\'ecoute',
|
||
key: 'Cle',
|
||
keyHint: 'Cle d\'acces API',
|
||
cors: 'Origines CORS',
|
||
corsHint: 'Sources cross-origin autorisees',
|
||
},
|
||
},
|
||
|
||
// Platform channel settings
|
||
platform: {
|
||
requireMention: "Exiger une mention {'@'}",
|
||
requireMentionGroup: "Exiger une mention {'@'} dans les groupes pour repondre",
|
||
requireMentionChannel: "Exiger une mention {'@'} dans les canaux pour repondre",
|
||
requireMentionRoom: "Exiger une mention {'@'} dans les salles pour repondre",
|
||
reactions: 'Reactions',
|
||
reactionsHint: 'Reagir aux messages avec des emoji',
|
||
freeResponseChats: 'Discussions en reponse libre',
|
||
freeResponseChatsHint: "ID de discussions repondant sans mention {'@'} (separes par des virgules)",
|
||
freeResponseChannels: 'Canaux en reponse libre',
|
||
freeResponseChannelsHint: "ID de canaux repondant sans mention {'@'} (separes par des virgules)",
|
||
freeResponseRooms: 'Salles en reponse libre',
|
||
freeResponseRoomsHint: "ID de salles repondant sans mention {'@'} (separes par des virgules)",
|
||
mentionPatterns: 'Motifs de mention personnalises',
|
||
mentionPatternsHint: 'Motifs de declenchement supplementaires',
|
||
autoThread: 'Fil automatique',
|
||
autoThreadHint: "Creer automatiquement des fils de reponse apres une mention {'@'}",
|
||
autoThreadHintRoom: 'Creer automatiquement des fils de reponse dans les salles',
|
||
dmMentionThreads: 'Fils de mention en MP',
|
||
dmMentionThreadsHint: 'Utiliser des fils de reponse pour les mentions en MP',
|
||
allowBots: 'Autoriser les messages de bots',
|
||
allowBotsHint: 'Repondre aux messages d\'autres bots',
|
||
allowedChannels: 'Canaux autorises',
|
||
allowedChannelsHint: 'Liste blanche des ID de canaux (separes par des virgules)',
|
||
ignoredChannels: 'Canaux ignores',
|
||
ignoredChannelsHint: 'Canaux ou le bot ne repond jamais (separes par des virgules)',
|
||
noThreadChannels: 'Canaux sans fil',
|
||
noThreadChannelsHint: 'Canaux ou le bot repond sans fil (separes par des virgules)',
|
||
botToken: 'Jeton de bot',
|
||
botTokenHint: 'Jeton de bot depuis le portail developpeur',
|
||
accessToken: 'Jeton d\'acces',
|
||
accessTokenHint: 'Jeton d\'acces Matrix',
|
||
homeserver: 'URL du serveur domestique',
|
||
homeserverHint: 'URL du serveur domestique Matrix',
|
||
appId: 'ID de l\'application',
|
||
appIdHint: 'ID de l\'application Feishu',
|
||
appSecret: 'Secret de l\'application',
|
||
appSecretHint: 'Secret de l\'application Feishu',
|
||
clientId: 'ID client',
|
||
clientIdHint: 'ID client DingTalk',
|
||
clientSecret: 'Secret client',
|
||
clientSecretHint: 'Secret client DingTalk',
|
||
botId: 'ID du bot',
|
||
botIdHint: 'ID du bot WeCom',
|
||
wecomSecretHint: 'Secret du bot WeCom',
|
||
waEnabled: 'Activer WhatsApp',
|
||
waEnabledHint: 'Activer WhatsApp via appairage par code QR',
|
||
weixinToken: 'Jeton Weixin',
|
||
weixinTokenHint: 'Depuis la connexion QR de la CLI weixin (hermes weixin)',
|
||
accountId: 'ID de compte',
|
||
accountIdHint: 'ID du compte Weixin',
|
||
qrLogin: 'Connexion QR',
|
||
qrRelogin: 'Reconnexion',
|
||
qrFetching: 'Recuperation du code QR...',
|
||
qrScanHint: 'Scannez avec WeChat pour vous connecter',
|
||
qrScanedHint: 'Scanne, veuillez confirmer sur le telephone...',
|
||
},
|
||
|
||
// Language
|
||
language: {
|
||
label: 'Langue',
|
||
zh: '中文',
|
||
en: 'English',
|
||
fr: 'Francais',
|
||
},
|
||
|
||
// Terminal
|
||
terminal: {
|
||
sessions: 'Sessions',
|
||
newTab: 'Nouveau terminal',
|
||
closeSession: 'Fermer cette session ?',
|
||
sessionExited: 'Terminee',
|
||
processExited: 'Processus termine avec le code {code}',
|
||
},
|
||
|
||
// Usage
|
||
usage: {
|
||
title: 'Statistiques d\'utilisation',
|
||
refresh: 'Actualiser',
|
||
totalTokens: 'Total des jetons',
|
||
inputTokens: 'Entree',
|
||
outputTokens: 'Sortie',
|
||
totalSessions: 'Total des sessions',
|
||
avgPerDay: '~{n}/jour en moy.',
|
||
estimatedCost: 'Cout est.',
|
||
cacheHitRate: 'Taux de succes du cache',
|
||
modelBreakdown: 'Repartition par modele',
|
||
dailyTrend: 'Utilisation quotidienne (30 derniers jours)',
|
||
date: 'Date',
|
||
tokens: 'Jetons',
|
||
cache: 'Cache',
|
||
sessions: 'Sessions',
|
||
cost: 'Cout',
|
||
noData: 'Aucune donnee d\'utilisation',
|
||
},
|
||
|
||
// Journal des modifications
|
||
changelog: {
|
||
new_0_4_5_1: 'Add group chat with multi-agent rooms, @mention routing, and typing status recovery',
|
||
new_0_4_5_2: 'Rewrite model-context config to use YAML with context_length setting',
|
||
new_0_4_5_3: 'Add gpt-5.5 to OpenAI Codex model list',
|
||
new_0_4_5_4: 'Replace jobs proxy with local controller and optimize model loading',
|
||
new_0_4_5_5: 'Add i18n support for custom model feature in ModelSelector',
|
||
new_0_4_5_6: 'Fix sidebar i18n missing key warnings',
|
||
new_0_4_5_7: 'Clear all localStorage on logout',
|
||
new_0_4_5_8: 'Add periodic log rotation to prevent unbounded log growth',
|
||
new_0_4_3_1: 'Ajouter la connexion par nom d\'utilisateur/mot de passe en plus du token',
|
||
new_0_4_3_2: 'Ajouter les parametres de compte pour gerer les identifiants',
|
||
new_0_4_3_3: 'Ajouter le bouton de deconnexion dans la barre laterale',
|
||
new_0_4_3_4: 'Ajouter un popup de journal des modifications en cliquant sur le numero de version',
|
||
new_0_4_2_1: 'Ajouter le suivi de l\'utilisation des tokens et la longueur de contexte dynamique',
|
||
new_0_4_2_2: 'Ajouter la modal de recherche de sessions',
|
||
new_0_4_2_3: 'Restaurer le systeme de chat de groupe avec Socket.IO et SQLite',
|
||
new_0_4_2_4: 'Ajouter les sessions epinglees et le moniteur en direct',
|
||
new_0_4_2_5: 'Corriger la detection des fournisseurs integres et l\'appariement des modeles',
|
||
new_0_4_1_1: 'Corriger le contournement d\'authentification et le service de fichiers SPA',
|
||
},
|
||
|
||
// Fichiers
|
||
files: {
|
||
title: 'Fichiers',
|
||
tree: 'Arborescence',
|
||
list: 'Liste des fichiers',
|
||
breadcrumbRoot: 'Accueil',
|
||
newFile: 'Nouveau fichier',
|
||
newFolder: 'Nouveau dossier',
|
||
upload: 'Telecharger',
|
||
refresh: 'Actualiser',
|
||
open: 'Ouvrir',
|
||
edit: 'Modifier',
|
||
preview: 'Apercu',
|
||
download: 'Telecharger',
|
||
copyPath: 'Copier le chemin',
|
||
rename: 'Renommer',
|
||
delete: 'Supprimer',
|
||
name: 'Nom',
|
||
size: 'Taille',
|
||
modified: 'Modifie',
|
||
actions: 'Actions',
|
||
emptyDir: 'Dossier vide',
|
||
loading: 'Chargement...',
|
||
confirmDelete: 'Voulez-vous vraiment supprimer "{name}" ?',
|
||
confirmDeleteDir: 'Voulez-vous vraiment supprimer le dossier "{name}" et tout son contenu ?',
|
||
deleteFailed: 'Echec de la suppression',
|
||
deleted: 'Supprime',
|
||
renameTo: 'Renommer en',
|
||
newFileName: 'Nom du fichier',
|
||
newFolderName: 'Nom du dossier',
|
||
created: 'Cree',
|
||
createFailed: 'Echec de la creation',
|
||
renamed: 'Renomme',
|
||
renameFailed: 'Echec du renommage',
|
||
uploadSuccess: '{count} fichier(s) televerse(s)',
|
||
uploadFailed: 'Echec du televersement',
|
||
saveFailed: 'Echec de l\'enregistrement',
|
||
saved: 'Enregistre',
|
||
unsavedChanges: 'Vous avez des modifications non enregistrees. Annuler ?',
|
||
pathCopied: 'Chemin copie',
|
||
fileTooLarge: 'Fichier trop volumineux (max 10 Mo)',
|
||
permissionDenied: 'Impossible de modifier un fichier protege',
|
||
notFound: 'Fichier ou dossier introuvable',
|
||
backendError: 'Echec de l\'operation sur le fichier',
|
||
dragDropHint: 'Glissez des fichiers ici pour les televerser',
|
||
closeEditor: 'Fermer l\'editeur',
|
||
closePreview: 'Fermer',
|
||
saveFile: 'Enregistrer',
|
||
},
|
||
|
||
// Chat de groupe
|
||
groupChat: {
|
||
title: 'Chat de groupe',
|
||
createRoom: 'Creer un salon',
|
||
joinByCode: 'Rejoindre avec un code',
|
||
roomName: 'Nom du salon',
|
||
roomNamePlaceholder: 'Entrez le nom du salon',
|
||
inviteCode: "Code d'invitation",
|
||
autoGenerate: 'Generer automatiquement',
|
||
noRooms: 'Aucun salon pour le moment',
|
||
selectOrCreate: 'Selectionnez ou creez un salon pour commencer a discuter',
|
||
agents: 'Agents',
|
||
addAgent: 'Ajouter un agent',
|
||
selectProfile: 'Selectionnez un profil',
|
||
agentAdded: 'Agent ajoute',
|
||
agentAlreadyInRoom: "L'agent est deja dans ce salon",
|
||
noAgents: 'Aucun agent dans ce salon',
|
||
members: 'Membres',
|
||
roomCreated: 'Salon cree',
|
||
roomDeleted: 'Salon supprime',
|
||
deleteRoomConfirm: 'Supprimer ce salon ?',
|
||
you: 'Vous',
|
||
joined: 'Vous avez rejoint le salon',
|
||
joinFailed: 'Echec de la connexion au salon',
|
||
inputPlaceholder: 'Tapez un message... (Entree pour envoyer)',
|
||
enterCode: "Entrez le code d'invitation",
|
||
yourName: 'Votre nom',
|
||
yourNamePlaceholder: "Entrez votre nom d'affichage",
|
||
yourDescription: 'Description (facultatif)',
|
||
yourDescriptionPlaceholder: 'Decrivez-vous aux autres...',
|
||
agentName: 'Nom de l\'agent',
|
||
agentNamePlaceholder: 'Nom personnalise (vide = nom du profil)',
|
||
agentDesc: 'Description de l\'agent',
|
||
agentDescPlaceholder: 'Decrivez le role de cet agent...',
|
||
agentReplying: 'est en train de répondre...',
|
||
agentCompressing: 'compresse le contexte...',
|
||
compressionSettings: 'Paramètres de compression',
|
||
triggerTokens: 'Seuil de tokens',
|
||
triggerTokensDesc: 'Seuil de tokens pour déclencher la compression',
|
||
maxHistoryTokens: 'Max tokens historique',
|
||
maxHistoryTokensDesc: 'Maximum de tokens pour le contexte compressé',
|
||
tailMessageCount: 'Messages récents',
|
||
tailMessageCountDesc: 'Nombre de messages récents à conserver',
|
||
compressionConfig: 'Config. compression',
|
||
compressNow: 'Comprimer maintenant',
|
||
compressingInProgress: 'Compression en cours',
|
||
compressionSaved: 'Configuration enregistrée',
|
||
},
|
||
|
||
// Telechargement
|
||
download: {
|
||
downloading: 'Telechargement...',
|
||
downloadFailed: 'Echec du telechargement',
|
||
fileNotFound: 'Fichier introuvable ou supprime',
|
||
fileTooLarge: 'Fichier trop volumineux (limite depassee)',
|
||
backendError: 'Echec de la lecture du fichier, l\'environnement distant est peut-etre indisponible',
|
||
backendTimeout: 'Delai de lecture du fichier depasse',
|
||
unsupportedBackend: 'Le backend de terminal actuel ne prend pas en charge le telechargement de fichiers',
|
||
invalidPath: 'Chemin de fichier invalide',
|
||
download: 'Telecharger',
|
||
downloadFile: 'Telecharger le fichier',
|
||
},
|
||
}
|