本地化(Localization)

作者: JervieQin | 来源:发表于2018-03-03 13:23 被阅读75次

需求

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

语言选择

如图,我们需要实现语言的切换。

分析

实现语言切换,首先要有容器预先记录不同语言的文本信息,然后通过选择不同的语言,来切换显示内容。这里采用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);            
        }

相关文章

网友评论

    本文标题:本地化(Localization)

    本文链接:https://www.haomeiwen.com/subject/wzjaxftx.html