Files
office_translator/frontend/src/lib/i18n/messages/de/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
12 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": "Ihre Glossare",
"glossaries.title": "Glossare & Kontext",
"glossaries.description": "Verwalten Sie Ihre Glossare und Kontextanweisungen für genauere Übersetzungen.",
"glossaries.createNew": "Neu erstellen",
"glossaries.empty": "Noch keine Glossare",
"glossaries.emptyDesc": "Erstellen Sie Ihr erstes Glossar oder laden Sie eine professionelle Vorlage oben",
"glossaries.defineTerms": "Begriffe",
"glossaries.aboutTitle": "Über Glossare",
"glossaries.aboutDesc": "Glossare ermöglichen es Ihnen, exakte Übersetzungen für bestimmte Begriffe zu definieren. Bei der Übersetzung werden die Glossarbegriffe für konsistente und präzise Übersetzungen verwendet.",
"glossaries.aboutFormat": "Jeder Begriff hat ein Quellwort und Übersetzungen in mehreren Sprachen. Wählen Sie ein Glossar auf der Übersetzungsseite aus, um es anzuwenden.",
"glossaries.toast.created": "Glossar erstellt",
"glossaries.toast.createdDesc": "Das Glossar \\\"{name}\\\" wurde erstellt.",
"glossaries.toast.imported": "Glossar importiert",
"glossaries.toast.importedDesc": "Das Glossar \\\"{name}\\\" wurde importiert.",
"glossaries.toast.updated": "Glossar aktualisiert",
"glossaries.toast.updatedDesc": "Das Glossar \\\"{name}\\\" wurde aktualisiert.",
"glossaries.toast.deleted": "Glossar gelöscht",
"glossaries.toast.deletedDesc": "Das Glossar wurde gelöscht.",
"glossaries.toast.error": "Fehler",
"glossaries.toast.errorCreate": "Glossar konnte nicht erstellt werden",
"glossaries.toast.errorImport": "Glossar konnte nicht importiert werden",
"glossaries.toast.errorUpdate": "Glossar konnte nicht aktualisiert werden",
"glossaries.toast.errorDelete": "Glossar konnte nicht gelöscht werden",
"glossaries.dialog.title": "Neues Glossar",
"glossaries.dialog.description": "Erstellen Sie ein Glossar für Ihre Übersetzungen",
"glossaries.dialog.nameLabel": "Name",
"glossaries.dialog.namePlaceholder": "Mein Glossar",
"glossaries.dialog.tabTemplates": "Vorlagen",
"glossaries.dialog.tabFile": "Datei",
"glossaries.dialog.tabManual": "Manuell",
"glossaries.dialog.cancel": "Abbrechen",
"glossaries.dialog.creating": "Wird erstellt…",
"glossaries.dialog.importing": "Wird importiert…",
"glossaries.dialog.importBtn": "Importieren",
"glossaries.dialog.selectPrompt": "Auswählen",
"glossaries.dialog.createBtn": "Erstellen",
"glossaries.dialog.createEmpty": "Leer erstellen",
"glossaries.dialog.terms": "Begriffe",
"glossaries.dialog.templatesDesc": "Wählen Sie eine vordefinierte Vorlage",
"glossaries.dialog.templatesEmpty": "Keine Vorlagen verfügbar",
"glossaries.dialog.dropTitle": "CSV-Datei hierher ziehen",
"glossaries.dialog.dropOr": "oder",
"glossaries.dialog.dropFormats": "CSV, TSV, TXT",
"glossaries.termEditor.addTerm": "Begriff hinzufügen",
"glossaries.termEditor.maxReached": "Maximal {max} Begriffe pro Glossar erreicht.",
"glossaries.dialog.formatTitle": "Format",
"glossaries.dialog.formatDesc": "Quelle,Ziel (eine pro Zeile)",
"glossaries.dialog.formatNote": "Erste Zeile wird bei erkanntem Header übersprungen",
"glossaries.dialog.errorFormat": "Nicht unterstütztes Format",
"glossaries.dialog.errorSize": "Datei zu groß",
"glossaries.dialog.errorEmpty": "Leere Datei",
"glossaries.dialog.errorRead": "Lesefehler",
"glossaries.dialog.parsing": "Wird analysiert…",
"glossaries.dialog.termsImported": "Begriffe importiert",
"glossaries.dialog.changeFile": "Datei ändern",
"glossaries.dialog.retry": "Erneut versuchen",
"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": "Erstellt",
"glossaries.card.term": "Begriff",
"glossaries.card.delete": "Supprimer",
"glossaries.grid.searchPlaceholder": "Search a glossary…",
"glossaries.grid.noResults": "No results for this search.",
"glossaries.detail.backToList": "Back to glossaries",
"glossaries.detail.save": "Save",
"glossaries.detail.savedTitle": "Saved",
"glossaries.detail.savedDesc": "The glossary has been updated.",
"glossaries.detail.settingsTitle": "Settings",
"glossaries.detail.sourceLang": "Source language",
"glossaries.detail.targetLang": "Target language",
"glossaries.detail.termsTitle": "Terms",
"glossaries.detail.terms": "terms",
"glossaries.detail.searchTerms": "Filter…",
"glossaries.detail.noTerms": "No terms yet.",
"glossaries.detail.addFirstTerm": "Add the first term",
"glossaries.detail.addTerm": "Add a term",
"glossaries.detail.maxReached": "Maximum limit reached",
"glossaries.detail.source": "Source",
"glossaries.detail.target": "Target",
"glossaries.detail.sourcePlaceholder": "source term",
"glossaries.detail.targetPlaceholder": "target term",
"glossaries.detail.csvTitle": "CSV",
"glossaries.detail.csvDesc": "Export your terms as CSV or import new ones (replaces the current list).",
"glossaries.detail.export": "Export",
"glossaries.detail.import": "Import",
"glossaries.detail.dangerTitle": "Danger zone",
"glossaries.detail.dangerDesc": "Deletion is permanent. All associated terms will be lost.",
"glossaries.detail.deleteGlossary": "Delete this glossary",
"glossaries.detail.confirmDelete": "Confirm deletion?",
"glossaries.detail.confirm": "Confirm",
"glossaries.detail.cancel": "Cancel",
"glossaries.detail.sourceLangNote": "'The original source is in French. For other languages, we read the term's translations field (if available).'",
"glossaries.detail.sourceLocked": "fixed",
"glossaries.detail.sourceLockedNote": "Templates only store the source in French. Multilingual source is on the roadmap.",
"glossaries.detail.targetLangNote": "Pick a language to see the matching translations, or « Multilingual » for the default value.",
"glossaries.detail.notFoundTitle": "Glossary not found",
"glossaries.detail.notFoundDesc": "This glossary does not exist or you don't have access to it.",
"glossaries.detail.maxTermsTitle": "Limit reached",
"glossaries.detail.maxTermsDesc": "Maximum {max} terms per glossary.",
"glossaries.detail.importEmptyTitle": "Empty file",
"glossaries.detail.importEmptyDesc": "No terms detected in this file.",
"glossaries.detail.importedTitle": "Imported",
"glossaries.detail.importedDesc": "{count} terms imported.",
"glossaries.detail.importErrorTitle": "Read error",
"glossaries.detail.importErrorDesc": "Unable to read the file."
}