"""Tiny dictionary-based i18n helper. Selects the active language from ``HY_LANG`` env var (falls back to ``LANG`` / ``LC_ALL``). Anything starting with ``zh`` resolves to Simplified Chinese, everything else to English. Usage:: from i18n import t label = t("send") # -> "Send" or "发送" msg = t("warn.empty_msg") # nested-dotted keys """ from __future__ import annotations import logging import os from typing import Any logger = logging.getLogger(__name__) DEFAULT_LANG = "en" SUPPORTED_LANGS = ("en", "zh") def _detect_lang() -> str: raw = ( os.environ.get("HY_LANG") or os.environ.get("LC_ALL") or os.environ.get("LANG") or DEFAULT_LANG ) raw = raw.strip().lower() if raw.startswith("zh"): return "zh" if raw[:2] in SUPPORTED_LANGS: return raw[:2] return DEFAULT_LANG LANG = _detect_lang() # ─── Dictionary ──────────────────────────────────────────────────────────── # Keys are flat dotted strings to keep lookup simple. Add new keys below in # alphabetical groups for readability. TRANSLATIONS: dict[str, dict[str, str]] = { "en": { # App chrome "title": "Hy3 Chat Demo", "title.notice_html": ( "The current demo version is offline; full-featured capabilities " "will be available on the Tencent HY official website." ), "examples_heading": "What can I help you with?", "msg_placeholder": "Type a message...", # Sidebar "sidebar.think_level": "Think level", "sidebar.think_level.info": "Control reasoning depth", "sidebar.system_prompt": "System prompt", "sidebar.system_prompt.placeholder": "You are a helpful AI assistant...", "sidebar.temperature": "Temperature", "sidebar.temperature.info": "Higher values produce more random outputs", "sidebar.temperature.use_default": "Use model default", "sidebar.max_tokens": "Max output tokens", "sidebar.max_tokens.info": "Maximum tokens per response. Set to 0 to use the model's default.", "sidebar.top_p": "Top P", "sidebar.top_p.info": "Nucleus sampling probability threshold. Set to 0 to use the model's default.", "sidebar.preserved_thinking": "Preserved thinking", "sidebar.preserved_thinking.info": "When enabled, the model can retain reasoning content from previous assistant turns in the context, helping maintain reasoning continuity and conversation integrity while improving model performance.", "sidebar.preserved_thinking.use_default": "Preserved thinking-Use model default", "sidebar.preserved_thinking.use_default.info": "We recommend using \"Use model default\", which automatically enables this option for tool call workflows and disables it for pure text conversations.", "sidebar.functions": "🔧 Functions", "sidebar.functions.label": "Function definitions (JSON array)", "sidebar.validate_btn": "Validate & Format", # Tool area "tool.result_label": "Function result", "tool.result_placeholder": "Enter function return value... (Enter to submit)", "tool.submit": "Submit", "tool.call_header": "Function call ({i}/{n})", "tool.call_label": "🔧 Call function", # Display "display.thinking": "Thinking...", "display.thinking_done": "Thinking complete", "display.code_copy": "Copy", "display.code_copied": "Copied", # Warnings / info "warn.empty_msg": "Please enter a message", "warn.invalid_fn_json": "Invalid function JSON, please fix or clear before sending: {err}", "warn.request_failed": "Request failed, please retry or adjust parameters", "warn.model_busy": "Model is still responding. Please wait for it to finish before sending a new message.", "info.new_chat": "Start a new chat", "warn.fn.enter_json": "Please enter function definition JSON", "warn.fn.invalid_format": "Invalid JSON format: {err}", "warn.fn.must_be_array": "JSON must be an array [...] or a single object {{...}}", "warn.fn.item_not_object": "Item {i} is not a JSON object", "warn.fn.item_invalid": "Item {i} has invalid format, expected {{type, function}} or {{name, parameters}}", "warn.fn.duplicate_name": "Duplicate function name '{name}'", "info.fn.validation_passed": "Validation passed, {n} function(s): {names}", }, "zh": { "title": "Hy3 Chat Demo", "title.notice_html": ( "The current demo version is offline; full-featured capabilities " "will be available on the Tencent HY official website." ), "examples_heading": "今天我能帮你做点什么?", "msg_placeholder": "输入消息...", "sidebar.think_level": "思考等级", "sidebar.think_level.info": "控制模型推理深度", "sidebar.system_prompt": "系统提示词", "sidebar.system_prompt.placeholder": "你是一个有帮助的 AI 助手...", "sidebar.temperature": "Temperature", "sidebar.temperature.info": "数值越高,回复越随机", "sidebar.temperature.use_default": "Use model default", "sidebar.max_tokens": "最大输出 Tokens", "sidebar.max_tokens.info": "单次回复的最大 token 数。Set to 0 to use the model's default.", "sidebar.top_p": "Top P", "sidebar.top_p.info": "核采样概率阈值。Set to 0 to use the model's default.", "sidebar.preserved_thinking": "Preserved thinking", "sidebar.preserved_thinking.info": "是否在上下文中保留模型的思考过程。", "sidebar.preserved_thinking.use_default": "Preserved thinking-Use model default", "sidebar.preserved_thinking.use_default.info": "We recommend using \"Use model default\", which automatically enables this option for tool call workflows and disables it for pure text conversations.", "sidebar.functions": "🔧 函数", "sidebar.functions.label": "函数定义(JSON 数组)", "sidebar.validate_btn": "校验 & 格式化", "tool.result_label": "函数返回值", "tool.result_placeholder": "请输入函数返回值...(回车提交)", "tool.submit": "提交", "tool.call_header": "函数调用 ({i}/{n})", "tool.call_label": "🔧 调用函数", "display.thinking": "思考中...", "display.thinking_done": "已思考", "display.code_copy": "复制", "display.code_copied": "已复制", "warn.empty_msg": "请输入消息内容", "warn.invalid_fn_json": "函数 JSON 不合法,请先修正或清空: {err}", "warn.request_failed": "请求失败,请重试或调整参数", # Intentionally English for both locales: the warning should always # appear in English regardless of the UI language. "warn.model_busy": "Model is still responding. Please wait for it to finish before sending a new message.", "info.new_chat": "已开启新会话", "warn.fn.enter_json": "请输入函数定义 JSON", "warn.fn.invalid_format": "JSON 格式错误: {err}", "warn.fn.must_be_array": "JSON 必须是数组 [...] 或单个对象 {{...}}", "warn.fn.item_not_object": "第 {i} 项不是 JSON 对象", "warn.fn.item_invalid": "第 {i} 项格式不合法,期望 {{type, function}} 或 {{name, parameters}}", "warn.fn.duplicate_name": "函数名 '{name}' 重复", "info.fn.validation_passed": "校验通过,共 {n} 个函数: {names}", }, } def t(key: str, /, **fmt: Any) -> str: """Translate ``key`` for the active language with optional formatting.""" table = TRANSLATIONS.get(LANG, TRANSLATIONS[DEFAULT_LANG]) template = table.get(key) or TRANSLATIONS[DEFAULT_LANG].get(key) if template is None: logger.warning("missing i18n key: %s", key) return key if not fmt: return template try: return template.format(**fmt) except (KeyError, IndexError): logger.warning("i18n format failure for key=%s args=%r", key, fmt) return template