Localization System
Introduction
The localization system consists of two parts:
- Localization: ET framework's basic localization component, providing runtime multi-language support
- AnyLocalization: Enhanced localization tool, providing complete multi-language solutions, including editor support
System Architecture
1. Directory Structure
Assets/
├── Scripts/
│ └── ETCore/
│ └── Localization/
│ └── LocalizationSingleton.cs # Localization Component
│
├── Res/
│ └── AnyLocalization/
│ ├── Editor/ # Editor Tools
│ └── Dictionaries/ # Language Configuration Files
2. Core Code
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($"Download failed: {request.error}");
}
}
catch (Exception ex)
{
Debug.LogError($"Request exception: {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;
}
}
}
Features
1. Language Management
Supported Languages
Currently supported language enums:

Enum
csharp
namespace MH
{
/// <summary>
/// Localization language.
/// </summary>
public enum Language
{
/// <summary>
/// Unspecified.
/// </summary>
Unspecified = 0,
/// <summary>
/// Afrikaans.
/// </summary>
Afrikaans,
/// <summary>
/// Albanian.
/// </summary>
Albanian,
/// <summary>
/// Arabic.
/// </summary>
Arabic,
/// <summary>
/// Basque.
/// </summary>
Basque,
/// <summary>
/// Belarusian.
/// </summary>
Belarusian,
/// <summary>
/// Bulgarian.
/// </summary>
Bulgarian,
/// <summary>
/// Catalan.
/// </summary>
Catalan,
/// <summary>
/// Chinese Simplified.
/// </summary>
ChineseSimplified,
/// <summary>
/// Chinese Traditional.
/// </summary>
ChineseTraditional,
/// <summary>
/// Croatian.
/// </summary>
Croatian,
/// <summary>
/// Czech.
/// </summary>
Czech,
/// <summary>
/// Danish.
/// </summary>
Danish,
/// <summary>
/// Dutch.
/// </summary>
Dutch,
/// <summary>
/// English.
/// </summary>
English,
/// <summary>
/// Estonian.
/// </summary>
Estonian,
/// <summary>
/// Faroese.
/// </summary>
Faroese,
/// <summary>
/// Finnish.
/// </summary>
Finnish,
/// <summary>
/// French.
/// </summary>
French,
/// <summary>
/// Georgian.
/// </summary>
Georgian,
/// <summary>
/// German.
/// </summary>
German,
/// <summary>
/// Greek.
/// </summary>
Greek,
/// <summary>
/// Hebrew.
/// </summary>
Hebrew,
/// <summary>
/// Hungarian.
/// </summary>
Hungarian,
/// <summary>
/// Icelandic.
/// </summary>
Icelandic,
/// <summary>
/// Indonesian.
/// </summary>
Indonesian,
/// <summary>
/// Italian.
/// </summary>
Italian,
/// <summary>
/// Japanese.
/// </summary>
Japanese,
/// <summary>
/// Korean.
/// </summary>
Korean,
/// <summary>
/// Latvian.
/// </summary>
Latvian,
/// <summary>
/// Lithuanian.
/// </summary>
Lithuanian,
/// <summary>
/// Macedonian.
/// </summary>
Macedonian,
/// <summary>
/// Malayalam.
/// </summary>
Malayalam,
/// <summary>
/// Norwegian.
/// </summary>
Norwegian,
/// <summary>
/// Persian.
/// </summary>
Persian,
/// <summary>
/// Polish.
/// </summary>
Polish,
/// <summary>
/// Brazilian Portuguese.
/// </summary>
PortugueseBrazil,
/// <summary>
/// Portuguese.
/// </summary>
PortuguesePortugal,
/// <summary>
/// Romanian.
/// </summary>
Romanian,
/// <summary>
/// Russian.
/// </summary>
Russian,
/// <summary>
/// Serbo-Croatian.
/// </summary>
SerboCroatian,
/// <summary>
/// Serbian Cyrillic.
/// </summary>
SerbianCyrillic,
/// <summary>
/// Serbian Latin.
/// </summary>
SerbianLatin,
/// <summary>
/// Slovak.
/// </summary>
Slovak,
/// <summary>
/// Slovenian.
/// </summary>
Slovenian,
/// <summary>
/// Spanish.
/// </summary>
Spanish,
/// <summary>
/// Swedish.
/// </summary>
Swedish,
/// <summary>
/// Thai.
/// </summary>
Thai,
/// <summary>
/// Turkish.
/// </summary>
Turkish,
/// <summary>
/// Ukrainian.
/// </summary>
Ukrainian,
/// <summary>
/// Vietnamese.
/// </summary>
Vietnamese
}
}
Note that you still need to configure the corresponding languages manually
Language Switching
csharp
// Set language
LocalizationSingleton.Instance.SetLanguage(Language.Chinese);
// Get current language
var language = LocalizationSingleton.Instance.Language;
2. Localized Text
Localized XML texts are stored in the Assets/StreamingAssets/AnyLocalization/XML/
folder
Text Retrieval
- By selecting the Key in advance for the corresponding script on the prefab
Usage
- Add
AutoLocalization
component to UI text component - Set the corresponding localization key
- Text will automatically update at runtime and respond to language switching
- Through code for dynamic assignment
csharp
// Direct text retrieval
var str = LocalizationSingleton.Instance.GetString("Settings");
There are also methods with placeholders, see LocalizationSingleton
for details
Editor Tools
1. Localization Editor
Feature List
- Visual XML file editing
- Support for multi-language parallel editing
- Import/Export Excel
Usage Steps
- Open Editor:
Window/AnyLocalization
- Select Directory:
Assets/Res/AnyLocalization/Dictionaries
- Edit Content
- Save and Generate XML Files
Important Notes
WARNING
- Keep all language files synchronized
- Regularly backup language files
- Be aware of file path differences across platforms
Technical Support
Get Help
- 💬 Join QQ Group for discussion
(ET Framework Group)
: 474643097 - ⭐ Follow the project on GitHub for latest updates