本指南涵盖设置和配置 Unity Localization,包括语言环境、字符串和资源表、Addressables 集成以及通过资源表实现的 CJK 字体支持。
0. 包安装检查
在执行其他操作之前,请先验证 Localization 包是否已安装。如果该包不存在,此技能中的许多 API 将会静默失败或抛出令人困惑的错误。
- 通过读取项目文件检查,而不是询问 Package Manager。 在
Packages/packages-lock.json中查找com.unity.localization。该文件记录了 Unity 实际解析的内容,是纯 JSON 格式,读取它无需编辑器也无需异步调用。(Packages/manifest.json仅记录请求的内容,所以请检查锁文件。) - 如果缺失则安装:
UnityEditor.PackageManager.Client.Add("com.unity.localization")。 - 正确等待。
Client.Add和Client.List是异步的:它们返回一个在调用返回时仍处于InProgress状态的请求,因此在同一语句中读取结果没有任何意义。也不要忙等IsCompleted;这会阻塞您正在运行的主线程。相反,在触发安装后返回,然后在后续调用中轮询packages-lock.json,直到 ID 出现。安装还会触发域重载,因此预计前一到两次轮询可能会失败;新安装通常会在几秒钟内完成解析。 - 在使用前确认类型已实际加载,因为锁文件可能在程序集准备就绪之前就被写入:
仅在返回var t = System.Type.GetType( "UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization"); return t != null ? "ready" : "not loaded yet";ready后才继续操作。
1. Localization 设置与语言环境
如果 LocalizationEditorSettings.ActiveLocalizationSettings 为 null,则必须找到或创建它:
- 查找: 使用
AssetDatabase.FindAssets("t:LocalizationSettings", new[] { "Assets" })。如果找到,加载第一个并将其分配给LocalizationEditorSettings.ActiveLocalizationSettings。- 始终传递搜索文件夹。 无范围限定的
FindAssets会搜索整个项目,包括只读包,因此它可能会返回包中的资源,导致项目指向您无法编辑的内容。这适用于此技能中的每个FindAssets调用。
- 始终传递搜索文件夹。 无范围限定的
- 创建: 如果未找到,创建一个新实例并将其保存到
Assets/Localization/LocalizationSettings.asset。使用ScriptableObject.CreateInstance<LocalizationSettings>(),然后使用AssetDatabase.CreateAsset()。 - 激活: 设置
LocalizationEditorSettings.ActiveLocalizationSettings = settings。 - 语言环境: 确保语言环境(en、fr、de 等)存在。如果缺失则创建,并使用
LocalizationEditorSettings.AddLocale(locale)将其添加到设置中。
2. 修改 Localization 表
对字符串或资源表的程序化更改需要通知编辑器。始终创建所需的资源表,除非项目中已存在现有表。
安全填充模式
从数据集填充表时,显式地通过 Locale.Identifier.Code 进行匹配。GetLocales() 的顺序不能保证与您的输入数据数组匹配——假设它会匹配将导致难以调试的静默数据不匹配。
对于资源表,使用资源的 GUID:table.GetEntry(sharedId) ?? table.AddEntry(sharedId, guid);。
刷新与通知
进行任何修改(添加键、更新值)后,通知编辑器以便其刷新内部状态。跳过此步骤将使编辑器显示过时数据,直到下次重新导入。
- 在每个修改过的
Table上调用EditorUtility.SetDirty(collection)、EditorUtility.SetDirty(collection.SharedData)。 - Unity 6+ 通知:
LocalizationEditorSettings.EditorEvents.RaiseCollectionModified(sender, collection); - 最后始终调用
AssetDatabase.SaveAssets()。
3. UI 本地化与布局
命名空间与冲突
- 始终使用完全限定名称: 使用
UnityEngine.UI.Image、UnityEngine.UI.VerticalLayoutGroup、UnityEngine.UI.ScrollRect、UnityEngine.UI.Mask、UnityEngine.UI.CanvasScaler、UnityEngine.UI.GraphicRaycaster、UnityEngine.UI.ContentSizeFitter、UnityEngine.UI.LayoutRebuilder等。 UnityEngine.UI既是一个命名空间也是一个类容器,因此非限定名称会导致CS0118(将命名空间用作类型)错误。完全限定可以完全避免这一点。- 单实例: 在创建新画布之前,始终检查
GameObject.Find("YourCanvasName")并销毁旧画布。 - 语言环境切换:使用包,并将预览和运行时分开。 这是两种不同的机制,将它们混为一谈正是导致语言环境切换最终经常手写的症结所在。
- 在编辑时预览语言环境,请使用 Localization Scene Controls 窗口 (
Window > Asset Management > Localization Scene Controls)。这仅限于编辑器。它不是运行时功能,因此当游戏本身需要语言设置时,它并不是答案。 - 在运行时切换语言环境,请分配
LocalizationSettings.SelectedLocale。这是受支持的入口点,通过LocalizeStringEvent绑定的所有内容都会随之更新。 - 要固定游戏启动时的语言环境,请在 Localization Settings 资源上配置启动语言环境选择器。
SpecificLocaleSelector是强制使用所选语言环境的选择器;否则,默认链会选择系统语言。 - 永远不要手写语言环境状态。 在游戏中实现一个真正的语言菜单是可以的,也是预期的,只要它设置
SelectedLocale并让包传播更改。禁止的做法是:一个调试下拉菜单或菜单维护自己的“当前语言”变量,自己交换字符串,或绕开包进行操作,因为项目中的其他任何东西都不会跟随它。
- 在编辑时预览语言环境,请使用 Localization Scene Controls 窗口 (
本地化字符串事件(稳健绑定)
-
检查组件类型: 确定目标是
TextMeshPro还是传统的UnityEngine.UI.Text。 -
正确绑定: 添加公开的
UnityEngine.Localization.Components.LocalizeStringEvent组件并自行连接——将StringReference设置为表条目,然后添加一个OnUpdateString监听器,将值分配给文本组件(TextMeshPro 使用TMP_Text.text,传统 Text 使用UnityEngine.UI.Text.text)。不要反射到
UnityEditor.Localization.Plugins.TMPro.LocalizeComponent_TMPro或其 UGUI 对应类中。这些是internal的(在 Localization 1.5.12 上测量),因此访问它们意味着绕过访问控制,使用一个 Unity 未做稳定性承诺的 API——它可以在任何包版本中更改或消失。LocalizeStringEvent是公开的,并且通过显式的接线完成相同的工作。 -
布局重建: 设置本地化文本或填充列表后,调用
UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(parentTransform)以确保尺寸更新。
4. 亚洲语言字体支持(CJK)
避免为 CJK 语言环境使用 TMP 后备字体。应改为对每个特定语言环境使用资源表字体切换——在字形缺失时,后备字体不可靠且难以调试。
先决条件:必须导入 TMP Essential Resources
在接触任何 TMP API 之前进行此项检查。 在从未导入过这些资源的项目中,TMP_Settings.instance 为 null,并且 TMP 调用会因一个不提供有用信息的简单 NullReferenceException 而失败。TMP_FontAsset.CreateFontAsset 就是其中之一,因此字体创建会在第一行就因错误而终止。