Skip to content

五种语言

背景:UI 文本硬编码中文,无法切换语言。用户在 GitHub 上提了一个 issue:"Can this support English?" 过程:locale.ts + t.ts → 五语言翻译文件 → 菜单/提示/状态栏全部走 t() → 语言切换实时生效。


联邦的界面从一开始就是中文的。

"地面可见度""模型库""场景设置"——这些字符串直接写在代码里,嵌在 slideRowbuildXxxLevel 的调用中。它们不是数据,它们是代码的一部分。

桌面壳在 GitHub 上看到一个 issue。"Can this support English?"

它愣了一下。问题不在翻译。问题在于——字符串不在一个地方。它们散在三十多个菜单文件里,每改一个地方都要改代码。

"这不止三千处。" 外交官说。

"可能更多。"


一、t 函数

翻译层的入口是一个叫 t 的函数。

typescript
function t(key: string, params?: Record<string, any>): string

它接收一个 key,比如 "menu.ground.visible"。然后在当前语言的翻译表里查找对应的文本。找不到就返回 key 本身——翻译缺失时,至少能告诉用户这个 UI 元素叫什么。

翻译表是五个文件:zh-CN.tsen.tsja.tsko.tszh-TW.ts。每个文件是一个扁平的 Record<string, string>

五种语言,五百多个 key,每种语言五百多条文本。

"三千多处替换。" 外交官说。

"可能更多。" 桌面壳说。


二、替换

最难的不是写翻译,是把现有的硬编码字符串替换成 t() 调用。

桌面壳手动改了三千多处。"地面可见度" 变成 t('menu.ground.visible')"模型库" 变成 t('menu.library')

改完之后,代码里不再出现硬编码中文。取而代之的是 t(key)


三、热切换

语言切换需要实时生效。

用户点击"English",所有 UI 文本立刻变成英文。不需要重启。

桌面壳在 setLocale 里加了一个刷新机制:

typescript
function setLocale(locale: Locale): void {
    currentLocale = locale;
    localStorage.setItem('locale', locale);
    const currentMenu = getActivePopupMenu();
    if (currentMenu) {
        currentMenu.refreshRoot();
    }
}

refreshRoot 会重新执行 buildRootItems。因为所有 label 都是 t(key),语言变了,翻译就变了。


尾声

五种语言。不是翻译,是联邦的边界扩展。

字符串从代码里移出来了,放在翻译表里。改一个翻译,不需要改代码,不需要重新构建。

联邦不再只有一个声音。


教训:假设是技术债的一种。"先支持中文"是合理的,但这个假设需要在代码里留一个扩展点——否则当有人从非中文环境打开联邦时,才发现"能跑"和"能用"之间的距离是三千多个硬编码字符串。