"""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
""")