多语言系统
简介
多语言系统由两部分组成:
- Localization: ET框架的基础本地化组件,提供运行时多语言支持
- AnyLocalization: 增强版本地化工具,提供完整的多语言解决方案,包括编辑器支持
系统架构
1. 目录结构
Assets/
├── Scripts/
│ └── ETCore/
│ └── Localization/
│ └── LocalizationSingleton.cs # 本地化组件
│
├── Res/
│ └── AnyLocalization/
│ ├── Editor/ # 编辑器工具
│ └── Dictionaries/ # 语言配置文件
2. 核心代码
LocalizationSingleton.cs
csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
namespace MH
{
public class LocalizationSingleton : LogicSingleton<LocalizationSingleton>, ISingletonAwake
{
public Dictionary<string, string> StrKeyValuePairs;
public Language DefaultLanguage;
public string StreamPath;
public Language Language;
public string XmlStreamPath;
public XmlDocument XmlDocument;
public void Awake()
{
XmlDocument = new XmlDocument();
StrKeyValuePairs = new Dictionary<string, string>();
DefaultLanguage = Language.ChineseSimplified;
StreamPath = XMLStreamingAssetsPath;
Language = (Language)PlayerPrefs.GetInt("Setting.Language", 0);
if (Language == Language.Unspecified)
{
Language = DefaultLanguage;
Debug.LogWarning("Language Unspecified! Using Default Language!");
}
LoadXmlStream().Coroutine();
SetLanguage(Language);
}
private readonly HashSet<AutoLocalization> autoLocalizationCache = new HashSet<AutoLocalization>();
private async ETTask LoadXmlStream()
{
XmlStreamPath = $"{Application.streamingAssetsPath}/{StreamPath}/{Language}.xml";
Debug.Log($"XML Stream Path: {XmlStreamPath}");
Debug.Log("Loading Languages XML Stream....");
if (Utility.IsWebGL())
{
await LoadXmlStreamAsync();
return;
}
else if (Utility.IsAndroid())
{
using var request = UnityWebRequest.Get(StreamPath);
try
{
await request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
XmlDocument.LoadXml(Utility.RemoveXMLBom(request.downloadHandler.text));
}
else
{
Debug.LogError($"下载失败: {request.error}");
}
}
catch (Exception ex)
{
Debug.LogError($"请求异常: {ex.Message}");
}
}
else
{
if (File.Exists(XmlStreamPath)) XmlDocument.Load(XmlStreamPath);
}
CreateDictionary();
}
private async ETTask LoadXmlStreamAsync()
{
var request = UnityWebRequest.Get(XmlStreamPath);
await request.SendWebRequest().GetAwaiter();
if (request.isDone)
{
Debug.Log($"Request is done!");
XmlDocument.LoadXml(Utility.RemoveXMLBom(request.downloadHandler.text));
CreateDictionary();
RefreshText();
}
else
{
Debug.LogError($"Request failure!{request.error}");
}
}
private void CreateDictionary()
{
XmlNodeList xmlNodeList = null;
try
{
xmlNodeList = XmlDocument.SelectSingleNode("Dictionaries/Dictionary").ChildNodes;
}
catch
{
throw new System.Exception($"Non-existent Language:{Language}");
}
StrKeyValuePairs = new Dictionary<string, string>();
foreach (XmlNode item in xmlNodeList)
{
string key = item.Attributes["Key"].Value;
string value = item.Attributes["Value"].Value;
StrKeyValuePairs.Add(key, value);
}
Debug.Log("Load Simple Languages XML Succeed.");
}
public void SetLanguage(Language language){
SetLanguageAsync(language).Coroutine();
}
private async ETTask SetLanguageAsync(Language language)
{
Language = language;
PlayerPrefs.SetInt("Setting.Language", (int)language);
await LoadXmlStream();
RefreshText();
}
public void RegisterAutoLocalization(AutoLocalization autoLocalization)
{
if (autoLocalization == null)
return;
autoLocalizationCache.Add(autoLocalization);
}
public void UnRegisterAutoLocalization(AutoLocalization autoLocalization)
{
if (autoLocalization == null)
return;
autoLocalizationCache.Remove(autoLocalization);
}
private void RefreshText()
{
foreach (var obj in autoLocalizationCache)
{
if (obj != null && obj.gameObject != null)
{
obj.ShowText();
}
}
}
public const string XMLStreamingAssetsPath = "AnyLocalization/XML";
public string GetString(string key)
{
if (StrKeyValuePairs.TryGetValue(key, out string value)) return System.Text.RegularExpressions.Regex.Unescape(value);
Debug.LogWarning("Non-existent Key:" + key);
return $"[No Key]{key}";
}
public string GetString(string key, object arg0)
{
string value = GetString(key);
try
{
return Utility.Format(value, arg0);
}
catch (Exception exception)
{
return Utility.Format("[Error]{0},{1},{2},{3}", key, value, arg0, exception.ToString());
}
}
public string GetString(string key, object arg0, object arg1)
{
string value = GetString(key);
try
{
return Utility.Format(value, arg0, arg1);
}
catch (Exception exception)
{
return Utility.Format("[Error]{0},{1},{2},{3},{4}", key, value, arg0, arg1, exception.ToString());
}
}
public string GetString(string key, object arg0, object arg1, object arg2)
{
string value = GetString(key);
try
{
return Utility.Format(value, arg0, arg1, arg2);
}
catch (Exception exception)
{
return Utility.Format("[Error]{0},{1},{2},{3},{4},{5}", key, value, arg0, arg1, arg2, exception.ToString());
}
}
public string GetString(string key, params object[] args)
{
string value = GetString(key);
try
{
return Utility.Format(value, args);
}
catch (Exception exception)
{
string errorString = Utility.Format("[Error]{0},{1}", key, value);
if (args != null)
{
foreach (object arg in args)
{
errorString += "," + arg.ToString();
}
}
errorString += "," + exception.ToString();
return errorString;
}
}
public TMP_FontAsset GetFont(Scene root)
{
var config = ConfigsSingleton.Instance.Tables.TbLanguageConfig.GetLanguageConfigByLanguageType(Language);
var resourcesComponent = root.GetComponent<ResourcesComponent>();
var loadAssetSync = resourcesComponent.LoadAssetSync<TMP_FontAsset>(config.LanguageFonts);
return loadAssetSync;
}
public TMP_FontAsset GetFontByName(Scene root, string name)
{
var resourcesComponent = root.GetComponent<ResourcesComponent>();
var loadAssetSync = resourcesComponent.LoadAssetSync<TMP_FontAsset>(name);
return loadAssetSync;
}
}
}
功能特性
1. 语言管理
支持的语言类型
目前的语言枚举都有:

枚举
csharp
namespace MH
{
/// <summary>
/// 本地化语言。
/// </summary>
public enum Language
{
/// <summary>
/// 未指定。
/// </summary>
Unspecified = 0,
/// <summary>
/// 南非荷兰语。
/// </summary>
Afrikaans,
/// <summary>
/// 阿尔巴尼亚语。
/// </summary>
Albanian,
/// <summary>
/// 阿拉伯语。
/// </summary>
Arabic,
/// <summary>
/// 巴斯克语。
/// </summary>
Basque,
/// <summary>
/// 白俄罗斯语。
/// </summary>
Belarusian,
/// <summary>
/// 保加利亚语。
/// </summary>
Bulgarian,
/// <summary>
/// 加泰罗尼亚语。
/// </summary>
Catalan,
/// <summary>
/// 简体中文。
/// </summary>
ChineseSimplified,
/// <summary>
/// 繁体中文。
/// </summary>
ChineseTraditional,
/// <summary>
/// 克罗地亚语。
/// </summary>
Croatian,
/// <summary>
/// 捷克语。
/// </summary>
Czech,
/// <summary>
/// 丹麦语。
/// </summary>
Danish,
/// <summary>
/// 荷兰语。
/// </summary>
Dutch,
/// <summary>
/// 英语。
/// </summary>
English,
/// <summary>
/// 爱沙尼亚语。
/// </summary>
Estonian,
/// <summary>
/// 法罗语。
/// </summary>
Faroese,
/// <summary>
/// 芬兰语。
/// </summary>
Finnish,
/// <summary>
/// 法语。
/// </summary>
French,
/// <summary>
/// 格鲁吉亚语。
/// </summary>
Georgian,
/// <summary>
/// 德语。
/// </summary>
German,
/// <summary>
/// 希腊语。
/// </summary>
Greek,
/// <summary>
/// 希伯来语。
/// </summary>
Hebrew,
/// <summary>
/// 匈牙利语。
/// </summary>
Hungarian,
/// <summary>
/// 冰岛语。
/// </summary>
Icelandic,
/// <summary>
/// 印尼语。
/// </summary>
Indonesian,
/// <summary>
/// 意大利语。
/// </summary>
Italian,
/// <summary>
/// 日语。
/// </summary>
Japanese,
/// <summary>
/// 韩语。
/// </summary>
Korean,
/// <summary>
/// 拉脱维亚语。
/// </summary>
Latvian,
/// <summary>
/// 立陶宛语。
/// </summary>
Lithuanian,
/// <summary>
/// 马其顿语。
/// </summary>
Macedonian,
/// <summary>
/// 马拉雅拉姆语。
/// </summary>
Malayalam,
/// <summary>
/// 挪威语。
/// </summary>
Norwegian,
/// <summary>
/// 波斯语。
/// </summary>
Persian,
/// <summary>
/// 波兰语。
/// </summary>
Polish,
/// <summary>
/// 巴西葡萄牙语。
/// </summary>
PortugueseBrazil,
/// <summary>
/// 葡萄牙语。
/// </summary>
PortuguesePortugal,
/// <summary>
/// 罗马尼亚语。
/// </summary>
Romanian,
/// <summary>
/// 俄语。
/// </summary>
Russian,
/// <summary>
/// 塞尔维亚克罗地亚语。
/// </summary>
SerboCroatian,
/// <summary>
/// 塞尔维亚西里尔语。
/// </summary>
SerbianCyrillic,
/// <summary>
/// 塞尔维亚拉丁语。
/// </summary>
SerbianLatin,
/// <summary>
/// 斯洛伐克语。
/// </summary>
Slovak,
/// <summary>
/// 斯洛文尼亚语。
/// </summary>
Slovenian,
/// <summary>
/// 西班牙语。
/// </summary>
Spanish,
/// <summary>
/// 瑞典语。
/// </summary>
Swedish,
/// <summary>
/// 泰语。
/// </summary>
Thai,
/// <summary>
/// 土耳其语。
/// </summary>
Turkish,
/// <summary>
/// 乌克兰语。
/// </summary>
Ukrainian,
/// <summary>
/// 越南语。
/// </summary>
Vietnamese
}
}
不过还是需要自行配置对应的多语言才行
语言切换
csharp
//设置语言
LocalizationSingleton.Instance.SetLanguage(Language.Chinese);
// 获取当前语言
var language = LocalizationSingleton.Instance.Language;
2. 本地化文本
本地化的XML文本存放在Assets/StreamingAssets/AnyLocalization/XML/
文件夹下
文本获取
- 通过给预制体上对应的脚本提前选择好Key即可
使用方式
- 在UI文本组件上添加
AutoLocalization
组件 - 设置对应的本地化键值
- 运行时会自动更新文本,并响应语言切换
- 通过代码来进行动态赋值
csharp
// 直接获取文本
var str = LocalizationSingleton.Instance.GetString("Settings");
还有部分是带着占位符的获取方法,详细请看LocalizationSingleton
编辑器工具
1. 本地化编辑器
功能列表
- 可视化编辑XML文件
- 支持多语言对照编辑
- 导入/导出Excel
使用步骤
- 打开编辑器:
Window/AnyLocalization
- 选择文件夹目录:
Assets/Res/AnyLocalization/Dictionaries
- 编辑内容
- 保存生成XML文件
注意事项
WARNING
- 保持所有语言文件的同步更新
- 定期备份语言文件
- 注意多平台文件路径差异
技术支持
获取帮助
- 💬 加入QQ群讨论交流
(ET框架群)
: 474643097 - ⭐ 在GitHub上关注项目获取最新更新