需求
有些情况下需要支持多语言系统,本文将会结合官方教程,探讨多语言系统的组织及实现。当然了最后可以根据需要扩展。

如图,我们需要实现语言的切换。
分析
实现语言切换,首先要有容器预先记录不同语言的文本信息,然后通过选择不同的语言,来切换显示内容。这里采用JSON来记录要显示的文字信息,然后通过Dictionary键值对来提取JSON信息。‘
准备
首先下载 基础资源文件(需要“科学上网”),没办法可以先下载 完成后的项目。
实现
第一步,自然而然的创建存放数据的数据结构。
/// <summary>
/// 可序列化数据类
/// </summary>
[System.Serializable]
public class LocalizationData {
public LocalizationItem[] items; //用来存放条目
}
[System.Serializable]
public class LocalizationItem //每个条目的属性,便于Dictionary创建键值对
{
public string key;
public string value;
}
这里我们要注意到的是这两个类都需要加上 [Serializable] 特性,原因后面用到JSON时会讲,先记下。
第二步,创建一个LocalizationManager类。然后用来实现读取json的功能。这里还要再工程中创建一个Streaming Assets(大小写敏感),把JSON文件都放在该目录下。
using UnityEngine;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// 本地化控制器
/// </summary>
public class LocalizationManager : MonoBehaviour
{
//单例,避免场景切换时重复加载
public static LocalizationManager instance;
//创建字典,用于记录本地化的数据键值对
private Dictionary<string, string> localizedText;
//flag用来
private bool isReady = false;
string missingTextString = "Localized text not found";
//避免重复创建
private void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
//加载本地化文件 【1】
public void LoadLocalizedText(string fileName)
{
//创建对应的键值对
localizedText = new Dictionary<string, string>();
//获取文件路径 【2】
string filePath = Path.Combine(Application.streamingAssetsPath,fileName);
//如果文件系统中存在指定路径
if (File.Exists(filePath))
{
//读取文件所有行并且返回一个字符串
string jsonfile = fileName + ".json";
string dataAsJson = File.ReadAllText(jsonfile);
//将JSON格式的输入转换成LocalizationData下的属性 【3】
LocalizationData loadData = JsonUtility.FromJson<LocalizationData>(dataAsJson);
//使用生成匹配的键值对
for (int i = 0; i < loadData.items.Length; i++)
localizedText.Add(loadData.items[i].key,loadData.items[i].value);
Debug.Log("Data loaded, dictionary contains: " + localizedText.Count + " entries");
}
else
Debug.LogError("Cannot find file!");
isReady = true;
}
//通过键 获取对应的值,供类外调用
public string GetLocalizedValue(string key)
{
string result = missingTextString;
if (localizedText.ContainsKey(key))
{
result = localizedText[key];
}
return result;
}
// 标志位,供进入场景时判断是否要修改语言
public bool GetIsReady()
{
return isReady;
}
}
【1】指定到按钮的点击事件中,filename是要加载的json文件名
【2】在读取Streaming Assets中的文件时,要注意的时不同平台下路径时不一样的:
PC上的地址具体是:
path = Application.dataPath + "/StreamingAssets";
IOS上的地址具体是:
path = Application.dataPath + "/Raw";
Andorid上的地址具体是:
path = "jar:file://" + Application.dataPath + "!/assets/";
这里多说一句,在Android中,这些文件包含在压缩的.jar文件中(与标准zip压缩文件的格式基本相同)。这意味着如果你不使用Unity的WWW类来检索文件,则需要使用其他软件来查看.jar归档文件并获取文件。
等下会说明如何在android下正确调用Streaming Assets文件。
【3】JSON采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
【3】JsonUtility.FromJson<T>(string json)这种方法使用Unity序列化器; 因此你创建的类型T必须由序列化程序支持。它必须是一个用Serializable特性标记的普通类/结构体,且非继承。
第三步,创建一个StartupManager类,主要用来判断是否需要加载语言
/// <summary>
/// 挂载在LocalizationManager上
/// </summary>
public class StartupManager : MonoBehaviour
{
// Use this for initialization
private IEnumerator Start()
{
while (!LocalizationManager.instance.GetIsReady())
{
yield return null;
}
SceneManager.LoadScene("MenuScreen"); //或者做些其他的事情
}
}
第四步,写完了读取功能,那么就要使用了。创建一个LocalizedText类挂载在所有要更换语言的Text上,然后在组件面板中指定它的Key.
/// <summary>
/// 挂载Text上,更换语言
/// </summary>
public class LocalizedText : MonoBehaviour
{
public string key;
// Use this for initialization
void Start()
{
Text text = GetComponent<Text>();
text.text = LocalizationManager.instance.GetLocalizedValue(key);
}
}
这样,本地化操作基本就完成了。当然了,现在还有一个问题,JSON文件只能手动生成。我们得简化这个步骤。
第五步, 自定义编辑窗口。在工程下创建一个Editor文件夹,然后在里面新建一个LocalizedTextEditor类。
using UnityEngine;
using UnityEditor;
using System.IO;
/// <summary>
/// 自定义编辑器:
/// </summary>
public class LocalizedTextEditor : EditorWindow 【1】
{
public LocalizationData localizationData;
//创建一个本地化文本编辑窗口
[MenuItem("Window/Localized Text Editor")] 【2】
static void Init()
{
EditorWindow.GetWindow(typeof(LocalizedTextEditor)).Show();
}
private void OnGUI()
{
if (localizationData != null)
{
//创建一个序列化对象并设置其属性 【3】
SerializedObject serializedObject = new SerializedObject(this);
SerializedProperty serializedProperty = serializedObject.FindProperty("localizationData");
EditorGUILayout.PropertyField(serializedProperty, true); 【5】
serializedObject.ApplyModifiedProperties();
//保存数据
if (GUILayout.Button("Save data"))
{
SaveGameData();
}
}
//加载数据
if (GUILayout.Button("Load data"))
{
LoadGameData();
}
//创建数据
if (GUILayout.Button("Create new data"))
{
CreateNewData();
}
}
//加载数据
private void LoadGameData()
{
//打开文件浏览窗口
string filePath = EditorUtility.OpenFilePanel("Select localization data file", Application.streamingAssetsPath, "json");
if (!string.IsNullOrEmpty(filePath))
{
string dataAsJson = File.ReadAllText(filePath);
localizationData = JsonUtility.FromJson<LocalizationData>(dataAsJson);
}
}
//保存数据
private void SaveGameData()
{
string filePath = EditorUtility.SaveFilePanel("Save localization data file", Application.streamingAssetsPath, "", "json");
if (!string.IsNullOrEmpty(filePath))
{
string dataAsJson = JsonUtility.ToJson(localizationData);
File.WriteAllText(filePath, dataAsJson);
}
}
//创建数据
private void CreateNewData()
{
localizationData = new LocalizationData();
}
}
【1】 继承自EditorWindow的类可以使你创建自定义编辑器窗口。
【2】 这个特性表示了菜单栏上,自定义窗口的打开路径
【3】 SerializedObject和SerializedProperty是用于以完全通用的方式编辑对象属性的类,可以自动处理预制的撤消和样式UI。SerializedObject与SerializedProperty和Editor类一起使用。它可以使你在编辑器中编辑文件中的属性。这样就省去了手动创建JSON文件的麻烦。
安卓中使用Streaming Assets的问题
上文也提到了,Streaming Assets在不同的平台下,文件路径是不一样的。在安卓中我们要用WWW来调用文件地址。
if (Application.platform == RuntimePlatform.Android)
{
string filePathAD = Application.streamingAssetsPath + jsonfile;
string jsonString;
WWW reader = new WWW(filePath);
while (!reader.isDone) { }
jsonString = reader.text;
LocalizationData loadData = JsonUtility.FromJson<LocalizationData>(jsonString);
for (int i = 0; i < loadData.items.Length; i++)
localizedText.Add(loadData.items[i].key, loadData.items[i].value);
}
网友评论