Translation quality & format preservation: - Word: merge adjacent same-format runs into one unit (sentence-level coherence like inline-tag handling); translate comments/balloons; dedupe textbox collection (was translated twice); RTL no longer overrides center/justify alignment; CJK/Arabic font hints (eastAsia/cs) - PPTX: chart translations now actually reach the output file (ChartPart.blob is read-only — rewrite chart XML in the saved ZIP); CJK typeface hints (a:ea) - Excel: sheet renames no longer break references — rewrite cell formulas (3D/quoted), defined names, data validations, cond. formats - PDF: bold/italic honored (hebo/heit/hebi); table cells never merge; unchanged blocks left untouched (typography preserved, fixes duplicate hyperlinks); attempted/changed stats + route gate now cover PDF; CJK font paths; scanned PDFs via Mistral OCR (detection + admin settings) Features: - formality param (formal/informal) + automatic regional-variant prompts - output_mode=bilingual docx (source above translation) - per-user translation memory on Redis (falls back to LRU), context-hashed - QA report + 0-100 confidence score in job status; L0 on by default - OpenAI-compatible providers: whole chunk in ONE numbered-JSON request (~15x fewer calls) with per-item fallback; base prompt always present (custom prompt no longer replaces translation instructions) Infra & marketing alignment: - plan-based engine gating + vision gating (closes paid-engine leak); /providers/available filtered per plan; 107 languages exposed - zh-CN/zh-TW validation fixed; libmagic disabled on Windows (native crash) - admin: Mistral OCR settings + engine status dashboard; httpx<0.28 pin (TestClient breakage); Prometheus test fixture fixed - marketing docs aligned with code (PDF+OCR, retention, engines, pricing) - security: .env.ionos/.env.production/provider_settings.json removed Tests: 1173 passed / 0 failed (6 network tests deselected: free Google endpoint temporarily blocked from this machine)
15 KiB
Audit fonctionnel — Pipeline de traduction (wordly.art)
Date : 29 août 2026 · Périmètre : backend de traduction (routes → providers → translators docx/xlsx/pptx/pdf) + benchmark concurrentiel web Méthode : lecture du code, traduction réelle de fichiers de test via le pipeline de production (provider Google, fr→en), vérification programmatique des sorties, revue de la suite pytest, recherche web concurrents/bonnes pratiques.
0. Corrections appliquées (même jour)
| # | Correction | Fichiers | Vérification |
|---|---|---|---|
| 1 | zh-CN/zh-TW acceptés : validation insensible à la casse, forme canonique propagée (validate("zh-cn") → "zh-CN", alias chinese/tw conservés) |
middleware/validation.py · routes/translate_routes.py (valeurs de retour utilisées) |
11 tests tests/test_language_validation.py ✅ |
| 2 | NameError current_user dans _run_translation_job : nouveau helper _tier_from_plan_str() (gère "PlanType.PRO" et "pro") ; les couches qualité L0/L1/L2 reçoivent enfin le bon tier |
routes/translate_routes.py |
Suite pytest ✅ |
| 3 | Crash libmagic sous Windows : import magic désactivé sur win32, dégradation magie-bytes (PDF/ZIP) ; la suite pytest ne bloque plus |
middleware/validation.py |
207 tests passent (vs hang avant) |
| 4 | OCR Mistral pour PDF scannés : détection (page quasi sans texte ET couverte ≥50 % par une image), client API services/mistral_ocr.py (chunks de 8 pages, retries, erreurs typées), chemin OCR → traduction → PDF propre ; erreur explicite sans MISTRAL_API_KEY |
services/mistral_ocr.py (nouveau) · translators/pdf_translator.py · config.py · .env.example |
12 tests tests/test_scanned_pdf_ocr.py + E2E réel (OCR mocké, traduction Google réelle) ✅ |
Total : 207 tests passent (tests/test_translators + les 2 nouveaux fichiers). La sortie OCR est un PDF re-mis en page propre : les pages scannées étant des images, la disposition d'origine ne peut pas être réécrite en place.
1. Synthèse exécutive
Le pipeline fonctionne : un document Word, Excel ou PowerPoint envoyé sur POST /api/v1/translate est traduit, mis en forme à l'identique (gras, tableaux, formules, styles) et reste téléchargeable. Les tests réels effectués passent à 14/14, et les 106 tests unitaires des traducteurs Office passent en 3,7 s.
En revanche, l'audit a confirmé 4 bugs fonctionnels dont un bloquant pour le chinois (rejeté côté API alors que l'UI le propose), 2failles qualité structurelles (glossaire ignoré par les providers non-LLM, phrase découpée en « runs » traduite morceau par morceau), et un point d'architecture fragile (jobs perdus au redémarrage). Les couches qualité L0/L1/L2 déjà développées sont désactivées par défaut et la L2 est cassée par un bug (NameError).
Le benchmark web montre que le positionnement est bon (LLM + préservation du format + prix), mais que les différenciateurs attendus en 2026 sont : PDF scannés via OCR (refusés par DeepL/Azure), sortie bilingue, mémoire de traduction persistante (le code existe mais n'est pas branché) et scores de qualité visibles.
2. Ce qui fonctionne (vérifié en conditions réelles)
Script de test : temp/audit/audit_functional.py (fichiers générés puis traduits via WordTranslator/ExcelTranslator/PowerPointTranslator + provider Google de production).
| Vérification | Résultat |
|---|---|
| DOCX — titre, paragraphes traduits | ✅ « Rapport financier annuel » → « Annual financial report » |
| DOCX — phrase coupée en 3 runs (gras au milieu) | ✅ cohérente dans ce cas, gras conservé sur le bon run |
| DOCX — nombres/monnaies préservés | ✅ « 4 500 000 euros en 2025 » intact |
| DOCX — tableau traduit | ✅ |
| XLSX — cellules, en-têtes, mois | ✅ « Janvier » → « January » |
| XLSX — formules préservées | ✅ =SUM(B3:B4) inchangée |
| PPTX — titre + puces | ✅ « Stratégie commerciale 2026 » → « Commercial strategy 2026 » |
Statistiques anti-échec (attempted/changed) |
✅ remontées au job |
Points d'architecture solides constatés : validation magic bytes + zip-bomb, protection SSRF sur file_url, quotas mensuels atomiques, progression temps réel (Redis, TTL 2h), webhooks signés par jeton par-job, watermark gratuit, RTL (ar/he/fa), notes de bas de page, SmartArt, graphiques (ré-injection ZIP), noms de feuilles Excel reécrits avec mise à jour des références.
3. Bugs confirmés
P1 — Le chinois est rejeté comme langue cible (bloquant, visible utilisateur) — ✅ CORRIGÉ
middleware/validation.py:488 met le code en minuscules (zh-cn) mais SUPPORTED_LANGUAGES contient "zh-CN"/"zh-TW" en casse mixte (:349-350) → LanguageValidator.validate("zh-CN") lève une erreur → 400 systématique. L'UI propose pourtant zh-CN/zh-TW (frontend/src/app/dashboard/translate/useTranslationConfig.ts:31-32). De plus la valeur normalisée retournée par validate() est ignorée dans routes/translate_routes.py:796,806 (alias chinese→zh-CN jamais propagés).
Fix appliqué (voir §0).
P1 — NameError: current_user : la couche qualité L2 ne fonctionne jamais — ✅ CORRIGÉ
Dans _run_translation_job (routes/translate_routes.py:1574-1575, 1592-1593, 1611), current_user n'existe pas (signature :1105-1120). L'NameError est avalé par les except : quand QUALITY_L2_ENABLED=true, le juge qualité Pro échoue silencieusement à chaque job ; les métriques record_translation_retry L0/L1 sont mortes aussi.
Fix appliqué (voir §0).
P2 — Glossaire silencieusement ignoré selon le provider
Le glossaire n'est injecté que via metadata["custom_prompt"], lu uniquement par openai/deepseek/minimax. Avec google, deepl ou google_cloud, un utilisateur Pro qui paie pour le glossaire n'a aucune application des termes — sans avertissement. build_full_prompt avec glossaire seul produit en outre un prompt système sans instruction de traduction (le prompt par défaut est remplacé : services/providers/openai_provider.py:124-125), ce qui fragilise la qualité même en LLM.
Fix : fusionner glossaire + prompt par défaut au lieu de remplacer ; refuser ou avertir quand provider non-LLM + glossary_id ; à terme, utiliser les glossaires natifs DeepL.
P2 — URL de téléchargement cassée sur /translate-batch (legacy)
routes/legacy_routes.py:304 renvoie /api/v1/download/{output_filename} alors que la route exige un id tr_* (translate_routes.py:26) → 400 INVALID_JOB_ID pour tout client de l'endpoint batch.
P2 — Crash natif Windows dans le chemin « format loss » PDF + violation de couche — ✅ CORRIGÉ
translators/pdf_translator.py:69 (_record_format_loss_metric) importe middleware.metrics au milieu de la traduction PDF. L'import déclenche middleware/__init__.py → middleware/validation.py:7 → import magic → python-magic/libmagic plante en « access violation » sous Windows (crash natif non rattrapable par le try/except du wrapper). Conséquences : (a) la suite pytest bloque indéfiniment sur tests/test_translators/test_b3_5_pdf_smart_fit.py::test_tier_3_4_limit_to_two_shrink_steps ; (b) en production Windows, tout PDF avec perte de format (overflow → placeholder [translation overflow]) risque le même sort. C'est aussi une violation de couche : translators/ ne devrait pas dépendre de middleware/.
Fix appliqué (voir §0) : import magic désactivé sous Windows + dégradation magie-bytes ; l'import paresseux middleware.metrics dans pdf_translator est désormais inoffensif. La répartition translators/→core/ reste à faire (P3).
P3 — Divers
- Échec de traduction = texte source renvoyé silencieusement (chaque provider). Le garde-fou
changed == 0(translate_routes.py:1480) ne bloque que l'échec total : un document traduit à 10 % passe (warning seul,:1490). L'utilisateur paie pour un document partiel. - Modèle OpenRouter réécrit silencieusement (
:1182-1183) : la config admindeepseek/deepseek-v3.2est forcée versgoogle/gemini-3.5-flash. max_tokens=500dans le provider legacyOpenAITranslationProvider(services/translation_service.py:1071) peut tronquer un paragraphe long.OllamaTranslationProvider.list_modelsdéfini 2 fois (l'instance method est masquée par le staticmethod,translation_service.py:649vs:693).- Provider « zai » pointe par défaut vers xAI/Grok (
translate_routes.py:1319-1329) — dénomination trompeuse. - Code mort :
WebLLMTranslationProvider,_GoogleCloudWithFallback,services/translation_cache.py(cache Redis TM — voir §5), chaîne de fallbackservices/providers/fallback.pynon branchée sur la route principale.
4. Risques qualité structurels (traduction)
- Segmentation par run (docx/pptx). Chaque
<w:r>est traduit indépendamment (translators/word_translator.py:1147-1220). Dès qu'une phrase est découpée par du gras, une couleur, une correction orthographique (rsid) ou un saut de champ, les morceaux sont traduits séparément : l'ordre des mots cible peut rendre la phrase incohérente (cas fr→en avec adjectifs). Le test est passé parce que la découpe tombait bien ; c'est fragile par construction. C'est l'écart principal face à DeepL qui traduit au niveau document/phrase. Piste : traduire au niveau paragraphe avec masquage de placeholders (<0>,<1>…) pour protéger les frontières de mise en forme, puis redistribuer — technique standard validée (cf. recherche web, « tag protection »). - Moteur par défaut = Google Translate gratuit non officiel (deep_translator, scraping). Limité à 5 000 caractères/requête, non contractuel, risque de blocage/ToS, qualité plafonnée. Acceptable en free tier, risqué comme moteur par défaut des offres payantes.
- Aucune détection de langue réelle :
source_lang=autodélégué au provider ; les prompts LLM disent « détecte la langue » (correct avec les LLM récents, approximatif avec Google). - Cohérence document : chaque segment est traduit sans contexte document (pas de contexte titre/section). Les LLM supporteraient un batch contextuel.
5. Architecture — points fragiles
- Jobs non persistés :
asyncio.create_task(translate_routes.py:959) — un redémarrage du process perd tous les jobs en cours (le statut Redis survit mais rien ne reprend). Pour un SaaS payant, prévoir une queue (Celery/Arq/RQ ou table jobs + worker) avec reprise. - Cache mémoire seulement (LRU 5 000, perdu au redémarrage). Le cache Redis avec clés sha256 + TTL 24h existe (
services/translation_cache.py) mais n'est branché nulle part en production — c'est une mémoire de traduction (TM) gratuite à activer.
6. Benchmark & recommandations produit (recherche web, août 2026)
Concurrents et standards du marché
| Acteur | Positionnement | Enseignement |
|---|---|---|
| DeepL | Référence qualité ; documents pdf/docx/pptx/xlsx/idml ; facturation API min 50 000 chars/document ; glossaires natives, formality, variantes pt-BR/pt-PT, fr-CA | Le minimum 50K chars est un pain point : un pipeline LLM peut être moins cher. Refuse les PDF scannés. |
| Google Cloud Translation Advanced | 0,08 $/page, PDF natifs et scannés (OCR intégré, 1 000 pages) | Alternative crédible au scraping gratuit pour le paid tier |
| Azure Translator | 15 $/M chars, batch documents, refuse PDF scannés | |
| Immersive Translate / BabelDOC | 20M+ utilisateurs, PDF bilingue, OCR | La sortie bilingue est très demandée |
| PDFMathTranslate (35K★) / BabelDOC (open source) | Préservation layout PDF, formules, multi-moteurs | À étudier pour améliorer pdf_translator |
| Pairaphrase/Redokun/Smartcat | TM + glossaires + relecture humaine, 285 $+/mois | Le segment pro attend TM/glossaires |
| DocTranslator, oTranslator | 14,99 $/mois ou ~0,006-0,06 $/page | Prix marché prosumer : 8-15 $/mois |
État de l'art modèles (WMT25) : GPT-4.1 et Gemini-2.5-Pro mènent la traduction IA ; DeepL next-gen « stable mais mid-tier ». Qualité : MetricX-24/25, xCOMET (QE), RUBRIC-MQM (LLM-judge) — aucun concurrent grand public n'affiche de scores de confiance par segment : différenciateur ouvert.
Recommandations priorisées
- P0 — Corriger les 2 bugs P1 (chinois, L2) : quelques lignes, impact utilisateur direct.
- P0 — Activer le cache Redis existant comme TM (déjà codé) : baisse de coût + cohérence terminologique entre documents.
- P1 — Paragraph-level translation avec placeholders pour docx/pptx : plus grand gain qualité perçue.
- P1 — PDF scannés via OCR : ✅ implémenté (Mistral OCR, voir §0). Reste : brancher Mistral OCR (~1 $/1 000 pages) — il suffit de définir
MISTRAL_API_KEY. - P1 — Glossaire : fusionner avec le prompt par défaut ; supporter les glossaires natifs DeepL ; avertir si provider incompatible.
- P2 — Sortie bilingue (docx avec colonnes/annotations, PDF duo) : forte demande, faible coût.
- P2 — Activer L0/L1 en production après fix (coût ~0,0003 $/job) et exposer un « score de confiance » par document.
- P2 — Formalité & variantes régionales (fr-CA, pt-BR/PT) via instructions prompt (LLM) — table stakes 2026.
- P3 — Queue persistante pour les jobs ; remplacer le moteur par défaut du paid tier par DeepL API ou Google Cloud Advanced (0,08 $/page) plutôt que le scraping gratuit.
- P3 — Nettoyage : supprimer le code mort listé §3, documenter l'alias « zai »→xAI.
7. Annexes
A. Résultats des tests réels
Voir §2 — script temp/audit/audit_functional.py, sorties dans temp/audit/.
B. Suite pytest
tests/test_translators/test_word_translator.py + test_excel_translator.py + test_pptx_translator.py: 106 passed en 3,68 s ✅tests/test_translators/test_b3_5_pdf_smart_fit.py: bloque indéfiniment surtest_tier_3_4_limit_to_two_shrink_steps(crash natif libmagic, cf. bug P2 ci-dessus) — reproduit isolément ; dumps faulthandler « Windows fatal exception: access violation » dansmagic/compat.py:189.- Recommandations : corriger l'import
magic, ajouterpytest-timeout, et noter quepytest.iniactive la couverture par défaut (addopts --cov=...) ce qui rend les runs complets très lents.
C. Sources principales (benchmark)
- DeepL document translation & API : deepl.com/en/features/document-translation · developers.deepl.com (document upload, glossaries, formality, variants)
- Google Cloud Translation (documents, 0,08 $/page, OCR) : cloud.google.com/translate/docs/advanced/translate-documents
- Azure Translator document translation : learn.microsoft.com/azure/ai-services/translator/document-translation/
- WMT25 (GPT-4.1/Gemini 2.5 Pro en tête) : slator.com/wmt25-preliminary-results-gemini-2-5-pro-gpt-4-1-lead-ai-translation/
- MetricX-24/25 (QE) : github.com/google-research/metricx · RUBRIC-MQM : aclanthology.org/2025.acl-industry.12/
- PDFMathTranslate : github.com/PDFMathTranslate/PDFMathTranslate · BabelDOC : github.com/funstory-ai/BabelDOC
- Mistral OCR (1 $/1 000 pages) : mistral.ai/news/mistral-ocr/ · PaddleOCR : github.com/PaddlePaddle/PaddleOCR
- Immersive Translate (bilingue, PDF Pro) : immersivetranslate.com/pricing/
- Pairaphrase (pricing entreprise) : pairaphrase.com · Redokun : redokun.com/pricing