mcpField
L
localization

localization

github.com/unity-technologies开发1.2k5.7k 次安装1 天前更新
代码仓库

关于 localization

这是一份 Unity Localization 工作流指南,涵盖语言环境、表格、UI 绑定和 CJK 字体资源配置,面向自动化处理 Unity 项目本地化设置与更新的开发者。

主要功能

  1. 01检查并安装 Localization 包
  2. 02配置设置与语言环境
  3. 03创建和更新字符串及资源表
  4. 04将本地化字符串绑定到 UI 组件
  5. 05设置特定语言的 CJK 字体
  6. 06刷新资源并构建 Addressables

适用场景

  1. 01在 Unity 项目中设置本地化
  2. 02根据本地化数据填充表格
  3. 03在预览或运行时切换语言环境
  4. 04为本地化内容添加 CJK 字体资源

安装

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

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

安装后在 Claude Code 中输入/localization确认已加载。

包含的文件

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

references/
api-notes.md
resources/
L10nBatchProcessor.cs
LocalizedFontAsset.cs

详细介绍

本指南介绍如何设置和配置 Unity Localization,包括地区设置、字符串表和资产表、Addressables 集成,以及通过资产表支持 CJK 字体。

0. 检查软件包安装情况

开始之前,请先确认已安装 Localization 软件包。如果未安装,本文中的许多 API 会静默失败或抛出令人困惑的错误。

  1. 通过读取项目检查,而不是询问 Package Manager。 在 Packages/packages-lock.json 中查找 com.unity.localization。该文件记录了 Unity 实际解析的内容,是纯 JSON,无需 Editor,也无需异步调用即可读取。 (Packages/manifest.json 只记录了 请求安装 的内容,因此请检查 lock 文件。)
  2. 如果缺少,则安装: UnityEditor.PackageManager.Client.Add("com.unity.localization")。
  3. 正确等待。 Client.Add 和 Client.List 是异步的:调用时会返回一个仍处于 InProgress 状态的请求,因此在同一语句中读取结果毫无意义。也不要对 IsCompleted 进行忙等待,因为这会阻塞当前运行的主线程。应在触发安装后返回,然后在后续调用中轮询 packages-lock.json,直到出现该 ID。安装也会触发域重载,因此前一两次轮询可能会失败;全新安装通常会在几秒内完成。
  4. 使用这些类型之前,先确认它们已实际加载,因为 lock 文件可能会在程序集就绪前写入:
    var t = System.Type.GetType(
        "UnityEngine.Localization.Settings.LocalizationSettings, Unity.Localization");
    return t != null ? "ready" : "not loaded yet";
    
    只有在返回 ready 后才能继续。

1. Localization 设置和地区

如果 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) 将其添加到设置中。

2. 修改 Localization 表

以编程方式更改字符串表或资产表时,必须通知 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. Unity 6+ 通知: LocalizationEditorSettings.EditorEvents.RaiseCollectionModified(sender, collection);
  3. 最后务必调用 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)。此功能仅限 Editor 使用,不适用于运行时,因此无法满足游戏本身需要语言设置的场景。
    • **在运行时切换区域设置,**请设置 LocalizationSettings.SelectedLocale。这是受支持的入口,通过 LocalizeStringEvent 绑定的所有内容都会据此更新。
    • **指定游戏启动时使用的区域设置,**请在 Localization Settings 资源上配置启动区域设置选择器。SpecificLocaleSelector 用于强制选择指定区域设置;否则,默认链会采用系统语言。
    • **绝不要自行实现区域设置状态。**真正的游戏内语言菜单完全可行且符合预期,只要它设置 SelectedLocale 并让软件包传播更改即可。禁止的是使用调试下拉菜单或菜单跟踪自己的“当前语言”变量、自行替换字符串或绕过软件包,因为项目中的其他内容都不会随之更新。
本地化字符串事件(可靠绑定)
  • **检查组件类型:**确认目标是 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 也属于此类调用,因此字体创建会在第一行就失败,错误看起来像是代码本身有问题。

// The check.
var ready = TMPro.TMP_Settings.instance != null;

如果尚未就绪,请以非交互方式导入:

// Do NOT use EditorApplication.ExecuteMenuItem("Window/TextMeshPro/Import TMP Essential Resources").
// It returns true and then opens a dialog that waits for a human, so nothing gets imported and the
// run appears to hang. Import the package directly instead.
string package = null;
var cache = System.IO.Path.GetFullPath(System.IO.Path.Combine(
    UnityEngine.Application.dataPath, "..", "Library", "PackageCache"));
foreach (var dir in System.IO.Directory.GetDirectories(cache))
{
    // TMP ships inside com.unity.ugui in Unity 6, and the folder name carries a version hash,
    // so search for the file rather than hardcoding a path.
    var candidate = System.IO.Path.Combine(dir, "Package Resources", "TMP Essential Resources.unitypackage");
    if (System.IO.File.Exists(candidate)) { package = candidate; break; }
}
UnityEditor.AssetDatabase.ImportPackage(package, false);   // false = non-interactive

然后在后续调用中轮询 TMP_Settings.instance != null,方式与步骤 0 中的软件包检查相同;只有确认其不为 null 后才继续。已在 Unity 6000.5.8f1 上验证:非交互式导入会在几秒内完成,资源会放在 Assets/TextMesh Pro 中。

  1. 使用特定语言的字体: Arial 或 Liberation Sans 等西文字体不包含 CJK 字形,因此会显示“豆腐块”(方块)。务必使用为目标语言设计的字体:
    • 对于简体中文(zh-Hans):使用 msyh.ttc(Microsoft YaHei)或同等字体。
    • 对于日语(ja):使用 msgothic.ttc(MS Gothic)或同等字体。
    • 对于韩语(ko):使用 malgun.ttf(Malgun Gothic)或同等字体。
    • 如果复制系统字体失败,请停止并报告。不要用西文字体替代。
  2. 可靠地创建字体: 从导入的字体动态创建 TMP_FontAsset。
  3. 多图集与动态模式: CJK 字符集太大,无法使用静态图集;单个图集很快就会耗尽空间。
    • fontAsset.atlasPopulationMode = AtlasPopulationMode.Dynamic;
    • fontAsset.isMultiAtlasTexturesEnabled = true;
  4. 子资源: 添加图集纹理和材质。只添加纹理是常见错误,这样材质就永远不会写入文件:在 Unity 6000.5.8f1 上测得,保存时未调用第二次方法的字体资源在磁盘上包含零个 Material 对象,而调用了该方法的则包含一个。仅存在于内存中的材质不属于该资源,因此任何重新加载该资源的对象都会获得 TMP 重建的材质,而不是你配置的材质,你对其应用的任何设置也会悄然丢失。
    // 每个图集纹理,而不只是第一个。步骤 3 启用了多图集,因此可能会有多个图集。
    foreach (var atlas in fontAsset.atlasTextures)
    {
        UnityEditor.AssetDatabase.AddObjectToAsset(atlas, fontAsset);
    }
    // 还要添加材质。没有这一行,材质就不会写入资源。
    UnityEditor.AssetDatabase.AddObjectToAsset(fontAsset.material, fontAsset);
    
    • 显式关联材质的纹理:fontAsset.material.mainTexture = fontAsset.atlasTexture; 并在保存前将字体资源、其材质和纹理标记为脏数据。(atlasTexture 是 atlasTextures 中的第一个条目,也是主材质绘制所用的纹理,因此这与上面添加所有纹理的做法一致。)
    • 根据文件验证,而不是根据你手头的对象验证。 调用 AssetDatabase.SaveAssets() 后,调用 AssetDatabase.LoadAllAssetsAtPath(path),并确认返回的对象中包含一个 Material。不要只满足于 fontAsset.material != null:无论材质是否已保存,这个条件都会成立,因为 TMP 会返回一个内存中的材质,因此无法据此判断材质是否已保存。
  5. Addressables: Asset Table 中引用的每个资源都必须标记为 Addressable。
    • 不要在 Asset Table 中引用 Resources/ 文件夹里的资源。这会导致 OperationException: Failed to load sub-asset 错误。如果资源位于 Resources/ 中,请先将其复制到 Assets/Fonts/ 或类似位置,再将其标记为 Addressable。
    • 如果字体资源被删除并重新创建,必须手动更新 Asset Table 中的新 GUID,并将其重新添加到 Addressables。
  6. 专用类型: 对于 TextMesh Pro 字体切换,优先使用 LocalizedTmpFont,而不是 LocalizedAsset<TMP_FontAsset>,以避免隐式转换错误。
  7. 构建要求: 更新 Asset Table 或 Addressable 组后,触发一次构建:AddressableAssetSettings.BuildPlayerContent();。
验证步骤

在完成任何 CJK 本地化任务之前:

  1. 豆腐块检查: 将编辑器区域设置切换为 zh-Hans、ja 和 ko。检查 UI。如果有任何字符显示为方块(豆腐块),则字体配置失败。
  2. Asset Table 检查: 验证 CJK 区域设置对应的 AssetTable 指向正确的 CJK TMP_FontAsset,而不是默认的西文字体。
  3. 多图集检查: 确认 CJK 字体资源上的 isMultiAtlasTexturesEnabled 为 true。

5. 自动布局(UGUI)

  • 父对象: VerticalLayoutGroup 的 Child Control Height: True、Child Force Expand Height: False。
  • 标签: 每个标签都必须设置 ContentSizeFitter,并将 Vertical Fit 设为 Preferred Size。
  • TMP: 将 Enable Word Wrapping 设为 True,并将 Overflow 设为 Overflow。
翻译现有项目时的注意事项
  • 尽量减少代码改动:不要修改与本地化无关的代码。使用静态辅助类(例如 L10n)封装 LocalizationSettings.StringDatabase.GetLocalizedString,以便轻松注入现有脚本。
  • 稳健的映射策略:将现有 UI 文本映射到键时,按字符串长度降序排列键,并优先匹配最长的字符串。这样可以避免较短的字符串(如 "NO")匹配到较长句子的部分内容。适当时使用不区分大小写的匹配。
  • 组件事件监听器:使用 UnityEventTools.AddPersistentListener 为 LocalizeStringEvent.OnUpdateString 连接监听器,并传入基于文本组件公开 text setter 创建的委托。setter 没有 C# 方法组名称,因此要按名称创建委托: (UnityAction<string>)Delegate.CreateDelegate(typeof(UnityAction<string>), text, "set_text")。 这是对公开成员进行反射,没有问题。参见 resources/L10nBatchProcessor.cs,其中包含可正常工作的版本,并会先清除所有现有的持久监听器,以免重复运行时叠加重复项。
    • 不要通过 SerializedObject 直接写入持久调用字段(m_MethodName、 m_Mode、m_PersistentCalls)。这些是没有兼容性保证的私有序列化名称,而且没有必要这样做:使用上述委托调用 AddPersistentListener 会生成相同的序列化调用(目标 = 文本组件,方法 = set_text,模式 = EventDefined)。

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

      UnityEventTools.AddPersistentListener(lse.OnUpdateString, setText);
      var index = lse.OnUpdateString.GetPersistentEventCount() - 1;
      lse.OnUpdateString.SetPersistentListenerState(
          index, UnityEngine.Events.UnityEventCallState.EditorAndRuntime);
      

      已在 Unity 6000.5.8f1 上验证:不进行第二次调用时,即使预制件保存并重新加载,监听器也不会在 Edit 模式下触发;进行第二次调用后,调用状态会变为 EditorAndRuntime,文本会立即更新。

    • 持久监听器必须指向 UnityEngine.Object 上的方法;lambda 会失败。

    • 然后确认绑定确实生效,不要想当然。 在 Inspector 中看似正确的连接却不起作用,是此步骤的典型故障。所需的所有回读操作都是事件的公开 API,因此无需触碰任何序列化字段:

      检查项调用预期结果
      已连接监听器GetPersistentEventCount()> 0
      目标是文本组件GetPersistentTarget(i)、GetPersistentMethodName(i)组件、set_text
      编辑时会触发GetPersistentListenerState(i)EditorAndRuntime
      确实更新标签lEvent.RefreshString()文本值发生变化
      全部执行这四项。计数大于零只能证明某些内容已接线,而 RuntimeOnly 调用会导致最后一项检查失败,尽管它对构建来说完全正确,因此读取状态才能区分休眠绑定和损坏的绑定。已添加并配置却从未触发的组件比未本地化的标签更糟,因为它看起来像是已经完成。
  • 初始化与刷新:
    • LocalizationEditorSettings.CreateStringTableCollection 需要的是目录路径(例如 Assets/Localization),而不是完整的资源路径。
    • 以编程方式分配 LocalizedString 引用后,务必调用 lEvent.RefreshString(),以立即更新 UI。
    • 每个集合中的所有表(en、de、ja、……)都必须存在对应键,且值不能为空。键存在但值为空是最常见的遗漏,而且这不会悄无声息:程序包会将自己的“找不到……的翻译”文本显示在游戏 UI 中,导致发布的界面显示开发者消息。不要凭目测判断。运行下面的完整性检查。
  • 命名空间与 Linq:搜索集合时,务必包含 using System.Linq;;处理区域设置或表时,务必包含 using UnityEngine.Localization;。
  • 验证:修改表或 Addressables 后,运行 AddressableAssetSettings.BuildPlayerContent() 并切换 Editor 区域设置以验证更改。激活后检查 LocalizationSettings.Instance 的状态。
表完整性检查(宣布工作完成前运行此检查)

枚举表可以机械地回答“每个区域设置中的每个键是否都有值”,从而在有人开始玩游戏前发现缺失项。运行检查并报告输出。

var gaps = new System.Collections.Generic.List<string>();
var checkedCount = 0;

foreach (var col in UnityEditor.Localization.LocalizationEditorSettings.GetStringTableCollections())
{
    foreach (var key in col.SharedData.Entries)
    {
        foreach (var table in col.StringTables)
        {
            checkedCount++;
            var entry = table.GetEntry(key.Id);
            // A missing entry and a present-but-empty entry both show as untranslated in game.
            if (entry == null || string.IsNullOrWhiteSpace(entry.Value))
            {
                gaps.Add($"{col.TableCollectionName} / {table.LocaleIdentifier.Code} / {key.Key}");
            }
        }
    }
}

// Zero entries is not a pass. It means no collections or no locales were found, so the check
// examined nothing: report that distinctly instead of letting it read as success.
if (checkedCount == 0)
{
    return "INCONCLUSIVE: no table entries found. Either no String Table Collection exists yet, "
         + "or the collection has no locale tables. Fix that before trusting this check.";
}

return gaps.Count == 0
    ? $"COMPLETE: {checkedCount} entries checked, no gaps"
    : $"GAPS ({gaps.Count} of {checkedCount} checked):\n  " + string.Join("\n  ", gaps);

在 Unity 6000.5.8f1 上验证:对于一个故意将 ja 值清空的表,检查会报告 GAPS 1 of 4,并准确指出该条目;填入值后会报告 COMPLETE (4 checked);在没有表的项目中则会报告 INCONCLUSIVE。

请报告缺失项列表,不要悄悄解决。 有些缺失项是待定决策,而非错误:例如,用户未要求翻译的区域设置,或有意在不同语言中保持一致的键。用英文文本填补这些缺失项会掩盖相关决策。请列出这些项,让用户指出哪些是有意为之。

  • 智能字符串:在需要时设置智能字符串。检查每个字符串的上下文时,应考虑其所在的整个 UI,以及所有会影响它的脚本。在字符串表中设置上下文,确保翻译符合语境。

6. 推荐的翻译策略

要高效地翻译现有项目,请遵循以下多步工作流:

  1. 提取与组件设置:

    • 查找所有出现的位置。有两个独立的隐藏位置,只扫描其中一个会漏掉另一个。
      • 编写的文本位于场景和预制件中的组件上:旧版 UnityEngine.UI.Text 和 TextMeshPro(TMP_Text,是 TextMeshProUGUI 和 TextMeshPro 的基类)。请遍历这两个系列。在实际项目中测得:FindObjectsByType<Text> 找到 1 个组件,而 FindObjectsByType<TMP_Text> 找到 13 个,因此只扫描旧版组件会在几乎什么都没做的情况下报告成功。
      • 在代码中拼接的文本在编辑时不会出现在组件上,因此遍历场景无法发现它。scoreLabel.text = $"EXP {value}" 对所有基于组件的扫描都不可见,而这个字符串会在“已完成”的本地化流程结束后遗留下来。请直接在 C# 中查找:
        # 携带字符串字面量的赋值和 SetText 调用。
        grep -rnE '\.text\s*(=|\+=)\s*\$?"|SetText\(\s*\$?"' --include='*.cs' Assets/
        
        该模式能捕获普通、插值、拼接和 += 形式,以及 SetText,并有意不匹配 label.text = someVariable 或 label.text = Localize("KEY")(前者没有可提取的内容,后者已完成路由)。它无法发现存放在其他位置声明的变量或常量中的字面量;如果项目的匹配数量看起来偏少,也请搜索该文件中的字符串字面量。
    • 报告未转换的内容。 拼接字符串通常需要智能字符串或格式参数,这需要进行判断;而有些字符串确实不值得本地化。无论选择哪种做法,都要列出扫描发现的每个位置,并说明是否已转换;如果没有转换,也要说明原因。如果扫描发现 9 处未处理,却只报告“已本地化 24 个字符串”,这正是此清单要避免的问题:工作看起来已经完成,漏洞却只会在其他语言环境的截图中暴露出来。
    • 共享表格: 创建一个集中的 String Table(例如 UIStrings),其中包含基础语言,并为每个键添加“Context”列,为译者提供参考。
    • 附加组件: 为找到的每个 UI 元素附加一个 LocalizeStringEvent(用于文本)和一个 LocalizedFont 辅助组件(用于替换字体)。
    • 验证: 确保这些组件已设置持久化侦听器(EditorAndRuntime),以便在区域设置变化时立即在编辑器中更新。
  2. 结合上下文进行翻译:

    • 翻译: 填充表格后,为每种语言提供译文。
    • 上下文至关重要: 始终参考“Context”列或检查 UI 布局,确保译文符合预期含义并适合显示空间。
    • 语法与语气: 确保语气符合游戏风格。例如,按钮应使用祈使动词(例如,德语使用“Lauf!”而不是“Laufen”),标签应使用正确的复数形式(例如,使用“Punkte”而不是“Punkt”)。
  3. 质量保证(QA):

    • 场景控件: 使用 Window > Asset Management > Localization Scene Controls,或运行脚本:LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.GetLocale("de");。
    • 目视检查: 系统地检查基础语言和所有目标语言中的每个预制件和场景。
    • 布局适配: 检查文本是否溢出,或是否出现“豆腐块”(缺失字形)。如果字符串太长,可调整字号或使用 ContentSizeFitter。

API 参考

有关详细的 API 用法、常见命名空间冲突、Addressables 模式和字体修复步骤,请参阅 references/api-notes.md。

7. 加速本地化工作流

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

操作前先询问: 运行任何批处理操作前,请先向用户确认:

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

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

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

它会遍历 Text 和 TMP_Text,而且 LocalizeAll 会返回无法匹配的标签 (格式为 scene :: object :: "text")。请打印此列表。返回此列表正是其目的:一次运行连接了 20 个标签,却悄悄漏掉另外 9 个,除此之外看起来会和全部处理完成毫无区别。该列表仅涵盖编写在场景中的文本,因此要结合第 6 节的代码扫描和第 5 节的表格完整性检查。

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

评论

支持 Markdown