// French dictionary. Mirror the shape of src/lang/en.ts — // missing keys fall back to English per createI18n's fallbackLocale. const frMessages = { common: { downloadZip: "ZIP", downloadFailed: "Échec du téléchargement", save: "Enregistrer", cancel: "Annuler", loading: "Chargement...", close: "Fermer", dismiss: "Ignorer", add: "Ajouter", remove: "Supprimer", yes: "Oui", no: "Non", saving: "Enregistrement...", saved: "Enregistré", noResultsYet: "Aucun résultat pour le moment", noImageYet: "Aucune image pour le moment", sendChat: "Démarrer une nouvelle conversation", }, sessionTabBar: { newSession: "Nouvelle session", activeSessions: "{count} session active (agent en cours d'exécution) | {count} sessions actives (agent en cours d'exécution)", unreadReplies: "{count} réponse non lue | {count} réponses non lues", unreadDot: "Nouvelle réponse", origin: { scheduler: "Démarrée par le planificateur", skill: "Démarrée par une skill", bridge: "Démarrée par un bridge", }, }, chatInput: { placeholder: "Message à Claude…", send: "Envoyer", stop: "Arrêter", runningPlaceholder: "En cours… appuyez sur Entrée pour mettre en file", removeBuffered: "Supprimer le message en file", attachFile: "Joindre un fichier", fileTooLarge: "Fichier trop volumineux ({sizeMB} Mo). La limite est de 30 Mo.", unsupportedFileType: "Type de fichier non pris en charge. Acceptés : images, PDF, DOCX, XLSX, PPTX, fichiers texte.", attachImageFailed: "Échec de l'attache de l'image : {error}", stopFailed: "Échec de l'arrêt du traitement : {error}", dropHint: "Déposez le fichier pour joindre", tooManyFiles: "Vous pouvez joindre jusqu'à {max} fichiers à la fois.", removeAttachment: "Retirer {name}", attachmentFallbackName: "pièce jointe", voice: { start: "Démarrer la saisie vocale", stop: "Arrêter la saisie vocale", }, }, cspViolation: { notice: "⚠ Une vue a tenté de charger {host}, mais la politique de sécurité du contenu l'a bloqué ({directive}). Pour l'autoriser, ajoutez l'hôte à config/csp.json, uniquement si vous lui faites confiance.", dismiss: "Fermer", }, sessionHistoryPanel: { filters: { all: "Toutes", unread: "Non lues", bookmarked: "Favoris", longRunning: "Longue durée (24h+)", human: "Humain", scheduler: "Planificateur", skill: "Skill", bridge: "Bridge", }, failedToRefresh: "⚠ Échec de l'actualisation : {error}", showingLastKnown: " — affichage de la dernière liste connue.", noSessions: "Aucune session pour le moment.", noMatching: "Aucune session correspondante.", running: "En cours", noMessages: "(aucun message)", openRowAria: "Ouvrir la session : {preview}", rowMenuAria: "Actions de la session", bookmark: "Ajouter aux favoris", unbookmark: "Retirer des favoris", delete: "Supprimer", deleteConfirm: "Supprimer cette session ?\n\n{preview}\n\nCette action est irréversible.", }, notificationBell: { notifications: "Notifications", activeSection: "Actives", historySection: "Historique", noActive: "Aucune notification active", noHistory: "Aucune activité récente", clearAll: "Tout effacer", dismiss: "Ignorer", cancel: "Annuler", showMore: "Voir plus ({count})", showLess: "Réduire", openTarget: "Ouvrir", expandDetails: "Afficher les détails", }, pluginDiagnostics: { title: "Problème de configuration du plugin", hostBody: "Le plugin « {plugin} » a tenté d'enregistrer la clé {label} « {key} », mais elle est réservée par l'hôte. L'entrée du plugin a été supprimée.", intraBody: "Les plugins « {first} » et « {second} » enregistrent tous deux le {dimension} « {key} ». « {first} » l'a réclamé en premier, donc l'enregistrement de « {second} » est ignoré.", }, shadowedEnv: { title: "Le shell remplace .env", body: "Défini à la fois dans le shell et dans .env : {keys}. La valeur du shell l'emporte, donc .env est ignoré. Si vous avez modifié .env, mettez à jour ou supprimez la valeur du shell puis redémarrez.", }, optionalDeps: { // `title` générique conservé pour la compatibilité avec les // entrées d'historique persistées avant la séparation par motif ; // les nouvelles utilisent `titleNotFound` / `titleNotResponding`. title: "Dépendance optionnelle indisponible", titleNotFound: "{command} n'est pas installé", titleNotResponding: "{command} n'est pas en cours d'exécution", notFound: "{command} introuvable — les fonctionnalités associées ont été désactivées. Installez {command} et redémarrez MulmoClaude pour les activer.", notResponding: "{command} est installé mais n'est pas en cours d'exécution — les fonctionnalités associées ont été désactivées. Démarrez {command} et redémarrez MulmoClaude pour les activer.", }, billingMigration: { title: "La facturation est passée à une configuration à la demande", body: "Les collections intégrées clients, worklog, invoice et profile ont été retirées de votre tableau de bord, mais vos données sont en sécurité et intactes. Demandez à configurer le suivi des clients et du temps, puis la facturation pour les recréer ; vos enregistrements existants réapparaîtront.", }, backendOffline: { title: "Impossible de joindre le backend", body: "Le serveur MulmoClaude n'est peut-être pas démarré. Vérifiez le serveur de développement, puis réessayez.", retry: "Réessayer", }, pluginErrorBoundary: { title: "Le plugin {pkg} a planté", subtitle: "Le plugin n'a pas pu être affiché. L'erreur a été consignée dans la console.", showDetails: "Afficher les détails", hideDetails: "Masquer les détails", retry: "Réessayer", }, remoteHostOffline: { title: "Hôte distant déconnecté", body: "Votre téléphone ne pourra pas envoyer vers cet appareil tant que vous n'êtes pas reconnecté.", reconnect: "Se reconnecter", }, remoteHost: { title: "Hôte distant", online: "Hôte distant en ligne", offline: "Hôte distant hors ligne", uid: "uid {uid}", signIn: "Se connecter avec Google", connecting: "Connexion…", disconnect: "Déconnecter", disconnecting: "Déconnexion…", noToken: "La connexion Google n'a renvoyé aucun idToken", connectFailed: "Échec de la connexion", disconnectFailed: "Échec de la déconnexion", signInFailed: "Échec de la connexion Google", statusFailed: "Échec du chargement de l'état", description: "L'accès distant permet à un appareil mobile de se connecter aux collections et aux flux de ce MulmoClaude.", howTo: "Sur votre téléphone, ouvrez {url} et connectez-vous avec le même compte Google.", customViewHint: "Pour une vue adaptée au mobile, demandez à Claude de créer une {keyword} (pas une custom view classique).", qrHint: "Ou scannez ce code QR avec l'appareil photo de votre téléphone.", }, sidebarHeader: { newMessages: "Nouveaux messages", home: "Aller à la dernière conversation", toolCallHistory: "Historique des appels d'outils", settings: "Paramètres", settingsGeminiMissing: "Paramètres — Clé API Gemini manquante", todayJournal: "Résumé du jour", todayJournalNotFound: "Pas encore de résumé — discutez un peu et le journal en générera un.", todayJournalLoadFailed: "Échec du chargement du journal (status {status}) : {error}", copyMarkdown: "Copier la conversation en Markdown", copiedMarkdown: "Copié !", }, rightSidebar: { permalink: "Lien vers le message sélectionné", copyPermalink: "Copier le lien vers le message sélectionné", copiedPermalink: "Copié !", toggleSystemPrompt: "Basculer le system prompt", systemPrompt: "System Prompt", availableTools: "Outils disponibles", toggleToolDescription: "Basculer la description de l'outil", toolCallHistory: "Historique des appels d'outils", copyHistory: "Copier l'historique des appels d'outils", copiedHistory: "Copié !", noToolCalls: "Aucun appel d'outil pour le moment", arguments: "Arguments", error: "Erreur", result: "Résultat", running: "En cours...", mcpHint: { title: (ctx: { named: (key: "server") => string }) => `Aide à la configuration : ${ctx.named("server")}`, requiredKeys: "Clés requises", setupGuide: "Ouvrir le guide de configuration", }, }, fileTreePane: { sort: "Tri :", sortByName: "Trier par nom", name: "Nom", sortByRecent: "Trier par date de modification (plus récent en premier)", recent: "Récent", reference: "Référence", // "RO" = Read-Only. Abréviation anglaise conservée volontairement // pour tenir dans un badge compact à côté du libellé Référence. readOnlyBadge: "RO", showSystemFiles: "Afficher les fichiers système", showSystemFilesTitle: "Affiche les répertoires racine internes de l'agent (conversations/, feeds/, etc.) en plus des contenus utilisateur (data/, artifacts/, config/).", }, fileTree: { dropHint: "Déposez des fichiers ici pour les enregistrer dans ce dossier", upload: { progress: "Téléversement {done} sur {total}…", done: "{count} fichier(s) enregistré(s)", failed: "{count} fichier(s) n'ont pas pu être enregistrés", }, workspace: "(espace de travail)", recentlyChanged: "Modifiés récemment", newFileMenuItem: "Nouveau fichier", newFileInputAria: "Nom du nouveau fichier", newFilePlaceholder: { wikiPage: "slug-de-page", summary: "nom-du-résumé", document: "nom-du-document", html: "nom-de-page", story: "nom-de-histoire", }, newFileError: { empty: "Le nom de fichier ne peut pas être vide.", unsafe: "Le nom de fichier contient des caractères non valides.", exists: "Un fichier nommé {filename} existe déjà ici.", saveFailed: "Impossible de créer le fichier. Veuillez réessayer.", }, }, lockStatusPopup: { sandboxEnabledTooltip: "Sandbox activé (Docker)", noSandboxTooltip: "Pas de sandbox (Docker introuvable)", sandboxEnabledLabel: "Sandbox activé :", sandboxEnabledBody: "Docker est en cours d'exécution. L'accès au système de fichiers est isolé.", noSandboxLabel: "Pas de sandbox :", noSandboxBodyPrefix: "Claude peut accéder à tous les fichiers de votre machine. Installez", noSandboxBodySuffix: "pour activer l'isolation du système de fichiers.", dockerDesktop: "Docker Desktop", hostCredentials: "Identifiants de l'hôte attachés :", credsLoading: "chargement…", sshAgent: "Agent SSH :", forwarded: "transféré", notForwarded: "non transféré", mountedConfigs: "Configurations montées :", none: "aucune", testIsolation: "Tester l'isolation du sandbox :", }, settingsModal: { title: "Paramètres", version: "MulmoClaude v{version}", tabs: { gemini: "Clé API Gemini", tools: "Outils autorisés", mcp: "Serveurs MCP", dirs: "Répertoires", refs: "Répertoires de référence", map: "Carte", photos: "Photos", google: "Google", model: "Modèle", voice: "Voix", chatIndex: "Index du chat", journal: "Journal", notifications: "Web Push", skills: "Skills", roles: "Rôles", quit: "Quitter", }, groups: { llm: "LLM", servers: "Serveurs", workspace: "Espace de travail", notifications: "Notifications", plugins: "Plugins", management: "Gestion", server: "Serveur", }, navAriaLabel: "Sections des paramètres", googleTab: { description: "Associez votre compte Google pour que cette machine puisse appeler les API Google (Calendar en premier). Le jeton d'actualisation est stocké uniquement sur cette machine et n'est jamais envoyé à un autre serveur que Google.", statusLinked: "Associé", statusNotLinked: "Non associé", statusPending: "En attente de la fin du consentement dans le navigateur…", connect: "Associer le compte Google", unlink: "Dissocier", unlinkConfirm: "Dissocier le compte Google ? Le jeton enregistré sera révoqué et supprimé de cette machine.", clientSecretAmbiguous: "Plusieurs fichiers client_secret_*.json ont été trouvés dans ~/.secrets/. N'en conservez qu'un seul afin que le jeton enregistré reste associé au bon client OAuth.", loadError: "Échec du chargement de l'état de l'association Google.", connectError: "Échec du démarrage du flux d'autorisation Google.", unlinkError: "Échec de la dissociation du compte Google.", }, mapTab: { description: "Définit la clé API Google Maps utilisée par le plugin de carte. La clé est stockée localement et n'est envoyée qu'à Google Maps.", apiKeyLabel: "Clé API Google Maps", apiKeyPlaceholder: "AIza…", helperText: "Créez ou copiez une clé depuis {consoleLink}.", requiredApis: "APIs requises : Maps JavaScript API, Geocoding API, Places API (New), Directions API.", configured: "Configurée", notConfigured: "Non configurée", clear: "Effacer", loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, photosTab: { description: "Contrôles de confidentialité pour les photos reçues par le chat ou par un bridge connecté. Les données de localisation EXIF sont sensibles — décochez pour désactiver la capture automatique.", autoCaptureLabel: "Capturer automatiquement la localisation des photos", autoCaptureHint: "Activé : chaque image envoyée avec GPS EXIF génère un sidecar de localisation dans data/locations/. Désactivé : rien n'est capturé automatiquement ; le LLM peut toujours extraire EXIF à la demande.", statusOn: "Capture automatique ACTIVÉE", statusOff: "Capture automatique DÉSACTIVÉE", loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, quitTab: { description: "Arrête le serveur MulmoClaude qui tourne sur cette machine. Lancé depuis l'icône, il continue de tourner même si vous fermez cet onglet — c'est ici qu'on l'arrête sans terminal.", // Message function form — skips vue-i18n's message compiler so the // literal `@` in `mulmoclaude@latest` is not parsed as a linked-message // reference (the compiler throws, and the whole tab renders as nothing). restartHint: () => "Pour le relancer, double-cliquez sur l'icône MulmoClaude (ou lancez `npx mulmoclaude@latest`).", quitLabel: "Quitter MulmoClaude", confirmBody: "Le serveur s'arrête et cette page cesse de fonctionner. Tout traitement en cours est interrompu.", confirmLabel: "Quitter", stopping: "Arrêt en cours…", stoppedTitle: "MulmoClaude est arrêté", stoppedBody: "Vous pouvez fermer cet onglet. Double-cliquez sur l'icône pour le relancer.", error: "Échec de l'arrêt du serveur", }, notificationsTab: { description: "Recevez une notification push sur vos appareils enregistrés lorsqu'une tâche que vous avez lancée ici se termine — pratique quand vous posez une question, vous éloignez et voulez savoir dès que la réponse est prête.", enableLabel: "Envoyer une Web Push à la fin d'une tâche", enableHint: "Se déclenche à la fin d'une conversation que vous avez lancée ici. Les tâches planifiées et en arrière-plan ne la déclenchent pas.", remoteHostNote: "Nécessite la connexion RemoteHost (qui fournit l'authentification) et au moins un appareil enregistré. Si l'un manque, rien ne se passe.", macosRemindersLabel: "Créer un rappel macOS à la fin d'une tâche", macosRemindersHint: "Ajoute la tâche terminée à votre liste de Rappels par défaut. La synchronisation iCloud la répercute sur votre iPhone, qui délivre la notification.", macosRemindersForcedOff: "Désactivé au démarrage par --disable-macos-reminders ou DISABLE_MACOS_REMINDER_NOTIFICATIONS. Retirez l'option ou supprimez la variable d'environnement, puis redémarrez pour le contrôler ici.", statusOn: "Web Push est ACTIVÉ", statusOff: "Web Push est DÉSACTIVÉ", loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, modelTab: { description: "Contrôle l'effort de raisonnement utilisé par Claude Code à chaque tour. Laissez vide pour utiliser la valeur par défaut de Claude.", effortLabel: "Effort de raisonnement", effortUnset: "(non défini — utiliser la valeur par défaut de Claude)", helperText: "Les niveaux plus élevés autorisent plus de temps de réflexion mais augmentent la latence et la consommation de tokens.", configured: "Effort : {level}", notConfigured: "Non défini", loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, voiceTab: { description: "Dictez vos messages de chat à la voix. L'audio est transcrit localement sur la machine qui exécute MulmoClaude — rien n'est envoyé à un service externe.", requirements: "Disponible uniquement sur macOS. Nécessite le serveur whisper.cpp — compilez-le avec yarn build:whisper (voir la section Local Voice Input du README).", unsupported: "La saisie vocale nécessite macOS avec le serveur whisper.cpp installé. Elle n'est pas disponible sur cette machine.", enableLabel: "Activer la saisie vocale", enableHint: "L'activation télécharge le modèle vocal (1–3 Go) une seule fois. Un bouton micro apparaît ensuite dans la zone de saisie du chat.", modelLabel: "Modèle vocal", downloading: "Téléchargement du modèle… {percent}%", ready: "Modèle prêt", downloadError: "Échec du téléchargement du modèle.", retry: "Réessayer", loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, chatIndexTab: { description: "Titres et résumés générés par IA pour l'historique du chat. Désactivé par défaut — les sessions d'automatisation (scheduler / workers système) sont toujours ignorées même quand c'est activé ; les sessions humaines ne paient qu'un appel au résumeur à la fin de chaque tour.", modeLabel: "Modèle de l'index de chat", helperText: "Haiku est moins cher ; Sonnet donne des titres plus précis pour de longues sessions changeant de sujet.", mode: { off: "Désactivé", haiku: "Haiku", sonnet: "Sonnet", }, status: { off: "Indexation DÉSACTIVÉE", haiku: "Indexation avec Haiku", sonnet: "Indexation avec Sonnet", }, loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, journalTab: { description: "Journal quotidien automatisé — résume les sessions de chat récentes dans journal/*.md et extrait des notes de mémoire persistante. Désactivé par défaut. Les sessions d'automatisation (scheduler / workers système) sont toujours exclues, quel que soit ce réglage.", modeLabel: "Modèle du journal", helperText: "Haiku est moins cher ; Sonnet produit des résumés quotidiens et thématiques plus riches. Le passage horaire ne s'exécute que lorsque c'est activé.", mode: { off: "Désactivé", haiku: "Haiku", sonnet: "Sonnet", }, status: { off: "Journal DÉSACTIVÉ", haiku: "Journal en cours avec Haiku", sonnet: "Journal en cours avec Sonnet", }, loadError: "Échec du chargement des paramètres", saveError: "Échec de l'enregistrement", }, // Slots `` — les noms `envKey` / `envFile` sont rendus sous // forme de `` inline dans SettingsModal.vue ; les littéraux // (nom de variable et nom de fichier) restent donc non traduits. geminiRequired: "La génération d'images nécessite {envKey}. Ajoutez-le à {envFile} et redémarrez l'application.", geminiAskButton: "Demander à Claude", geminiAskMessage: "Quel est le rôle de la clé API Gemini dans cette application ?", toolNamesLabel: "Noms d'outils", invalidToolNamesPrefix: "Ceux-ci semblent non standards (préfixe attendu", invalidToolNamesSuffix: ") :", mcpToolsError: "⚠ Impossible de récupérer l'état des outils MCP : {error}. Tous les outils sont affichés quel que soit leur état d'activation.", changesHint: "Les modifications s'appliquent au prochain message. Pas besoin de redémarrer.", cannotSaveTooltip: "Impossible d'enregistrer tant que les paramètres ne sont pas chargés", saving: "Enregistrement…", loadingLabel: "Chargement…", unsavedMarker: "●", unsavedToolsConfirm: "Allowed Tools contient des modifications non enregistrées. Fermer quand même ?", unsavedMcpDraftConfirm: "Un brouillon de serveur MCP est encore ouvert. Fermer quand même ?", mcpSaveFailed: "Impossible d'enregistrer les modifications des serveurs MCP.", }, canvasViewToggle: { stackViewTooltip: "Vue empilée · cliquez pour passer à la vue simple", singleViewTooltip: "Vue simple · cliquez pour passer à la vue empilée", switchToSingle: "Passer à la vue simple", switchToStack: "Passer à la vue empilée", }, sessionHistoryToggle: { showTooltip: "Afficher le panneau d'historique des sessions à gauche", hideTooltip: "Masquer le panneau d'historique des sessions", show: "Afficher l'historique des sessions", hide: "Masquer l'historique des sessions", }, sessionHistoryExpand: { expandTooltip: "Agrandir le panneau d'historique des sessions en pleine largeur", collapseTooltip: "Réduire le panneau d'historique des sessions", expand: "Agrandir l'historique des sessions", collapse: "Réduire l'historique des sessions", }, settingsWorkspaceDirs: { explanation: "Répertoires personnalisés pour organiser les fichiers sous {dataDir} et {artifactsDir}. Claude s'en sert pour router l'enregistrement des fichiers.", noEntries: "Aucun répertoire personnalisé configuré.", addDirTitle: "Ajouter un répertoire", pathPlaceholder: "data/clients ou artifacts/rapports", descPlaceholder: "Description (ce qui va dans ce dossier)", errPathRequired: "Le chemin est obligatoire", errMustStartWith: "Doit commencer par data/ ou artifacts/", errAlreadyExists: "Existe déjà", }, settingsReferenceDirs: { explanation: "Répertoires externes que Claude peut lire mais pas modifier. En mode Docker, ils sont montés en lecture seule. Utile pour référencer des coffres Obsidian, du code de projet ou des dossiers de documents.", noEntries: "Aucun répertoire de référence configuré.", addDirTitle: "Ajouter un répertoire de référence", pathPlaceholder: "/Users/moi/ObsidianVault ou ~/Documents/notes", labelPlaceholder: "Étiquette (facultatif — par défaut le nom du dossier)", readOnlyBadge: "lecture seule", errPathRequired: "Le chemin est obligatoire", errMustBeAbsolute: "Doit être un chemin absolu ou commencer par ~/", errAlreadyExists: "Existe déjà", errLabelConflict: 'L\'étiquette "{label}" existe déjà', }, dashboard: { empty: "Aucune collection favorite pour l'instant.", emptyHint: "Épinglez une collection (★) pour la voir ici.", viewTable: "Tableau", viewCalendar: "Calendrier", viewKanban: "Kanban", viewPickerLabel: "Choisir la vue", openFull: "Double-cliquez pour ouvrir la vue complète", dragHint: "Glisser pour réorganiser", resizeHint: "Glisser pour redimensionner", }, pluginLauncher: { chat: { label: "Discussion" }, dashboard: { label: "Tableau de bord" }, automations: { label: "Actions" }, wiki: { label: "Wiki" }, collections: { label: "Collections" }, feeds: { label: "Flux" }, accounting: { label: "Comptabilité" }, files: { label: "Fichiers" }, }, shortcuts: { pin: "Épingler au lanceur", unpin: "Détacher du lanceur", zoneAriaLabel: "Raccourcis épinglés", reorder: { open: "Réorganiser les raccourcis", title: "Réorganiser", moveUp: "Monter", moveDown: "Descendre", }, }, fileContentHeader: { showRendered: "Afficher le Markdown rendu", showRaw: "Afficher la source brute", rendered: "Rendu", raw: "Source", closeFile: "Fermer le fichier", revealInOs: "Afficher dans le dossier", revealInOsFailed: "Échec de l'affichage dans le dossier", }, fileContentRenderer: { download: "ZIP", downloadZip: "Télécharger en zip autonome (ressources incluses)", downloadError: "Échec du téléchargement", selectFile: "Sélectionnez un fichier", htmlPreview: "Aperçu HTML", pdfPreview: "Aperçu PDF", parseError: "erreur d'analyse", editJson: "Modifier le JSON", jsonEditorLabel: "Éditeur JSON", invalidJson: "JSON invalide", undo: "Annuler", redo: "Rétablir", editMarp: "Modifier la source de la diapositive", marpEditorLabel: "Source des diapositives Marp", openInOs: "Ouvrir dans le système", openingInOs: "Ouverture…", openInOsFailed: "Échec de l'ouverture dans le système", }, filesView: { chatPlaceholder: "Posez une question sur ce fichier…", }, systemFiles: { schemaLabel: "Schéma", showDetails: "Afficher les détails", hideDetails: "Masquer les détails", editPolicy: { "agent-managed-but-hand-editable": "Géré par l'agent (édition manuelle OK)", "user-editable": "Éditable par l'utilisateur", "agent-managed": "Géré par l'agent", "fragile-format": "Format fragile", ephemeral: "Éphémère", }, mcp: { title: "Serveurs MCP", summary: "Serveurs externes Model Context Protocol attachés à l'agent. Ajoutez des serveurs HTTP ou stdio pour étendre les outils disponibles.", }, settings: { title: "Paramètres de l'app", summary: "Préférences de comportement éditables — clé API Gemini, outils autorisés, configuration du sandbox, etc.", }, schedulerTasks: { title: "Tâches du planificateur", summary: "Automatisations récurrentes de l'agent déclenchées selon un planning. Gérées via l'UI Automations ; ce fichier est la source sur disque.", }, schedulerOverrides: { title: "Surcharges du planificateur", summary: "Surcharges horaires / d'intervalle par tâche appliquées au-dessus du planning système. L'agent édite ce fichier quand vous demandez à changer l'horaire d'une tâche récurrente.", }, schedulerItems: { title: "File d'éléments du planificateur", summary: "Invocations planifiées prêtes à se déclencher. Géré par l'agent ; n'éditez pas à la main sans comprendre chaque champ.", }, wikiIndex: { title: "Index du wiki", summary: "Index auto-généré de toutes les pages du wiki. Régénéré à chaque édition — n'éditez pas à la main (vos changements seront écrasés).", }, wikiLog: { title: "Journal d'édition du wiki", summary: "Journal d'activité des créations et éditions de pages. Géré par l'agent et en append seulement — utile comme flux des changements récents.", }, wikiSummary: { title: "Résumé du wiki", summary: "Vue d'ensemble auto-générée du wiki — clusters thématiques, nombre de pages, activité récente. Régénérée par l'agent.", }, wikiSchema: { title: "Schéma du wiki", summary: "Spécification de format que l'agent lit pour garder les pages du wiki cohérentes. Fragile — il attend une structure précise ; préférez les éditions via l'agent.", }, memory: { title: "Mémoire", summary: "Faits distillés à votre sujet, toujours chargés comme contexte pour les nouvelles conversations. L'extracteur du journal y ajoute automatiquement ; vous pouvez aussi éditer à la main.", }, summariesIndex: { title: "Index des résumés", summary: "Index navigable reliant les résumés quotidiens et thématiques générés par le journal. Géré par l'agent ; régénéré à chaque passe.", }, rolesJson: { title: "Définition du rôle (JSON)", summary: "Configuration du rôle — choix du modèle, serveurs MCP, plugins autorisés, suggestions de requêtes. Éditable, pas besoin de redémarrage.", }, rolesMd: { title: "Description du rôle (Markdown)", summary: "Persona et system prompt du rôle, chargés comme contexte quand ce rôle est actif. Éditable ; les changements s'appliquent au prochain message.", }, journalDaily: { title: "Résumé quotidien du journal", summary: "Récapitulatif auto-généré de votre activité pour une journée, distillé par le journal à partir des sessions de chat.", }, journalTopic: { title: "Journal thématique", summary: "Notes à long terme sur un sujet spécifique, accumulées et révisées au fur et à mesure que vous en reparlez. Géré par l'agent.", }, }, settingsMcpTab: { explanation: "Ajoutez des serveurs MCP externes. Les serveurs HTTP fonctionnent dans tous les modes. Les serveurs Stdio utilisent le {npx} / {node} / {tsx} de l'image du sandbox ; lorsque Docker est activé, les chemins doivent se trouver dans l'espace de travail.", localhostRewrite: "En mode Docker, {localhost} est réécrit en {hostDockerInternal}.", noServers: "Aucun serveur MCP configuré pour le moment.", enabled: "activé", urlLabel: "URL :", commandLabel: "Commande :", dockerStdioUnsupported: "⚠ Ne s'exécutera pas lorsque le sandbox Docker est activé.", dockerStdioHostExecActive: "⚠ S'exécute sur l'hôte : ce serveur sort du sandbox Docker.", dockerStdioHostExecOptIn: "Exécuter quand même sur l'hôte (avancé). Ce serveur s'exécute hors du sandbox Docker via une passerelle HTTP locale et peut accéder à votre machine.", learnMore: "En savoir plus", addServerButton: "+ Ajouter un serveur MCP", nameLabel: "Nom", namePlaceholder: "mon-serveur", typeHttp: "HTTP", typeStdio: "Stdio (commande)", urlFieldLabel: "URL", urlPlaceholder: "https://example.com/mcp", commandFieldLabel: "Commande", argsLabel: "Arguments (un par ligne)", // Forme de fonction de message — contourne le compilateur de // messages de vue-i18n pour que le `@` littéral ne soit pas // interprété comme une référence à un message lié. argsPlaceholder: () => "-y\n@modelcontextprotocol/server-filesystem\n/workspace/path", errNoName: "Indiquez un Nom, ou saisissez une URL / des arguments dont nous pouvons le déduire.", errBadName: "Le nom doit commencer par une lettre minuscule et contenir uniquement [a-z0-9_-].", errIdExists: 'L\'identifiant de serveur "{id}" existe déjà.', errBadHttpUrl: "L'URL HTTP doit commencer par http:// ou https://", pendingEntryWarning: "Terminez ou annulez d'abord l'entrée de serveur MCP en attente.", customHeading: "Serveurs personnalisés", catalog: { heading: "Serveurs MCP préconfigurés", audience: { general: "🟢 Général", developer: "🔵 Développeur" }, risk: { low: "faible", medium: "moyen", high: "élevé" }, upstream: "📦 Source", setupGuide: "📚 Configuration", entry: { memory: { displayName: "Mémoire", description: "Permet à Claude de se souvenir du contexte entre les sessions.", }, sequentialThinking: { displayName: "Pensée séquentielle", description: "Aide Claude à aborder les problèmes complexes étape par étape.", }, context7: { displayName: "Context7 (docs de bibliothèques)", description: "Documentation à jour des bibliothèques populaires — au-delà de la date de coupure d'entraînement du modèle.", }, deepwiki: { displayName: "DeepWiki (wiki de dépôts GitHub)", description: "Posez des questions sur n'importe quel dépôt GitHub et obtenez une réponse structurée style wiki.", }, notion: { displayName: "Notion", description: "Lire et écrire dans votre espace Notion — pages, bases de données et recherche.", field: { apiKey: { label: "Jeton d'intégration Notion", help: "Créez une intégration Notion et copiez l'Internal Integration Secret. Cliquez sur 🔑 pour ouvrir la page des intégrations.", }, }, }, slack: { displayName: "Slack", description: "Lister les canaux, envoyer des messages et rechercher l'historique de votre espace Slack.", field: { botToken: { label: "Jeton bot", help: "App Slack → OAuth & Permissions → Bot User OAuth Token. Commence par xoxb-.", }, teamId: { label: "ID d'équipe / espace de travail", help: "Exécutez team.info ou consultez l'URL de l'espace — du type T01ABC23DEF.", }, }, }, googleMaps: { displayName: "Google Maps", description: "Rechercher des lieux, obtenir des itinéraires et consulter les détails d'une position.", field: { apiKey: { label: "Clé d'API Google Maps", help: "Google Cloud Console → APIs & Services → Credentials → Créer une clé d'API. Activez Places + Directions.", }, }, }, appleNative: { displayName: "Apps natives Apple (macOS)", description: "Lit et écrit Rappels, Calendrier, Notes, Mail et Plans via AppleScript. macOS uniquement — sans identifiants.", }, gmail: { displayName: "Gmail", description: "Lit, envoie et étiquette votre Gmail. Utilise un client OAuth Google que vous créez dans votre propre projet Google Cloud (sans vérification d'app).", field: { credentials: { label: "Chemin vers credentials.json", help: "Google Cloud Console → APIs & Services → Credentials → ID client OAuth (Desktop app). Téléchargez credentials.json et collez son chemin absolu.", }, }, }, googleCalendar: { displayName: "Google Agenda", description: "Lit et crée des événements Google Agenda. Même modèle BYO credentials.json que Gmail.", field: { credentials: { label: "Chemin vers credentials.json", help: "Réutilisez le même client OAuth Google Cloud que Gmail, ou créez-en un séparé pour le Calendar.", }, }, }, googleDrive: { displayName: "Google Drive", description: "Recherche et lit les fichiers Google Drive. BYO identifiants OAuth Google — le jeton est mis en cache localement à côté du fichier.", field: { credentials: { label: "Chemin vers credentials.json", help: "Google Cloud Console → APIs & Services → Credentials → ID client OAuth (Desktop app). Activez l'API Google Drive dans le même projet.", }, }, }, github: { displayName: "GitHub", description: "Lit les repos, issues, PRs et exécute des recherches avec un Personal Access Token. Limitez la portée du token — les permissions d'écriture (`repo`) laissent l'agent push sur n'importe quel repo accessible.", field: { token: { label: "Personal Access Token", help: "GitHub → Settings → Developer settings → Personal access tokens. Préférez les fine-grained tokens limités aux repos où l'agent doit agir.", }, }, }, linear: { displayName: "Linear", description: "Lit et met à jour les issues, projets et cycles Linear avec une clé d'API personnelle.", field: { apiKey: { label: "Clé d'API Linear", help: "Linear → Settings → API → Personal API keys. Cliquez sur 🔑 pour ouvrir la page et sur Create key.", }, }, }, weatherOpenMeteo: { displayName: "Météo (Open-Meteo)", description: "Prévisions météo gratuites et conditions actuelles dans le monde entier — sans clé d'API.", }, spotify: { displayName: "Spotify", description: "Recherche des morceaux, gère les playlists, contrôle la lecture. BYO app développeur Spotify — Client ID uniquement (flux PKCE, pas de client secret).", field: { clientId: { label: "Client ID", help: "Spotify Developer Dashboard → Create app, configurez la Redirect URI à http://127.0.0.1:8888/callback, copiez le Client ID. Puis exécutez une fois dans le terminal `SPOTIFY_CLIENT_ID= npx spotify-mcp@latest auth` pour vous connecter (refresh token mis en cache dans ~/.spotify-mcp/tokens.json).", }, }, }, youtubeTranscript: { displayName: "Transcription YouTube", description: "Récupère les sous-titres de n'importe quelle vidéo YouTube publique par URL. Sans identifiants.", }, }, config: { howToGet: "Comment l'obtenir", install: "Installer", errMissingRequired: "Champs obligatoires manquants : {fields}", requiredMarker: "*", requiredAria: "obligatoire", }, }, }, pluginScheduler: { previewIcon: "📅", previewAutomations: "{count} automatisation | {count} automatisations", previewMore: "+ {count} de plus…", }, pluginSchedulerTasks: { recommendedFrequencies: "Fréquences recommandées", tableTaskType: "Type de tâche", tableSuggestedSchedule: "Planification suggérée", noTasks: "Aucune tâche planifiée", runNow: "Exécuter maintenant", enable: "Activer", disable: "Désactiver", delete: "Supprimer", nextRun: "Prochaine : {time}", originSystem: "Système", originUser: "Utilisateur", originSkill: "Skill", runFailed: "Échec de l'exécution : {error}", toggleFailed: "Échec du basculement : {error}", deleteFailed: "Échec de la suppression : {error}", detailsToggle: "Afficher les détails", promptLabel: "Prompt", roleLabel: "Rôle", confirmDelete: "Supprimer la tâche « {name} » ? Cette action est irréversible.", hintNewsRss: "Récupération actualités / RSS", hintJournal: "Passage quotidien du journal", hintWiki: "Maintenance du wiki", hintMemory: "Extraction de mémoire", hintCalendar: "Synchronisation agenda / contacts", }, pluginCanvas: { undo: "Annuler", redo: "Rétablir", clear: "Effacer", styleLabel: "Style :", stylePromptWithPath: "Transforme l'image en `{path}` en une image de style {style}.", stylePromptNoPath: "Transforme mon dessin sur le canevas en une image de style {style}.", saveFailed: "Non enregistré", }, pluginWiki: { backToIndex: "Retour à l'index", pdf: "PDF", pdfFailed: "⚠ Échec du PDF", tabIndex: "Index", tabLog: "Journal", tabLint: "Lint", tabGraph: "Graphe", graphEmpty: "Aucun lien à représenter pour le moment.", linkedReferences: "Références entrantes", empty: "Le wiki est vide. Demandez au Wiki Manager d'ingérer une source.", previewMore: "+ {count} de plus…", chatPlaceholder: "Posez une question sur cette page…", emptyPage: "La page « {title} » n'existe pas encore.", emptyContent: "La page « {title} » existe mais n'a pas de contenu.", createPage: "Demander la création de cette page wiki", updatePage: "Demander la mise à jour de cette page wiki", tagFilterAll: "Toutes", noMatches: "Aucune page avec le tag #{tag}", lintChat: "Vérifier mon wiki", taskCountMismatch: "La source du wiki et le rendu diffèrent sur le nombre de tâches. La modification a été refusée pour éviter de corrompre le fichier.", metadataCreated: "Créé", metadataUpdated: "Mis à jour", metadataEditor: "Éditeur", pageEditHeader: "Édition du wiki", snapshotExpired: "Instantané expiré — affichage de la page actuelle", snapshotLoadError: "Impossible de charger l'instantané — la page existe peut-être encore. Actualisez la page.", pageDeleted: "Page supprimée", history: { tabContent: "Contenu", tabHistory: "Historique", empty: "Aucun historique — modifiez cette page et la première version apparaîtra ici.", loading: "Chargement de l'historique…", backToList: "Retour à l'historique", restoreButton: "Restaurer cette version", restoreConfirmTitle: "Restaurer cette version ?", restoreConfirmBody: "Restaurer la page vers la version du {ts} par {editor}. La page actuelle sera remplacée. L'historique existant est conservé.", restoreConfirmAction: "Restaurer", restoreConfirmCancel: "Annuler", restoreSuccessToast: "Page restaurée.", restoreFailureBanner: "Échec de la restauration : {error}", compareCurrent: "Comparer avec la page actuelle", comparePrevious: "Comparer avec la version précédente", diffNoPrevious: "Aucune version précédente à comparer.", diffNoChanges: "Aucune différence de contenu entre cette version et l'élément de comparaison.", editorBadgeUser: "Utilisateur", editorBadgeLLM: "LLM", editorBadgeSystem: "Système", hiddenLines: "{count} lignes inchangées masquées", expandHidden: "Afficher", }, }, pluginPresentForm: { fallbackTitle: "Formulaire", fieldCount: "{count} champ | {count} champs", submitted: "Envoyé", errorSummary: "Veuillez corriger les erreurs suivantes", requiredMarker: "*", selectOption: "Sélectionnez une option", charactersCount: "{current} / {max} caractères", charactersCountNoMax: "{current} caractères", submit: "Envoyer", progress: "{filled} sur {total} champs obligatoires remplis", }, pluginPresentSvg: { saveAsPng: "Télécharger en PNG", png: "PNG", saveAsPdf: "Enregistrer en PDF (ouvre la boîte de dialogue d'impression)", pdf: "PDF", untitled: "Dessin SVG", editSource: "Modifier la source SVG", cancel: "Annuler", applyChanges: "Appliquer les modifications", saving: "Enregistrement...", saveError: "⚠ Échec de l'enregistrement : {error}", exportError: "⚠ Échec de l'exportation : {error}", loadingSource: "Chargement de la source…", sourceError: "Échec du chargement de la source : {error}", }, photoLocations: { title: "Emplacements des photos", summary: "{total} capturées · {withGps} avec GPS", mapHint: "Demandez à Claude « affiche-les sur la carte » pour les tracer avec le plugin Google Map.", loading: "Chargement…", empty: "Aucun emplacement capturé pour le moment. Envoyez une photo avec balise GPS via le chat ou un bridge connecté pour commencer.", noGps: "Pas de données GPS", }, pluginManageSkills: { deleteProjectSkill: "Supprimer cette skill de projet", unstarPresetSkill: "Retirer ce préréglage des favoris — il retourne au catalogue", heading: "Skills", previewCount: "{count} skill | {count} skills", previewMore: "+{count} de plus", subheading: ({ named }: { named: (key: string) => unknown }) => `${named("count")} disponibles · cliquez pour afficher · "Run" l'invoque comme /`, emptyWithPath: "Aucune skill trouvée. Ajoutez des dossiers de skills sous {path}.", emptySkillPath: "~/.claude/skills/", selectHint: "Sélectionnez une skill à gauche pour afficher son SKILL.md.", loading: "Chargement…", fieldDescription: "Description", fieldBody: "Corps (Markdown)", emptyBody: "(corps vide)", btnEdit: "Modifier", btnDelete: "Supprimer", btnUnstar: "Retirer des favoris", errListFailed: "Échec du chargement des skills : {error}", errDetailFailed: "Échec du chargement de la skill : {error}", errSaveFailed: "Échec de l'enregistrement : {error}", errDeleteFailed: "Échec de la suppression", confirmDelete: 'Supprimer la skill "{name}" ? Cela retire ~/mulmoclaude/.claude/skills/{name}/SKILL.md.', confirmUnstar: 'Remettre "{name}" dans le catalogue ? Elle ne sera plus chargée dans le prompt, mais la copie du catalogue reste — vous pouvez la remettre en favori à tout moment.', sectionActive: "Actives", sectionCatalog: "Catalogue", sectionLegendActive: "Skills que Claude peut utiliser dès maintenant. Claude les utilise automatiquement dans le fil de la conversation, ou vous pouvez en invoquer une en saisissant son nom. {system} Système (mc- intégrée) / {project} Projet (modifiable, uniquement dans ce workspace) / {user} Utilisateur (skills dans ~/.claude/skills/).", sectionLegendCatalog: "Catalogue : skills qui deviennent Actives quand vous les marquez par {star}. Retirer {star} depuis Actives renvoie la skill au Catalogue — Claude cesse de l'utiliser (la skill n'est pas supprimée).", catalogEmpty: "Aucune skill de préréglage disponible.", catalogPresetHeading: "Préréglages", catalogStar: "Favori", catalogStarred: "Favoris", sourceUserTitle: "Skill utilisateur (~/.claude/skills/, disponible dans tous les espaces)", sourceSystemTitle: "Skill système (incluse, préfixe mc- — lecture seule, écrasée par le launcher)", sourceProjectTitle: "Skill de projet (.claude/skills/ de l'espace, espace courant uniquement)", sourcePresetTitle: "Catalogue de préréglages — cliquez sur Favori pour activer dans cet espace", errCatalogListFailed: "Échec du chargement du catalogue : {error}", errCatalogStarFailed: "Échec de l'ajout aux favoris : {error}", errCatalogPreviewFailed: "Échec du chargement de l'aperçu : {error}", catalogAddRepo: "Ajouter un dépôt de skills", catalogAddRepoTitle: "Ajouter un dépôt de skills", catalogRepoUrlLabel: "URL GitHub", catalogRepoUrlPlaceholder: "https://github.com/owner/repo", catalogRepoSubpathLabel: "Sous-chemin (facultatif)", catalogRepoSubpathPlaceholder: "skills", catalogAddRepoSubmit: "Installer", catalogAddRepoSuggestions: "Dépôts suggérés", catalogUninstallRepo: "Désinstaller le dépôt", catalogUpdateRepo: "Mettre à jour le dépôt (récupérer la dernière version)", catalogRepoOpenLink: "Ouvrir le dépôt sur GitHub (nouvel onglet)", catalogUninstallConfirm: "Désinstaller ce dépôt ? Les skills déjà mises en favori restent dans votre liste active.", catalogRepoInstalling: "Installation…", catalogRepoEmpty: "Aucune skill trouvée dans ce dépôt.", sourceExternalTitle: "Skill externe (installée depuis un dépôt GitHub — cliquez sur l'étoile pour activer)", errCatalogRepoListFailed: "Échec du chargement des dépôts installés : {error}", errCatalogRepoInstallFailed: "Échec de l'installation du dépôt : {error}", errCatalogRepoUninstallFailed: "Échec de la désinstallation du dépôt : {error}", errCatalogRepoInvalidUrl: "Saisissez une URL de dépôt GitHub.", }, pluginManageRoles: { heading: "Rôles personnalisés", roleCount: "{count} rôle | {count} rôles", addButton: "+ Ajouter", createPanel: "Créer un nouveau rôle", fieldId: "ID", fieldName: "Nom", fieldIcon: "Icône", fieldPrompt: "Prompt", fieldPlugins: "Plugins", fieldStarterQueries: "Questions de démarrage", onePerLine: "(une par ligne)", helpLink: "?", idFormatted: "({id})", missingEnv: "({env} manquant)", requiresEnv: "Nécessite {env} dans .env", collapse: "Réduire", expand: "Développer", idPlaceholder: "id-unique", creating: "Création…", create: "Créer", updating: "Mise à jour…", update: "Mettre à jour", cancel: "Annuler", delete: "Supprimer", emptyHint: 'Aucun rôle personnalisé pour le moment. Cliquez sur "+ Ajouter" ou demandez à Claude d\'en créer un.', errIdRequired: "L'ID est obligatoire.", errIdInvalid: "L'ID ne peut contenir que des lettres, des chiffres, '-' et '_'.", errNameRequired: "Le nom est obligatoire.", errIdDuplicate: "Un rôle avec l'ID '{id}' existe déjà.", errCreateFailed: "Échec de la création", errSaveFailed: "Échec de l'enregistrement", errDeleteFailed: "Échec de la suppression", errNetworkError: "Erreur réseau", errServerError: "Erreur du serveur : {status}", errRefreshFailed: "Enregistré, mais l'actualisation de la liste a échoué.", confirmDelete: "Supprimer le rôle « {name} » ? Cette action est irréversible.", }, pluginUiImage: { promptLabel: "{label} :", }, markdownMermaid: { loadFailed: "⚠ Échec du chargement de Mermaid : {error}", renderFailed: "⚠ Échec du rendu Mermaid : {error}", }, pluginTextResponse: { pdf: "PDF", pdfFailed: "⚠ Échec PDF", editContent: "Modifier le contenu texte", applyChanges: "Appliquer les modifications", copyLabel: "Copier", speakerSystem: "Système", speakerUser: "Vous", speakerAssistant: "Assistant", copiedLabel: "Copié !", cancel: "Annuler", seededByPlugin: "depuis {pkg}", seededByPluginTooltip: "Ce message a été généré par le plugin {pkg}, et non envoyé par vous.", truncatedForRender: "Ce message est exceptionnellement long ({total} caractères au total). Seule la première partie est affichée — {omitted} caractères masqués pour garder l'onglet réactif. Utilisez « Copier » pour obtenir le texte complet.", }, pluginSkill: { noDescription: "(aucune description)", }, pluginSpreadsheet: { previewUntitled: "Tableur", previewSheets: "{count} feuille | {count} feuilles", untitled: "Tableur", excel: "Excel", valuePlaceholder: "Valeur", valueOrFormulaPlaceholder: "Valeur ou formule (ex. 100 ou SUM(B2:B11))", formatPlaceholder: "Format (ex. $#,##0.00)", loading: "Chargement du tableur...", noData: "Aucune donnée de tableur disponible", editData: "Modifier les données du tableur", applyChanges: "Appliquer les modifications", dataMustBeArray: "Les données doivent être un tableau de feuilles", loadFailed: "Échec du chargement du tableur : {error}", invalidJsonAlert: "Format JSON non valide : {error}", unknownError: "Erreur inconnue", update: "Mettre à jour", stringType: "Texte", formulaType: "Formule", }, app: { startConversation: "Démarrer une conversation", thinking: "Réflexion…", }, suggestionsPanel: { suggestions: "Suggestions", skills: "Compétences", tooltip: "Suggestions et compétences", emptySuggestions: "Aucune suggestion.", emptySkills: "Aucune compétence installée.", skillsError: "Échec du chargement des compétences : {error}", sendEditHint: "cliquez pour envoyer · shift+clic pour modifier", }, settingsToolsTab: { // Rendu via avec les slots nommés `allowedTools` et // `claudeMcp` afin que les balises `` inline conservent leur // style tandis que le texte reste traduisible. explanation: "Noms d'outils supplémentaires à transmettre à Claude via {allowedTools}. Un par ligne. Utile pour les serveurs MCP intégrés à Claude Code comme Gmail / Google Agenda après authentification via {claudeMcp}.", connectorsSectionTitle: "Connecteurs actifs", connectorsEmpty: "Aucun connecteur trouvé.", connectorConnected: "Connecté", connectorDisconnected: "Non connecté", connectorsGuide: "Les connecteurs comme Slack et Gmail permettent à Claude d'accéder à vos comptes. Ajoutez ou supprimez des connecteurs depuis Claude Desktop ou configurez-les {configLink}. (Ouvre claude.ai)", connectorsConfigLinkText: "ici", }, confirmModal: { defaultTitle: "Confirmer", defaultConfirm: "Confirmer", defaultCancel: "Annuler", }, }; export default frMessages;