本地化

键到文本的查找、运行时语言切换,以及在玩家切换语言时实时更新的 UI 文本。

无需硬编码 UI 文本,即可让你的游戏以多种语言发行。

接口ILocalizationService
关闭开关NullLocalization
程序集CommonGameSystem.Localization(可选——需要 Unity 内置的 uGUI 包)
启动启动时自动注册;翻译表由你加载

功能概览

你不再把 "Start" 直接写进菜单,而是写一个键(key)——形如 "menu.start" 的稳定标识符。本地化服务在运行时把这个键解析为当前语言的文本。当玩家在设置菜单里切换语言时,屏幕上的每个 LocalizedText 组件都会立即更新——无需重载场景,无需重启。

缺失的翻译永远不会让游戏崩溃。在当前语言中查找失败会回退到后备语言(默认为英语),若仍然失败,则返回键本身。你的 UI 总能显示出点什么

翻译存放在 LocalizationTableAsset ScriptableObject 中——既可在 Inspector 里填写、也可从电子表格生成的普通数据资产。语言用标准的 BCP-47 语言标签标识,与浏览器和操作系统使用的短代码相同:"en""ko""ja""zh-CN" 等等。

由于该模块位于自己的可选程序集中,从项目中移除 uGUI 只会把这个程序集排除在外——框架的其余部分仍能正常编译和启动。

快速上手

在启动脚本中解析该服务并一次性加载翻译表:

using CommonGameSystem.Core;
using UnityEngine;

[DefaultExecutionOrder(100)] // Run after the framework has started.
public class GameStartup : MonoBehaviour
{
    [SerializeField] private LocalizationTableAsset[] tables; // assign in the Inspector

    private ILocalizationService _loc;

    private void Awake()
    {
        _loc = ServiceLocator.Resolve<ILocalizationService>();
        _loc.LoadTables(tables);

        // Optional: start in the player's OS language.
        _loc.SetLocale(LocaleId.FromSystemLanguage(Application.systemLanguage));
    }
}

之后,任何脚本都可以用 Get 获取文本:

string title = _loc.Get("menu.title");

API 参考

查找

  • string Get(string key) — 解析一个键:当前语言 → 后备语言 → 键本身。绝不抛出异常。
  • string Get(string key, params object[] args) — 相同的查找,随后用 string.Format 按不变文化(invariant culture)格式化结果。格式字符串有误时返回未格式化的文本,而不是抛出异常。
  • bool TryGet(string key, out string value) — 无副作用的查找。键缺失时返回 falsevalue 被设为该键)。不记录任何日志——适合在不刷警告的前提下探测键是否存在。

语言切换

  • void SetLocale(string localeId) — 切换当前语言。在事件总线上发布 LocaleChanged 事件,并通过配置服务保存这一选择,使其跨会话保留。
  • string CurrentLocale { get; } — 当前激活的区域设置 id。
  • IReadOnlyList<string> AvailableLocales { get; } — 已加载表中找到的区域设置 id。用这个列表驱动你的语言选择 UI。
  • string GetLocaleDisplayName(string localeId) — 该语言用其自身语言书写的名称(例如韩语显示为 "한국어"),用于语言选择 UI。未设置时回退为 id。

初始化

  • void LoadTables(IReadOnlyList<LocalizationTableAsset> tables) — 加载翻译表并激活已保存(或默认)的语言。启动时调用一次。

静态辅助方法

  • LocaleId.FromSystemLanguage(SystemLanguage) — 把 Application.systemLanguage 映射为 BCP-47 标签(例如 SystemLanguage.Korean"ko")。用它在首次启动时自动检测玩家的操作系统语言。

LocalizedText 组件

对于静态 UI 文本,完全不用写代码:把 LocalizedText 组件挂到任意 uGUI Text 或 TextMeshPro 组件上,在 Inspector 中设置它的键。该组件在启用时获取文本,并在语言变化时重新获取。

对于带数值的动态文本,使用 SetArgs

using CommonGameSystem.Core;
using UnityEngine;

public class ScoreDisplay : MonoBehaviour
{
    private LocalizedText _locText;

    private void Start()
    {
        _locText = GetComponent<LocalizedText>();
    }

    public void SetScore(int newScore)
    {
        // Table entry: "score.current" = "Score: {0}"  →  displays "Score: 42"
        _locText.SetArgs(newScore);
    }
}

示例:语言按钮

using CommonGameSystem.Core;
using UnityEngine;

public class LanguageMenu : MonoBehaviour
{
    private ILocalizationService _loc;

    private void Start()
    {
        _loc = ServiceLocator.Resolve<ILocalizationService>();
    }

    // Wire this to a UI button, passing "en", "ko", "ja", ...
    public void OnLanguageButtonClicked(string localeId)
    {
        _loc.SetLocale(localeId); // Every LocalizedText on screen updates live.
    }
}

这一选择会被自动保存——下次会话将以玩家挑选的语言启动。

关闭该服务

ServiceLocator.Replace<ILocalizationService>(new NullLocalization());

Get(key) 原样返回键,SetLocaleLoadTables 是静默的空操作,不记录任何日志。用它在轻量构建或测试中彻底剥离本地化。

常见陷阱

  • 仅限主线程。 从工作线程调用 GetSetLocale 会抛出 InvalidOperationException。所有 MonoBehaviour 回调都是安全的。
  • 缓存服务。Update() 中解析等于每帧重复一次字典查找。请改为在 Awake()Start() 中缓存引用。
  • 区域设置 id 必须严格匹配。 "EN" 不等于 "en",而 Application.systemLanguage.ToString() 得到的是 "Korean" 而非 "ko"。自动检测请用 LocaleId.FromSystemLanguage(),表中请使用小写的 BCP-47 标签。
  • LocalizedText 每次启用都会重新订阅。 在池化的 UI 预制体上(伤害数字、弹出提示),每个池循环都会订阅并退订 LocaleChanged。这是正确的行为——重新激活会拿到当前语言——但要留意高频池。动态文本请优先用 SetArgs 更新。
  • 关闭顺序。 服务按启动顺序的逆序关闭,因此本地化服务可能先于事件总线被释放。如果在那之后又有 LocaleChanged 事件到达,LocalizedText 会静默吞掉它——文本不更新,也不会崩溃。
  • 自定义类型在 IL2CPP 下需要 link.xml 如果你序列化了自己携带本地化键的设置类型,请确保项目的 link.xml 保留了它们。随附的框架程序集已保留其自身类型。

相关页面