mcpField
L
localize-unity-project

localize-unity-project

github.com/unity-technologies开发1.3k355 次安装今天更新
代码仓库

关于 localize-unity-project

使用 Unity Localization 包为 Unity 项目添加和维护本地化的技能,指导设置、表格、界面文本、字体与布局变更,并强调保留项目原有行为。

主要功能

  1. 01检查 Localization 包及版本
  2. 02设置语言区域和配置
  3. 03更新字符串与资源表
  4. 04绑定本地化界面文本
  5. 05CJK 字体与布局指导
  6. 06基线检查和还原点保护

适用场景

  1. 01为 Unity 项目添加语言区域
  2. 02更新本地化字符串或资源表
  3. 03修复不同语言下的文本溢出
  4. 04为界面应用本地化字体

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/unity-technologies/skills --skill localize-unity-project

安装后在 Claude Code 中输入/localize-unity-project确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-notes.md
cjk-fonts.md
docs-map.md
table-completeness-check.md
translation-strategy.md
troubleshooting.md
scripts/
L10nBatchProcessor.cs
LocalizedFontAsset.cs

详细介绍

重要事项

  • 每一步都使用 Unity CLI(unity command --format json)以及 unity-cli 和 unity-pipeline 技能。
  • 修改之前,先建立当前行为的基线,以便判断后续结果是否确实由你的修改导致。
  • 创建一个还原点,以便在修改失败时回滚。
  • 只做要求的内容。不要更改无关的资源或文件。避免冗长说明。
  • 绝不要在用户的脚本中留下验证代码。绝不要添加用户未要求的日志记录或检测功能。

术语

这些名称在 Editor UI、包 API 和本技能中有所不同:

  • 表集合:保存每个区域设置对应的一个表以及共享键列表 SharedData 的资源(StringTableCollection 或 AssetTableCollection)。
  • LocalizedTmpFont:包类型,引用 Asset Table 中的一个 TMP_FontAsset 条目。
  • LocalizedFontAsset:本技能的组件模板(scripts/LocalizedFontAsset.cs),不是包类型。它会将 LocalizedTmpFont 应用到其 GameObject 上的 TMP_Text。

1. 检查是否已安装 Localization 包

在进行任何操作之前,先确认已安装 Localization 包。如果未安装该包,本技能中的许多 API 会静默失败或抛出令人困惑的错误。

  1. 通过读取项目来检查,不要询问 Package Manager。 在 Packages/packages-lock.json 中查找 com.unity.localization,因为该文件记录了 Unity 实际解析的内容,读取它不需要 Editor 或异步调用。Packages/manifest.json 只记录了请求安装的内容。
  2. 如果缺少则安装: UnityEditor.PackageManager.Client.Add("com.unity.localization")。
  3. 正确等待。 Client.Add 是异步的,在当前运行线程上忙等 IsCompleted 会阻塞主线程。触发安装后立即返回,然后在后续调用中轮询 packages-lock.json,直到出现该 ID。安装会触发域重新加载,因此预计最初一两次轮询会失败。
  4. 在使用这些类型之前,确认它们已实际加载,因为锁定文件可能会在程序集准备就绪前写入:
    var t = System.Type.GetType(
        "UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization");
    return t != null ? "ready" : "not loaded yet";
    
    只有在返回 ready 后才能继续。

2. 阅读文档

完整主题地图

在继续执行工作流或诊断之前:

  1. 打开并阅读涵盖你计划执行或修复事项的文档页面,并检查是否有遗漏。
  2. 在回复中列出你阅读过的文件名。

这是因为你关于 Unity 的训练知识可能已经过时、有误,或不适用于当前的 Unity 版本。

文档页面是 Unity Manual 的本地 Markdown 转换文件,存储在 <project-folder>/Library/PackageCache/com.unity.localization@*/。阅读文档时,只阅读该子文件夹中的 .md 文件,不要浏览其他 Library 或 PackageCache 文件夹,因为这会耗费太多时间。此限制不适用于其他步骤执行的包查找,例如 references/cjk-fonts.md 中的 TMP 导入。如果该文件夹中没有 .md 文件,请改用在线页面 https://docs.unity3d.com/Packages/com.unity.localization@<VERSION>/manual/,因为某些包版本不附带文档。

优先使用 WebFetch 而非 WebSearch——速度更快,并且会直接打开准确的参考页面。只获取所需内容。获取前,将 <VERSION> 替换为以下版本:

  • docs.unity3d.com 上的包文档:包版本,例如 @6.6 或 @2.0。不确定时,检查包版本。
  • docs.unity.com:产品版本,例如 7000.0

从 ProjectSettings/ProjectVersion.txt 读取 Unity 版本,并从 Packages/packages-lock.json 读取每个包的主版本号和次版本号(例如,com.unity.localization 的 1.5)。TextMesh Pro(TMP)API 页面位于 com.unity.ugui 文档中。如果 packages-lock.json 列出的是 com.unity.textmeshpro,则使用 https://docs.unity3d.com/Packages/com.unity.textmeshpro@<VERSION>/api/,并采用该包的版本。

阅读 references/docs-map.md,查看需要阅读的 Manual、API 和 Script Reference 页面。

API 参考

API 为 UnityEngine.Localization 和 UnityEditor.Localization,属于 com.unity.localization 包。其版本按包版本划分,而非按 Unity 版本划分(例如 @1.4、@1.5),因此在依赖特定类或方法之前,请检查项目已安装的包版本。

  1. 按照步骤 1中的说明,从 Packages/packages-lock.json 读取已安装的包版本。
  2. 获取并缓存 https://docs.unity3d.com/Packages/com.unity.localization@<version>/xrefmap.yml(将 <version> 替换为已安装的包版本,例如 1.5)。此文件将每个公共 API 成员的完全限定名称映射到其文档页面的 href(例如,href: api/UnityEngine.Localization.LocalizedTmpFont.html => https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Localization.LocalizedTmpFont.html)。
  3. 在本地 yml 文件中 grep 类或方法,而不要针对每次查询重新获取文件。不要读取整个文件。

务必获取与已安装包相匹配的版本,不要依赖训练知识,因为 API 经常会在主版本之间被标记为过时。TMP(com.unity.ugui 或 com.unity.textmeshpro)和 com.unity.addressables API 也适用相同步骤。

对于 Engine API(UnityEditor.AssetDatabase、UnityEditor.PackageManager、UnityEngine.Events),请按照 references/docs-map.md 中的 Engine API 查询步骤操作。

关于 Asset Table 条目、Resources/ 文件夹和字体修复方面的注意事项,请参阅 references/api-notes.md。

3. 本地化设置与区域

如果 LocalizationEditorSettings.ActiveLocalizationSettings 为 null,必须找到或创建它:

  1. **查找:**使用 AssetDatabase.FindAssets("t:LocalizationSettings", new[] { "Assets" })。如果找到,则加载第一个并将其赋值给 LocalizationEditorSettings.ActiveLocalizationSettings。
    • **始终传入搜索文件夹。**不限定范围的 FindAssets 会搜索整个项目,包括只读程序包,因此可能返回程序包中的资源,导致项目指向无法编辑的内容。本技能中的每次 FindAssets 调用都适用此规则。
  2. **创建:**如果未找到,则创建新实例并将其保存到 Assets/Localization/LocalizationSettings.asset。使用 ScriptableObject.CreateInstance<LocalizationSettings>(),然后调用 AssetDatabase.CreateAsset()。
  3. **激活:**设置 LocalizationEditorSettings.ActiveLocalizationSettings = settings。
  4. **区域:**确保区域(en、fr、de 等)存在。若缺失则创建,并使用 LocalizationEditorSettings.AddLocale(locale) 将其添加到设置中。

4. 修改本地化表

以编程方式修改字符串表或资源表时,必须通知 Editor。 始终创建所需的资源表,除非项目中已存在该表。

安全填充模式

使用数据集填充表时,请明确按照 Locale.Identifier.Code 匹配。GetLocales() 的顺序不一定与输入数据数组一致——假设二者顺序相同会导致数据悄然错配,而且很难调试。 对于资源表,请使用资源的 GUID:table.GetEntry(sharedId) ?? table.AddEntry(sharedId, guid);。

刷新与通知

每次修改(添加键、更新值)后,都要通知 Editor,以便刷新其内部状态。跳过此步骤会导致 Editor 一直显示过期数据,直到下次重新导入。

  1. 对每个已修改的 Table 调用 EditorUtility.SetDirty(collection) 和 EditorUtility.SetDirty(collection.SharedData)。
  2. 通知:LocalizationEditorSettings.EditorEvents.RaiseCollectionModified(sender, collection);
  3. 最后始终调用 AssetDatabase.SaveAssets()。

5. UI 本地化与布局

命名空间与冲突
  • **单实例:**始终检查 GameObject.Find("YourCanvasName"),并在创建新对象前销毁旧对象。
  • **区域切换:使用程序包,并将预览与运行时分开。**这是两种不同的机制;将二者混为一谈,正是区域切换常常需要手动实现的原因。
    • **在创作时预览某个区域,**请使用 Localization Scene Controls 窗口(Window > Asset Management > Localization Scene Controls)。此功能仅限 Editor 使用,并非运行时功能,因此不能用于游戏本身需要语言设置的场景。
    • **在运行时切换区域,**请设置 LocalizationSettings.SelectedLocale。这是受支持的入口,所有通过 LocalizeStringEvent 绑定的内容都会随之更新。
    • **指定游戏启动时使用的区域,**请在 Localization Settings 资源上配置启动区域选择器。SpecificLocaleSelector 用于强制指定区域;否则默认选择链会采用系统语言。
    • **绝不要手动实现区域状态管理。**真正的游戏内语言菜单完全没问题,而且也符合预期,只要它设置 SelectedLocale 并让程序包传播更改即可。禁止的是会追踪自身“当前语言”变量、自行替换字符串或绕过程序包的调试下拉菜单或菜单,因为项目中的其他内容都不会随之更新。
本地化字符串事件(稳健绑定)
  1. 检查组件类型: 确认目标是 TextMeshPro 还是旧版 UnityEngine.UI.Text。
  2. 正确绑定: 添加公共的 UnityEngine.Localization.Components.LocalizeStringEvent 组件,并自行连接——将 StringReference 设置为表项,然后添加一个 OnUpdateString 侦听器,将值赋给文本组件(对于 TextMeshPro,赋给 TMP_Text.text;对于旧版 Text,赋给 UnityEngine.UI.Text.text)。

不要反射访问 UnityEditor.Localization.Plugins.TMPro.LocalizeComponent_TMPro 或其 UGUI 对应组件,因为它们是 internal,可能会在任何软件包版本中发生变化。 3. 重建布局: 设置本地化文本或填充列表后,调用 UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(parentTransform),确保尺寸更新。

6. 亚洲语言字体支持(CJK)

为中文、日语或韩语区域设置配置字体前,请阅读 references/cjk-fonts.md,并按其中的顺序操作。

7. 自动布局(UGUI)

翻译后的字符串通常比源文本长得多,因此为某种语言设置尺寸的标签在另一种语言中可能会被裁切或溢出。

  1. 更改布局前,请阅读 references/docs-map.md 中列出的 UGUI 自动布局页面,因为正确的布局组和适配器设置取决于现有层级结构。
  2. 仅更改至少有一个区域设置中出现溢出的标签容器,因为已经能适配所有区域设置的固定布局就是用户的设计。
  3. 切换到每种区域设置并检查受影响标签的 TMP_Text.isTextOverflowing,以进行验证。
翻译现有项目时的注意事项
  • 尽量少改代码:切勿修改与本地化无关的代码。使用静态辅助类(例如 L10n)封装 LocalizationSettings.StringDatabase.GetLocalizedString,以便轻松将其注入现有脚本。
  • 稳健的映射策略:将现有 UI 文本映射到键时,按字符串长度降序排列键,并优先匹配最长的字符串。这样可以避免短字符串(例如 "NO")匹配到较长句子的部分内容。在适当情况下使用不区分大小写的匹配。
  • 组件事件侦听器:使用 UnityEventTools.AddPersistentListener 为 LocalizeStringEvent.OnUpdateString 接线,并传入基于文本组件公共 text setter 构建的委托。setter 没有 C# 方法组名称,因此要按名称构建委托: (UnityAction<string>)Delegate.CreateDelegate(typeof(UnityAction<string>), text, "set_text")。 这是对公共成员的反射调用,没有问题。请参阅 scripts/L10nBatchProcessor.cs,了解可正常工作的版本,其中也会先清除现有的持久侦听器,以免重复运行时叠加重复项。
    • 不要通过 SerializedObject 直接写入持久调用字段(m_MethodName、 m_Mode、m_PersistentCalls)。这些是私有序列化名称,没有兼容性保证,而且没有必要:使用上述委托调用 AddPersistentListener 会生成相同的序列化调用(目标 = 文本组件,方法 = set_text,模式 = EventDefined)。

    • 接下来必须设置调用状态,否则标签不会在 Editor 中更新。 AddPersistentListener 会将调用状态设为 UnityEventCallState.RuntimeOnly,因此绑定虽然正确,但在 Play 模式之外不会触发:在 Editor 中切换区域设置不会有任何变化,保存并重新加载后仍然如此。使用公共方法 UnityEventBase.SetPersistentListenerState 修复:

      UnityEventTools.AddPersistentListener(lse.OnUpdateString, setText);
      var index = lse.OnUpdateString.GetPersistentEventCount() - 1;
      lse.OnUpdateString.SetPersistentListenerState(
          index, UnityEngine.Events.UnityEventCallState.EditorAndRuntime);
      
    • 持久侦听器必须指向 UnityEngine.Object 上的方法;lambda 会失败。

    • **然后确认绑定已生效,不要想当然。**在 Inspector 中看起来接线正确却没有任何效果,是这一步最典型的失败情况。你需要的所有回读信息都能通过事件的公共 API 获取,因此无需接触任何序列化字段:

      检查项调用预期结果
      已完成接线GetPersistentEventCount()> 0
      指向文本组件GetPersistentTarget(i)、GetPersistentMethodName(i)该组件、set_text
      在编辑时会触发GetPersistentListenerState(i)EditorAndRuntime
      确实更新了标签lEvent.RefreshString()文本值发生变化
      四项都要完成,因为已接线但从未触发的绑定看起来像是已完成,实际上标签仍未本地化。
  • 初始化与刷新:
    • LocalizationEditorSettings.CreateStringTableCollection 需要的是目录路径(例如 Assets/Localization),而不是完整的资源路径。
    • 以编程方式分配 LocalizedString 引用后,务必调用 lEvent.RefreshString(),以立即更新 UI。
    • 集合中的每个表(en、de、ja,等等)都必须包含键,且键对应的值不能为空。键存在但值为空是最常见的遗漏,而且这不会悄无声息:该包会将自己的“No translation found for …”文本显示在游戏 UI 中,导致发布的界面上出现开发者提示。不要凭肉眼判断。运行下面的完整性检查。
  • 验证:修改表或 Addressables 后,运行 AddressableAssetSettings.BuildPlayerContent(),并切换 Editor 语言环境以验证更改。激活后检查 LocalizationSettings.Instance 的状态。
表完整性检查(宣布工作完成前运行此检查)

阅读 references/table-completeness-check.md,运行其中的检查并报告输出。

  • 智能字符串:在需要的地方设置智能字符串。检查每个字符串的上下文时,要考虑它所在的完整 UI,以及会影响它的任何脚本。在字符串表中设置上下文,确保翻译恰当。

8. 推荐的翻译策略

在翻译现有项目或查找仍需本地化的文本时,请阅读 references/translation-strategy.md。

9. 加速本地化工作流

若要高效地本地化整个项目,请使用批处理脚本一次性处理所有场景。

**执行前先征求同意:**运行任何批处理操作前,请向用户确认:

“这将打开项目中的每个场景,附加 LocalizeStringEvent 组件,并保存所有已修改的场景。此操作无法自动撤销。是否继续?”

只有在用户确认后才能继续。批处理器模板位于 scripts/L10nBatchProcessor.cs。

LocalizeAll 自身也会强制执行这些检查,因此改用模板时不要移除或绕过这些检查:它会拒绝在批处理模式下运行,提示用户保存或丢弃未保存的场景更改,并显示一个阻止继续的 Editor 对话框,列出将被重写的场景。请告知用户留意 Editor 并点击确认按钮;如果用户取消操作,它会抛出 OperationCanceledException 且不会写入任何内容。应将此情况报告为取消,而不是设法绕过的错误。

它会遍历 Text 和 TMP_Text,而 LocalizeAll 会返回无法匹配的标签 (格式为 scene :: object :: "text")。请打印此列表。这正是返回值存在的意义:一次接线了 20 个标签、却悄悄遗漏另外 9 个的运行结果,除此之外看起来会与完整运行毫无区别。该列表仅涵盖编写在场景中的文本,因此还要结合 references/translation-strategy.md 中的代码扫描,以及第 7 步中的表完整性检查。

加速技巧
  • **表引用:**使用 TableReference 名称(字符串)而不是 GUID,这样更易于阅读和维护。
  • **批量刷新:**修改后使用 LocalizationSettings.Instance.ForceRefresh(),强制更新 Editor 中的 UI。
  • **字体替换自动化:**创建一次 GameAssets 表,然后使用脚本为所有标签批量附加 LocalizedFontAsset 组件(模板)。

错误排查

如果控制台或游戏 UI 中出现错误,请在 references/troubleshooting.md 中查找,应用相应解决方法,然后重新阅读产生该错误的步骤对应的 文档页面。

最后一步

按顺序运行以下检查。如果任何检查未通过,请重新阅读文档页面,找出遗漏之处,修复后重新运行检查。

  1. 运行 unity command console status(console_status 检查),并确认 groundTruth.consoleErrors 为 0,且 compilationFailed 为 false。
  2. 运行表格完整性检查,并保留其输出以供报告使用。
  3. 到此为止,除非用户要求提供证明,或怀疑存在特定故障,例如标签不会随区域设置变化。
  4. 如果怀疑存在故障,且更改会影响运行时行为,则进入 Play 模式检查一次。通过 eval 读取世界状态,例如 LocalizationSettings.SelectedLocale 和相关标签的 text,而不是添加日志。不要截图,因为这会耗费太多时间。完成后退出 Play 模式。

最终报告

按以下顺序报告:

  1. 更改内容:添加或更改的区域设置、表格、键、组件和字体资源。
  2. 未转换内容:列出所有未转换的文本位置及原因,包括 LocalizeAll 未匹配的标签。
  3. 验证结果:报告“最后一步”中每项检查的结果,包括表格完整性检查的输出及任何缺漏。
  4. 后续事项:说明用户必须执行的操作,例如确认哪些缺漏是有意为之、审阅翻译,或重建 Addressables 内容。

评论

支持 Markdown