Unity UGUI 本地化方案 - Localiztion Tool

问题及难点

相信做海外游戏代理的同学一定会遇到需要做本地化的问题,其中资源可以通过替换合图来处理。而文本是其中一个比较难处理的问题。其中主要难点在于UGUI本身没有提供相应的插件,而对于一开始没规划的项目来说,你需要去找出其中所有的外语文本,找到后再将对应Text控件替换成你自定义的控件,替换后再需要填入对应的Key值。这对于策划来说简直是恶梦~

工具的基本原理

LocalizationText控件

UGUI中的Text控件只提供了text属性接口供你进行对文本的设置,那么要实现本地化有下面两种做法:

  • 1.在Text控件初始化后使用代码将其text属性修改,这样的话,我们需要在所有的有Text控件的地方添加相应的修改代码,这显然是不符合可持续发展的。
  • 2.自定义新的Text控件,在编辑器阶段完成对Key值的设置。这种方案需要我们继承Text控件,并修改其输入接口,从修改text改为修改Key,通过Key来得到相应语言的文本。

综上,我们采取第二种方案来实现我们的LocalizationText控件,通过对Key的设置得到文本,如图:
这里写图片描述
有了LocaliztionText我们只需要输入Key**Help**而不需要直接填写帮助二字,通过为不同语言建立映射表我们就可得到对应语言的Help的文本。这里写图片描述

相关代码实现

这里我主要是先去看了UGUI源码,根据其中Text的实现做了修改。

using System;
using UnityEngine.UI;
using UnityEngine;

namespace Localization
{
    public class LocalizationText : Text
    {
        #region Param


        [Header("Localization")]
        [SerializeField]
        protected string m_KeyString;

        public string keyString {
            get {return this.m_KeyString; }
            set { this.m_KeyString =  value; }
        }
        #endregion

        #region Override Part
        public override string text
        {
            get
            {
                if (!ClientString.LocalizationDict.ContainsKey(keyString))
                {
                    m_Text = string.Format("[{0}]",m_KeyString);
                }
                else
                {
                    m_Text = ClientString.LocalizationDict[keyString];
                }
                return m_Text;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    if (String.IsNullOrEmpty(m_Text))
                        return;
                    m_Text = "";
                    SetVerticesDirty();
                }
                else if (m_Text != value)
                {
                    m_Text = value;
                    SetVerticesDirty();
                    SetLayoutDirty();
                }
            }
        }
        #endregion
    }
}

这部分的代码其实比较简单,主要是进行了一个Key/Value的映射,然后设置对应的Text。其中的KeyString则是多语言文本映射用的Key值。
总的来说,此控件的原理是比较简单的,Key->Value->Set Text。但对于一个完整的方案来说这是远远不够的,我们还需要有自动化修改的工具。

控件替换工具

这个工具简直是那些用UGUI的Text控件开发了很久后,突然想做多语言的项目的救星。因为如果你通过将项目中的Text控件删除后再AddComponent< LocalizationText >的话会出现一个很灾难性的问题,那就是MonoBehaviour中对相应Text控件的引用都会丢失,因为Unity是通过GUID和FileID来找到对应类型控件的,而这样的处理方式会让GUID和FileID改变从而导致严重的后果。所以这个控件替换工具就非常重要了~

原理

上面说到Unity中用GUID和FileID来找对应类型控件的,那么只需要找到初始Text控件的GUID和FileID,然后更改为LocaliztionText控件的GUID和FileID则可以达到我们的目的。下面是一个场景文件(.unity)文件的内容,我们在场景文件中添加了一个物体名叫Normal Text上面挂着Text控件。
这里写图片描述
中间的图片表示的是Normal Text这个物体的结构,他有三个m_Component其中有一个是Text,Text控件的结构如图,所以在右边Text Component部分的m_Script处,只要把FileID和GUID修改为Localization Text对应的FileID和GUID即可,这样就避免了上面提到的引用丢失问题。那么怎么得到Localiztion Text的GUID和FileID呢。最简单的方法是添加一个Localization Text物体(也就是现在场景中的Localization Text),然后去场景文件内容中查看。
这里写图片描述

所以此工具的基本原理就是将Text控件的FileID和GUID换成LocaliztionText控件的FileID和GUID。
具体代码如下(基本就是一个文本级别的操作):

public static void UpgradeToLocalizationText(string assetPath, string textCompGUID, string textCompFileID, string localizeCompGUID, string localizeCompFileID, List<string> jpTextList)
{
    ClearConsole();
    string formatedGUID = string.Format("guid: {0},", localizeCompGUID);
    string fullPath = Path.Combine(Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/')), assetPath);
    string[] lines = System.IO.File.ReadAllLines(fullPath);
    string fullText = System.IO.File.ReadAllText(fullPath);

    for (int i = 0; i < lines.Length; i++)
    {
        if(lines[i].Contains(textCompFileID)) {
            int scriptLineIdx = i;  //往下遍历直到找到m_Text
            while(i < lines.Length) {

                if(lines[i].Contains("--- !u!"))    //发现下一个控件时跳出
                    break;  

                if (JpUtil.PlainText_IsContainsJapanese(lines[i]))
                {
                    string jpText = Regex.Unescape(lines[i].Split('\"')[1]).Replace("\n", "\\n");
                    jpTextList.Add(jpText);

                    //发现了FileID相同 但GUID不同的情况,所以直接把FileID后面的GUID替换
                    lines[scriptLineIdx] = Regex.Replace(lines[scriptLineIdx], guidReplacePattern, formatedGUID);
                    lines[scriptLineIdx] = lines[scriptLineIdx].Replace(textCompFileID, localizeCompFileID);
                    break;
                }
                i++;
            }
        }
    }
    string text = string.Join("\n", lines);
    System.IO.File.WriteAllText(fullPath, text);
    AssetDatabase.Refresh();
}

优化方案

上面的控件及工具解决了这一方案中的两个难点,但是我们还可以进行优化,就是对有需要变化(例如内容为日文或者英文)的控件才进行转化,这其中的处理就是

JpUtil.PlainText_IsContainsJapanese(lines[i])

这一处理会判断文本中是否有日文,有的话才对Text控件进行升级,所以你需要为不同的语言做不同的文本判断,我们项目为日文所以我做了日文的判断。

除此之外在策划将对应的翻译都处理好后需要来设置LocaliztionText的Key值,我新增了一个自动设置Key值的功能,原理是将其中的内容进行比对,用Value找到Key值,然后填入。

这个工具已经上传到GitHub:Localization Text Tool (麻烦Star一哈)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章