Files
office_translator/frontend/src/lib/i18n/messages/fr/glossaries.json
sepehr fa637abff0
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
perf+security: fix build, secure downloads, dedupe translations, refactor i18n
Frontend:
- Fix Framer Motion / motion-dom build error by pinning framer-motion to
  11.18.2 (compatible with React 19 and Next.js 16).
- Add cross-env and build:local script to bypass standalone symlink errors
  on Windows without Developer Mode.
- Allow NEXT_OUTPUT=default to disable standalone output for local builds.
- Refactor i18n: split 14,177-line src/lib/i18n.tsx into per-locale,
  per-namespace JSON files under src/lib/i18n/messages/.
- Load English synchronously; other locales loaded on demand via dynamic
  imports (reduces initial bundle, improves maintainability).
- Remove unused next-intl message files src/messages/en.json and fr.json.

Backend:
- Remove insecure legacy /api/v1/download/{filename} and /api/v1/cleanup/{filename}
  endpoints. The job-based /api/v1/download/{job_id} already enforces ownership.
- Deduplicate texts in TranslationService.translate_batch before sending them
  to the provider, reducing API calls for repeated strings.
- Pin httpx to <0.28 to fix TestClient incompatibility with starlette 0.35.1.
- Add pytest-cov and ruff dev dependencies/config.

DevOps:
- Remove hardcoded Grafana password from docker-compose.yml and
  docker-compose.monitoring.yml; use GRAFANA_PASSWORD env var.
- Change default TRANSLATION_SERVICE from ollama to google in
  docker-compose.yml (Ollama is an optional profile).
- Add GRAFANA_PASSWORD to .env.example.
- Add .coverage and frontend/pnpm-workspace.yaml to .gitignore.

Tests:
- Update API versioning tests for removed legacy endpoints.
- Add tests/test_translation_service.py for deduplication behavior.

Verified:
- pnpm run build:local passes.
- uv run pytest tests/test_providers/* tests/test_translation_service.py
  tests/test_story_3_5_api_versioning.py tests/test_download_endpoint.py
  tests/test_translators/test_excel_translator.py: provider/translator tests
  pass; one pre-existing French error-message test still fails (message is
  returned in English, unrelated to this change).
2026-06-14 16:44:18 +02:00

190 lines
13 KiB
JSON
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"glossaries.yourGlossaries": "Vos glossaires",
"glossaries.title": "Glossaires & Contexte",
"glossaries.description": "Gérez vos glossaires et instructions de contexte pour des traductions plus précises.",
"glossaries.createNew": "Créer nouveau",
"glossaries.empty": "Aucun glossaire",
"glossaries.emptyDesc": "Créez votre premier glossaire ou chargez un préréglage professionnel ci-dessus",
"glossaries.defineTerms": "termes",
"glossaries.aboutTitle": "À propos des glossaires",
"glossaries.aboutDesc": "Les glossaires vous permettent de définir des traductions exactes pour des termes spécifiques. Lors de la traduction, les termes du glossaire garantissent des traductions cohérentes et précises.",
"glossaries.aboutFormat": "Chaque terme a un mot source et des traductions en plusieurs langues. Sélectionnez un glossaire dans la page de traduction pour l'appliquer.",
"glossaries.toast.created": "Glossaire créé",
"glossaries.toast.createdDesc": "Le glossaire \\\"{name}\\\" a été créé.",
"glossaries.toast.imported": "Glossaire importé",
"glossaries.toast.importedDesc": "Le glossaire \\\"{name}\\\" a été importé.",
"glossaries.toast.updated": "Glossaire mis à jour",
"glossaries.toast.updatedDesc": "Le glossaire \\\"{name}\\\" a été mis à jour.",
"glossaries.toast.deleted": "Glossaire supprimé",
"glossaries.toast.deletedDesc": "Le glossaire a été supprimé.",
"glossaries.toast.error": "Erreur",
"glossaries.toast.errorCreate": "Impossible de créer le glossaire",
"glossaries.toast.errorImport": "Impossible d'importer le glossaire",
"glossaries.toast.errorUpdate": "Impossible de mettre à jour le glossaire",
"glossaries.toast.errorDelete": "Impossible de supprimer le glossaire",
"glossaries.dialog.title": "Nouveau glossaire",
"glossaries.dialog.description": "Créez un glossaire pour vos traductions",
"glossaries.dialog.nameLabel": "Nom",
"glossaries.dialog.namePlaceholder": "Mon glossaire",
"glossaries.dialog.tabTemplates": "Modèles",
"glossaries.dialog.tabFile": "Fichier",
"glossaries.dialog.tabManual": "Manuel",
"glossaries.dialog.cancel": "Annuler",
"glossaries.dialog.creating": "Création…",
"glossaries.dialog.importing": "Import…",
"glossaries.dialog.importBtn": "Importer",
"glossaries.dialog.selectPrompt": "Sélectionner",
"glossaries.dialog.createBtn": "Créer",
"glossaries.dialog.createEmpty": "Créer vide",
"glossaries.dialog.terms": "termes",
"glossaries.dialog.templatesDesc": "Choisissez un modèle prédéfini",
"glossaries.dialog.templatesEmpty": "Aucun modèle disponible",
"glossaries.dialog.dropTitle": "Glissez un fichier CSV ici",
"glossaries.dialog.dropOr": "ou",
"glossaries.dialog.dropFormats": "CSV, TSV, TXT",
"glossaries.termEditor.addTerm": "Ajouter un terme",
"glossaries.termEditor.maxReached": "Maximum {max} termes par glossaire atteint.",
"glossaries.dialog.formatTitle": "Format",
"glossaries.dialog.formatDesc": "source,target (un par ligne)",
"glossaries.dialog.formatNote": "Première ligne ignorée si en-tête détecté",
"glossaries.dialog.errorFormat": "Format non supporté",
"glossaries.dialog.errorSize": "Fichier trop volumineux",
"glossaries.dialog.errorEmpty": "Fichier vide",
"glossaries.dialog.errorRead": "Erreur de lecture",
"glossaries.dialog.parsing": "Analyse…",
"glossaries.dialog.termsImported": "termes importés",
"glossaries.dialog.changeFile": "Changer le fichier",
"glossaries.dialog.retry": "Réessayer",
"glossaries.edit.title": "Modifier le glossaire",
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
"glossaries.edit.nameLabel": "Nom du glossaire",
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
"glossaries.edit.sourceLang": "Langue source",
"glossaries.edit.targetLang": "Langue cible",
"glossaries.edit.termsLabel": "Termes ({count} valides)",
"glossaries.edit.exportCsv": "Exporter CSV",
"glossaries.edit.importCsv": "Importer CSV",
"glossaries.edit.cancel": "Annuler",
"glossaries.edit.saving": "Enregistrement...",
"glossaries.edit.saveChanges": "Enregistrer les modifications",
"glossaries.edit.importFailedTitle": "Échec de l'importation",
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
"glossaries.edit.importSuccessTitle": "Importation réussie",
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
"glossaries.delete.title": "Supprimer le glossaire",
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
"glossaries.delete.warning": "Cette action est irréversible",
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
"glossaries.delete.cancel": "Annuler",
"glossaries.delete.deleting": "Suppression...",
"glossaries.delete.deleteBtn": "Supprimer",
"glossaries.upgrade.title": "Glossaires",
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
"glossaries.upgrade.proLabel": "Pro",
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
"glossaries.loading": "Chargement...",
"glossaries.howItWorks.title": "Comment ces paramètres sont utilisés",
"glossaries.howItWorks.step1Title": "Configurez ici",
"glossaries.howItWorks.step1Desc": "Rédigez vos instructions de contexte ou créez/importez un glossaire de termes.",
"glossaries.howItWorks.step2Title": "Activez dans Traduire",
"glossaries.howItWorks.step2Desc": "Sur la page de traduction, dans la colonne de droite, sélectionnez votre glossaire.",
"glossaries.howItWorks.warning": "Les instructions de contexte s'appliquent automatiquement à toutes vos traductions IA une fois enregistrées. Les glossaires doivent être sélectionnés manuellement sur la page Traduire.",
"glossaries.howItWorks.goToTranslate": "Aller à Traduire",
"glossaries.status.unsaved": "Non enregistré",
"glossaries.status.active": "Actif · s'applique à toutes les traductions IA",
"glossaries.status.inactive": "Inactif",
"glossaries.instructions.whatForBold": "À quoi ça sert ?",
"glossaries.instructions.whatForDesc": "Ces instructions sont envoyées automatiquement à l'IA avant chaque traduction, sans que vous ayez besoin de faire quoi que ce soit sur la page Traduire. Utilisez-les pour guider le style, le registre ou la terminologie générale.",
"glossaries.instructions.example": "Exemple : « Vous traduisez des rapports financiers. Soyez formel, précis et conservez tous les chiffres. »",
"glossaries.instructions.charCount": "{count} caractères",
"glossaries.instructions.emptyHint": "Vide — aucune instruction envoyée à l'IA",
"glossaries.instructions.clearAll": "Tout effacer",
"glossaries.instructions.saving": "Enregistrement…",
"glossaries.instructions.saved": "Enregistré",
"glossaries.presets.whatForBold": "À quoi ça sert ?",
"glossaries.presets.whatForDesc": "Cliquer sur une carte crée un glossaire pré-rempli avec les termes spécialisés du domaine. Ce glossaire apparaîtra dans vos glossaires ci-dessous, et vous pourrez le sélectionner manuellement sur la page Traduire pour forcer des traductions de termes précis.",
"glossaries.presets.clickHint": "Cliquez sur une carte → glossaire créé → sélectionnez-le dans Traduire",
"glossaries.presets.creating": "Création…",
"glossaries.presets.alreadyImported": "Déjà importé",
"glossaries.presets.it.title": "IT / Logiciel",
"glossaries.presets.it.desc": "Développement, infrastructure, DevOps",
"glossaries.presets.legal.title": "Juridique / Contrats",
"glossaries.presets.legal.desc": "Droit des affaires, contentieux",
"glossaries.presets.medical.title": "Médical / Santé",
"glossaries.presets.medical.desc": "Pharmacologie, chirurgie, diagnostic",
"glossaries.presets.finance.title": "Finance / Comptabilité",
"glossaries.presets.finance.desc": "IFRS, bilans, fiscalité",
"glossaries.presets.marketing.title": "Marketing / Publicité",
"glossaries.presets.marketing.desc": "Digital, branding, analytics",
"glossaries.presets.hr.title": "RH / Ressources Humaines",
"glossaries.presets.hr.desc": "Contrats, politiques, recrutement",
"glossaries.presets.scientific.title": "Scientifique / Recherche",
"glossaries.presets.scientific.desc": "Publications, thèses, articles",
"glossaries.presets.ecommerce.title": "E-commerce / Vente",
"glossaries.presets.ecommerce.desc": "Boutiques en ligne, catalogues, CRM",
"glossaries.grid.title": "Vos",
"glossaries.grid.titleHighlight": "glossaires",
"glossaries.grid.countWithAction": "{count} glossaire({plural}) — cliquez sur une carte pour la modifier",
"glossaries.grid.emptyAction": "Créez votre premier glossaire ou importez un preset ci-dessus",
"glossaries.grid.activeTranslation": "Traduction active :",
"glossaries.grid.goToTranslate": "Aller à Traduire pour activer",
"glossaries.badge.compatible": "Compatible",
"glossaries.badge.otherTarget": "Autre cible",
"glossaries.card.editTerms": "Modifier les termes",
"glossaries.card.created": "Créé",
"glossaries.card.term": "terme",
"glossaries.card.delete": "Supprimer",
"glossaries.grid.searchPlaceholder": "Rechercher un glossaire…",
"glossaries.grid.noResults": "Aucun résultat pour cette recherche.",
"glossaries.detail.backToList": "Retour aux glossaires",
"glossaries.detail.save": "Enregistrer",
"glossaries.detail.savedTitle": "Enregistré",
"glossaries.detail.savedDesc": "Le glossaire a été mis à jour.",
"glossaries.detail.settingsTitle": "Paramètres",
"glossaries.detail.sourceLang": "Langue source",
"glossaries.detail.targetLang": "Langue cible",
"glossaries.detail.termsTitle": "Termes",
"glossaries.detail.terms": "termes",
"glossaries.detail.searchTerms": "Filtrer…",
"glossaries.detail.noTerms": "Aucun terme pour l'instant.",
"glossaries.detail.addFirstTerm": "Ajouter le premier terme",
"glossaries.detail.addTerm": "Ajouter un terme",
"glossaries.detail.maxReached": "Limite maximale atteinte",
"glossaries.detail.source": "Source",
"glossaries.detail.target": "Cible",
"glossaries.detail.sourcePlaceholder": "terme source",
"glossaries.detail.targetPlaceholder": "terme cible",
"glossaries.detail.csvTitle": "CSV",
"glossaries.detail.csvDesc": "Exportez vos termes en CSV ou importez-en de nouveaux (remplace la liste actuelle).",
"glossaries.detail.export": "Exporter",
"glossaries.detail.import": "Importer",
"glossaries.detail.dangerTitle": "Zone danger",
"glossaries.detail.dangerDesc": "La suppression est définitive. Tous les termes associés seront perdus.",
"glossaries.detail.deleteGlossary": "Supprimer ce glossaire",
"glossaries.detail.confirmDelete": "Confirmer la suppression ?",
"glossaries.detail.confirm": "Confirmer",
"glossaries.detail.cancel": "Annuler",
"glossaries.detail.sourceLangNote": "'La source originale est en francais. Pour les autres langues, on lit le champ translations du terme (si disponible).'",
"glossaries.detail.sourceLocked": "fixé",
"glossaries.detail.sourceLockedNote": "Les templates ne stockent la source qu'en français. Le multilingue source est sur la roadmap.",
"glossaries.detail.targetLangNote": "Choisissez une langue pour voir les traductions correspondantes, ou « Multilingue » pour la valeur par défaut.",
"glossaries.detail.notFoundTitle": "Glossaire introuvable",
"glossaries.detail.notFoundDesc": "Ce glossaire n'existe pas ou vous n'y avez pas accès.",
"glossaries.detail.maxTermsTitle": "Limite atteinte",
"glossaries.detail.maxTermsDesc": "Maximum {max} termes par glossaire.",
"glossaries.detail.importEmptyTitle": "Fichier vide",
"glossaries.detail.importEmptyDesc": "Aucun terme détecté dans ce fichier.",
"glossaries.detail.importedTitle": "Importé",
"glossaries.detail.importedDesc": "{count} termes importés.",
"glossaries.detail.importErrorTitle": "Erreur de lecture",
"glossaries.detail.importErrorDesc": "Impossible de lire le fichier."
}