Files
office_translator/docs/AUDIT_TRADUCTION_2026-08-29.md
sepehr 526c87348f
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m20s
feat(translation): quality pipeline overhaul + new features (audit 2026-08-29)
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)
2026-08-29 18:38:09 +02:00

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 chinesezh-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__.pymiddleware/validation.py:7import magicpython-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 admin deepseek/deepseek-v3.2 est forcée vers google/gemini-3.5-flash.
  • max_tokens=500 dans le provider legacy OpenAITranslationProvider (services/translation_service.py:1071) peut tronquer un paragraphe long.
  • OllamaTranslationProvider.list_models défini 2 fois (l'instance method est masquée par le staticmethod, translation_service.py:649 vs :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 fallback services/providers/fallback.py non branchée sur la route principale.

4. Risques qualité structurels (traduction)

  1. 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 »).
  2. 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.
  3. Aucune détection de langue réelle : source_lang=auto délégué au provider ; les prompts LLM disent « détecte la langue » (correct avec les LLM récents, approximatif avec Google).
  4. 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

  1. P0 — Corriger les 2 bugs P1 (chinois, L2) : quelques lignes, impact utilisateur direct.
  2. P0 — Activer le cache Redis existant comme TM (déjà codé) : baisse de coût + cohérence terminologique entre documents.
  3. P1 — Paragraph-level translation avec placeholders pour docx/pptx : plus grand gain qualité perçue.
  4. 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.
  5. P1 — Glossaire : fusionner avec le prompt par défaut ; supporter les glossaires natifs DeepL ; avertir si provider incompatible.
  6. P2 — Sortie bilingue (docx avec colonnes/annotations, PDF duo) : forte demande, faible coût.
  7. P2 — Activer L0/L1 en production après fix (coût ~0,0003 $/job) et exposer un « score de confiance » par document.
  8. P2 — Formalité & variantes régionales (fr-CA, pt-BR/PT) via instructions prompt (LLM) — table stakes 2026.
  9. 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.
  10. 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 sur test_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 » dans magic/compat.py:189.
  • Recommandations : corriger l'import magic, ajouter pytest-timeout, et noter que pytest.ini active 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