"""Translation tab β€” NLLB-200 multilingual translation via capability bus.""" from __future__ import annotations import asyncio import concurrent.futures from typing import Any _LANGUAGES = [ "auto", "en", "de", "fr", "es", "pt", "it", "nl", "pl", "ru", "zh", "ja", "ko", "ar", "hi", "tr", "sv", "da", "fi", "no", "cs", "ro", "hu", "uk", "vi", "th", "id", "ms", "fa", "he", ] _LANG_NAMES = { "auto": "Auto-detect", "en": "English", "de": "German", "fr": "French", "es": "Spanish", "pt": "Portuguese", "it": "Italian", "nl": "Dutch", "pl": "Polish", "ru": "Russian", "zh": "Chinese", "ja": "Japanese", "ko": "Korean", "ar": "Arabic", "hi": "Hindi", "tr": "Turkish", "sv": "Swedish", "da": "Danish", "fi": "Finnish", "no": "Norwegian", "cs": "Czech", "ro": "Romanian", "hu": "Hungarian", "uk": "Ukrainian", "vi": "Vietnamese", "th": "Thai", "id": "Indonesian", "ms": "Malay", "fa": "Persian", "he": "Hebrew", } def _run(coro): try: loop = asyncio.get_running_loop() except RuntimeError: loop = None if loop and loop.is_running(): with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: return pool.submit(asyncio.run, coro).result() return asyncio.run(coro) def build_translation_tab(bus: Any | None = None) -> None: import gradio as gr gr.HTML("""

🌍 Translation β€” 200 Languages

NLLB-200 Β· Meta's No Language Left Behind Β· 200 languages Β· offline-first

""") lang_choices = [f"{code} β€” {_LANG_NAMES[code]}" for code in _LANGUAGES] with gr.Row(): with gr.Column(scale=2): src_text = gr.Textbox( label="Text to translate", placeholder="Enter text here…", lines=6, ) with gr.Row(): src_lang = gr.Dropdown( choices=lang_choices, value="auto β€” Auto-detect", label="From", ) tgt_lang = gr.Dropdown( choices=[c for c in lang_choices if not c.startswith("auto")], value="de β€” German", label="To", ) translate_btn = gr.Button("🌍 Translate", variant="primary", size="lg") with gr.Column(scale=3): out_text = gr.Textbox(label="Translation", lines=6, interactive=False) status_out = gr.Textbox(label="Status", lines=1, interactive=False) def _translate(text: str, src: str, tgt: str) -> tuple[str, str]: if not text.strip(): return "", "⚠ Enter text to translate" if bus is None: return "", "⚠ No bus β€” run inside a HearthNet node" src_code = src.split(" β€”")[0].strip() tgt_code = tgt.split(" β€”")[0].strip() if src_code == "auto": src_code = None async def _call(): return await bus.call( "trans.text", (1, 0), {"params": {"source_lang": src_code, "target_lang": tgt_code}, "input": {"text": text}}, ) try: result = _run(_call()) except Exception as exc: return "", f"⚠ Bus error: {exc}" if "error" in result: if result["error"] == "backend_unavailable": return "", "⚠ No translation backend β€” install: pip install transformers sentencepiece" return "", f"⚠ {result.get('message', result['error'])}" translated = result.get("output", result).get("text", str(result)) detected = result.get("output", result).get("detected_lang", "") note = f" (detected: {detected})" if detected and not src_code else "" return translated, f"βœ“ Translated{note}" translate_btn.click( _translate, inputs=[src_text, src_lang, tgt_lang], outputs=[out_text, status_out], ) gr.HTML("""
β„Ή Setup help
Requirements: pip install transformers sentencepiece torch
Model: facebook/nllb-200-distilled-600M (~2.5GB) β€” loads on first use
200 languages supported including low-resource languages not covered by Google Translate
Offline: once the model is downloaded, translation works without internet
""")