跳至主要内容
SheetForge

本地化表格

一张工作表就能容纳你的游戏在所有语言下的文本:行是键,列是语言。本地化表格在所有重要的方面都是一张普通的 SheetForge 工作表——它的导入、校验、导出、推送与往返方式都和数据表完全一样——而当安装了 Unity Localization 包(com.unity.localization)时,每一次完成的导入还会用它来填充该包自身的 StringTable 集合。这样,你的运行时就能消费标准的 LocalizedString 引用,同时工作表始终是唯一真实来源。

本页讲的是你的游戏文本。产品自身的 10 语言 UI 是一个独立的主题——见本地化

表格的形状(@loc

一张工作表通过带有一行 @loc 标记而成为本地化表格。与 @overlap@style 一样,它可以位于数据之上的任意位置;它在每一列中的单元格都为那一列命名其语言代码

@loc     |            | en             | ko          |       |
@name    | codeName   | en             | ko          | smart | comment
@type    | RecordId   | string?        | string?     | bool? | string?
@desc    | key        | source text    | Korean      |       |
         | ui.ok      | OK             | 확인        | false | Confirm button
         | ui.cancel  | Cancel         | 취소        |       |
  • RecordId 键列是必需的——每一行的键值(ui.ok)就是本地化键,而标签页名称就是 StringTable 集合的名称:一个标签页对应一个集合。
  • 语言列是字符串列,其 @loc 单元格携带该语言的代码(enkopt-BR——任何类似标识符的标签都可以;SheetForge 校验的是代码的拼写形态,而不是这个代码是否真实存在,两个仅大小写不同的代码会被拒绝)。请把它们写成 string?:这样一个未翻译的单元格代表的就是覆盖率缺口,而不是导入错误——见下文的覆盖率
  • 第一个语言列是源语言。 它的文本就是数据表引用单元格内联预览的内容,也是自动铸造(minting)所写入的内容。
  • 两个按名称保留的可选列smart(布尔值——把该条目标记为一个 Unity Localization Smart String)与 comment(字符串——会同步进该条目的注释元数据)。当这些列存在时,工作表就是这些信息的权威来源;当它们缺失时,桥接会让对应的表格元数据保持原样。一个保留列不能同时携带一个语言代码。
  • 至少需要一个语言代码,并且一张工作表不能同时既是枚举定义表又是本地化表格(@enum@loc 是一个冲突错误,只会报告一次)。

除此之外,它就是一张普通的工作表:暂存与 Ctrl+Z、结构编辑、@style 分组、xlsx 与 Google 往返、推送,以及 web 应用,全都把它当作一张普通表格来对待。发生变化的是输出:一个本地化标签页不会产生任何生成的记录类,不会产生 Database ScriptableObject,也不会产生任何 Addressables 地址。取而代之,它会供给两样东西——键常量桥接

从数据表引用文本(LocRef@Tab

一张数据表用一个 LocRef 引用列来指向一个本地化条目:

@name    | codeName    | displayName
@type    | RecordId    | LocRef@Strings
@desc    | unique key  | shown in UI
         | item.sword  | item.sword.name

LocRef@Strings 的行为与你已经熟悉的内置引用(RecordId@Tab)完全一致:

  • 在导入时进行完整性验证——一个不存在于 Strings 标签页中的键,会成为一个带有最接近匹配建议的结构化错误;一个拼写错误会死在导入阶段,而不是运行时。目标必须是一张本地化表格(否则会报 LocRefTargetNotLocalizationSheet),不带 @TargetLocRef 会被拒绝,并给出正确写法的建议。
  • 完整的引用轨道——可搜索的键选择器、键重命名传播(重命名一个键会在同一批次内重写每一个引用它的单元格)、记录画布上的图边、导出的下拉菜单规则,以及孤儿检测,这一切在编辑器与 web 应用中都同样可用。
  • 可以像任何引用一样组合——List<LocRef@Strings> 与可选形式 LocRef@Strings?(一个空单元格就是一个空引用)都可以正常工作。
  • 单元格展示的是文本,而不只是键。 一个 LocRef 单元格会内联预览该条目源语言的文本,因此一张写满键的工作表读起来依然像一句句话。记录画布也一样——引用行会附带源文本,被截断的值总能在工具提示中看到全文。
  • 在空单元格中输入内容会铸造出该条目。 在一个空的 LocRef 单元格中输入源文本,SheetForge 会作为一次操作、一步撤销暂存下以下内容:目标本地化表格中的一个新键(根据记录名和字段名建议生成——之后可以随意重命名,传播机制会保持每一处引用完好无损)、你输入的文本作为它的源语言取值,以及你输入所在单元格中的这个引用。两种宿主环境皆可。

落到你代码里的是什么

代码生成会把这个字段输出为 LocRef——一个存在于 SheetForge 运行时程序集中的普通可序列化结构体(包含目标表格与键),并且无论是否安装了 Unity Localization 包都能编译;生成的代码与烘焙出的 ScriptableObject 永远不会包含这个包中的类型。当该包已安装时,一次扩展方法调用即可桥接进去:

var text = definition.displayName.ToLocalizedString(); // UnityEngine.Localization.LocalizedString

ToLocalizedString() 只有在该包存在时才存在(一个版本 define,SHEETFORGE_LOCALIZATION,会开启这层扩展——与 SHEETFORGE_ADDRESSABLES 使用的是同一套机制)。没有这个包时,这个字段依然是一对形式完好、可以由你自己消费的表格/键。

一次导入会生成什么

除了常规输出之外,一次导入还会为整个项目写出一个 SheetForgeLocalizationKeys.cs——每个本地化标签页对应一个静态类(StringsKeys,……),其中每个键对应一个 public const string,这样游戏代码就可以写 StringsKeys.ui_ok,而不是裸的 "ui.ok",从而获得编译期安全性与 IDE 自动补全。

  • 成员名是被清理为 C# 标识符的键(ASCII 字母、数字和 _ 之外的字符会变成 _;发生冲突时会附加一个确定性的数字后缀)。如果你想要可用的常量,就把键保持为 ASCII——一个完全非 ASCII 的键会被清理成一团下划线。
  • 与工作表定义的枚举文件一样,这个常量文件是一项项目级别的输出,总是落在设置指定的生成代码文件夹中——功能与限制中同样的程序集说明适用于此。
  • 一个没有任何行的本地化标签页依然会保留一个空类,因此清空一张工作表不会破坏引用了该类型的代码。

Unity Localization 桥接

在安装了该包的情况下,SheetForge 会为每个本地化标签页维护一个 StringTable 集合——键、值,以及在这些列存在时的 smart/comment 信息。

  • 何时运行: 自动地,在一次导入完成的那一刻——这与导出、推送作为出口所处的地位相同——此外还有一个手动重新同步操作,供你按需运行。
  • 方向: 单向的,工作表 → 表格。工作表是权威的,表格是产出物。
  • 语言: 一个在项目中找不到匹配 Locale 资源的工作表语言,会被自动创建,并在报告中被点名。一个只存在于项目中的语言会被原样保留,并被报告为未被工作表覆盖
  • 重命名键会让场景引用保持存活。 在 Data Studio 中重命名一个键,会经过与所有引用相同的重命名机制,桥接会原地重命名该表格条目,同时保留其内部 id——场景或预制件中的一个 LocalizedString 绑定的正是这个 id,因此它能在重命名后存活下来。如实说明的边界: 一次发生在 Studio 之外的重命名——直接在 Google 表格或 Excel 中编辑工作表来源——与删除一个键、再新增另一个键是无法区分的。桥接会创建一个全新的条目(新的 id),并把旧的那个当作孤儿;指向旧条目的场景引用会继续指向那个孤儿。请在 Studio 中重命名键。
  • 工作表未建模的元数据始终会被保留。 注释(当没有 comment 列时)、排除标志,以及任何其他表格元数据,都会原样通过每一次同步。

外部编辑只会被询问,绝不会被静默合并

桥接会给它所拥有的表格打上标记,并记住上一次同步的指纹。如果此后表格发生了变化——有人在 Localization Tables 窗口中编辑了它,或者用 Unity 自带的 Google 表格扩展把内容拉了进去——下一次同步就会停下来询问:是从工作表覆盖,还是带着差异报告中止。这里没有静默合并,也没有静默覆盖。如果你想要一种双驾驶舱的工作流,请改为让另一个驾驶舱经由工作表流转——这正是翻译导出存在的原因。

孤儿键默认会被保留

一个存在于表格中、但已不在工作表里的键,就是一个孤儿:它会被保留,被列在一份孤儿报告中,并且只能通过一次显式的清理操作来移除(一次性全部清理,或逐个清理)。如果你想让表格精确镜像工作表,一个设置开关可以切换到同步时删除。任何东西都不会作为副作用被销毁。

没有这个包的情况下

Unity Localization 包是可选的。没有它:

  • 本地化表格依然是完整的工作表——创作、校验、覆盖率、导出、推送、xlsx、web 应用、键常量,以及 LocRef 字段,全部完全可用。
  • 唯一会等待的是 StringTable 同步这个出口,它会显示一条安装提示(每个会话一次)然后停下——与 Addressables 相同的引导模式,同样绝不会以程序方式安装。
  • 每个程序集、生成代码的每一行,在没有这个包的情况下都能编译通过。支持的包版本:1.5 或更高

把已有的表格迁移进一张工作表

已经在使用 Unity Localization 了?一个反向导入器可以把一个已有的 StringTable 集合转换成一张本地化表格,直接写入你的导入来源并自动导入——与创建工作表所走的是同一条路径。检查与修改都放在这之后,和其他任何工作表一样。如果当前激活的导入来源无法从编辑器写入,文件会落在导出输出的旁边,并附上一条提示告诉你把它移过去。当桥接之后把这张工作表同步回同一个集合时,条目 id 会通过键名匹配来继承——场景与预制件中已有的 LocalizedString 引用会完好无损地经历这次迁移。

翻译工作流

覆盖率:未翻译的单元格会被报告,而不是被拒绝

一个空的语言单元格并不是错误——导入会报告按语言统计的覆盖率(每种语言翻译了多少个键、还缺哪些),并且每一个出口都会保持开放。文本是逐步到位的;工作表绝不会因为一次尚未完成的翻译而被阻塞。

语言视图

一次只想专注于一种语言?一个语言视图可以切换哪些语言列是可见的。它属于 @style 这一族的显示元数据——绝不会触及导入指纹、代码生成,或任何输出。两种宿主环境皆可。

翻译导出与部分再导入

要把一种语言交给翻译人员,就导出一份翻译工作簿:选择需要的语言,得到一个包含键 + 源文本 + 注释 + 状态列的 xlsx,其中,自上次导出以来源文本发生过变化的条目会被标记为过时。文件返回后,把它作为一次部分合并再导入:行会按键匹配,并且只写入语言列——结构、其他语言以及工作表中的其他一切都保持不变。两种宿主环境皆可。 有一处诚实的不对称:状态记忆保存在 Unity 项目旁的本机文件中,因此从浏览器导出的工作簿其状态列始终为 new;重新导入时"针对旧源文本的翻译"提示比较的是文件自身携带的源文本,所以在两种宿主环境中都有效。

XLIFF 与 pseudo-locale

SheetForge 刻意重新实现 XLIFF 或伪本地化(pseudo-localization)——桥接所填充的表格就是普通的 Unity Localization 表格,因此该包自身的 XLIFF 导出/导入与 pseudo-locale 工具,能够像在任何其他项目中一样原样地在其上工作。不过请记住这种单向的权威关系:工具写表格的输出,会被下一次同步当作一次外部编辑来询问。要让翻译保持在唯一真实来源之内,请通过工作表(上面的翻译工作簿)把它们带回来,而不是直接写入表格。

如实说明两个相关的边界:桥接只覆盖字符串表——资源表是一项已被认领的待办事项——并且 SheetForge 不附带任何语言专属的 Smart Format 辅助工具(例如韩语的语法助词)。smart 列会把条目标记为 Smart String;超出该包所提供范围的格式化器,需要你通过该包自身的扩展点自行编写。

在 web 应用中

本地化表格在浏览器中也是一张普通的工作表:创作、校验、覆盖率、带内联源文本的 LocRef 选择器、铸造、语言视图,以及翻译工作簿,全部都能在 web.sheetforge.workers.dev 上使用。StringTable 同步是 Unity 编辑器的职责——浏览器没有可供写入表格的 Unity 项目,也不会假装有。

相关页面