testingfaces commited on
Commit
2ea700a
·
verified ·
1 Parent(s): 04aecbe

Update translator.py

Browse files
Files changed (1) hide show
  1. translator.py +27 -460
translator.py CHANGED
@@ -1,469 +1,36 @@
1
- """
2
- Department 3 — Translator
3
- UPGRADED v2: IndicTrans2 as primary for Telugu/Hindi/Tamil/Kannada
4
-
5
- MERGED — best of both versions:
6
- ⭐ KEPT from friend's version:
7
- - IndicTransToolkit.IndicProcessor for correct pre/post-processing
8
- - Non-gated 1B models (ai4bharat/indictrans2-en-indic-1B) — no HF token needed
9
- - Helsinki fallback handles both "translation_text" and "generated_text" keys
10
- - Indic→English uses Google first (opus-mt-mul-en transliterates, not translates)
11
- - L8: Wrong <2te> prefix already removed (friend fixed this one correctly)
12
- ✅ FIXED bugs that friend's version missed:
13
- - L9: forced_bos_token_id now passed to model.generate() — target lang enforced
14
- - L10: _chunk word-boundary fallback for unpunctuated transcripts
15
- """
16
-
17
- import re
18
- import time
19
  import logging
20
- import threading
21
 
22
  logger = logging.getLogger(__name__)
23
 
24
- # ⭐ KEPT: non-gated 1B models
25
- INDICTRANS2_EN_INDIC = "ai4bharat/indictrans2-en-indic-1B"
26
- INDICTRANS2_INDIC_EN = "ai4bharat/indictrans2-indic-en-1B"
27
-
28
- INDICTRANS2_SUPPORTED = {"te", "hi", "ta", "kn", "ml", "bn", "gu", "mr", "pa", "ur"}
29
-
30
- INDICTRANS2_CODES = {
31
- "en": "eng_Latn", "te": "tel_Telu", "hi": "hin_Deva",
32
- "ta": "tam_Taml", "kn": "kan_Knda", "ml": "mal_Mlym",
33
- "bn": "ben_Beng", "gu": "guj_Gujr", "mr": "mar_Deva",
34
- "pa": "pan_Guru", "ur": "urd_Arab",
35
- }
36
-
37
- HELSINKI_MODELS = {
38
- ("en", "te"): "Helsinki-NLP/opus-mt-en-mul",
39
- ("en", "hi"): "Helsinki-NLP/opus-mt-en-hi",
40
- ("en", "ta"): "Helsinki-NLP/opus-mt-en-mul",
41
- ("en", "kn"): "Helsinki-NLP/opus-mt-en-mul",
42
- ("hi", "en"): "Helsinki-NLP/opus-mt-hi-en",
43
- ("te", "en"): "Helsinki-NLP/opus-mt-mul-en",
44
- ("ta", "en"): "Helsinki-NLP/opus-mt-mul-en",
45
- ("en", "es"): "Helsinki-NLP/opus-mt-en-es",
46
- ("en", "fr"): "Helsinki-NLP/opus-mt-en-fr",
47
- ("en", "de"): "Helsinki-NLP/opus-mt-en-de",
48
- ("en", "zh"): "Helsinki-NLP/opus-mt-en-zh",
49
- ("en", "ar"): "Helsinki-NLP/opus-mt-en-ar",
50
- ("en", "ru"): "Helsinki-NLP/opus-mt-en-ru",
51
- }
52
-
53
- NLLB_CODES = {
54
- "en": "eng_Latn", "te": "tel_Telu", "hi": "hin_Deva",
55
- "ta": "tam_Taml", "kn": "kan_Knda", "es": "spa_Latn",
56
- "fr": "fra_Latn", "de": "deu_Latn", "ja": "jpn_Jpan",
57
- "zh": "zho_Hans", "ar": "arb_Arab", "pt": "por_Latn",
58
- "ru": "rus_Cyrl",
59
- }
60
-
61
- INDIC_LANGS = {"te", "hi", "ta", "kn", "ar", "ml", "bn", "gu", "mr", "pa"}
62
- CHUNK_WORDS = 80
63
- CHUNK_WORDS_INDIC = 50
64
- NLLB_MODEL_ID = "facebook/nllb-200-distilled-1.3B"
65
- MAX_TOKENS = 512
66
-
67
-
68
  class Translator:
69
  def __init__(self):
70
- # ⭐ KEPT: separate tokenizer/model handles + IndicProcessor
71
- self._it2_tok_ei = None
72
- self._it2_mod_ei = None
73
- self._it2_tok_ie = None
74
- self._it2_mod_ie = None
75
- self._it2_ip = None # IndicProcessor instance
76
- self._it2_loaded = False
77
-
78
- self._helsinki_models = {}
79
-
80
- self._pipeline = None
81
- self._tokenizer = None
82
- self._model = None
83
- self._nllb_loaded = False
84
-
85
- print("[Translator] IndicTrans2 v2 — pre-loading Telugu + Hindi...")
86
- threading.Thread(target=self._preload_common_models, daemon=True).start()
87
-
88
- def _preload_common_models(self):
89
- time.sleep(5)
90
- try:
91
- print("[Translator] Pre-loading IndicTrans2...")
92
- self._load_indictrans2()
93
- print("[Translator] ✅ IndicTrans2 ready! Telugu/Hindi fast from now.")
94
- except Exception as e:
95
- print(f"[Translator] IndicTrans2 pre-load failed: {e}")
96
- print("[Translator] Falling back to Helsinki-NLP pre-load...")
97
- try:
98
- for src, tgt in [("en", "te"), ("en", "hi")]:
99
- model_id = HELSINKI_MODELS.get((src, tgt))
100
- if model_id:
101
- self._get_helsinki_pipeline(model_id)
102
- print(f"[Translator] ✅ Helsinki {src}→{tgt} ready")
103
- except Exception as e2:
104
- print(f"[Translator] Helsinki pre-load failed: {e2}")
105
-
106
- # ══════════════════════════════════════════════════════════════════
107
- # PUBLIC — TRANSLATE
108
- # ══════════════════════════════════════════════════════════════════
109
- def translate(self, text: str, src_lang: str, tgt_lang: str):
110
- if not text or not text.strip():
111
- return "", "skipped (empty)"
112
- if src_lang == tgt_lang:
113
- return text, "skipped (same language)"
114
-
115
- max_words = CHUNK_WORDS_INDIC if src_lang in INDIC_LANGS else CHUNK_WORDS
116
- chunks = self._chunk(text, max_words)
117
- print(f"[Translator] {len(chunks)} chunks ({max_words}w), "
118
- f"{len(text)} chars, {src_lang}→{tgt_lang}")
119
-
120
- # ⭐ KEPT: Indic→English: use Google first (better than transliterating opus-mt-mul-en)
121
- INDIC_TO_EN = {"te", "kn", "ml", "bn", "gu", "mr", "pa", "ur"}
122
- if src_lang in INDIC_TO_EN and tgt_lang == "en":
123
- try:
124
- result = self._google_chunks(chunks, src_lang, tgt_lang)
125
- if "[Translation failed" not in result[0]:
126
- return result
127
- except Exception as e:
128
- logger.warning(f"Google {src_lang}→en failed ({e}), trying IndicTrans2")
129
-
130
- # Priority 1: IndicTrans2
131
- is_indic = (
132
- (src_lang == "en" and tgt_lang in INDICTRANS2_SUPPORTED) or
133
- (src_lang in INDICTRANS2_SUPPORTED and tgt_lang == "en")
134
- )
135
- if is_indic:
136
- try:
137
- return self._indictrans2_chunks(chunks, src_lang, tgt_lang)
138
- except Exception as e:
139
- logger.warning(f"IndicTrans2 failed ({e}), trying Helsinki")
140
-
141
- # Priority 2: Helsinki-NLP
142
- if (src_lang, tgt_lang) in HELSINKI_MODELS:
143
- try:
144
- return self._helsinki_chunks(chunks, src_lang, tgt_lang)
145
- except Exception as e:
146
- logger.warning(f"Helsinki failed ({e}), trying NLLB")
147
-
148
- # Priority 3: NLLB
149
- try:
150
- if not self._nllb_loaded:
151
- self._init_nllb()
152
- self._nllb_loaded = True
153
- if self._pipeline is not None or self._model is not None:
154
- return self._nllb_chunks(chunks, src_lang, tgt_lang)
155
- except Exception as e:
156
- logger.warning(f"NLLB failed ({e}), using Google")
157
-
158
- # Priority 4: Google
159
- return self._google_chunks(chunks, src_lang, tgt_lang)
160
-
161
- # ══════════════════════════════════════════════════════════════════
162
- # PUBLIC — SUMMARIZE
163
- # ══════════════════════════════════════════════════════════════════
164
- def summarize(self, text: str, max_sentences: int = 5) -> str:
165
- try:
166
- sentences = re.split(r'(?<=[.!?।])\s+', text.strip())
167
- sentences = [s.strip() for s in sentences if len(s.split()) > 5]
168
- if len(sentences) <= max_sentences:
169
- return text
170
- n = len(sentences)
171
-
172
- def score(idx, sent):
173
- if idx == 0: pos = 1.0
174
- elif idx == n - 1: pos = 0.7
175
- elif idx <= n * 0.2: pos = 0.6
176
- else: pos = 0.3
177
- wc = len(sent.split())
178
- bonus = 0.3 if 10 <= wc <= 30 else (0.0 if wc < 10 else 0.1)
179
- return pos + bonus
180
-
181
- scored = sorted(enumerate(sentences),
182
- key=lambda x: score(x[0], x[1]), reverse=True)
183
- top_indices = sorted([i for i, _ in scored[:max_sentences]])
184
- return " ".join(sentences[i] for i in top_indices).strip()
185
- except Exception as e:
186
- logger.warning(f"Summarize failed: {e}")
187
- return text[:800] + "..."
188
-
189
- # ══════════════════════════════════════════════════════════════════
190
- # INDICTRANS2
191
- # ══════════════════════════════════════════════════════════════════
192
- def _load_indictrans2(self):
193
- if self._it2_loaded:
194
- return
195
- from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
196
- # IndicTransToolkit is NOT on PyPI — must be installed manually.
197
- # If missing, _load_indictrans2 raises RuntimeError and the caller
198
- # falls back to Helsinki / NLLB / Google automatically.
199
- try:
200
- from IndicTransToolkit.processor import IndicProcessor
201
- except ImportError:
202
- raise RuntimeError(
203
- "IndicTransToolkit not installed. "
204
- "Run: pip install git+https://github.com/AI4Bharat/IndicTransToolkit.git"
205
- )
206
- import torch
207
-
208
- dtype = torch.float16 if torch.cuda.is_available() else torch.float32
209
-
210
- print(f"[Translator] Loading {INDICTRANS2_EN_INDIC}...")
211
- self._it2_tok_ei = AutoTokenizer.from_pretrained(
212
- INDICTRANS2_EN_INDIC, trust_remote_code=True)
213
- self._it2_mod_ei = AutoModelForSeq2SeqLM.from_pretrained(
214
- INDICTRANS2_EN_INDIC, trust_remote_code=True, torch_dtype=dtype)
215
- if torch.cuda.is_available():
216
- self._it2_mod_ei = self._it2_mod_ei.cuda()
217
- self._it2_mod_ei.eval()
218
-
219
- print(f"[Translator] Loading {INDICTRANS2_INDIC_EN}...")
220
- self._it2_tok_ie = AutoTokenizer.from_pretrained(
221
- INDICTRANS2_INDIC_EN, trust_remote_code=True)
222
- self._it2_mod_ie = AutoModelForSeq2SeqLM.from_pretrained(
223
- INDICTRANS2_INDIC_EN, trust_remote_code=True, torch_dtype=dtype)
224
- if torch.cuda.is_available():
225
- self._it2_mod_ie = self._it2_mod_ie.cuda()
226
- self._it2_mod_ie.eval()
227
-
228
- self._it2_ip = IndicProcessor(inference=True)
229
- self._it2_loaded = True
230
- print("[Translator] ✅ IndicTrans2 fully loaded!")
231
-
232
- def _indictrans2_chunks(self, chunks, src_lang, tgt_lang):
233
- """
234
- ⭐ KEPT: IndicProcessor.preprocess_batch / postprocess_batch
235
- FIX L9: forced_bos_token_id now resolved and passed to model.generate()
236
- so the decoder is forced to output the correct target language.
237
- """
238
- t0 = time.time()
239
- if not self._it2_loaded:
240
- self._load_indictrans2()
241
-
242
- import torch
243
-
244
- src_code = INDICTRANS2_CODES.get(src_lang, "eng_Latn")
245
- tgt_code = INDICTRANS2_CODES.get(tgt_lang, "tel_Telu")
246
-
247
- if src_lang == "en":
248
- tokenizer, model = self._it2_tok_ei, self._it2_mod_ei
249
- else:
250
- tokenizer, model = self._it2_tok_ie, self._it2_mod_ie
251
-
252
- # FIX L9: resolve forced_bos_token_id for target language
253
  try:
254
- forced_bos_token_id = tokenizer.convert_tokens_to_ids(tgt_code)
255
- except Exception:
256
- forced_bos_token_id = None
257
- logger.warning(f"[Translator] Could not resolve token id for {tgt_code}")
258
-
259
- results = []
260
- for i, chunk in enumerate(chunks):
261
- if not chunk.strip():
262
- continue
263
- try:
264
- # ⭐ KEPT: IndicProcessor pre-processing
265
- batch = self._it2_ip.preprocess_batch(
266
- [chunk], src_lang=src_code, tgt_lang=tgt_code)
267
- inputs = tokenizer(
268
- batch, truncation=True, padding="longest",
269
- return_tensors="pt", return_attention_mask=True,
270
- max_length=MAX_TOKENS,
271
- )
272
- if torch.cuda.is_available():
273
- inputs = {k: v.cuda() for k, v in inputs.items()}
274
-
275
- gen_kwargs = dict(
276
- num_beams=4,
277
- num_return_sequences=1,
278
- max_length=MAX_TOKENS,
279
- )
280
- # FIX L9: enforce target language in decoder
281
- if forced_bos_token_id is not None:
282
- gen_kwargs["forced_bos_token_id"] = forced_bos_token_id
283
-
284
- with torch.no_grad():
285
- ids = model.generate(**inputs, **gen_kwargs)
286
-
287
- decoded = tokenizer.batch_decode(
288
- ids, skip_special_tokens=True,
289
- clean_up_tokenization_spaces=True,
290
- )
291
- # ⭐ KEPT: IndicProcessor post-processing
292
- postprocessed = self._it2_ip.postprocess_batch(decoded, lang=tgt_code)
293
- results.append(postprocessed[0])
294
-
295
- except Exception as e:
296
- logger.warning(f"IndicTrans2 chunk {i+1} failed: {e}")
297
- results.append(chunk)
298
-
299
- translated = " ".join(results)
300
- logger.info(f"IndicTrans2 done in {time.time()-t0:.2f}s")
301
- return translated, f"IndicTrans2-1B ({src_lang}→{tgt_lang}, {len(chunks)} chunks)"
302
-
303
- # ══════════════════════════════════════════════════════════════════
304
- # HELSINKI-NLP
305
- # ══════════════════════════════════════════════════════════════════
306
- def _helsinki_chunks(self, chunks, src_lang, tgt_lang):
307
- t0 = time.time()
308
- model_id = HELSINKI_MODELS[(src_lang, tgt_lang)]
309
- pipe = self._get_helsinki_pipeline(model_id)
310
- results = []
311
- for i, chunk in enumerate(chunks):
312
- if not chunk.strip():
313
- continue
314
- try:
315
- out = pipe(chunk, max_length=MAX_TOKENS)
316
- # ⭐ KEPT: handles both key names across transformers versions
317
- text = out[0].get("translation_text") or out[0].get("generated_text", chunk)
318
- results.append(text)
319
- except Exception as e:
320
- logger.warning(f"Helsinki chunk {i+1} failed: {e}")
321
- results.append(chunk)
322
- translated = " ".join(results)
323
- logger.info(f"Helsinki done in {time.time()-t0:.2f}s")
324
- return translated, f"Helsinki-NLP ({model_id.split('/')[-1]}, {len(chunks)} chunks)"
325
-
326
- def _get_helsinki_pipeline(self, model_id: str):
327
- if model_id not in self._helsinki_models:
328
- from transformers import pipeline as hf_pipeline
329
- print(f"[Translator] Loading Helsinki {model_id}...")
330
- # ⭐ KEPT: fallback to text2text-generation for newer transformers
331
- try:
332
- pipe = hf_pipeline("translation", model=model_id,
333
- device_map="auto", max_length=MAX_TOKENS)
334
- except Exception:
335
- pipe = hf_pipeline("text2text-generation", model=model_id,
336
- device_map="auto", max_length=MAX_TOKENS)
337
- self._helsinki_models[model_id] = pipe
338
- print(f"[Translator] ✅ {model_id} ready")
339
- return self._helsinki_models[model_id]
340
-
341
- # ══════════════════════════════════════════════════════════════════
342
- # CHUNKING — FIXED (L10)
343
- # ══════════════════════════════════════════════════════════════════
344
- def _chunk(self, text, max_words):
345
- """
346
- FIX L10: Added word-boundary fallback.
347
- Transcripts often have no sentence-ending punctuation — without this,
348
- the entire text becomes one oversized chunk causing OOM / truncation.
349
- """
350
- sentences = re.split(r'(?<=[.!?।])\s+', text.strip())
351
- chunks, cur, count = [], [], 0
352
-
353
- for s in sentences:
354
- words_in_sent = s.split()
355
- w = len(words_in_sent)
356
-
357
- # FIX L10: sentence longer than max_words → split on word boundaries
358
- if w > max_words:
359
- if cur:
360
- chunks.append(" ".join(cur))
361
- cur, count = [], 0
362
- for start in range(0, w, max_words):
363
- sub = " ".join(words_in_sent[start:start + max_words])
364
- chunks.append(sub)
365
- continue
366
-
367
- if count + w > max_words and cur:
368
- chunks.append(" ".join(cur))
369
- cur, count = [], 0
370
-
371
- cur.append(s)
372
- count += w
373
-
374
- if cur:
375
- chunks.append(" ".join(cur))
376
- return chunks
377
-
378
- # ══════════════════════════════════════════════════════════════════
379
- # NLLB
380
- # ══════════════════════════════════════════════════════════════════
381
- def _nllb_chunks(self, chunks, src_lang, tgt_lang):
382
- t0 = time.time()
383
- src_code = NLLB_CODES.get(src_lang, "eng_Latn")
384
- tgt_code = NLLB_CODES.get(tgt_lang, "tel_Telu")
385
- results = []
386
- for i, chunk in enumerate(chunks):
387
- if not chunk.strip():
388
- continue
389
- try:
390
- if self._pipeline is not None:
391
- out = self._pipeline(chunk, src_lang=src_code,
392
- tgt_lang=tgt_code, max_length=MAX_TOKENS)
393
- results.append(out[0]["translation_text"])
394
- else:
395
- import torch
396
- inputs = self._tokenizer(
397
- chunk, return_tensors="pt", padding=True,
398
- truncation=True, max_length=MAX_TOKENS,
399
- )
400
- if torch.cuda.is_available():
401
- inputs = {k: v.cuda() for k, v in inputs.items()}
402
- tid = self._tokenizer.convert_tokens_to_ids(tgt_code)
403
- with torch.no_grad():
404
- ids = self._model.generate(
405
- **inputs, forced_bos_token_id=tid,
406
- max_length=MAX_TOKENS, num_beams=4, early_stopping=True,
407
- )
408
- results.append(
409
- self._tokenizer.batch_decode(ids, skip_special_tokens=True)[0])
410
- except Exception as e:
411
- logger.warning(f"NLLB chunk {i+1} failed: {e}")
412
- results.append(chunk)
413
- translated = " ".join(results)
414
- logger.info(f"NLLB done in {time.time()-t0:.2f}s")
415
- return translated, f"NLLB-200-1.3B ({len(chunks)} chunks)"
416
-
417
- # ══════════════════════════════════════════════════════════════════
418
- # GOOGLE
419
- # ══════════════════════════════════════════════════════════════════
420
- def _google_chunks(self, chunks, src_lang, tgt_lang):
421
- t0 = time.time()
422
- try:
423
- from deep_translator import GoogleTranslator
424
- results = []
425
- for chunk in chunks:
426
- if not chunk.strip():
427
- continue
428
- out = GoogleTranslator(
429
- source=src_lang if src_lang != "auto" else "auto",
430
- target=tgt_lang,
431
- ).translate(chunk)
432
- results.append(out)
433
- full = " ".join(results)
434
- logger.info(f"Google done in {time.time()-t0:.2f}s")
435
- return full, f"Google Translate ({len(chunks)} chunks)"
436
  except Exception as e:
437
- logger.error(f"Google failed: {e}")
438
- return f"[Translation failed: {e}]", "error"
439
-
440
- # ══════════════════════════════════════════════════════════════════
441
- # NLLB INIT
442
- # ══════════════════════════════════════════════════════════════════
443
- def _init_nllb(self):
444
- try:
445
- from transformers import pipeline as hf_pipeline
446
- self._pipeline = hf_pipeline(
447
- "translation", model=NLLB_MODEL_ID,
448
- device_map="auto", max_length=MAX_TOKENS,
449
- )
450
- print("[Translator] ✅ NLLB pipeline ready")
451
- except Exception as e:
452
- logger.warning(f"NLLB pipeline init failed ({e}), trying manual")
453
- self._init_nllb_manual()
454
-
455
- def _init_nllb_manual(self):
456
- try:
457
- from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
458
- import torch
459
- self._tokenizer = AutoTokenizer.from_pretrained(NLLB_MODEL_ID)
460
- self._model = AutoModelForSeq2SeqLM.from_pretrained(
461
- NLLB_MODEL_ID,
462
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
463
- )
464
- if torch.cuda.is_available():
465
- self._model = self._model.cuda()
466
- self._model.eval()
467
- print("[Translator] ✅ NLLB manual load ready")
468
  except Exception as e:
469
- logger.error(f"NLLB manual load failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import logging
2
+ from transformers import pipeline
3
 
4
  logger = logging.getLogger(__name__)
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  class Translator:
7
  def __init__(self):
8
+ self.model_name = "Helsinki-NLP/opus-mt-en-mul"
9
+ self.translator = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  try:
11
+ # FIX: Change task to "translation" to match Helsinki requirements
12
+ self.translator = pipeline("translation", model=self.model_name)
13
+ logger.info("[Translator] 🌐 Helsinki-NLP loaded successfully")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  except Exception as e:
15
+ logger.warning(f"[Translator] Pre-load waiting for first run: {e}")
16
+
17
+ def translate(self, text, target_lang):
18
+ if not text: return ""
19
+
20
+ lang_map = {
21
+ "Hindi": ">>hin<<", "Telugu": ">>tel<<",
22
+ "Spanish": ">>spa<<", "French": ">>fra<<", "German": ">>deu<<"
23
+ }
24
+
25
+ prefix = lang_map.get(target_lang, ">>hin<<")
26
+ input_text = f"{prefix} {text}"
27
+
28
+ try:
29
+ if self.translator is None:
30
+ self.translator = pipeline("translation", model=self.model_name)
31
+
32
+ result = self.translator(input_text)
33
+ return result[0]['translation_text']
 
 
 
 
 
 
 
 
 
 
 
 
34
  except Exception as e:
35
+ logger.error(f"Translation failed: {e}")
36
+ return f"[Translation Error: {e}]"