File size: 7,740 Bytes
9aabd3a
 
 
 
 
 
 
 
04bace6
9aabd3a
 
 
 
04bace6
 
9aabd3a
04bace6
9aabd3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04bace6
9aabd3a
 
 
 
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
"""
CodeAlpha - Task 1: Language Translation Tool
Author: (Loic HOUNYOVI)
Description: Application web permettant de traduire du texte d'une langue
             source vers une langue cible en utilisant un vrai modèle de
             deep learning (Meta NLLB-200) chargé localement via la
             librairie Hugging Face `transformers`, avec en bonus une
             synthèse vocale (text-to-speech) et un bouton copier.

Modèle utilisé : facebook/nllb-200-distilled-600M
  - Modèle Seq2Seq (encoder-decoder) multilingue, pré-entraîné par Meta AI
  - Supporte ~200 langues avec un seul et même modèle
  - Chargé via transformers.pipeline("translation", ...)
"""

import io

import streamlit as st
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
from gtts import gTTS

try:
    from langdetect import detect as detect_lang
    LANGDETECT_AVAILABLE = True
except ImportError:
    LANGDETECT_AVAILABLE = False

# ---------------------------------------------------------
# Configuration de la page
st.set_page_config(
    page_title="CodeAlpha Language Translator (AI Model)",
    page_icon="🌐",
    layout="centered",
)

# ---------------------------------------------------------
# Langues supportées : code affichage -> (code NLLB / FLORES-200, code gTTS)
LANGUAGES = {
    "en": "Anglais",
    "fr": "Français",
    "es": "Espagnol",
    "de": "Allemand",
    "it": "Italien",
    "pt": "Portugais",
    "ar": "Arabe",
    "zh-CN": "Chinois (simplifié)",
    "ja": "Japonais",
    "ko": "Coréen",
    "ru": "Russe",
    "hi": "Hindi",
    "nl": "Néerlandais",
    "tr": "Turc",
}

NLLB_CODES = {
    "en": "eng_Latn",
    "fr": "fra_Latn",
    "es": "spa_Latn",
    "de": "deu_Latn",
    "it": "ita_Latn",
    "pt": "por_Latn",
    "ar": "arb_Arab",
    "zh-CN": "zho_Hans",
    "ja": "jpn_Jpan",
    "ko": "kor_Hang",
    "ru": "rus_Cyrl",
    "hi": "hin_Deva",
    "nl": "nld_Latn",
    "tr": "tur_Latn",
}

# gTTS accepte directement des codes ISO classiques (tous nos codes conviennent)
TTS_SUPPORTED = set(LANGUAGES.keys())


# ---------------------------------------------------------
# Chargement du modèle (mis en cache : ne se recharge pas à chaque interaction)
MODEL_NAME = "facebook/nllb-200-distilled-600M"


@st.cache_resource(show_spinner="Chargement du modèle NLLB-200 (peut prendre un moment la 1ère fois)...")
def load_model():
    tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
    model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)
    return tokenizer, model


def translate_text(text: str, src_code: str, tgt_code: str) -> str:
    """Traduit `text` de src_code vers tgt_code (codes FLORES-200) via NLLB-200."""
    tokenizer, model = load_model()
    tokenizer.src_lang = src_code
    inputs = tokenizer(text, return_tensors="pt", truncation=True)
    forced_bos_token_id = tokenizer.convert_tokens_to_ids(tgt_code)
    generated_tokens = model.generate(
        **inputs,
        forced_bos_token_id=forced_bos_token_id,
        max_length=400,
    )
    return tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]

# ---------------------------------------------------------
# En-tête
st.title("🌐 Language Translation Tool")
st.caption("CodeAlpha Artificial Intelligence Internship — Task 1")

st.write(
    "Entrez votre texte, choisissez la langue source et la langue cible, "
    "puis cliquez sur **Traduire**."
)

# ---------------------------------------------------------
# Zone de saisie
input_text = st.text_area("Texte à traduire :", height=150, placeholder="Écrivez ou collez votre texte ici...")

col1, col2 = st.columns(2)
with col1:
    source_options = ["auto"] + list(LANGUAGES.keys()) if LANGDETECT_AVAILABLE else list(LANGUAGES.keys())
    source_lang = st.selectbox(
        "Langue source",
        options=source_options,
        format_func=lambda code: "Détection automatique" if code == "auto" else LANGUAGES[code],
        index=0,
    )
with col2:
    # Langue cible par défaut : anglais si la source n'est pas anglais, sinon français
    target_options = [c for c in LANGUAGES.keys() if c != "auto"]
    target_lang = st.selectbox(
        "Langue cible",
        options=target_options,
        format_func=lambda code: LANGUAGES[code],
        index=target_options.index("fr") if "fr" in target_options else 0,
    )

translate_clicked = st.button("🔄 Traduire", type="primary", use_container_width=True)

# ---------------------------------------------------------
# Logique de traduction
if "translated_text" not in st.session_state:
    st.session_state.translated_text = ""

if translate_clicked:
    if not input_text.strip():
        st.warning("Merci d'entrer du texte avant de traduire.")
    else:
        try:
            # --- Détection automatique de la langue source si demandé ---
            actual_source = source_lang
            if source_lang == "auto":
                if LANGDETECT_AVAILABLE:
                    detected = detect_lang(input_text)
                    # langdetect renvoie parfois "zh-cn" au lieu de "zh-CN"
                    detected = "zh-CN" if detected.lower().startswith("zh") else detected
                    actual_source = detected if detected in LANGUAGES else "en"
                else:
                    actual_source = "en"

            src_code = NLLB_CODES.get(actual_source)
            tgt_code = NLLB_CODES.get(target_lang)

            if src_code is None or tgt_code is None:
                st.error("Langue non supportée par le modèle.")
            else:
                with st.spinner("Traduction en cours (inférence du modèle)..."):
                    result = translate_text(input_text, src_code, tgt_code)
                    st.session_state.translated_text = result
                    if source_lang == "auto":
                        st.caption(f"🔍 Langue détectée : {LANGUAGES.get(actual_source, actual_source)}")
        except Exception as e:
            st.error(f"Une erreur est survenue lors de la traduction : {e}")

# ---------------------------------------------------------
# Affichage du résultat
if st.session_state.translated_text:
    st.subheader("Traduction :")
    st.text_area(
        "Résultat",
        value=st.session_state.translated_text,
        height=150,
        label_visibility="collapsed",
    )

    col_a, col_b = st.columns(2)

    # --- Bouton copier (via un petit composant HTML/JS) ---
    with col_a:
        copy_html = f"""
        <textarea id="toCopy" style="display:none;">{st.session_state.translated_text}</textarea>
        <button onclick="navigator.clipboard.writeText(document.getElementById('toCopy').value)"
            style="width:100%; padding:8px; border-radius:6px; border:1px solid #ccc; cursor:pointer;">
            📋 Copier le texte
        </button>
        """
        st.components.v1.html(copy_html, height=45)

    # --- Bouton text-to-speech ---
    with col_b:
        if target_lang in TTS_SUPPORTED:
            if st.button("🔊 Écouter la traduction", use_container_width=True):
                try:
                    tts = gTTS(text=st.session_state.translated_text, lang=target_lang)
                    audio_bytes = io.BytesIO()
                    tts.write_to_fp(audio_bytes)
                    audio_bytes.seek(0)
                    st.audio(audio_bytes, format="audio/mp3")
                except Exception as e:
                    st.error(f"Impossible de générer l'audio : {e}")
        else:
            st.info("🔇 Audio non disponible pour cette langue.")

# ---------------------------------------------------------
# Pied de page
st.divider()
st.caption("Projet réalisé dans le cadre du stage AI @CodeAlpha")