本地化

本页面为自动翻译,可能包含错误。如果发现问题,请通过“编辑此页面”链接帮助修正。

Turian 的本地化系统(ADR 0011)由引擎、已发布的游戏和 Studio 本身共享——一个运行时,一种创作格式,一个 CLI。本页介绍其工作流程:创作 → 提取 → 翻译 → 编译 → 发布。


到达字符串的两种方式

入口点 用于 创作者
tr("Open Scene…") UI 界面(菜单、按钮、HUD 标签) 源字符串本身 程序员,在代码中
Locale.key("dlg.act1.intro") 游戏内容(对话、物品名称、任务) 显式的稳定 id 设计师,在 .strings 资源中

两者都编译到同一个表中,并由同一个 Locale 服务提供——唯一的区别是 id 的来源。

// 简单文本
frame.tr("Open Scene…")

// 上下文消歧(两个相同的英文字符串,含义不同)
frame.trc("door", "Open")

// 复数(CLDR 基数规则为活动语言选择正确的形式)
frame.trn("# file", "# files", count, &.{})

// 插值(ICU 子集命名占位符,而非 std.fmt 说明符)
frame.trArgs("Save changes to \"{title}\" before closing?", &.{
    .{ .name = "title", .value = .{ .text = doc_title } },
})

// 设计师创作的游戏内容,按 id 查找
frame.trKey("dlg.act1.intro", &.{})

tr/trc/trn 将其消息作为 comptime 参数——非字面量参数会导致编译错误,而不是审查意见。缺失的翻译会降级为英文源文本,绝不会降级为原始键。key/trKey 在调试构建中降级为 ⟦the.key⟧,在发布版本中降级为裸键,因此缺失的设计师内容翻译永远不会悄无声息地变得不可见。

消息语法是 ICU MessageFormat 的一个有文档记录的子集:{name}{count, plural, one {# file} other {# files}}(带有可选的精确值分支 =0 {...}),以及 {gender, select, male {He} other {They}}

注册服务

var locale = engine.Locale.init("en"); // default_locale
locale.loadTable(embedded_pt_br_strtab) catch {};
frame.services.register(engine.Locale, &locale);

在运行时切换语言——无需重新加载场景,因为 UI 每帧都会重新渲染:

locale.setLocale("pt-BR");

已加载的表会在整个会话期间缓存,切换时永远不会被释放,因此任何已经发放的字符串切片都保持有效(在被重新获取之前,它只会显示旧的语言环境)。

创作翻译

.strings 文件是唯一的真实来源——JSON 格式,每个语言环境一个文件:

{
  "version": 1,
  "locale": "pt-BR",
  "units": [
    { "id": "Open Scene…", "source": "Open Scene…", "target": "Abrir Cena…", "state": "translated" }
  ]
}

提取 → 翻译 → 编译

# 遍历源码树以查找 tr/trc/trn/trKey 调用点,写入(或更新,同时保留
# 现有翻译)一个 .strings 文件:
turian-cli i18n extract <src-dir> pt-BR my-project/i18n/pt-BR.strings.json

# 为每个单元填充 "target",然后烘焙为编译后的运行时格式:
turian-cli i18n compile my-project/i18n/pt-BR.strings.json my-project/i18n/pt-BR.strtab

extract 可以在任何时候安全地重新运行:匹配的 id 会保留其现有的 target;新的调用点会以 state: "new" 添加。compile 只会输出 target 非空的单元——未翻译的字符串根本不会出现在表中,并在运行时回退到英文源文本,因此部分翻译覆盖率永远不会成为硬性错误。

翻译 Studio 本身

Studio 自身的 UI 使用完全相同的机制,通过 studio/services/StudioLocale.zig(一个薄包装器,公开由单个 Locale 实例支持的 tr/trc/trn/trArgs)。Studio 不是一个项目,因此它的目录会通过 @embedFile 嵌入,而不是经过资源管线:studio/i18n/<locale>.strings.json(源文件)和 studio/i18n/<locale>.strtab(编译后,已嵌入)。

在修改 UI 代码后更新 Studio 的 pt-BR 目录:

turian-cli i18n extract studio pt-BR studio/i18n/pt-BR.strings.json
# 翻译 .strings.json 中任何新的单元(state: "new")
turian-cli i18n compile studio/i18n/pt-BR.strings.json studio/i18n/pt-BR.strtab

语言选择器位于 Settings → UI → Language;切换会立即生效(Studio 会将选择持久化到 editor.ui.language)。

复数

复数类别是根据活动语言的 CLDR 基数规则解析的——one/few/many/other(对于阿拉伯语等语言还有 zero/two)——而不是手写的按语言条件判断。trn/{n, plural, ...} 覆盖了常见情况;存在显式的 =0 {...} 风格分支时,它的优先级高于类别。

不在范围内

RTL 布局镜像、BiDi 塑形和机器翻译不属于此系统的一部分。语言环境元数据中保留了一个 direction 字段,供未来的 RTL 支持使用。


← 所有文档 编辑此页面