> 技术文档 > Unity之(多语言)Localization本地化工具_unity localization

Unity之(多语言)Localization本地化工具_unity localization


一、安装和配置 Localization

Localization是Unity基于对多种语言和区域变体所设计的一个本地化工具,常用与切换多国语言时文本、图片的动态替换。

1.安装Localization插件

Window—> Package Manager,打开Package Manager面板

Packages选择Unity Registy 选项

搜索 Localization安装

2.创建配置文件(Localization Settings)

安装完成后需要进入到Project Settings对Localization进行本地化设置。

 创建一个本地化设置

这里我将配置文件创建在 Languages文件夹下

 3.配置文件添加语言环境

给我们的设置文件添加语言,例如我们项目需要发布到中国、英国、日本等国家时,需要给配置文件添加这三个国家的语种。

在Locale Generator面板中添加语种

 选择完成后点击Generate Locales按钮生成语种

将语种创建到Languages的Locales文件夹下

二、Localization Tables本地表创建与配置

Localization Tables面板主要用于创建和管理语言表。

Window—>Asset Management—>Localization Tables打开Localization Tables面板,选择 New Table Collection选项卡,进入创建表界面。

1.创建本地表  

将表创建到 Languages—> Tables文件夹下,可以看到创建了很多表文件,这里先暂时不讲这几个文件的作用和关系,等后面 “五、Localization各文件之间的关系” 中在统一详细讲解。

 2.设置表内容

创建完表后,选择Edit Table Collection选项卡进入到表格内容设置界面

添加内容

3.通过CSV文件进行内容设置

多语言内容一般是让翻译同事给我们需要的字段填写好内容后导入项目中直接使用,我们上面的方法适用于程序使用,但是翻译同事是不会使用我们的引擎工具进行内容填充的,所以需要将其创建为csv表格文件来进行协作开发。

 ① 导出CSV表格

在Edit Table Collection选项卡界面,点击右上角三个点按钮,选择Export—>CSV...来导出表格

打开导出的.CSV表格直接编辑即可

② 导入CSV表格

在Edit Table Collection选项卡界面,点击右上角三个点按钮,选择Import—>CSV...来导出表格

选择刚才设置的myTable.csv表格即可

③ CSV表格注意事项 (重要)

我在使用wps修改表格后保存出现了下面问题

选择“是”后, 在导入Unity内容出现了乱码现象

其原因就是导入Unity的CSV必须是UTF-8编码格式,使用wps保存后更改了编码格式导致出现乱码问,我们可以使用其他工具将这个表格更改为UTF-8编码,也可以直接在wps中另存为文件,将文件类型设置为CSV UTF-8文件

另存为可能也会出现上面的不兼容警告,没关系点击 “是” 就行,下面导入进去看看效果

可以看到再次导入没有出现乱码问题了。 

三、文本调用和替换

设置完表格内容后就可以开始我们的Text文本调用了,总共分为静态文本调用和动态脚本赋值两种方法

我们先给Table表多添加几个Key值内容,方便后面测试用

然后在场景中创建一个TextMeshPro组件,用老版本的Text组件也可以。

从系统中随便导入一个Font字体(这里我用的常规黑体)

设置字体为动态的TMP Font Asset资源

然后将 TMP Font Asset资源赋值给TextMeshPro组件并设置字体颜色为黑色 

到这里准备工作完成,下面开始案例测试。 

1.静态文本调用

右键TextMeshPro组件,选择Localize选项, 添加一个Localize String Event 组件。

点击Localize String Event 组件的String Reference选项卡,在打开的 “Select string table entry...” 面板中可以看到我们创建的myTable中的所有Key值,通过设置Key值来替换文字内容

2.动态文本调用 (脚本)

除了上方这种手动在场景中配置的方法外,还可以通过脚本来动态修改Key值内容。下面演示几种脚本动态调用文本方法

 ① 动态修改LocalizeStringEvent组件Key值

在 “静态文本调用” 中我们对TextMeshPro添加了LocalizeStringEvent组件。通过该组件的String Reference选项卡来选择Table表的Key值修改Text内容。同理我们可以通过在脚本中获取LocalizeStringEvent组件然后使用该组件自带的SetEntry方法修改Key值来更换text内容。

using System.Collections.Generic;using TMPro;using UnityEngine;using UnityEngine.Localization;using UnityEngine.Localization.Components;using UnityEngine.Localization.Settings;//动态系系修改语言public class SetLanguage_Test : MonoBehaviour{ TMP_Text m_Text; LocalizeStringEvent m_Localize; void Start() { m_Text = GetComponent(); //获取 本地化字符串事件 m_Localize=GetComponent(); } private void OnGUI() { if (GUI.Button(new Rect(0, 0, 100, 50), \"打招呼\")) { m_Localize.SetEntry(\"id_hello\"); } if (GUI.Button(new Rect(0, 60, 100, 50), \"名字\")) { m_Localize.SetEntry(\"id_name\"); } if (GUI.Button(new Rect(0, 120, 100, 50), \"性别\")) { m_Localize.SetEntry(\"id_sex\"); } }}

将此脚本挂载在Text上运行

② 从请求的表中返回条目来替换文本(简单)

使用方法函数 LocalizationSettings.StringDatabase.GetTableEntry(\"表\", \"Key值\") 来获取内容。这种方式通常是最简单的,但是他有两个依赖:

  • 依赖LocalizationSettings类初始化,如果未初始化就调用此方法可能会失败或返回默认值
  • 调用GetTableEntry方法时,需要确保相关语言表已经被加载,同时当前选定的语言环境已经被初始化

关于这两个初始化判断,会在第③条中体现。

我们先将Text上的 LocalizeStringEvent组件移除,然后修改脚本

using System.Collections;using System.Collections.Generic;using TMPro;using UnityEngine;using UnityEngine.Localization.Components;using UnityEngine.Localization.Settings;//修改语言内容public class SetLanguageText_Test : MonoBehaviour{ TMP_Text m_Text; void Start() { m_Text = GetComponent(); } private void OnGUI() { if (GUI.Button(new Rect(0, 0, 100, 50), \"打招呼\")) { /*获取语言表的Entry值 * 参数1:表名 * 参数2:Entry名(key值) */  var tableEntryResult= LocalizationSettings.StringDatabase.GetTableEntry(\"myTable\", \"id_hello\");  m_Text.text = tableEntryResult.Entry.GetLocalizedString(); } if (GUI.Button(new Rect(0, 60, 100, 50), \"名字\")) { var tableEntryResult= LocalizationSettings.StringDatabase.GetTableEntry(\"myTable\", \"id_name\"); m_Text.text = tableEntryResult.Entry.GetLocalizedString(); } if (GUI.Button(new Rect(0, 120, 100, 50), \"性别\")) { var tableEntryResult= LocalizationSettings.StringDatabase.GetTableEntry(\"myTable\", \"id_sex\"); m_Text.text = tableEntryResult.Entry.GetLocalizedString(); } }}

运行效果

③等待表初始化后在替换文本(最保险)

在 配置文件初始化选定的语言环境初始化语言表未加载 时执行第②条替换文本可能会出现不可预知的错误,所以在替换文本前先对这三个状态进行预处理是最稳妥的方式。

using System.Collections;using System.Collections.Generic;using TMPro;using UnityEngine;using UnityEngine.Localization.Settings;using UnityEngine.ResourceManagement.AsyncOperations;//修改语言内容public class SetLanguageText_Test : MonoBehaviour{ TMP_Text text; void Start() { text = GetComponent(); //1.协程加载 // StartCoroutine(LoadStrings()); //2.async加载语言内容 AsyncLoadStrings(); } #region 协程加载语言内容 IEnumerator LoadStrings() { /* 1.确保 Localization 系统完全初始化 * 它是整个本地化系统的核心初始化操作,加载基础配置(如 Locale 列表、默认语言环境、语言环境切换器等)。 * 如果 Localization 系统尚未初始化完成,直接使用其他操作可能会导致不可预期的行为。 */ var initialization = LocalizationSettings.InitializationOperation; yield return initialization; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"Localization 系统初始化失败:\\n\" + initialization.OperationException?.Message); yield return null; } // 2.确保选定的语言环境初始化 SelectedLocaleAsync 将确保语言环境设置已经初始化,并且已经选择了一个区域设置。 var m_InitializeOperation = LocalizationSettings.SelectedLocaleAsync; yield return m_InitializeOperation; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"语言环境设置已经初始化失败:\\n\" + initialization.OperationException?.Message); yield return null; } // 3.确保语言表的初始化 GetTableAsync用于异步返回请求的表 var loadingOperation = LocalizationSettings.StringDatabase.GetTableAsync(\"myTable\"); yield return loadingOperation; //加载成功 if (loadingOperation.Status == AsyncOperationStatus.Succeeded) { var stringTable = loadingOperation.Result; text.text = stringTable.GetEntry(\"id_hello\").GetLocalizedString(); } else { Debug.LogError(\"无法加载字符串表\\n\" + loadingOperation.OperationException.ToString()); } } #endregion #region async加载语言内容 private async void AsyncLoadStrings() { /* 1.确保 Localization 系统完全初始化 * 它是整个本地化系统的核心初始化操作,加载基础配置(如 Locale 列表、默认语言环境、语言环境切换器等)。 * 如果 Localization 系统尚未初始化完成,直接使用其他操作可能会导致不可预期的行为。 */ var initialization = LocalizationSettings.InitializationOperation; await initialization.Task; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"Localization 系统初始化失败:\\n\" + initialization.OperationException?.Message); return; } // 2.确保选定的语言环境初始化 SelectedLocaleAsync 将确保语言环境设置已经初始化,并且已经选择了一个区域设置。 var m_InitializeOperation = LocalizationSettings.SelectedLocaleAsync; await m_InitializeOperation.Task; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"Localization 系统初始化失败:\\n\" + initialization.OperationException?.Message); return; } // 3.确保语言表的初始化 GetTableAsync用于异步返回请求的表 var loadingTable = LocalizationSettings.StringDatabase.GetTableAsync(\"myTable\"); await loadingTable.Task; if (loadingTable.Status == AsyncOperationStatus.Succeeded) { //成功修改语言内容 var stringTable = loadingTable.Result; text.text = stringTable.GetEntry(\"id_hello\").GetLocalizedString(); } else { Debug.LogError(\"无法加载字符串表\\n\" + loadingTable.OperationException?.Message); } } #endregion }

 ④通过回调函数来替换文本(最保险2)

此方法同样需要进行初始化判断,不同的是我们程序中如果存在多个表时,不同表加载完成后需要进行一些资源的回调处理时可以采用此方法。

using System.Collections.Generic;using TMPro;using UnityEngine;using UnityEngine.Localization;using UnityEngine.Localization.Settings;using UnityEngine.Localization.Tables;using UnityEngine.ResourceManagement.AsyncOperations;public class SetLanguageText_Test : MonoBehaviour{ TMP_Text text; private LocalizedStringTable stringTable = new() { TableReference = \"myTable\" }; void Start() { // 在此时,不做初始化工作,等待语言表加载后回调触发 } void OnEnable() { // 添加回调函数 stringTable.TableChanged += LoadStrings; // 确保本地化系统已初始化 EnsureLocalizationInitialized(); } void OnDisable() { // 卸载回调函数 stringTable.TableChanged -= LoadStrings; } // 确保Localization系统和语言环境已初始化 private async void EnsureLocalizationInitialized() { // 1.确保Localization系统完全初始化 var initialization = LocalizationSettings.InitializationOperation; await initialization.Task; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.LogError(\"Localization 系统初始化失败:\\n\" + initialization.OperationException?.Message); return; } // 2.确保当前语言环境已初始化 var localeInitialization = LocalizationSettings.SelectedLocaleAsync; await localeInitialization.Task; if (localeInitialization.Status != AsyncOperationStatus.Succeeded) { Debug.LogError(\"当前语言环境初始化失败\"); return; } // 3.确保语言表的初始化 GetTableAsync用于异步返回请求的表 var loadingOperation = LocalizationSettings.StringDatabase.GetTableAsync(\"myTable\"); await loadingOperation.Task; if (loadingOperation.Status != AsyncOperationStatus.Succeeded) { Debug.LogError(\"表初始化失败:\\n\" + initialization.OperationException?.Message); return; } Debug.Log(\"Localization配置文件、语言环境和语言表初始化成功\"); } // 加载文本内容 void LoadStrings(StringTable loadedTable) { Debug.Log(\"加载文本内容\"); if (text == null) text = GetComponent(); // 获取文本内容 var entry = loadedTable.GetEntry(\"id_hello\"); if (entry != null) { text.text = entry.GetLocalizedString(); } else { Debug.LogError(\"无法找到 id_hello 的文本条目!\"); } }}

3. 为什么要进行初始化处理?

我们在使用修改语言环境或文本内容的方法时,如果LocalizationSettings类语言环境或相关语言表都未初始化时,Localization插件也会自动检测和延迟初始化。只是如果不做初始化处理时,其中某个步骤初始化未完成或出现错误时,就会导致我们的功能不成功,而且在项目中切换文本内容时不提前初始化是会出现延迟效果的,对用户体验感来说也会大大降低。所以需要提前进行初始化处理。

四、文本占位符替换

举例:我们在统计数据一共可以分为多少页时,显示格式一般是“共10页”,但有时候这个数字10是需要动态修改的,解决这个问题我们通常会使用String.Format(\"共{0}页\",10);来实现动态替换。

多语言下也可以使用这种方式,只需要勾选内容上的Smart按钮即可。

1.设置文本占位符 

 可以在表格中进行设置

在表格中设置

或者在Localize String Event组件中设置

 2.文本占位符使用

通过上面设置可以让指定的多语言内容拥有文本占位符的使用功能,下面用三种方式来实现如何使用文本占位符

①Localize String Event中使用

Localize String Event组件的Local Variables属性是可以通过占位符中的名称充当id来动态替换内容的。

②自定义脚本添加

using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.Localization;using UnityEngine.UI;public class Test2 : MonoBehaviour{ [Header(\"本地化设置\")] public string tableName = \"myTable\"; // 在 Inspector 里指定 Table 名称 public string entryKey = \"id_hello\"; // 在 Inspector 里指定 Key private LocalizedString localizedString; // 在 Inspector 里赋值 void Start() { // 初始化 LocalizedString localizedString = new LocalizedString(tableName,entryKey); //方式二 // localizedString = new LocalizedString(); // localizedString.TableReference=tableName; // localizedString.TableEntryReference=entryKey; // 绑定事件, localizedString.Arguments = new object[] { \"山姆\" }; localizedString.StringChanged += OnStringChanged; localizedString.RefreshString(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) {  Debug.Log(\"Update动态替换\"); localizedString.Arguments[0] =\"杰克\"; localizedString.RefreshString(); } } void OnStringChanged(string updatedText) { Debug.Log(\"切换语言时的更新文本回调\"); GetComponent().text=localizedString.GetLocalizedString(); }}

添加到Text上设置表名和Key值

运行后效果

也可以开放 LocalizedString 属性

在Inspector面板中直接设置

 

3.补充

Localize String Event组件中勾选 Smart 后共有Edit、Debug、Preview三种属性模式。他们的作用如下 

 

  • Edit模式

编辑模式,选中此模式可以编辑文本

 

  • Debug打印模式

使用情况

  • 使用了 Smart Strings,但 UI 没有正确更新
  • 本地化文本没有正确解析参数(比如 {0} 变成空白)
  • 你需要调试复杂的文本格式(比如日期、数字格式化等)

一般是在editor书写完成后 切换到Debug下查看是否正确,但是通常你写错了Console也会进行错误提示

 

  • Preview预览模式

预览最终效果

 

 

五、设置语言环境选择器

语言环境选择器作用是 动态确定和选择语言环境(Locale),为用户指定项目在运行时应该加载的语言。它是 Unity Localization 插件中非常重要的组件,用于让项目根据特定的条件(如用户选择、系统设置等)加载合适的语言资源。

1.准备工作

在操作前进行准备工作,将之前的脚本都移除掉,然后给TextMeshPro添加Localize String Event并进行如下设置(就是还原场景到静态文本调用)

然后Win+R按键输入regedit 打开注册表

在注册表 HKEY CURRENT USER—>SOFTWARE—>Unity—>UnityEditor—>DefaultCompany —>language_Test 中,删除我们的之前存储的语言环境数据。在  “ 三、文本调用和替换 ” 测试时已经将语言环境存储到注册表中了所以在做下面的测试之前先将数据清空。

2.语言选择器

Localization Settings配置文件Locale Selectors中共有四种语言选择器

Locale Selectors的四种语言选择器 名称 描述 是否可读写 Player Pref Locale Selector 从PlayerPrefs(存档系统)存储和获取语言环境。 可读写,当语言环境改变后自动将数据存储在PlayerPrefs中 Command Line Locale Selector 使用命令行(cmd)启动程序时,通过传参进行语言切换 不可读写,只用于命令行启动 Specific Locale Selector 手动赋值语言环境切换 System Locale Selector 获取电脑系统的语言环境进行切换 不可读写,固定获取电脑系统语言切换

 ① Player Pref Locale Selector(PlayerPrefs存档系统)

此方法是从Unity自带的PlayerPrefs(存档系统)获取语言环境进行切换。需要自定义在PlayerPrefs中的Key值

我们先将其他选项删除。

 添加Player Pref Locale Selector选项

设置 PlayerPrefs的Key值为selected-locale,这样就可以通过selected-locale从PlayerPrefs获取语言信息了。

因为我们第一次设置PlayerPrefs中并没有创建selected-locale来存储语言环境,所以第一次运行默认环境为None。我们通过Game视图右上角来手动切换一个语言环境,目的是给PlayerPrefs的selected-locale存储一个语言环境。

这时我们的 PlayerPrefs的selected-locale中存储了一个语言环境是English (en),创建一个脚本挂载到场景中我们来读取以下selected-locale的内容。

using System.Collections;using System.Collections.Generic;using UnityEngine;public class GetPlayerPrefsValue : MonoBehaviour{ void Start() { // PlayerPrefs.DeleteKey(\"selected-locale\");//删除selected Debug.Log(\"从PlayerPrefs获取语言环境:\"+PlayerPrefs.GetString(\"selected-locale\")); } void Update() { }}

脚本挂载完成后,再次运行可以看到打印的信息是我们存储的en,同时语言环境也默认发生了改变。

② Command Line Locale Selector(命令行启动)

此方法是当我们使用命令行启动项目程序时(例如:cmd控制台应用程序启动),通过传参的方式进行语言切换。如果不添加此方法就无法用命令行的方式切换语言环境。举个常用例子A程序有个功能按钮点击后需要唤醒B程序,在唤醒B程序时要修改B程序的语言环境。

删除Player Pref Locale Selector选项。防止待会测试命令行时多语言从Player Pref Locale Selector中获取语言环境。

添加Command Line Locale Selector选项并设置命令行识别字符串 -language=

注意:识别字符串可以自定义。例如:locale、-locale等等都可以。

因为本地化工具文件数据是通过Addressables插件进行资源管理的,所以我们先进入Groups面板将资源进行Build后在打包成exe 

 

资源Build后 在将这个程序打包成exe,记住这个exe启动文件名和所处的路径。

我们先双击exe运行一下可以看到语言并没有切换语言环境

在Log文件中也是报没有找到语言环境的错。  下面我们使用命令行来运行看看效果

Win+R 输入cmd,点击确定,打开控制台应用程序。

 在控制台应用程序中输入以下命令打开exe程序并切换语言环境

回车运行后发现语言环境被切换了。

注意:在使用时命令行字符串(例如:-language=)与切换的语言环境(例如:ja)中间不能使用空格。否则会切换失败

③ Specific Locale Selector (手动赋值)

固定设置一种语言,它会忽略用户的语言偏好和系统设置,始终使用固定语言。

 运行后默认设置为ja语言

④ System Locale Selector (电脑系统语言)

根据电脑系统设置的语言环境来设置程序的语言环境。

 同样我们只留System Locale Selector一个方法选项,其他的删除。因为自动获取电脑系统语言所以这个方法不需要进行设置。

 查看我们电脑系统语言

运行后获取电脑系统语言

 

我们修改电脑系统语言为英语

 

再次运行可以看到变成了英文。

 

3.选择器优先级设置

Localization可以添加多种选择器配合使用,程序运行时会根据优先级对选择器进行加载,可以通过拖动来设置优先级,排列顺序是从上到下。

如果最高优先级选择器没有获取到语言就会从下一个高优先级的选择器中查找。如果所有Locale Selectors方法都没有找到语言就会报错。

我们先从注册表中删除了之前存储的语言环境数据,然后添加选择器进行如下设置,注意排列顺序和参数赋值。

因为前三个选择器属性为空,所以运行后会使用System Locale Selector选择器设置语言环境

我们先从注册表中删除了之前存储的语言环境数据,然后在删除System Locale Selector在运行,剩下所有选择器都无法设置语言就会报空错误

了解了选择器设置后,可以根据自己的需求进行相应设置,这里我先还原默认的选择器设置。

六、切换语言环境

上面讲了如何进行文本调用和语言选择器的配置,下面开始进行切换语言环境操作。

1.自带多语言切换(编辑器测试用) 

 打开 Windows—>Asset Management—> Localization Scene Controls面板。

不需要运行程序直接设置Localization Scene Controls面板的Active Locale选项来控制默认语言环境。

可以看到通过切换语言环境就能动态的修改对应的内容文本了。此方法在程序运行时同样有效。如下图我在运行状态下设置语言环境内容也会被修改,同时可以发现Game视图右上角选项卡也进行了同步修改。

注意事项

我们给Text添加Localize String Event组件后,我们发现Text无法修改内容了,这很正常因为添加Localize String Event会让其变成本地化字符串。如果因为误操作将Text文本设置成本地化字符串,通过移除Localize String Event组件发现还是无法对文字修改,并且UI的布局等属性都设置好了,删掉重新添加又很麻烦,那么可以通过设置 “ Localization Scene Controls面板的Active Locale选项为None ”来解决。

2.动态多语言切换

我们在运行程序时可以看到右上角有个选项卡,通过切换选项卡也可以替换不同的语言环境。如下:

这个选项卡是插件自带的,方便我们在编辑器中测试用,不会被打包到程序中。在程序中使用脚本进行语言环境的动态修改可以采用以下几种方式。

①SetSelectedLocale动态修改语言环境(简单,需存Locale)

通过读取语言环境表获取对应的Locale(语言环境),然后使用LocalizationSettings.Instance.SetSelectedLocale(Locale)方法来动态修改语言环境。

注意:这个方法是利用插件自动检测和初始化实现,脚本中不做手动初始化操作和检测

using UnityEngine;using UnityEngine.Localization;using UnityEngine.Localization.Settings;//动态系系修改语言public class SetLanguage_Test : MonoBehaviour{ private Locale chineseLocale; private Locale englishLocale; private Locale japaneseLocale; void Start() { GetAllLocale(); } void GetAllLocale() { //获取 var locales = LocalizationSettings.AvailableLocales.Locales; for (int i = 0; i < locales.Count; ++i) { var locale = locales[i]; Debug.Log(locale.LocaleName); //获取语言环境并赋值 switch (locale.LocaleName) { case \"Chinese (Simplified) (zh-Hans)\":  chineseLocale = locale;  break; case \"English (en)\":  englishLocale = locale;  break; case \"Japanese (ja)\":  japaneseLocale = locale;  break; }  } } private void OnGUI() { if (GUI.Button(new Rect(0, 0, 100, 50), \"中文\")) { //修改当前语言环境 LocalizationSettings.Instance.SetSelectedLocale(chineseLocale); } if (GUI.Button(new Rect(0, 60, 100, 50), \"英文\")) { LocalizationSettings.Instance.SetSelectedLocale(englishLocale); } if (GUI.Button(new Rect(0, 120, 100, 50), \"日语\")) { LocalizationSettings.Instance.SetSelectedLocale(japaneseLocale); } }}

将脚本随便挂载到一个物体上 运行查看

注意:语言环境的名称可以在配置文件中查看。

② SelectedLocale 直接替换语言环境(最简单)

通过语言表下标获取语言环境,使用 LocalizationSettings.SelectedLocale直接替换。

注意:这个方法是利用插件自动检测和初始化实现,脚本中不做手动初始化操作和检测

using UnityEngine;using UnityEngine.Localization.Settings;//动态系系修改语言public class SetLanguage_Test : MonoBehaviour{ void Start() { } private void OnGUI() { if (GUI.Button(new Rect(0, 0, 100, 50), \"中文\")) { //修改当前语言环境 LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[0]; } if (GUI.Button(new Rect(0, 60, 100, 50), \"英文\")) { LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[1]; } if (GUI.Button(new Rect(0, 120, 100, 50), \"日语\")) { LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[2]; } }}

索引值可以在语言表中查看

③等待表初始化并设置一个语言环境后在替换语言环境(最保险)

“三、文本调用和替换” 中讲解过配置文件初始化选定的语言环境初始化语言表未加载时来替换内容会出现不可预估的错误。替换语言环境也是同样的道理。下面使用两种异步方式进行案例演示:

  • async异步替换语言环境
  • 协程异步替换语言环境
(1) async异步替换语言环境
using System.Collections.Generic;using UnityEngine;using UnityEngine.Localization;using UnityEngine.Localization.Settings;using UnityEngine.ResourceManagement.AsyncOperations;//动态系系修改语言public class SetLanguage_Test : MonoBehaviour{ private Dictionary languageDic; //存储本地语言 void Start() { languageDic = new Dictionary(); //async加载语言环境 AsyncLoadStrings(); } #region async加载语言内容 private async void AsyncLoadStrings() { /* 1.确保 Localization 系统完全初始化 * 它是整个本地化系统的核心初始化操作,加载基础配置(如 Locale 列表、默认语言环境、语言环境切换器等)。 * 如果 Localization 系统尚未初始化完成,直接使用其他操作可能会导致不可预期的行为。 */ var initialization = LocalizationSettings.InitializationOperation; await initialization.Task; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"Localization 系统初始化失败:\\n\" + initialization.OperationException?.Message); return; } // 2.确保选定的语言环境初始化 SelectedLocaleAsync 将确保语言环境设置已经初始化,并且已经选择了一个区域设置。 var m_InitializeOperation = LocalizationSettings.SelectedLocaleAsync; await m_InitializeOperation.Task; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"语言环境设置已经初始化失败:\\n\" + initialization.OperationException?.Message); return; } // 3.确保语言表的初始化 GetTableAsync用于异步返回请求的表 var loadingTable = LocalizationSettings.StringDatabase.GetTableAsync(\"myTable\"); await loadingTable.Task; if (loadingTable.Status == AsyncOperationStatus.Succeeded) { GetAllLocals();//获取所有Locale (语言环境) } else { Debug.LogError(\"无法加载字符串表\\n\" + loadingTable.OperationException?.Message); } } #endregion //获取所有Locale (语言环境) private void GetAllLocals() { //获取语言表 var locales = LocalizationSettings.AvailableLocales.Locales; //将所有的语言环境添加到字典 for (int i = 0; i < locales.Count; ++i) { var locale = locales[i]; Debug.Log(locale.LocaleName); //添加到字典 if (!languageDic.ContainsKey(locale.LocaleName)) languageDic.Add(locale.LocaleName, locale); } isShow = true;//显示切换按钮 } private bool isShow = false; private void OnGUI() { if (!isShow) return; //通过语言环境名称进行替换 注意:语言环境名从设置文件的语言表中查看 if (GUI.Button(new Rect(0, 0, 100, 50), \"中文\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[0];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"Chinese (Simplified) (zh-Hans)\"]); //名称加载 } if (GUI.Button(new Rect(0, 60, 100, 50), \"英文\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[1];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"English (en)\"]); //名称加载 } if (GUI.Button(new Rect(0, 120, 100, 50), \"日语\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[2];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"Japanese (ja)\"]); //名称加载 } }}
(2) 协程替换语言环境
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.Localization;using UnityEngine.Localization.Settings;using UnityEngine.ResourceManagement.AsyncOperations;//动态系系修改语言public class SetLanguage_Test : MonoBehaviour{ private Dictionary languageDic; //存储本地语言 void Start() { languageDic = new Dictionary(); //协程加载语言环境 StartCoroutine(AsyncLoadStrings()); } #region async加载语言内容 private IEnumerator AsyncLoadStrings() { /* 1.确保 Localization 系统完全初始化 * 它是整个本地化系统的核心初始化操作,加载基础配置(如 Locale 列表、默认语言环境、语言环境切换器等)。 * 如果 Localization 系统尚未初始化完成,直接使用其他操作可能会导致不可预期的行为。 */ var initialization = LocalizationSettings.InitializationOperation; yield return initialization; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"Localization 系统初始化失败:\\n\" + initialization.OperationException?.Message); yield return null; } // 2.确保选定的语言环境初始化 SelectedLocaleAsync 将确保语言环境设置已经初始化,并且已经选择了一个区域设置。 var m_InitializeOperation = LocalizationSettings.SelectedLocaleAsync; yield return m_InitializeOperation; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"语言环境设置已经初始化失败:\\n\" + initialization.OperationException?.Message); yield return null; } // 3.确保语言表的初始化 GetTableAsync用于异步返回请求的表 var loadingTable = LocalizationSettings.StringDatabase.GetTableAsync(\"myTable\"); yield return loadingTable; if (loadingTable.Status == AsyncOperationStatus.Succeeded) { GetAllLocals();//获取所有Locale (语言环境) } else { Debug.LogError(\"无法加载字符串表\\n\" + loadingTable.OperationException?.Message); } } #endregion //获取所有Locale (语言环境) private void GetAllLocals() { //获取语言表 var locales = LocalizationSettings.AvailableLocales.Locales; //将所有的语言环境添加到字典 for (int i = 0; i < locales.Count; ++i) { var locale = locales[i]; Debug.Log(locale.LocaleName); //添加到字典 if (!languageDic.ContainsKey(locale.LocaleName)) languageDic.Add(locale.LocaleName, locale); } isShow = true;//显示切换按钮 } private bool isShow = false; private void OnGUI() { if (!isShow) return; //通过语言环境名称进行替换 注意:语言环境名从设置文件的语言表中查看 if (GUI.Button(new Rect(0, 0, 100, 50), \"中文\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[0];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"Chinese (Simplified) (zh-Hans)\"]); //名称加载 } if (GUI.Button(new Rect(0, 60, 100, 50), \"英文\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[1];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"English (en)\"]); //名称加载 } if (GUI.Button(new Rect(0, 120, 100, 50), \"日语\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[2];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"Japanese (ja)\"]); //名称加载 } }}

七、Localization各文件之间的关系

1.资源文件的作用描述 

 讲关系之前我们先捋一下我们创建的文件都有哪些

① Localization Settings全局配置文件文件

  • 作用:本地化系统的全局配置文件。用于管理和协调本地化资源的入口。
  • 职责:
    • 定义支持的语言列表(如:Chinese (Simplified) (zh-Hans)、English (en)、Japanese (ja))。
    • 配置默认语言。
    • 管理本地化表和资产的引用。
    • 提供设置,比如本地化资源的加载路径。
  • 与其他文件的关系:
    • Localization Settings 文件通过管理语言环境和资源表(如 myTable),将不同的语言内容和 Unity 项目整合在一起。

 ② Chinese (Simplified) (zh-Hans)(语言环境)

  • 作用: 这是一个语言环境,表示目标语言(如:中文简体)的支持。
  • 职责:
    • 是 Localization Settings 中的一部分,用于标识具体的语言文化。
    • 为语言表(如 myTable)和资源提供目标语言的内容。
  • 与其他文件的关系:
    • 通过语言表(如 myTable 和 myTable_zh-Hans)与具体语言的翻译内容关联。
    • 配置中可以将其与其他语言环境(如英文)进行切换。

③ myTable Shared Data 共享数据文件

  • 作用: 这是一个共享数据文件,用于存储表的元信息和标识符。
  • 职责:
    • 定义表的唯一标识符(GUID)。
    • 保存表的所有键值(Key)及其通用信息。
    • 为所有语言版本的表(如 myTable_zh-Hans)提供结构化的引用。
  • 与其他文件的关系:
    • 是 myTable 和所有语言表的基础,确保所有语言版本的表结构一致。
    • 每个语言版本的表(如 myTable_zh-Hans)都依赖 myTable Shared Data 提供的 Key 列表。

④ myTable 默认语言的本地化表

  • 作用: 这是默认语言的本地化表。
  • 职责:
    • 定义语言表的键值对内容,通常是默认语言(如英文)。
    • 使用 myTable共享数据 提供的键值结构。
  • 与其他文件的关系:
    • 与 myTable Shared Data 保持一致,用于存储默认语言的内容。
    • 当项目切换语言时,会被其他语言版本的表(如 myTable_zh-Hans)覆盖。

⑤ myTable_zh-Hans 语言具体翻译内容

  • 作用: 这是Chinese (Simplified) 的语言表,存储了该语言的具体翻译内容。
  • 职责:
    • 提供目标语言的本地化内容(如中文翻译)。
    • 继承和引用 myTable Shared Data 中的键值结构。
  • 与其他文件的关系:
    • 使用 myTable Shared Data 定义的 Key 作为基础,与其他语言表结构一致。
    • 当选择语言为zh-Hans 时,Localization Settings 将加载 myTable_zh-Hans 中的内容覆盖默认表 myTable。 

2.文件之间的关系总结

这里我简单描述一下这五个文件之间的关系。

1. Localization Settings 文件: 全局控制中心是老大,是一切操作的开始,我们在这个配置文件中设置一些可用的语言环境(例如:Chinese (Simplified) (zh-Hans))。

2.myTable Shared Data 文件:在创建Table时,会根据设置的Key来生成语言表结构myTable Shared Data文件,这个文件只是语言表的模板,只有创建语言表时才会用到此文件。

3.myTable文件:存储所有语言表的引用,通过语言环境获取语言表的GUID,然后在通过GUID获取指定的语言表。是语言环境和语言表之间的桥梁。

4. myTable_zh-Hans 语言表:一个语言环境对应一个语言表,每个语言表是通过myTable Shared Data 文件模板创建,并且通过Key存储具体翻译内容。

八、资源表创建使用(图片、音频等) 

Localization插件切换语言环境时除了语言文字替换,还可以进行其他资源的替换,例如图片、视频等。我们一般称这种表为资源表(Asset Table),其操作方式与文本表(stringTable)类似。

 在Localization Tables面板中创建表

 

设置资源,这里我们使用图片切换来做案例演示

在一个空场景中创建一个 RawImage然后右键添加Localize Texture Event组件

程序运行

九、自定义LocalizationEvent组件(字体替换 )

通过上方资源表案例我们用到了一个Localize Texture Event组件,这个组件和Localize String Event组件一样都是根据语言环境获取指定key下的内容然后对相关组件进行修改,区别就是两个替换的内容不同。

1.插件自带的Event事件 

我们可以点击Localize Texture Event组件定位到该组件脚本位置,可以发现Localization自带了五种用于切换不同资源的Event组件。

每个Event的功能使用就不多说了,大家可以自己探索一下。虽然插件自带了5种切换资源的方法,但在项目中这五种是不够用的,例如我们切换语言时如果当前Text的字体不支持该语言,就会导致替换的内容出现问题。基于这个问题插件给我们提供了自定义Event组件的方式。

2.给Table添加新语言 

先给配置文件添加一个韩语语言环境,用于替换字体案例使用。

 给我们的myTable添加该语言环境一共有两种添加方法,第一种是通过myTable文件进行添加

第二种通过Localization Tables面板添加语言

 继续添加韩语的翻译内容

3.设置TextMeshPro组件

 给Text添加Localize String 组件

 使用Localization Scene Controls面板切换到韩语。

4.给AssetTable资源表添加字体资源

网上找一个支持韩语的字体导入到Font文件夹下

处理为TextMeshPro字体文件

在Localization Tables面板中设置资源内容,其他语言都设置为黑体,只有韩语环境设置为韩语字体。

4.创建自定义Event脚本 

下面开始自定义Event,用于切换Text字体。自定义Event一般需要创建三个脚本。

  • LocalizedTmpFont:继承LocalizedAsset, 用于管理和加载与当前语言环境对应的本地化资源。
using System;namespace UnityEngine.Localization{ // 继承自 LocalizedAsset,用于管理和加载与当前语言环境对应的本地化资源。 // 泛型参数 TObject 指定为 TMPro.TMP_FontAsset,表示该类仅处理 TextMeshPro 字体资源的本地化。 [Serializable] public class LocalizedTmpFont : LocalizedAsset { }}
  • UnityEventTmpFont:继承UnityEvent,用于注册和触发带参数的事件。
using System;using UnityEngine.Events;namespace UnityEngine.Localization.Events{ //继承自 UnityEvent,用于注册和触发带参数的事件。 //泛型参数 T 为 TMPro.TMP_FontAsset,表示事件传递的参数类型为 TextMeshPro 字体资源。 [Serializable] public class UnityEventTmpFont : UnityEvent { }}
  • LocalizeTmpFontEvent:继承 LocalizedAssetEvent,当语言环境发生改变时将指定的资源类型发送给指定的Unity事件。
using TMPro;using UnityEngine.Localization.Events;namespace UnityEngine.Localization.Components{ //继承LocalizedAssetEvent //TObject:设置为TMP_FontAsset,代表只能获取当前语言环境的TMP_FontAsset类型资源 //TReference:设置为 LocalizedTmpFont,指定 TMP_FontAsset 类型的本地化引用,用于管理语言环境中对应的字体资源。 // TEvent:设置为 UnityEventTmpFont,定义一个 Unity 事件类型,用于在字体资源更新时通知并传递新的 TMP_FontAsset。 [AddComponentMenu(\"Localization/Asset/Localize TmpFont Event\")] public class LocalizeTmpFontEvent : LocalizedAssetEvent { private TextMeshProUGUI text; private void Awake() { text = GetComponent(); } protected override void UpdateAsset(TMP_FontAsset localizedAsset) { base.UpdateAsset(localizedAsset); text.font = localizedAsset; } }}

5.使用方法

切换到韩语环境下,文字内容不会出现错误了。

十、Addressables资源管理(预加载)

Localization本地化插件创建的资源文件是被Addressables插件进行资源管理的,打开Addressables Groups面板可以看到我们创建的资源。

1.多语言资源打包 

 打开Addressables Groups面板

 创建的资源都被自动分组了

 每次打包时注意Build一下资源,否则程序打包后资源不会被加载

2.远程下载多语言资源包(预加载)

Addressables提供资源从远端服务器下载功能

①使用HFS创建简易服务器

HFS简易服务器下载链接

 下载打开软件,进行如下配置

配置好后点击在浏览器中打开

打开此界面代表创建成功。 

② 创建Addressables远程下载配置文件

首先我们打开Addressables Profiles面板

创建一个远程下载配置文件并进行配置

③多语言资源组Groups设为远端下载

将AddressableAssetSettings配置文件设置为刚才设置的My

 回到Groups面板设置,将多语言资源组Groups全部设置成远程服务器加载

④Buid资源并将资源包部署到远程

 Build资源

 找到资源文件并部署到服务器

设置编辑器从远程加载资源 

 ⑤脚本书写

预加载思路:将所有资源先下载到本地,下载完成后生成切换语言环境按钮。

using System;using System.Collections.Generic;using System.Threading.Tasks;using UnityEngine;using UnityEngine.AddressableAssets;using UnityEngine.Localization;using UnityEngine.Localization.Settings;using UnityEngine.ResourceManagement.AsyncOperations;//动态系系修改语言public class Test : MonoBehaviour{ private string[] languageGroups; private Dictionary languageDic; //存储本地语言 void Start() { //bundle资源标签和key值 languageGroups = new[] { \"Locale\", \"Assets/Languages/Tables/myTable Shared Data.asset\", \"myTable_zh-Hans\", \"myTable_en\", \"myTable_ja\", }; languageDic = new Dictionary(); InitLocales(\"myTable\"); //初始化所有语言环境 } private float loadedNumber; //已加载数量 private float currentProgress; //当前进度 //初始化所有语言环境(从远端获取所有语言环境) private async void InitLocales(string tableName) { /* 1.确保 Localization 系统完全初始化 * 它是整个本地化系统的核心初始化操作,加载基础配置(如 Locale 列表、默认语言环境、语言环境切换器等)。 * 如果 Localization 系统尚未初始化完成,直接使用其他操作可能会导致不可预期的行为。 */ var initialization = LocalizationSettings.InitializationOperation; await initialization.Task; if (initialization.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"初始化失败:\\n\" + initialization.OperationException?.Message); return; } //3.获取所有语言环境 loadedNumber = currentProgress = 0; foreach (var group in languageGroups) { AsyncOperationHandle downloadHandle = Addressables.DownloadDependenciesAsync(group,false);//false 注意:一定要取消自动释放语柄,否则资源会被自动清除导致后面的加载依赖出现问题 while (!downloadHandle.IsDone) { // currentProgress = (loadedNumber + downloadHandle.PercentComplete) / languageGroups.Length; Debug.Log(\"下载总进度:\" + downloadHandle.PercentComplete); await Task.Yield(); }  await downloadHandle.Task; if (downloadHandle.Status != AsyncOperationStatus.Succeeded) Debug.Log($\"{group}语言环境包加载失败:\\n\" + downloadHandle.OperationException?.Message);  //加载结束后手动释放语柄 Addressables.Release(downloadHandle); loadedNumber++; } //3.提前加载语言表 var tableLocal = LocalizationSettings.StringDatabase.GetTableAsync(tableName); await tableLocal.Task; if (tableLocal.Status != AsyncOperationStatus.Succeeded) { Debug.Log(\"语言表加载失败:\\n\" + tableLocal.OperationException?.Message); return; } //获取所有语言环境 GetAllLocals(); isShow = true; } //获取所有Locale (语言环境) private void GetAllLocals() { //获取语言表 var locales = LocalizationSettings.AvailableLocales.Locales; //将所有的语言环境添加到字典 for (int i = 0; i < locales.Count; ++i) { var locale = locales[i]; Debug.Log(locale.LocaleName); //添加到字典 if (!languageDic.ContainsKey(locale.LocaleName)) languageDic.Add(locale.LocaleName, locale); } } private bool isShow = false; private void OnGUI() { if (!isShow) return; //通过语言环境名称进行替换 注意:语言环境名从设置文件的语言表中查看 if (GUI.Button(new Rect(0, 0, 100, 50), \"中文\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[0];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"Chinese (Simplified) (zh-Hans)\"]);//名称加载 } if (GUI.Button(new Rect(0, 60, 100, 50), \"英文\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[1];//下标加载 LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"English (en)\"]);//名称加载 } if (GUI.Button(new Rect(0, 120, 100, 50), \"日语\")) { // LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[2];//下标加载  LocalizationSettings.Instance.SetSelectedLocale(languageDic[\"Japanese (ja)\"]);//名称加载 } }}

将脚本挂在场景物体上

运行效果: 

十一、其他问题

如果编辑器出现以下报错时, 可以通过升级Addressables组件来解决。

打开Package Manager面板

Packages选择UnityRegistry,搜索 Addressables,选择Version History。然后更新版本即可。

这里我已经更新过了,所以只显示安装了1.21.2。