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:
146
frontend/src/lib/i18n/messages/nl/landing.json
Normal file
146
frontend/src/lib/i18n/messages/nl/landing.json
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"landing.nav.why": "Waarom wij?",
|
||||
"landing.nav.formats": "Formaten",
|
||||
"landing.nav.pricing": "Prijzen",
|
||||
"landing.nav.login": "Inloggen",
|
||||
"landing.nav.startFree": "Gratis starten",
|
||||
"landing.hero.tag": "Professionele Document-AI",
|
||||
"landing.hero.titleLine1": "Vertaal uw documenten.",
|
||||
"landing.hero.titleLine2": "Met perfecte opmaak.",
|
||||
"landing.hero.description": "De enige vertaler die SmartArt, grafieken, inhoudsopgaven, vormen en complexe lay-outs bewaart — precies zoals ze waren.",
|
||||
"landing.hero.ctaMain": "Gratis starten — 2 docs/maand",
|
||||
"landing.hero.ctaSec": "Aanbiedingen bekijken",
|
||||
"landing.hero.deleted": "Bestanden na 60 min. verwijderd",
|
||||
"landing.hero.noHidden": "Geen verborgen kosten",
|
||||
"landing.hero.preview": "Voorbeeld voor betaling",
|
||||
"landing.hero.formattedOk": "Opmaak OK",
|
||||
"landing.hero.aiActive": "AI-vertaling actief",
|
||||
"landing.steps.title": "Hoe werkt het?",
|
||||
"landing.steps.subtitle": "Drie stappen. Geen opmaakverlies.",
|
||||
"landing.steps.step1.num": "01",
|
||||
"landing.steps.step1.title": "Upload uw bestand",
|
||||
"landing.steps.step1.desc": "Sleep uw Excel-, Word-, PowerPoint- of PDF-document en laat het vallen.",
|
||||
"landing.steps.step2.num": "02",
|
||||
"landing.steps.step2.title": "Kies taal en engine",
|
||||
"landing.steps.step2.desc": "Selecteer de doeltaal en engine — klassiek of contextbewuste AI.",
|
||||
"landing.steps.step3.num": "03",
|
||||
"landing.steps.step3.title": "Download het resultaat",
|
||||
"landing.steps.step3.desc": "Ontvang uw vertaalde document met een opmaak identiek aan het origineel.",
|
||||
"landing.features.tag": "AI-vertaalengine",
|
||||
"landing.features.title": "Vertaling die uw vakgebied begrijpt",
|
||||
"landing.features.description": "Onze AI-modellen analyseren de context, respecteren uw terminologie en vertalen zelfs tekst in afbeeldingen.",
|
||||
"landing.features.context.title": "Branchcontext",
|
||||
"landing.features.context.desc": "Beschrijf uw vakgebied en ontvang toegespitste vertalingen, geen generieke.",
|
||||
"landing.features.glossary.title": "Brancheglossaria",
|
||||
"landing.features.glossary.desc": "Definieer uw vaktermen. CTA blijft «Luchtbehandelingsunit», nooit «Call To Action».",
|
||||
"landing.features.vision.title": "Beeldherkenning",
|
||||
"landing.features.vision.desc": "Tekst in afbeeldingen, diagrammen en grafieken wordt gedetecteerd en vertaald.",
|
||||
"landing.features.demo.source": "Bron (FR)",
|
||||
"landing.features.demo.google": "Google Translate",
|
||||
"landing.features.demo.ours": "Onze AI",
|
||||
"landing.layout.title": "Uw opmaak,",
|
||||
"landing.layout.title2": "perfect bewaard",
|
||||
"landing.layout.subtitle": "Andere vertalers breken uw lay-out. Wij niet.",
|
||||
"landing.layout.p1.title": "SmartArt en diagrammen",
|
||||
"landing.layout.p1.desc": "Organigrammen, stroomdiagrammen, hiërarchieën — alles identiek vertaald.",
|
||||
"landing.layout.p2.title": "Inhoudsopgaven",
|
||||
"landing.layout.p2.desc": "Inhoudsopgave-items, paginanummers en kruisverwijzingen correct bijgewerkt.",
|
||||
"landing.layout.p3.title": "Grafieken en diagrammen",
|
||||
"landing.layout.p3.desc": "Titels, aslabels, legendas en serienamen — alles vertaald.",
|
||||
"landing.layout.p4.title": "Vormen en tekstvakken",
|
||||
"landing.layout.p4.desc": "Rechthoeken, afgeronde blokken, callouts — overal gelokaliseerd.",
|
||||
"landing.layout.p5.title": "Kop- en voetteksten",
|
||||
"landing.layout.p5.desc": "Koppen, voetteksten en voetnoten worden nooit over het hoofd gezien.",
|
||||
"landing.layout.p6.title": "130+ talen",
|
||||
"landing.layout.p6.desc": "Google Translate, DeepL en professionele AI-engines.",
|
||||
"landing.formats.title": "Elk formaat,",
|
||||
"landing.formats.title2": "elk element",
|
||||
"landing.formats.subtitle": "Wij vertalen wat anderen missen. Uw bedrijf verdient onberispelijke documentatie.",
|
||||
"landing.formats.word.name": "Word",
|
||||
"landing.formats.word.i1": "Alinea's en koppen",
|
||||
"landing.formats.word.i2": "Tabellen en grafieken",
|
||||
"landing.formats.word.i3": "SmartArt-diagrammen",
|
||||
"landing.formats.word.i4": "Inhoudsopgave",
|
||||
"landing.formats.word.i5": "Kop- en voetteksten",
|
||||
"landing.formats.word.i6": "Vormen en tekstvakken",
|
||||
"landing.formats.word.i7": "Voet- en eindnoten",
|
||||
"landing.formats.excel.name": "Excel",
|
||||
"landing.formats.excel.i1": "Celwaarden",
|
||||
"landing.formats.excel.i2": "Werkbladnamen",
|
||||
"landing.formats.excel.i3": "Grafieken en labels",
|
||||
"landing.formats.excel.i4": "Kop- en voetteksten",
|
||||
"landing.formats.excel.i5": "Samengevoegde cellen behouden",
|
||||
"landing.formats.pptx.name": "PowerPoint",
|
||||
"landing.formats.pptx.i1": "Dia-tekst en notities",
|
||||
"landing.formats.pptx.i2": "Grafieken en diagrammen",
|
||||
"landing.formats.pptx.i3": "Vormen en tekstvakken",
|
||||
"landing.formats.pptx.i4": "Masterlay-outs",
|
||||
"landing.formats.pptx.i5": "Animaties behouden",
|
||||
"landing.formats.pdf.name": "PDF",
|
||||
"landing.formats.pdf.i1": "Tekstgebaseerde PDF's",
|
||||
"landing.formats.pdf.i2": "Lay-out behouden",
|
||||
"landing.formats.pdf.i3": "Afbeeldingen op hun plaats",
|
||||
"landing.formats.pdf.i4": "Tabellen behouden",
|
||||
"landing.formats.pdf.i5": "Output als DOCX of PDF",
|
||||
"landing.pricing.title": "Eenvoudige, eerlijke prijzen",
|
||||
"landing.pricing.subtitle": "Wat u ziet is wat u betaalt. Geen verborgen kosten.",
|
||||
"landing.pricing.monthly": "Maandelijks",
|
||||
"landing.pricing.annual": "Jaarlijks",
|
||||
"landing.pricing.bestValue": "Meest populair",
|
||||
"landing.pricing.month": "/maand",
|
||||
"landing.pricing.footer": "De getoonde prijs is de prijs die u betaalt. Geen verborgen kosten na vertaling.",
|
||||
"landing.pricing.starter.name": "Starter",
|
||||
"landing.pricing.starter.desc": "Voor particulieren en kleine projecten",
|
||||
"landing.pricing.starter.f1": "50 documenten / maand",
|
||||
"landing.pricing.starter.f2": "Tot 50 pagina's per doc",
|
||||
"landing.pricing.starter.f3": "Google Translate + DeepL",
|
||||
"landing.pricing.starter.f4": "Bestanden tot 10 MB",
|
||||
"landing.pricing.starter.cta": "Aan de slag",
|
||||
"landing.pricing.pro.name": "Pro",
|
||||
"landing.pricing.pro.desc": "Voor veeleisende professionals",
|
||||
"landing.pricing.pro.f1": "200 documenten / maand",
|
||||
"landing.pricing.pro.f2": "Tot 200 pagina's per doc",
|
||||
"landing.pricing.pro.f3": "IA-aangedreven vertaling",
|
||||
"landing.pricing.pro.f4": "Google + DeepL inbegrepen",
|
||||
"landing.pricing.pro.f5": "Eigen glossaria en prompts",
|
||||
"landing.pricing.pro.f6": "Prioriteitsondersteuning",
|
||||
"landing.pricing.pro.cta": "Probeer Pro",
|
||||
"landing.pricing.business.name": "Business",
|
||||
"landing.pricing.business.desc": "Voor teams met grote behoeften",
|
||||
"landing.pricing.business.f1": "1 000 documenten / maand",
|
||||
"landing.pricing.business.f2": "Tot 500 pagina's per doc",
|
||||
"landing.pricing.business.f3": "Premium AI (Claude)",
|
||||
"landing.pricing.business.f4": "Alle providers + API-toegang",
|
||||
"landing.pricing.business.f5": "Webhooks en automatisering",
|
||||
"landing.pricing.business.f6": "5 teamplekken",
|
||||
"landing.pricing.business.cta": "Neem contact op",
|
||||
"landing.cta.title": "Begin met vertalen in 30 seconden",
|
||||
"landing.cta.subtitle": "Geen creditcard vereist. Probeer nu gratis en geef uw meertalige documenten nieuw leven.",
|
||||
"landing.cta.button": "Gratis account aanmaken",
|
||||
"landing.cta.secure": "Beveiligd met AES-256-encryptie",
|
||||
"landing.footer.desc": "Expert in intelligente documentvertaling. Wij combineren de kunst van lay-out met de wetenschap van contextuele AI.",
|
||||
"landing.footer.product": "Product",
|
||||
"landing.footer.resources": "Bronnen",
|
||||
"landing.footer.legal": "Juridisch",
|
||||
"landing.footer.rights": "© 2026 Wordly.art — Alle rechten voorbehouden.",
|
||||
"landing.hero.contextEngine": "Vertaling gedetecteerd: Technische onderhoudsterm voor HVAC-systemen...",
|
||||
"landing.hero.liveAnalysis": "Live-analyse",
|
||||
"landing.hero.termsDetected": "termen gedetecteerd",
|
||||
"landing.steps.process": "PROCES",
|
||||
"landing.translate.newProject": "Nieuw project",
|
||||
"landing.translate.title": "Vertaal een document",
|
||||
"landing.translate.subtitle": "Importeer een bestand en kies de doeltaal",
|
||||
"landing.translate.sourceDocument": "Brondocument",
|
||||
"landing.translate.configuration": "Configuratie",
|
||||
"landing.translate.sourceLang": "Brontaal",
|
||||
"landing.translate.targetLang": "Doeltaal",
|
||||
"landing.translate.provider": "Provider",
|
||||
"landing.translate.startTranslation": "Vertaling starten",
|
||||
"landing.translate.zeroRetention": "Nul retentie",
|
||||
"landing.translate.filesDeleted": "Bestanden verwijderd na verwerking",
|
||||
"landing.translate.dropHere": "Sleep en zet hier neer",
|
||||
"landing.translate.supportedFormats": "DOCX, XLSX, PPTX of PDF bestanden ondersteund",
|
||||
"landing.translate.aiAnalysis": "Actieve AI-analyse",
|
||||
"landing.translate.processing": "Verwerking bezig",
|
||||
"landing.translate.preservingLayout": "Uw opmaak wordt behouden"
|
||||
}
|
||||
Reference in New Issue
Block a user