本地化
键到文本的查找、运行时语言切换,以及在玩家切换语言时实时更新的 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)— 无副作用的查找。键缺失时返回false(value被设为该键)。不记录任何日志——适合在不刷警告的前提下探测键是否存在。
语言切换
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) 原样返回键,SetLocale 和 LoadTables 是静默的空操作,不记录任何日志。用它在轻量构建或测试中彻底剥离本地化。
常见陷阱
- 仅限主线程。 从工作线程调用
Get或SetLocale会抛出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保留了它们。随附的框架程序集已保留其自身类型。