agro-diag-api / translator.py
Expendadeur's picture
ok
f6b4225
Raw
History Blame Contribute Delete
8.28 kB
"""
translator.py β€” Traduction multi-moteurs avec fallback automatique
Utilise deep-translator uniquement (pas de conflit avec gradio/httpx)
Ordre de fallback :
1. deep-translator GoogleTranslator (Google Translate, gratuit)
2. deep-translator MyMemoryTranslator (5000 mots/jour gratuit)
3. deep-translator LibreTranslator (open source)
4. Texte franΓ§ais original conservΓ© (jamais de crash)
Codes de langue supportΓ©s : fr, en, sw, rn (Rundi/Kirundi)
"""
import time
# ── Mapping codes internes β†’ codes deep-translator ───────────────────────────
# deep-translator GoogleTranslator accepte les noms complets ou codes ISO
DEEP_GOOGLE_CODES = {
"fr": "fr",
"en": "en",
"sw": "sw",
"rn": "rn", # Rundi β€” supportΓ© par Google Translate
}
# MyMemoryTranslator utilise le format "lang-REGION"
MYMEMORY_CODES = {
"fr": "fr-FR",
"en": "en-US",
"sw": "sw-KE",
"rn": "rn-BI", # Rundi β€” Burundi
}
# LibreTranslator codes ISO 639-1
LIBRE_CODES = {
"fr": "fr",
"en": "en",
"sw": "sw",
"rn": "rn",
}
# ══════════════════════════════════════════════════════════════════════════════
# MOTEUR 1 β€” deep-translator GoogleTranslator
# Utilise Google Translate en arrière-plan, gratuit et sans API key
# ══════════════════════════════════════════════════════════════════════════════
def _translate_deep_google(text: str, lang_code: str) -> str | None:
dest = DEEP_GOOGLE_CODES.get(lang_code)
if not dest:
return None
try:
from deep_translator import GoogleTranslator
result = GoogleTranslator(source="fr", target=dest).translate(text)
if result and result.strip() and len(result.strip()) > 3:
return result.strip()
except Exception as e:
print(f" deep-google ECHEC ({lang_code}) : {str(e)[:70]}")
return None
# ══════════════════════════════════════════════════════════════════════════════
# MOTEUR 2 β€” deep-translator MyMemoryTranslator
# 5000 mots/jour gratuit sans compte
# ══════════════════════════════════════════════════════════════════════════════
def _translate_mymemory(text: str, lang_code: str) -> str | None:
dest = MYMEMORY_CODES.get(lang_code)
if not dest:
return None
try:
from deep_translator import MyMemoryTranslator
result = MyMemoryTranslator(source="fr-FR", target=dest).translate(text)
if result and result.strip() and len(result.strip()) > 3:
# MyMemory retourne un avertissement si la limite est atteinte
if any(w in result for w in ["MYMEMORY WARNING", "QUOTA FINISHED", "YOU USED"]):
print(f" MyMemory : limite atteinte")
return None
return result.strip()
except Exception as e:
print(f" MyMemory ECHEC ({lang_code}) : {str(e)[:70]}")
return None
# ══════════════════════════════════════════════════════════════════════════════
# MOTEUR 3 β€” deep-translator LibreTranslator
# Open source, serveur public disponible
# ══════════════════════════════════════════════════════════════════════════════
def _translate_libre(text: str, lang_code: str) -> str | None:
dest = LIBRE_CODES.get(lang_code)
if not dest:
return None
try:
from deep_translator import LibreTranslator
result = LibreTranslator(
source="fr",
target=dest,
url="https://libretranslate.com",
api_key=""
).translate(text)
if result and result.strip() and len(result.strip()) > 3:
return result.strip()
except Exception as e:
print(f" LibreTranslate ECHEC ({lang_code}) : {str(e)[:70]}")
return None
# ══════════════════════════════════════════════════════════════════════════════
# FONCTION PRINCIPALE β€” fallback automatique
# ══════════════════════════════════════════════════════════════════════════════
# Ordre des moteurs : du plus fiable au moins fiable
_ENGINES = [
("deep-google", _translate_deep_google),
("mymemory", _translate_mymemory),
("libretranslate",_translate_libre),
]
def translate(text: str, lang_code: str, pause: float = 0.1) -> str:
"""
Traduit un texte franΓ§ais vers lang_code avec fallback automatique.
Essaie chaque moteur dans l'ordre jusqu'au premier succès.
Si tous Γ©chouent β†’ retourne le texte original.
lang_code : "fr" | "en" | "sw" | "rn"
pause : dΓ©lai entre tentatives (secondes)
"""
if lang_code == "fr" or not text or not text.strip():
return text
for engine_name, engine_fn in _ENGINES:
try:
result = engine_fn(text, lang_code)
if result:
print(f"Traduction OK via {engine_name} ({lang_code})")
time.sleep(pause)
return result
else:
print(f" {engine_name} : rΓ©sultat vide β†’ moteur suivant")
except Exception as e:
print(f" {engine_name} erreur inattendue : {str(e)[:60]}")
time.sleep(pause)
# Tous les moteurs ont Γ©chouΓ©
print(f"TOUS les moteurs ont Γ©chouΓ© ({lang_code}) β€” texte original conservΓ©")
return text
def translate_fields(fields: dict, lang_code: str) -> dict:
"""
Traduit un dictionnaire de champs texte du franΓ§ais vers lang_code.
Chaque champ est traduit indΓ©pendamment avec fallback.
fields : {"disease_name": "...", "cause": "...", ...}
Retourne les champs traduits (ou originaux si Γ©chec).
"""
if lang_code == "fr":
return fields
translated = {}
for key, value in fields.items():
if not value or value in ("β€”", ""):
translated[key] = value
continue
translated[key] = translate(value, lang_code)
return translated
# ══════════════════════════════════════════════════════════════════════════════
# TEST β€” lance ce fichier directement pour vΓ©rifier les moteurs
# python translator.py
# ══════════════════════════════════════════════════════════════════════════════
if __name__ == "__main__":
tests = {
"disease_name": "Mildiou de la tomate",
"cause": "Champignon Phytophthora infestans. FavorisΓ© par l'humiditΓ© Γ©levΓ©e.",
"treatment": "Appliquer un fongicide cuivrique. Retirer les feuilles infectΓ©es.",
}
for code, label in [("en", "English"), ("sw", "Swahili"), ("rn", "Rundi/Kirundi")]:
print(f"\n{'='*50}")
print(f"LANGUE : {label} ({code})")
print(f"{'='*50}")
results = translate_fields(tests, code)
for key, val in results.items():
print(f" {key}: {val}")