feat(webui): add i18n support and locale switcher

This commit is contained in:
Xubin Ren
2026-04-19 06:39:06 +00:00
parent be10ba1f0d
commit 4650b23d75
34 changed files with 6654 additions and 65 deletions
+86 -2
View File
@@ -4,7 +4,11 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<meta name="description" content="nanobot web UI — chat with your nanobot workspace." />
<meta
name="description"
content="nanobot web UI — chat with your nanobot workspace."
data-i18n-meta="description"
/>
<meta name="theme-color" content="#fafaf9" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#161618" media="(prefers-color-scheme: dark)" />
<link rel="icon" type="image/png" sizes="32x32" href="/brand/nanobot_favicon_32.png" />
@@ -84,6 +88,86 @@
} catch {}
})();
</script>
<script>
(function () {
var localeKey = "nanobot.locale";
var copy = {
en: {
boot: "Loading nanobot…",
description: "nanobot web UI — chat with your nanobot workspace."
},
"zh-CN": {
boot: "正在加载 nanobot…",
description: "nanobot Web UI —— 与你的 nanobot 工作区对话。"
},
"zh-TW": {
boot: "正在載入 nanobot…",
description: "nanobot Web UI —— 與你的 nanobot 工作區對話。"
},
fr: {
boot: "Chargement de nanobot…",
description: "Interface web nanobot — discutez avec votre espace de travail nanobot."
},
ja: {
boot: "nanobot を読み込み中…",
description: "nanobot Web UI — nanobot ワークスペースと会話します。"
},
ko: {
boot: "nanobot 불러오는 중…",
description: "nanobot 웹 UI — nanobot 작업공간과 대화하세요."
},
es: {
boot: "Cargando nanobot…",
description: "Interfaz web de nanobot: conversa con tu espacio de trabajo de nanobot."
},
vi: {
boot: "Đang tải nanobot…",
description: "Giao diện web nanobot — trò chuyện với workspace nanobot của bạn."
},
id: {
boot: "Memuat nanobot…",
description: "UI web nanobot — ngobrol dengan workspace nanobot Anda."
}
};
function normalizeLocale(input) {
if (!input) return "en";
var raw = String(input).trim();
if (!raw) return "en";
if (copy[raw]) return raw;
var lower = raw.toLowerCase();
if (lower === "zh" || lower.indexOf("zh-cn") === 0 || lower.indexOf("zh-sg") === 0) {
return "zh-CN";
}
if (
lower.indexOf("zh-tw") === 0 ||
lower.indexOf("zh-hk") === 0 ||
lower.indexOf("zh-mo") === 0 ||
lower.indexOf("zh-hant") === 0
) {
return "zh-TW";
}
var base = lower.split("-")[0];
return copy[base] ? base : "en";
}
try {
var stored = localStorage.getItem(localeKey);
var detected =
stored ||
(navigator.languages && navigator.languages[0]) ||
navigator.language ||
"en";
var locale = normalizeLocale(detected);
var localized = copy[locale] || copy.en;
document.documentElement.lang = locale;
var description = document.querySelector('[data-i18n-meta=\"description\"]');
if (description) description.setAttribute("content", localized.description);
var boot = document.querySelector("[data-boot-copy]");
if (boot) boot.textContent = localized.boot;
} catch {}
})();
</script>
<title>nanobot</title>
</head>
<body class="bg-background text-foreground antialiased">
@@ -91,7 +175,7 @@
<div class="boot-splash">
<div class="boot-splash-inner">
<span class="boot-dot" aria-hidden="true"></span>
<span>Loading nanobot…</span>
<span data-boot-copy>Loading nanobot…</span>
</div>
</div>
</div>