Unity Game FrameWork—Component Extension—Localization Extension

When using the UGF framework localization, there are three problems. The first one is that planning usually uses tables to edit localized languages, while the framework parses Xml files, and there is a lack of an Excel-to-xml tool. The second problem is that in the case of editor editing, the text of the UI prefab is the key value, and the effect of the displayed text cannot be seen, and the text can only be dynamically changed during runtime, which is troublesome to debug. The third is that the font of the text cannot be modified in batches. This article is an extension to solve these problems.
The download link of the Excel meter reading plug-in . After downloading, it will conflict with the ICSharpCode.SharpZipLib plug-in of the framework, and the framework can be deleted.

1. Excel to Xml

There are two main scripts: ExcelConfig.cs, ExcelBuild.cs. The form of the language table is as follows, four languages ​​are configured, which is consistent with the framework.
insert image description here
The path in the script has been changed, which is inconsistent with the framework path, so pay attention to the path when using it.
The ExcelConfig.cs script is as follows:

using System.Collections.Generic;
using System.Data;
using System.IO;
using Excel;
using UnityEngine;

public class ExcelConfig
{
    
    
    /// <summary>
    /// 存放excel表文件夹的的路径,本例Excel表放在了"Board/配置表/"当中,Board路径与Assets路径同级
    /// </summary>
    public static readonly string excelsFolderPath = Application.dataPath + "/../Board/配置表/MK_Language.xlsx";

    /// <summary>
    /// 存放Excel转化后文件的文件夹路径
    /// </summary>
    public static string assetPath(string language) 
    {
    
    
        return $"Assets/Scripts/Data/Localization/{
      
      language}/Dictionaries/Default.xml";
    }
}

/// <summary>
/// 处理Excel工具类
/// </summary>
public class ExcelTool
{
    
    
    /// <summary>
    /// 读取表数据,生成对应的数组
    /// </summary>
    /// <param name="filePath">excel文件全路径</param>
    /// <returns>Item数组</returns>
    public static ExcelItem[] CreateItemArrayWithExcel(string filePath)
    {
    
    
        //获得表数据
        int columnNum = 0, rowNum = 0;
        Debug.Log(filePath);
        DataRowCollection collect = ReadExcelContext(filePath, ref columnNum, ref rowNum);
        Debug.Log("读取到数据表 列数 columnNum : " + columnNum + " ,行数 rowNum: " + rowNum);
        // 第一行是标题(头)
        //for (int i = 0; i < columnNum; i++)
        //{ 
        //    rowTitleList.Add(collect[0][i].ToString());
        //}
        //第二行开始才是数据
        List<ExcelItem> array = new List<ExcelItem>();
        ExcelItem title = new ExcelItem();
        //解析标题数据
        title.key = collect[1][0].ToString();
        title.Language = new string[4];
        title.Language[0] = collect[1][1].ToString();
        title.Language[1] = collect[1][2].ToString();
        title.Language[2] = collect[1][3].ToString();
        title.Language[3] = collect[1][4].ToString();
        array.Add(title);
        //解析内容数据
        for (int i = 4; i < rowNum; i++)
        {
    
    
            if (collect[i][0].ToString()==null|| collect[i][0].ToString() == "")
            {
    
    
                continue;
            }
            ExcelItem item = new ExcelItem();
            //解析每列的数据
            item.key = collect[i][0].ToString();
            item.Language = new string[4];
            item.Language[0] = collect[i][1].ToString();
            item.Language[1] = collect[i][2].ToString();
            item.Language[2] = collect[i][3].ToString();
            item.Language[3] = collect[i][4].ToString();
            array.Add(item);
        }
        return array.ToArray();
    }

    /// <summary>
    /// 读取excel文件内容
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <param name="columnNum">行数</param>
    /// <param name="rowNum">列数</param>
    /// <returns></returns>
    static DataRowCollection ReadExcelContext(string filePath, ref int columnNum, ref int rowNum)
    {
    
    
        FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        //Debug.Log(stream == null);
        IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
        DataSet result = excelReader.AsDataSet();
        // Tables[0] 下标0表示excel文件中第一张表的数据
        columnNum = result.Tables[0].Columns.Count;
        rowNum = result.Tables[0].Rows.Count;
        return result.Tables[0].Rows;
    }
}

/// <summary>
/// 数据序列化类 -- 和Excel列一一对应
/// </summary>
[System.Serializable]
public class ExcelItem
{
    
    
    public string key;
    public string[] Language;
}

/// <summary>
/// 表数据管理器 -- 存最后的数据集
/// </summary>
public class ExcelManager : ScriptableObject
{
    
    
    public ExcelItem[] dataArray;
}

The ExcelBuild.cs script is as follows

using System.Xml;
using UnityEditor;
// <summary>
/// 编辑器扩展 将xlsx文件转换Xml
/// </summary>
public class ExcelBuild : Editor
{
    
    
    /// <summary>
    /// 转换为 Asset
    /// </summary>
    [MenuItem("Tools/CreateLocalizationXML")]
    public static void CreateLocalizationXML()
    {
    
    
        //创建表格管理器
        ExcelManager excelManager = CreateInstance<ExcelManager>();
        //获取表格的值
        excelManager.dataArray = ExcelTool.CreateItemArrayWithExcel(ExcelConfig.excelsFolderPath);
        // 文件保存路径
        string[] filePath = new string[excelManager.dataArray[0].Language.Length];
        for (int i = 0; i < excelManager.dataArray[0].Language.Length; i++)
        {
    
    
            filePath[i] = ExcelConfig.assetPath(excelManager.dataArray[0].Language[i]);
        }
        for (int language = 0; language < filePath.Length; language++)
        {
    
    
            XmlDocument xml = new XmlDocument();
            //创建声明信息
            XmlDeclaration Declaration = xml.CreateXmlDeclaration("1.0", "utf-8", null);
            //设置声明信息
            xml.AppendChild(Declaration);
            //创建根节点
            XmlElement Dictionaries = xml.CreateElement("Dictionaries");
            // 设置根节点
            xml.AppendChild(Dictionaries);
            // 一级子节点
            XmlElement Dictionary = xml.CreateElement("Dictionary");
            Dictionary.SetAttribute("Language", excelManager.dataArray[0].Language[language]);
            Dictionaries.AppendChild(Dictionary);
            for (int i = 0; i < excelManager.dataArray.Length; i++)
            {
    
    
                //创建数据子节点   
                XmlElement itemId = xml.CreateElement("String");
                itemId.SetAttribute("Key", excelManager.dataArray[i].key);
                itemId.SetAttribute("Value", excelManager.dataArray[i].Language[language]);
                // 设置节点间关系
                Dictionary.AppendChild(itemId);
            }
            // 保存到本地
            xml.Save(filePath[language]);
        }
        AssetDatabase.Refresh();
    }
}

2. Real-time update text display

Function realization:
1. When modifying the key or switching the language, replace the text content in real time.
2. When modifying the font, replace all texts using the same font.
3. The editor creates Text, no need to manually hang scripts.
This module has three scripts, UIFont.cs, LocalizationText.cs, LocalizationMenuExtension.cs
UIFont.cs font script, the script is made into a prefab, and the prefab has only one font attribute.
insert image description here
The script content is as follows:

using UnityEngine;
public class UIFont : MonoBehaviour {
    
    
    public Font UseFont;
}

LocalizationText.cs This script is hung on the object carried by the Text component. Its main purpose is to modify the text display in real time and modify the font. When setting the font, you only need to drag the prefab. When replacing the font, you don’t need to modify each text, just modify the font of the prefab.
insert image description here
code show as below:

using UnityEngine.UI;
using UnityEngine;
using System.Xml;
using UnityGameFramework.Runtime;
namespace StarForce
{
    
    
    /// <summary>
    /// Custom Text Control used for localization text.
    /// </summary>
    [AddComponentMenu("UI/Text_Local", 10)]
    [RequireComponent(typeof(Text))]
    public class LocalizationText : MonoBehaviour
    {
    
    
        [HideInInspector]
        public Text T;
        protected void Awake()
        {
    
    
            T = transform.GetComponent<Text>();
            if (CustomFont != null)
            {
    
    
                T.font = CustomFont.UseFont;
            }
        }
        /// <summary>
        /// 文本的key
        /// </summary>
        public string KeyString;

        /// <summary>
        /// 自定义字体,方便后期替换
        /// </summary>
        public UIFont CustomFont;

        /// <summary>
        /// 是否开启自身的本地化
        /// </summary>
        public bool IsOpenLocalize = true;

        /// <summary>
        /// 重新本地化,用于游戏内切换语言时调用
        /// </summary>
        public void OnLocalize()
        {
    
    
            if (IsOpenLocalize)
            {
    
    
                text = GameEntry.Localization.GetString(KeyString);
            }
        }

        public string text
        {
    
    
            get
            {
    
    
                if (IsOpenLocalize)
                {
    
    
                    T.text = GameEntry.Localization.GetString(KeyString);
                    return T.text;
                }
                else
                {
    
    
                    return T.text;
                }
            }
            set
            {
    
    
                if (string.IsNullOrEmpty(value))
                {
    
    
                    if (string.IsNullOrEmpty(T.text))
                        return;
                    T.text = "";
                    T.SetVerticesDirty();
                }
                else
                {
    
    
                    T.text = value;
                    T.SetVerticesDirty();
                    T.SetLayoutDirty();
                }
            }
        }
#if UNITY_EDITOR
        private void OnValidate()
        {
    
    
            if (string.IsNullOrEmpty(KeyString))
            {
    
    
                T.text = KeyString;
            }
            else
            {
    
    
                string txt = GetKey(KeyString);
                if (string.IsNullOrEmpty(txt))
                {
    
    
                    T.text ="<NoKey>"+ KeyString;
                }
                else
                {
    
    
                    T.text = txt;
                }
                if (CustomFont != null)
                {
    
    
                    T.font = CustomFont.UseFont;
                }
            }
        }
        private string GetKey(string KeyString)
        {
    
    
            string currentLanguage = FindObjectOfType<BaseComponent>().EditorLanguage.ToString();
            string dictionaryAssetName = AssetUtility.GetLocalXmlAsset(currentLanguage, "Default", false);
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(dictionaryAssetName);
            XmlNode xmlRoot = xmlDocument.SelectSingleNode("Dictionaries");
            XmlNodeList xmlNodeDictionaryList = xmlRoot.ChildNodes;
            for (int i = 0; i < xmlNodeDictionaryList.Count; i++)
            {
    
    
                XmlNode xmlNodeDictionary = xmlNodeDictionaryList.Item(i);
                if (xmlNodeDictionary.Name != "Dictionary")
                {
    
    
                    continue;
                }

                string language = xmlNodeDictionary.Attributes.GetNamedItem("Language").Value;
                if (language != currentLanguage)
                {
    
    
                    continue;
                }

                XmlNodeList xmlNodeStringList = xmlNodeDictionary.ChildNodes;
                for (int j = 0; j < xmlNodeStringList.Count; j++)
                {
    
    
                    XmlNode xmlNodeString = xmlNodeStringList.Item(j);
                    if (xmlNodeString.Name != "String")
                    {
    
    
                        continue;
                    }
                    string key = xmlNodeString.Attributes.GetNamedItem("Key").Value;
                    if (key == KeyString)
                    {
    
    
                        return xmlNodeString.Attributes.GetNamedItem("Value").Value;
                    }
                }
            }
            return null;
        }
#endif
    }
}

LocalizationMenuExtension.cs editor creates Text_Local text object
insert image description here

public class LocalizationMenuExtension : MonoBehaviour {
    
    
    public const string kUILayerName = "UI";
    [MenuItem("GameObject/UI/Text_Local", false, 2000)]
    static public void AddText(MenuCommand menuCommand)
    {
    
    
        GameObject go = new GameObject("Text");
        LocalizationText txt = go.AddComponent<LocalizationText>();
        InitValue(txt);
    }
    /// <summary>
    /// 初始化值为了和Text的初始值保持一致
    /// </summary>
    /// <param name="txt"></param>
    public static void InitValue(LocalizationText txt)
    {
    
    
        txt.T.color = new Color(50f / 255f, 50f / 255f, 50f / 255f);
        RectTransform contentRT = txt.GetComponent<RectTransform>();
        contentRT.sizeDelta = new Vector2(160f, 30f);
        txt.gameObject.layer = LayerMask.NameToLayer(kUILayerName);
    }
}

3. Framework auxiliary modification

The text read in the UGuiForm method OnInit initialization, modify the text directly, and after adding the localization script, read the LocalizationText script directly.

 protected override void OnInit(object userData)
    {
    
    
        base.OnInit(userData);

        m_CachedCanvas = gameObject.GetOrAddComponent<Canvas>();
        m_CachedCanvas.overrideSorting = true;
        OriginalDepth = m_CachedCanvas.sortingOrder;

        m_CanvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();

        RectTransform transform = GetComponent<RectTransform>();
        transform.anchorMin = Vector2.zero;
        transform.anchorMax = Vector2.one;
        transform.anchoredPosition = Vector2.zero;
        transform.sizeDelta = Vector2.zero;

        gameObject.GetOrAddComponent<GraphicRaycaster>();

        LocalizationText[] texts = GetComponentsInChildren<LocalizationText>(true);
        for (int i = 0; i < texts.Length; i++)
        {
    
    
            if (!string.IsNullOrEmpty(texts[i].KeyString))
            {
    
    
                texts[i].text = GameEntry.Localization.GetString(texts[i].KeyString);
            }
        }
    }

Note that when UI prefabs are edited, they are specially placed in a scene. Modify the language configuration in the editor mode, and the UI prefabs in the corresponding scene will update the text display. The UI prefabs used need to be placed in the scene.
insert image description here

Guess you like

Origin blog.csdn.net/qq_37619255/article/details/129212583