perf+security: fix build, secure downloads, dedupe translations, refactor i18n
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
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).
This commit is contained in:
111
frontend/src/lib/i18n/messages/de/dashboard.json
Normal file
111
frontend/src/lib/i18n/messages/de/dashboard.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"dashboard.nav.translate": "Übersetzen",
|
||||
"dashboard.nav.profile": "Mein Profil",
|
||||
"dashboard.nav.settings": "Einstellungen",
|
||||
"dashboard.nav.context": "Kontext",
|
||||
"dashboard.nav.services": "Dienste",
|
||||
"dashboard.nav.apiKeys": "API-Schlüssel",
|
||||
"dashboard.nav.glossaries": "Glossare",
|
||||
"dashboard.header.title": "Dashboard",
|
||||
"dashboard.header.subtitle": "Verwalten Sie Ihre Übersetzungen",
|
||||
"dashboard.header.toggleMenu": "Menü",
|
||||
"dashboard.header.profileTitle": "Mein Profil",
|
||||
"dashboard.sidebar.theme": "Design",
|
||||
"dashboard.sidebar.signOut": "Abmelden",
|
||||
"dashboard.sidebar.backHome": "Zurück zur Startseite",
|
||||
"dashboard.sidebar.upgradeToPro": "Auf Pro upgraden →",
|
||||
"dashboard.translate.pageTitle": "Dokument übersetzen",
|
||||
"dashboard.translate.pageSubtitle": "Datei importieren und Zielsprache wählen",
|
||||
"dashboard.translate.errorNotificationTitle": "Fehler",
|
||||
"dashboard.translate.dropzone.uploadAria": "Datei-Ablagebereich",
|
||||
"dashboard.translate.dropzone.title": "Datei hierher ziehen und ablegen",
|
||||
"dashboard.translate.dropzone.subtitle": "oder klicken, um auszuwählen (DOCX, XLSX, PPTX, PDF)",
|
||||
"dashboard.translate.dropzone.replaceFile": "Datei ersetzen",
|
||||
"dashboard.translate.language.source": "Ausgangssprache",
|
||||
"dashboard.translate.language.target": "Zielsprache",
|
||||
"dashboard.translate.language.loading": "Sprachen werden geladen…",
|
||||
"dashboard.translate.language.autoDetect": "Automatisch erkennen",
|
||||
"dashboard.translate.language.selectPlaceholder": "Auswählen…",
|
||||
"dashboard.translate.language.loadErrorPrefix": "Fehler beim Laden der Sprachen",
|
||||
"dashboard.translate.provider.loading": "Anbieter werden geladen…",
|
||||
"dashboard.translate.provider.noneConfigured": "Keine Anbieter konfiguriert",
|
||||
"dashboard.translate.provider.modelTitle": "Modell",
|
||||
"dashboard.translate.provider.sectionTitle": "Anbieter",
|
||||
"dashboard.translate.provider.llmDivider": "KI · Kontextbasiert",
|
||||
"dashboard.translate.provider.llmDividerPro": "KI · Kontextbasiert (Pro)",
|
||||
"dashboard.translate.provider.upgrade": "Auf Pro upgraden",
|
||||
"dashboard.translate.provider.upgradeSuffix": "um KI-Übersetzung freizuschalten",
|
||||
"dashboard.translate.trust.zeroRetention": "Keine Datenspeicherung",
|
||||
"dashboard.translate.trust.deletedAfter": "Dateien nach Verarbeitung gelöscht",
|
||||
"dashboard.translate.actions.uploading": "Wird hochgeladen…",
|
||||
"dashboard.translate.actions.translate": "Übersetzen",
|
||||
"dashboard.translate.actions.filePrefix": "Datei: ",
|
||||
"dashboard.translate.actions.cancel": "Abbrechen",
|
||||
"dashboard.translate.actions.tryAgain": "Erneut versuchen",
|
||||
"dashboard.translate.steps.uploading": "Datei wird hochgeladen…",
|
||||
"dashboard.translate.steps.starting": "Übersetzung wird gestartet…",
|
||||
"dashboard.translate.complete.title": "Übersetzung abgeschlossen!",
|
||||
"dashboard.translate.complete.descNamed": "Ihre Datei {name} wurde erfolgreich übersetzt.",
|
||||
"dashboard.translate.complete.descGeneric": "Ihre Datei wurde erfolgreich übersetzt.",
|
||||
"dashboard.translate.complete.downloading": "Wird heruntergeladen…",
|
||||
"dashboard.translate.complete.download": "Herunterladen",
|
||||
"dashboard.translate.complete.newTranslation": "Neue Übersetzung",
|
||||
"dashboard.translate.complete.toastOkTitle": "Erfolgreich",
|
||||
"dashboard.translate.complete.toastOkDesc": "{name} wurde erfolgreich heruntergeladen.",
|
||||
"dashboard.translate.complete.toastFailTitle": "Fehlgeschlagen",
|
||||
"dashboard.translate.complete.toastFailDesc": "Übersetzung fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"dashboard.translate.sourceDocument": "Quelldokument",
|
||||
"dashboard.translate.configuration": "Konfiguration",
|
||||
"dashboard.translate.translating": "Übersetzung läuft",
|
||||
"dashboard.translate.liveMonitor": "Live-Monitor",
|
||||
"dashboard.translate.summary": "Zusammenfassung",
|
||||
"dashboard.translate.engine": "Engine",
|
||||
"dashboard.translate.confidence": "Konfidenz",
|
||||
"dashboard.translate.cancel": "Abbrechen",
|
||||
"dashboard.translate.segments": "Segmente",
|
||||
"dashboard.translate.characters": "Zeichen",
|
||||
"dashboard.translate.elapsed": "Verstrichen",
|
||||
"dashboard.translate.segPerMin": "Seg/Min",
|
||||
"dashboard.translate.highQuality": "Hohe Qualität",
|
||||
"dashboard.translate.quality": "Qualität",
|
||||
"dashboard.translate.completed": "Übersetzung abgeschlossen",
|
||||
"dashboard.translate.replace": "Ersetzen",
|
||||
"dashboard.translate.pdfMode.title": "PDF-Übersetzungsmodus",
|
||||
"dashboard.translate.pdfMode.preserveLayout": "Layout beibehalten",
|
||||
"dashboard.translate.pdfMode.textOnly": "Nur Text",
|
||||
"dashboard.translate.pdfMode.preserveLayoutDesc": "Behält Bilder, Tabellen und Formatierung. Ideal für einfache PDFs.",
|
||||
"dashboard.translate.pdfMode.textOnlyDesc": "Übersetzt den gesamten Text perfekt. Saubere Ausgabe ohne Layoutprobleme.",
|
||||
"dashboard.translate.pipeline.upload": "Hochladen",
|
||||
"dashboard.translate.pipeline.analyze": "Analysieren",
|
||||
"dashboard.translate.pipeline.translate": "Übersetzung",
|
||||
"dashboard.translate.pipeline.rebuild": "Rekonstruieren",
|
||||
"dashboard.translate.pipeline.finalize": "Finalisieren",
|
||||
"dashboard.translate.progress.processingFallback": "Verarbeitung läuft…",
|
||||
"dashboard.translate.progress.connectionLost": "Verbindung verloren. Erneut versuchen…",
|
||||
"dashboard.translate.progress.failedTitle": "Übersetzung fehlgeschlagen",
|
||||
"dashboard.translate.error.unexpected": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
|
||||
"dashboard.translate.error.noResult": "Die Übersetzung hat keine Ergebnisse geliefert. Stellen Sie sicher, dass das Dokument Text enthält, und versuchen Sie es erneut oder wählen Sie eine andere Engine.",
|
||||
"dashboard.translate.error.apiKey": "Ungültiger oder fehlender API-Schlüssel. Kontaktieren Sie den Administrator zur Konfiguration.",
|
||||
"dashboard.translate.error.quota": "Nutzungslimit erreicht. Versuchen Sie es in wenigen Minuten erneut oder wählen Sie eine andere Engine.",
|
||||
"dashboard.translate.error.timeout": "Verbindung zum Übersetzungsdienst zeitüberschritten. Prüfen Sie Ihr Netzwerk und versuchen Sie es erneut.",
|
||||
"dashboard.translate.error.sessionExpired": "Sitzung abgelaufen. Klicken Sie auf Erneut versuchen, um die Übersetzung neu zu starten.",
|
||||
"dashboard.translate.error.empty": "Das Dokument scheint leer zu sein oder enthält keinen übersetzbaren Text (eingescanntes PDF?).",
|
||||
"dashboard.translate.error.unsupported": "Nicht unterstütztes Dateiformat oder beschädigte Datei.",
|
||||
"dashboard.translate.error.connection": "Verbindung verloren. Prüfen Sie Ihr Netzwerk und versuchen Sie es erneut.",
|
||||
"dashboard.translate.error.generic": "Übersetzung fehlgeschlagen: {detail}",
|
||||
"dashboard.translate.error.title": "Übersetzung fehlgeschlagen",
|
||||
"dashboard.translate.retry": "Übersetzung erneut versuchen",
|
||||
"dashboard.translate.newFile": "Neue Datei",
|
||||
"dashboard.translate.modeAI": "KI-Modus",
|
||||
"dashboard.translate.modeClassic": "Klassischer Modus",
|
||||
"dashboard.translate.glossaryLLMHint": "Glossare im KI-Modus verfügbar",
|
||||
"dashboard.translate.submitting": "Wird gesendet...",
|
||||
"dashboard.translate.submit": "Übersetzung starten",
|
||||
"dashboard.translate.noFile": "Laden Sie zuerst eine Datei hoch",
|
||||
"dashboard.translate.noTargetLang": "Wählen Sie eine Zielsprache",
|
||||
"dashboard.topbar.interfaceLabel": "Übersetzungsoberfläche",
|
||||
"dashboard.topbar.premiumAccess": "Premium-Zugang",
|
||||
"dashboard.checkoutSyncError": "Fehler beim Synchronisieren der Zahlung.",
|
||||
"dashboard.networkRefresh": "Netzwerkfehler. Bitte aktualisieren Sie die Seite.",
|
||||
"dashboard.continueToTranslate": "Weiter zur Übersetzung"
|
||||
}
|
||||
Reference in New Issue
Block a user