导读:在使用Unity开发游戏的过程中,本地化是必不可少的。网络上也有很多的本地化工具,本次我介绍的是Unity官方提供的Localization插件,大家可以在Package.Manager进行安装.一、语言配置,本地化表创建.在Project Setting中找到Localizat
在使用Unity开发游戏的过程中,本地化是必不可少的。网络上也有很多的本地化工具,本次我介绍的是Unity官方提供的Localization插件,大家可以在Package Manager进行安装
在Project Setting中找到Localization,(需要先创建这个Localization Setting文件)点击Locale Generator选择需要本地化的语言。创建好后会得到这些文件,这些文件可以用于切换语言(后面做切换语言界面时会用),先把英语拖入作为默认语言。
打开本地化表工具,创建本地化表
创建UILocalization和ScriptLocalization两个本地化表,分别用作UI和代码本地化。会得到以下文件
可以在表格中添加一些需要本地化的文本(这里只是先试试,先不要添加太多,后面会用Excel进行管理),注意最左侧的Key值,这个key值后续用来确定文本
给UI上的文本挂载LocalizeStringEvent脚本,点击其中最上面的String Reference搜索之前表格中创建的本地化文本。搜索Key值或者本地化的文本都可以搜得到。
注:如果显示不完整,可以调节右下角的小球
然后将LocalizeStringEvent的Update String调整为Text.text,就是要刷新的脚本。任何的string都可以刷新,Text Mesh Pro也是可以的
运行游戏查看效果,可以临时先使用右上角的下拉框调节本地化语言,界面上的文本会实时刷新。
我先将本地化的需求暂时分成两类
1,UI本地化:UI界面上面固定不变的文本
2,Script本地化:代码中实时更改的文本,包括(String.Format+数值),动态弹出的提示语,NPC对话,物品名字等
为了后续教程,我们暂时约定在制作界面时,UI本地化的内容正常命名,Script本地化的对象以$开头命名,例如:
首先,需要知道有哪些文本需要本地化,并给它们自动挂载上LocalizeStringEvent脚本。可以在Editor目录下创建一个扩展脚本实现此功能。
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;
using UnityEngine.UI;
public class LocalizeTextEditor : EditorWindow
{
private string outputFilePath = "LocalizedText.txt"; // 指定的txt文件路径
private List<string> localizedTextEntries = new List<string>();
[MenuItem("Custom/Localize Text")]
private static void ShowWindow()
{
GetWindow<LocalizeTextEditor>("Localize Text");
}
private void OnGUI()
{
GUILayout.Label("Localize Text Editor", EditorStyles.boldLabel);
if (GUILayout.Button("Localize Selected GameObjects"))
{
LocalizeSelectedGameObjects();
}
if (GUILayout.Button("Save Localized Text to File"))
{
SaveLocalizedTextToFile();
}
if (GUILayout.Button("Print Script Localize Selected GameObjects"))
{
PrintScriptLocalize();
}
}
private void LocalizeSelectedGameObjects()
{
localizedTextEntries.Clear();
GameObject[] selectedObjects = Selection.gameObjects;
foreach (GameObject selectedObject in selectedObjects)
{
Text[] textComponents = selectedObject.GetComponentsInChildren<Text>(true);
foreach (Text textComponent in textComponents)
{
if (!textComponent.name.StartsWith("$"))
{
// 需要本地化的Text
if(textComponent.gameObject.GetComponent<LocalizeStringEvent>() == null)
{
LocalizeStringEvent localizeEvent = textComponent.gameObject.AddComponent<LocalizeStringEvent>();
// 标记对象为“已修改”
EditorUtility.SetDirty(selectedObject);
}
// 添加到列表
string entry = $"{selectedObject.name}\t{textComponent.name}\t{textComponent.text}";
localizedTextEntries.Add(entry);
}
}
}
}
private void SaveLocalizedTextToFile()
{
if(!File.Exists(outputFilePath))
{
File.Create(outputFilePath).Dispose();
}
using (StreamWriter writer = new StreamWriter(outputFilePath, true))
{
foreach (string entry in localizedTextEntries)
{
writer.WriteLine(entry);
}
}
Debug.Log($"Localized text entries saved to {outputFilePath}");
}
private void PrintScriptLocalize()
{
GameObject[] selectedObjects = Selection.gameObjects;
foreach (GameObject selectedObject in selectedObjects)
{
Text[] textComponents = selectedObject.GetComponentsInChildren<Text>(true);
foreach (Text textComponent in textComponents)
{
if (textComponent.name.StartsWith("$"))
{
Debug.Log($"{selectedObject.name}\t{textComponent.name}\t{textComponent.text}");
}
}
}
}
}
我本来还想自动给LocalizeStringEvent的Update String赋值,但是未能实现。如果哪位高人有办法可以在评论区指出
全选需要本地化的UI预制体,依次点击扩展窗口的第1和第2个按钮,可以给非$开头的Text(你们如果命名规则不一样,请自行修改脚本)自动挂载LocalizeStringEvent脚本
这些Text中的内容会输出到txt中,可以查看(注:txt默认应该在项目根路径)
右键点击本地化表的选项卡,选择导出CSV
如果使用Office,导出时选择UTF-8即可。如果和我一样使用WPS,请另存为xlsx格式,不然部分语言会乱码。
2024.2更新:不建议另存为xlsx格式,因为ID超过15位会舍入。导致原本设置好的组件找不到本地化ID
可以使用python的pandas库将数据全部变成string,再写入xlsx
import pandas as pd
df = pd.read_csv('UILocalization.csv')
df = df.astype(str)
df.to_excel('UILocalization.xlsx', index=False, sheet_name="UILocalization")
df2 = pd.read_csv('ScriptLocalization.csv')
df2 = df.astype(str)
df2.to_excel('ScriptLocalization.xlsx', index=False, sheet_name="ScriptLocalization")
将之前txt中的内容复制到表格中,由于我输出的是\t,内容会自动分布到表格的不同列,大家可以把根据预制体名字来给Key起名。
丢给AI或者翻译软件翻译之后,对于没有空格的语言,可以使用alt+回车在适当位置输入换行,例如:中文,日语等。Unity的自适应换行只会在有空格的地方换行
对于WPS用户,我们需要将xlsx转回CSV才能在Unity中读取,这里使用python的pandas库进行处理。
import pandas as pd
# 读取 Excel 文件
df = pd.read_excel("UILocalization.xlsx", sheet_name="UILocalization")
# 将 DataFrame 写入 CSV 文件
df.to_csv('dataUI.csv', encoding='utf-8', index=False)
df2 = pd.read_excel("ScriptLocalization.xlsx", sheet_name="ScriptLocalization")
df2.to_csv('dataScript.csv', encoding='utf-8', index=False)
这里还处理了后面的Script本地化文件,大家还没看完后续教程的,可以先注释后两行代码
在本地化表导出CSV的上面还有导入CSV,这里就不截图了,导入之后就可以得到我们刚才在Office或WPS中编辑的表格
还是全选所有UI预制体,这次点击第三个按钮,会在控制台中输出所有$开头的Text。这些Text都是在代码中更改内容的,只能对其查找所有引用,然后逐个更改。
那么,如何在代码中获取本地化的内容呢,我们需要创建一个单例的LocalizationManager,提供一个获取本地化内容的函数。
以防止有编程小白,这里介绍一下单例模式,单例模式就是类最多只有一个对象,并且有一个指向该对象的静态指针。在Unity中可以用以下代码实现一个简单的单例模式
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : Singleton<T> {
protected static T _instance;
public static T Ins {
get {
return _instance;
}
}
protected virtual void Awake() {
_instance = (T)this;
}
protected virtual void OnDestroy() {
if (_instance == this) {
_instance = null;
}
}
}
在场景中创建一个(DontDestroyOnLoad)不会销毁的对象,并挂载LocalizationManager脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.ResourceManagement.AsyncOperations;
namespace UI
{
public class LocalizationManager : Singleton<LocalizationManager>
{
private StringTable ScriptStringTable; //代码本地化表
void Start()
{
if (LocalizationSettings.AvailableLocales.Locales.Count > 0)
{
GetLocalizationTable();
}
else
{
LocalizationSettings.InitializationOperation.Completed += OnLocalizationInitialized;
}
LocalizationSettings.SelectedLocaleChanged += OnLocaleChanged;
}
protected override void OnDestroy()
{
base.OnDestroy();
LocalizationSettings.SelectedLocaleChanged -= OnLocaleChanged;
}
/// <summary>
/// 本地化初始化完成
/// </summary>
private void OnLocalizationInitialized(AsyncOperationHandle<LocalizationSettings> handle)
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log("Localization initialized successfully!");
GetLocalizationTable();
}
else
{
Debug.LogError("Localization initialization failed.");
}
}
/// <summary>
/// 切换语言
/// </summary>
private void OnLocaleChanged(Locale newLocale)
{
GetLocalizationTable();
}
/// <summary>
/// 获取本地化表
/// </summary>
public void GetLocalizationTable()
{
ScriptStringTable = LocalizationSettings.StringDatabase.GetTable("ScriptLocalization");
//Debug.LogWarning(ScriptStringTable.GetEntry("CommonTip_NoItem").GetLocalizedString());
}
/// <summary>
/// 获取本地化文本
/// </summary>
public string GetLocalizedString(string key)
{
return ScriptStringTable.GetEntry(key).GetLocalizedString();
}
}
}
这里我们注册了两个事件,一个是LocalizationSettings.InitializationOperation.Completed,这个是LocalizationSettings初始化完成时调用。由于本地化插件是异步初始化,代码运行到start时不一定初始化完成,此处通过判断可用语言是否大于0来判断有没有初始化完成。
另一个事件是LocalizationSettings.SelectedLocaleChanged,这个是LocalizationSettings切换语言是调用。这两个事件都会执行获取本地化表的操作。对于之前在代码中更改的Text,可用调用GetLocalizedString来获取本地化文本。
例如:
物品名字要进行本地化,key值是GameItem_ID,每个物品ID不同
和之前使用表格管理UILocalization表类似,我们也使用表格管理ScriptLocalization表。
创建一个这样的UI界面,我设计的是每个按钮都能点。当前使用的语言是绿的。
给每个按钮拖上一个Locale,就是之前最开始创建的用于切换语言的
/// <summary>
/// 刷新按钮状态
/// </summary>
public void RefreshItemChooseState()
{
Locale currentLocale = LocalizationSettings.SelectedLocale;
foreach (var item in languageItems)
{
item.SetChooseState(currentLocale == item.locale);
}
}
/// <summary>
/// 设置语言
/// </summary>
public void SetLanguage(Locale locale)
{
if(locale == LocalizationSettings.SelectedLocale)
{
return;
}
LocalizationSettings.Instance.SetSelectedLocale(locale);
Client.Ins.Player.PlayerLanguage = locale.LocaleName;
RefreshItemChooseState();
}
获取当前语言:LocalizationSettings.SelectedLocale,获取之后和每个按钮上面拖入的Locale比对,一样的就是当前语言
设置语言:LocalizationSettings.Instance.SetSelectedLocale(locale);把拖入的Locale设置进去,就可以切换语言。
以下为拓展内容:
设置完语言之后,最好能够保存下来,用户下次启动自动使用。这里把locale.LocaleName,也就是语言的名字保存下来。
大家可以用任意方式保存一个string,我这边用的json,这里就涉及到游戏存档设计了,超出了本文的讨论范围。大家可以自行设计游戏存档,反正能够存取当前语言名字(public string PlayerLanguage)就行了
这里仍然使用了之前提到的LocalizationSettings.InitializationOperation.Completed事件,在LocalizationSettings初始化完成之后设置为保存的语言。
/// <summary>
/// 本地化初始化完成
/// </summary>
private void OnLocalizationInitialized(AsyncOperationHandle<LocalizationSettings> handle)
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log("Localization initialized successfully!");
UseSaveLanguage();
}
else
{
Debug.LogError("Localization initialization failed.");
}
}
/// <summary>
/// 使用保存的语言
/// </summary>
private void UseSaveLanguage()
{
var locales = LocalizationSettings.AvailableLocales.Locales;
foreach (var locale in locales)
{
if (locale.LocaleName == PlayerLanguage)
{
LocalizationSettings.Instance.SetSelectedLocale(locale);
break;
}
}
}
上一篇:刀光拖尾功能的改良
下一篇:【Oculus Interact