File size: 8,280 Bytes
f6b4225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"""
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}")