feat(qualite): retenter automatiquement les traductions livrees dans la mauvaise ecriture

Le defaut le plus sournois du pipeline etait silencieux : de l'arabe
livre pour une cible persane (meme ecriture, mauvaise langue) passe
inapercu et part chez le lecteur. Desormais chaque lot traduit est
verifie par le detecteur d'ecritures, et chaque segment fautif est
redemande une fois au moteur avec une consigne renforcee (nom de la
langue + lettres specifiques, ex. persan پ چ ژ گ). La seconde tentative
ne remplace la premiere que si elle passe le meme controle.

- services/quality/script_detector.py : extraction d'un controle
  script_issue() reutilisable ; detect_arabic_variant signale des
 ormais un long texte en ecriture arabe sans aucune lettre specifique
  de la langue cible (arabe pur livre pour du persan/ourdou/pachto)
- translators/segments.py : retry_wrong_script() + construction de la
  consigne renforcee, sans jamais faire echouer le travail
- Word, Excel, PDF : branchement apres la memoire de traduction et les
  validations humaines ; le texte inchange (chiffres, noms propres) ne
  declenche jamais de retentative
- 14 tests nouveaux (tests/test_translators/test_script_retry.py)
This commit is contained in:
2026-09-01 20:52:23 +02:00
parent ffbd85a7b6
commit d92bbf0fa6
6 changed files with 504 additions and 31 deletions

View File

@@ -1702,7 +1702,8 @@ class PDFTranslator:
# ------------------------------------------------------------------ #
def _translate_with_provider(
self, texts: List[str], target_language: str, source_language: str
self, texts: List[str], target_language: str, source_language: str,
extra_prompt: Optional[str] = None,
) -> List[str]:
"""Translate using the TranslationProvider interface (handles old & new styles)."""
from services.providers.base import TranslationProvider as NewTranslationProvider
@@ -1720,6 +1721,10 @@ class PDFTranslator:
if is_new_style:
from services.providers.schemas import TranslationRequest
custom_prompt = getattr(self, "_custom_prompt", None)
if extra_prompt:
custom_prompt = (
f"{custom_prompt}\n\n{extra_prompt}" if custom_prompt else extra_prompt
)
metadata = {"custom_prompt": custom_prompt} if custom_prompt else None
requests = [
@@ -1812,6 +1817,25 @@ class PDFTranslator:
logger.warning("legacy_translate_failed", error=str(e))
translated = texts
# Wrong-script guard: re-ask once (with a reinforced instruction)
# for translations delivered in the wrong alphabet — e.g. Arabic
# delivered for a Persian target.
from translators.segments import retry_wrong_script
def _retry_translate(retry_texts, hint):
if self._provider is not None:
return self._translate_with_provider(
retry_texts, target_language, source_language, extra_prompt=hint
)
from services.translation_service import translation_service
return translation_service.translate_batch(
retry_texts, target_language, source_language
)
translated, _script_fixed = retry_wrong_script(
texts, translated, target_language, _retry_translate
)
changed = sum(1 for orig, trans in zip(texts, translated) if orig != trans and trans.strip())
self._translation_stats["changed"] += changed