junaid17 commited on
Commit
7a60e77
·
verified ·
1 Parent(s): f7f30ad

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +45 -0
  2. requirements.txt +11 -0
  3. translator.py +229 -0
  4. utils.py +184 -0
app.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Form
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from translator import standard_translator, smart_auto_translator
4
+ from utils import source_tts_handler, target_tts_handler
5
+
6
+ app = FastAPI(title="Clean Translator Backend", version="2.0")
7
+
8
+ app.add_middleware(
9
+ CORSMiddleware,
10
+ allow_origins=["*"],
11
+ allow_credentials=True,
12
+ allow_methods=["*"],
13
+ allow_headers=["*"],
14
+ )
15
+
16
+ @app.get("/")
17
+ async def root():
18
+ return {"status": "ok", "message": "Backend running (In-Memory Mode) 🚀"}
19
+
20
+ # --- TRANSLATION ENDPOINTS (Unchanged) ---
21
+ @app.post("/translate/manual")
22
+ async def manual_translate(text: str = Form(...), src_lang: str = Form(...), tgt_lang: str = Form(...)):
23
+ translated = standard_translator(text, src_lang, tgt_lang)
24
+ return {"mode": "manual", "source_text": text, "translated_text": translated}
25
+
26
+ @app.post("/translate/auto")
27
+ async def auto_translate(text: str = Form(...), target_lang: str = Form(...)):
28
+ result = smart_auto_translator(text, target_lang)
29
+ return result
30
+
31
+ # --- TTS ENDPOINTS (Updated for Base64) ---
32
+
33
+ @app.post("/tts/source")
34
+ async def source_tts(text: str = Form(...)):
35
+ b64_audio = await source_tts_handler(text)
36
+ if not b64_audio:
37
+ return {"error": "Could not generate audio"}
38
+ return {"audio_base64": b64_audio}
39
+
40
+ @app.post("/tts/target")
41
+ async def target_tts(translated_text: str = Form(...), target_lang: str = Form(...)):
42
+ b64_audio = await target_tts_handler(translated_text, target_lang)
43
+ if not b64_audio:
44
+ return {"error": "Could not generate audio"}
45
+ return {"audio_base64": b64_audio}
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langdetect
2
+ transformers
3
+ torch
4
+ sentencepiece
5
+ groq
6
+ edge-tts
7
+ dotenv
8
+ uuid
9
+ fastapi
10
+ uvicorn
11
+ python-multipart
translator.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # translator.py
2
+
3
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
4
+ import torch
5
+ from langdetect import detect
6
+
7
+ MODEL_NAME = "facebook/nllb-200-distilled-600M"
8
+
9
+ # ===============================
10
+ # LANGUAGE MAP
11
+ # ===============================
12
+ LANGUAGE_MAP = {
13
+ # ===== EXISTING =====
14
+ "english": "eng_Latn", "french": "fra_Latn", "german": "deu_Latn", "spanish": "spa_Latn",
15
+ "hindi": "hin_Deva", "marathi": "mar_Deva", "tamil": "tam_Taml", "telugu": "tel_Telu",
16
+ "kannada": "kan_Knda", "bengali": "ben_Beng", "urdu": "urd_Arab", "arabic": "arb_Arab",
17
+ "persian": "pes_Arab", "japanese": "jpn_Jpan", "chinese": "zho_Hans", "korean": "kor_Hang",
18
+ "russian": "rus_Cyrl", "italian": "ita_Latn", "portuguese": "por_Latn", "dutch": "nld_Latn",
19
+ "swedish": "swe_Latn", "norwegian": "nob_Latn", "danish": "dan_Latn", "finnish": "fin_Latn",
20
+ "polish": "pol_Latn", "czech": "ces_Latn", "slovak": "slk_Latn", "hungarian": "hun_Latn",
21
+ "romanian": "ron_Latn", "bulgarian": "bul_Cyrl", "ukrainian": "ukr_Cyrl", "greek": "ell_Grek",
22
+ "gujarati": "guj_Gujr", "punjabi": "pan_Guru", "malayalam": "mal_Mlym",
23
+ "thai": "tha_Thai", "vietnamese": "vie_Latn", "indonesian": "ind_Latn",
24
+ "turkish": "tur_Latn", "hebrew": "heb_Hebr",
25
+
26
+ # ===== KURDISH (IMPORTANT) =====
27
+ "kurdish_kurmanji": "kmr_Latn", # Northern Kurdish (Latin)
28
+ "kurdish_sorani": "ckb_Arab", # Central Kurdish (Arabic)
29
+
30
+ # ===== SOUTH ASIAN =====
31
+ "nepali": "npi_Deva",
32
+ "sinhala": "sin_Sinh",
33
+ "odia": "ory_Orya",
34
+ "assamese": "asm_Beng",
35
+ "maithili": "mai_Deva",
36
+ "santali": "sat_Olck",
37
+
38
+ # ===== SOUTHEAST ASIA =====
39
+ "malay": "zsm_Latn",
40
+ "filipino": "tgl_Latn",
41
+ "khmer": "khm_Khmr",
42
+ "lao": "lao_Laoo",
43
+ "burmese": "mya_Mymr",
44
+
45
+ # ===== EAST ASIA =====
46
+ "traditional_chinese": "zho_Hant",
47
+ "mongolian": "mon_Cyrl",
48
+
49
+ # ===== CENTRAL ASIA =====
50
+ "kazakh": "kaz_Cyrl",
51
+ "uzbek": "uzn_Latn",
52
+ "tajik": "tgk_Cyrl",
53
+ "kyrgyz": "kir_Cyrl",
54
+ "turkmen": "tuk_Latn",
55
+
56
+ # ===== MIDDLE EAST =====
57
+ "pashto": "pbt_Arab",
58
+ "sindhi": "snd_Arab",
59
+
60
+ # ===== AFRICAN =====
61
+ "swahili": "swh_Latn",
62
+ "amharic": "amh_Ethi",
63
+ "yoruba": "yor_Latn",
64
+ "igbo": "ibo_Latn",
65
+ "hausa": "hau_Latn",
66
+ "zulu": "zul_Latn",
67
+ "xhosa": "xho_Latn",
68
+ "somali": "som_Latn",
69
+ "afrikaans": "afr_Latn",
70
+
71
+ # ===== EUROPE EXTRA =====
72
+ "estonian": "est_Latn",
73
+ "latvian": "lav_Latn",
74
+ "lithuanian": "lit_Latn",
75
+ "icelandic": "isl_Latn",
76
+ "irish": "gle_Latn",
77
+ "welsh": "cym_Latn",
78
+ "albanian": "sqi_Latn",
79
+ "serbian": "srp_Cyrl",
80
+ "croatian": "hrv_Latn",
81
+ "slovenian": "slv_Latn",
82
+
83
+ # ===== OTHERS =====
84
+ "latin": "lat_Latn",
85
+ "esperanto": "epo_Latn"
86
+ }
87
+
88
+ ISO_TO_LANGUAGE_KEY = {
89
+ # ===== EXISTING =====
90
+ "en": "english", "fr": "french", "de": "german", "es": "spanish", "hi": "hindi",
91
+ "mr": "marathi", "ta": "tamil", "te": "telugu", "kn": "kannada", "bn": "bengali",
92
+ "ur": "urdu", "ar": "arabic", "fa": "persian", "ja": "japanese", "zh": "chinese",
93
+ "ko": "korean", "ru": "russian", "it": "italian", "pt": "portuguese", "nl": "dutch",
94
+ "sv": "swedish", "no": "norwegian", "da": "danish", "fi": "finnish", "pl": "polish",
95
+ "cs": "czech", "sk": "slovak", "hu": "hungarian", "ro": "romanian", "bg": "bulgarian",
96
+ "uk": "ukrainian", "el": "greek", "gu": "gujarati", "pa": "punjabi", "ml": "malayalam",
97
+ "th": "thai", "vi": "vietnamese", "id": "indonesian", "tr": "turkish", "he": "hebrew",
98
+
99
+ # ===== KURDISH =====
100
+ "ku": "kurdish_kurmanji", # default kurdish
101
+ "ckb": "kurdish_sorani",
102
+
103
+ # ===== SOUTH ASIAN =====
104
+ "ne": "nepali",
105
+ "si": "sinhala",
106
+ "or": "odia",
107
+ "as": "assamese",
108
+ "mai": "maithili",
109
+ "sat": "santali",
110
+
111
+ # ===== SOUTHEAST ASIA =====
112
+ "ms": "malay",
113
+ "tl": "filipino",
114
+ "km": "khmer",
115
+ "lo": "lao",
116
+ "my": "burmese",
117
+
118
+ # ===== CENTRAL ASIA =====
119
+ "kk": "kazakh",
120
+ "uz": "uzbek",
121
+ "tg": "tajik",
122
+ "ky": "kyrgyz",
123
+ "tk": "turkmen",
124
+
125
+ # ===== MIDDLE EAST =====
126
+ "ps": "pashto",
127
+ "sd": "sindhi",
128
+
129
+ # ===== AFRICAN =====
130
+ "sw": "swahili",
131
+ "am": "amharic",
132
+ "yo": "yoruba",
133
+ "ig": "igbo",
134
+ "ha": "hausa",
135
+ "zu": "zulu",
136
+ "xh": "xhosa",
137
+ "so": "somali",
138
+ "af": "afrikaans",
139
+
140
+ # ===== EUROPE EXTRA =====
141
+ "et": "estonian",
142
+ "lv": "latvian",
143
+ "lt": "lithuanian",
144
+ "is": "icelandic",
145
+ "ga": "irish",
146
+ "cy": "welsh",
147
+ "sq": "albanian",
148
+ "sr": "serbian",
149
+ "hr": "croatian",
150
+ "sl": "slovenian",
151
+
152
+ # ===== OTHERS =====
153
+ "la": "latin",
154
+ "eo": "esperanto"
155
+ }
156
+
157
+
158
+ # ===============================
159
+ # LOAD MODEL ONCE
160
+ # ===============================
161
+ print("Loading translation model...")
162
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
163
+ model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)
164
+ print("Model loaded successfully.")
165
+
166
+ # ===============================
167
+ # HELPERS
168
+ # ===============================
169
+ def get_nllb_code(lang: str):
170
+ lang = lang.lower().strip()
171
+
172
+ if lang in LANGUAGE_MAP:
173
+ return LANGUAGE_MAP[lang]
174
+
175
+ if lang in ISO_TO_LANGUAGE_KEY:
176
+ key = ISO_TO_LANGUAGE_KEY[lang]
177
+ return LANGUAGE_MAP[key]
178
+
179
+ return "eng_Latn" # fallback
180
+
181
+
182
+ # ===============================
183
+ # MANUAL TRANSLATOR
184
+ # ===============================
185
+ def standard_translator(text: str, src_lang: str, tgt_lang: str) -> str:
186
+ src_code = get_nllb_code(src_lang)
187
+ tgt_code = get_nllb_code(tgt_lang)
188
+
189
+ tokenizer.src_lang = src_code
190
+ inputs = tokenizer(text, return_tensors="pt")
191
+
192
+ with torch.no_grad():
193
+ output = model.generate(
194
+ **inputs,
195
+ forced_bos_token_id=tokenizer.convert_tokens_to_ids(tgt_code),
196
+ max_length=512
197
+ )
198
+
199
+ return tokenizer.decode(output[0], skip_special_tokens=True)
200
+
201
+
202
+ # ===============================
203
+ # AUTO TRANSLATOR
204
+ # ===============================
205
+ def smart_auto_translator(text: str, target_lang: str):
206
+ detected_iso = detect(text)
207
+ detected_lang_key = ISO_TO_LANGUAGE_KEY.get(detected_iso, detected_iso)
208
+
209
+ src_code = get_nllb_code(detected_iso)
210
+ tgt_code = get_nllb_code(target_lang)
211
+
212
+ tokenizer.src_lang = src_code
213
+ inputs = tokenizer(text, return_tensors="pt")
214
+
215
+ with torch.no_grad():
216
+ output = model.generate(
217
+ **inputs,
218
+ forced_bos_token_id=tokenizer.convert_tokens_to_ids(tgt_code),
219
+ max_length=512
220
+ )
221
+
222
+ translated_text = tokenizer.decode(output[0], skip_special_tokens=True)
223
+
224
+ return {
225
+ "translated_text": translated_text,
226
+ "detected_iso": detected_iso,
227
+ "detected_language": detected_lang_key
228
+ }
229
+
utils.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils.py
2
+
3
+
4
+ # ===============================
5
+ # VOICE MAP
6
+ # ===============================
7
+ VOICE_MAP = {
8
+ # ===== EXISTING (UNCHANGED) =====
9
+ "english": "en-US-AriaNeural",
10
+ "french": "fr-FR-DeniseNeural",
11
+ "german": "de-DE-KatjaNeural",
12
+ "spanish": "es-ES-ElviraNeural",
13
+ "hindi": "hi-IN-SwaraNeural",
14
+ "arabic": "ar-SA-ZariyahNeural",
15
+ "japanese": "ja-JP-NanamiNeural",
16
+ "korean": "ko-KR-SunHiNeural",
17
+ "chinese": "zh-CN-XiaoxiaoNeural",
18
+ "russian": "ru-RU-SvetlanaNeural",
19
+ "marathi": "mr-IN-AarohiNeural",
20
+ "tamil": "ta-IN-PallaviNeural",
21
+ "telugu": "te-IN-ShrutiNeural",
22
+ "kannada": "kn-IN-SapnaNeural",
23
+ "bengali": "bn-IN-TanishaaNeural",
24
+ "urdu": "ur-PK-UzmaNeural",
25
+ "gujarati": "gu-IN-DhwaniNeural",
26
+ "punjabi": "pa-IN-GurpreetNeural",
27
+ "malayalam": "ml-IN-SobhanaNeural",
28
+ "italian": "it-IT-ElsaNeural",
29
+ "portuguese": "pt-PT-RaquelNeural",
30
+ "dutch": "nl-NL-ColetteNeural",
31
+ "swedish": "sv-SE-SofieNeural",
32
+ "norwegian": "nb-NO-IselinNeural",
33
+ "danish": "da-DK-ChristelNeural",
34
+ "finnish": "fi-FI-NooraNeural",
35
+ "polish": "pl-PL-ZofiaNeural",
36
+ "czech": "cs-CZ-VlastaNeural",
37
+ "slovak": "sk-SK-ViktoriaNeural",
38
+ "hungarian": "hu-HU-NoemiNeural",
39
+ "romanian": "ro-RO-AlinaNeural",
40
+ "bulgarian": "bg-BG-KalinaNeural",
41
+ "ukrainian": "uk-UA-PolinaNeural",
42
+ "greek": "el-GR-AthinaNeural",
43
+ "thai": "th-TH-PremwadeeNeural",
44
+ "vietnamese": "vi-VN-HoaiMyNeural",
45
+ "indonesian": "id-ID-GadisNeural",
46
+ "turkish": "tr-TR-EmelNeural",
47
+ "hebrew": "he-IL-HilaNeural",
48
+
49
+ # ===== NEW – Edge TTS Supported =====
50
+
51
+ # South Asia
52
+ "nepali": "ne-NP-HemkalaNeural",
53
+ "sinhala": "si-LK-ThiliniNeural",
54
+
55
+ # Southeast Asia
56
+ "malay": "ms-MY-YasminNeural",
57
+ "filipino": "fil-PH-BlessicaNeural",
58
+ "khmer": "km-KH-SreymomNeural",
59
+ "lao": "lo-LA-KeomanyNeural",
60
+ "burmese": "my-MM-NilarNeural",
61
+
62
+ # Central Asia
63
+ "kazakh": "kk-KZ-AigulNeural",
64
+ "uzbek": "uz-UZ-MadinaNeural",
65
+
66
+ # Africa
67
+ "swahili": "sw-KE-ZuriNeural",
68
+ "amharic": "am-ET-MekdesNeural",
69
+ "zulu": "zu-ZA-ThandoNeural",
70
+ "xhosa": "xh-ZA-NolwaziNeural",
71
+ "afrikaans": "af-ZA-AdriNeural",
72
+
73
+ # Middle East
74
+ "azerbaijani": "az-AZ-BanuNeural",
75
+ "persian": "fa-IR-DilaraNeural",
76
+
77
+ # Europe Extra
78
+ "estonian": "et-EE-AnuNeural",
79
+ "latvian": "lv-LV-NeveraNeural",
80
+ "lithuanian": "lt-LT-OnaNeural",
81
+ "icelandic": "is-IS-GudrunNeural",
82
+ "irish": "ga-IE-OrlaNeural",
83
+ "welsh": "cy-GB-NiaNeural",
84
+ "albanian": "sq-AL-AnilaNeural",
85
+ "serbian": "sr-RS-SophieNeural",
86
+ "croatian": "hr-HR-GabrijelaNeural",
87
+ "slovenian": "sl-SI-PetraNeural"
88
+ }
89
+ ISO_TO_LANGUAGE_KEY = {
90
+ # ===== EXISTING =====
91
+ "en": "english", "fr": "french", "de": "german", "es": "spanish", "hi": "hindi",
92
+ "mr": "marathi", "ta": "tamil", "te": "telugu", "kn": "kannada", "bn": "bengali",
93
+ "ur": "urdu", "ar": "arabic", "fa": "persian", "ja": "japanese", "zh": "chinese",
94
+ "ko": "korean", "ru": "russian", "it": "italian", "pt": "portuguese", "nl": "dutch",
95
+ "sv": "swedish", "no": "norwegian", "da": "danish", "fi": "finnish", "pl": "polish",
96
+ "cs": "czech", "sk": "slovak", "hu": "hungarian", "ro": "romanian", "bg": "bulgarian",
97
+ "uk": "ukrainian", "el": "greek", "gu": "gujarati", "pa": "punjabi", "ml": "malayalam",
98
+ "th": "thai", "vi": "vietnamese", "id": "indonesian", "tr": "turkish", "he": "hebrew",
99
+
100
+ # ===== NEW =====
101
+ "ne": "nepali",
102
+ "si": "sinhala",
103
+ "ms": "malay",
104
+ "tl": "filipino",
105
+ "km": "khmer",
106
+ "lo": "lao",
107
+ "my": "burmese",
108
+ "kk": "kazakh",
109
+ "uz": "uzbek",
110
+ "sw": "swahili",
111
+ "am": "amharic",
112
+ "zu": "zulu",
113
+ "xh": "xhosa",
114
+ "af": "afrikaans",
115
+ "az": "azerbaijani",
116
+ "et": "estonian",
117
+ "lv": "latvian",
118
+ "lt": "lithuanian",
119
+ "is": "icelandic",
120
+ "ga": "irish",
121
+ "cy": "welsh",
122
+ "sq": "albanian",
123
+ "sr": "serbian",
124
+ "hr": "croatian",
125
+ "sl": "slovenian"
126
+ }
127
+
128
+
129
+ import os
130
+ import time
131
+ from uuid import uuid4
132
+ import edge_tts
133
+ from langdetect import detect, LangDetectException
134
+ from groq import Groq
135
+ from dotenv import load_dotenv
136
+ import base64
137
+
138
+ load_dotenv()
139
+ client = Groq()
140
+
141
+ # ===============================
142
+ # CONFIGURATION
143
+ # ===============================
144
+ STATIC_DIR = "static"
145
+ DELETE_AFTER_SECONDS = 300 # Delete audio files older than 5 minutes
146
+
147
+ # ===============================
148
+ # MEMORY TTS (Base64)
149
+ # ===============================
150
+ async def TTS(text: str, voice: str):
151
+ """Generates audio in memory and returns Base64 string"""
152
+ communicate = edge_tts.Communicate(text, voice)
153
+ audio_data = b""
154
+
155
+ # Stream audio bytes into memory
156
+ async for chunk in communicate.stream():
157
+ if chunk["type"] == "audio":
158
+ audio_data += chunk["data"]
159
+
160
+ # Encode to Base64 string
161
+ b64_string = base64.b64encode(audio_data).decode('utf-8')
162
+ return b64_string
163
+
164
+ # ===============================
165
+ # HANDLERS
166
+ # ===============================
167
+ async def source_tts_handler(text: str):
168
+ if not text or not text.strip():
169
+ return None
170
+ try:
171
+ iso = detect(text)
172
+ lang_key = ISO_TO_LANGUAGE_KEY.get(iso, "english")
173
+ voice = VOICE_MAP.get(lang_key, "en-US-AriaNeural")
174
+ except LangDetectException:
175
+ voice = "en-US-AriaNeural"
176
+
177
+ return await TTS(text, voice)
178
+
179
+ async def target_tts_handler(text: str, target_lang: str):
180
+ if not text:
181
+ return None
182
+ clean_lang = target_lang.lower().strip()
183
+ voice = VOICE_MAP.get(clean_lang, "en-US-AriaNeural")
184
+ return await TTS(text, voice)