【Unity功能实现】SmipleLocalization多语言本地化简易版本

文章目录

前言

一、基本功能介绍

二、功能详解

1.创建Excel表格,确定所需数据

2.读取Excel表格

3.处理Unity中需要本地化的Text

三、扩展功能

总结


前言

开发超休闲小游戏时,由于大部分游戏都是出海发布的,所以需要支持多语言。

目前运用比较广泛的本地化插件为I2 Localization。不过该插件功能繁杂,于是编写了一个简易版的语言本地化功能。


一、基本功能介绍

项目开发过程中,将数据存放在Excel中方便查看也方便更改。然后通过读取Excel的内容,将其表中内容读取到项目中对应的class实例(LocalizationData)中,然后将该实例生成一个Asset,如图所示:

生成好之后,我们就可以在游戏中通过这个Asset来获取我们需要的数据啦~

二、功能详解

1.创建Excel表格,确定所需数据

  • 首先我们需要知道自己需要哪些数据。这里以我开发的俄罗斯方块案例来说明吧,游戏界面如下:

  • 创建Excel表格,填写数据信息,如图所示:

  • 根据Excel表格,我们需要定义两个枚举。

        第一个枚举:语言类型。

//这里保证与Excel表顺序一致
public enum LanguageType
{
    English = 0,//默认语言类型为英文
    Chinese = 1
}

        第二个枚举:需要进行本地化的文本类型Key。

public enum TextKey
{
    Racing_Bricks,
    Score,
    High_Score,
    Speed,
    Quick,
    Down,
    Left,
    Right,
    Rotate_Direction
}
  • 根据Excel表格,定义三个基础类。

        第一个类:用来存放每行的单元格表数据。

using System;
using UnityEngine;

namespace SmipleLocalization
{
    [Serializable]
    public class ItemData
    {
        [SerializeField]
        private string itemName;//语言名称,主要是方便编辑器上查看
        public LanguageType languageType;//语言类型
        public string content;//对应的内容

        //构造函数:初始化数据成员
        public ItemData(LanguageType _languageType, string _content)
        {
            this.languageType = _languageType;
            this.itemName = _languageType.ToString();
            this.content = _content;
        }
    }
}

        第二个类:用来存放前者的数组即每行的表数据。

using System;
using System.Collections.Generic;
using UnityEngine;

namespace SmipleLocalization
{
    [Serializable]
    public class TextLocalization
    {
        [SerializeField]
        private string keyName;//当前文本类型名称,主要是方便编辑器上查看
        public TextKey curTextKey;//该文本类型
        public List<ItemData> itemDatas = new List<ItemData>();//当前文本类型对应的语言本地化文本信息

        //构造函数:初始化
        public TextLocalization(string _textKey, params string[] language)
        {
            this.keyName = _textKey;
            this.curTextKey = (TextKey)Enum.Parse(typeof(TextKey), _textKey);
            for (int i = 0; i < language.Length; i++)
            {
                ItemData cnData = new ItemData((LanguageType)i, language[i]);
                itemDatas.Add(cnData);
            }
        }

        //根据语言类型查找对应语言文本信息
        public ItemData GetType(LanguageType t)
        {
            for (int i = 0; i < this.itemDatas.Count; i++)
            {
                if (this.itemDatas[i].languageType == t)
                {
                    return this.itemDatas[i];
                }
            }
            throw new KeyNotFoundException("Language not found: " + t);
        }
    }
}

        第三个类:用来存放第二个类的数组即所有的表数据。

using UnityEngine;
using System;
using System.Collections.Generic;


namespace SmipleLocalization
{
    public class LocalizationData : ScriptableObject
    {
        public List<TextLocalization> localizationTxts = new List<TextLocalization>();

        /// <summary>
        /// 根据TextKey获取对应的所有语言类型信息
        /// </summary>
        /// <param name="k">需要本地化的文本</param>
        public TextLocalization GetTextInfo(TextKey k)
        {
            for (int i = 0; i < this.localizationTxts.Count; i++)
            {
                if (this.localizationTxts[i].curTextKey == k)
                {
                    return this.localizationTxts[i];
                }
            }
            //return null;
            throw new KeyNotFoundException("TextKey not found: " + k);
        }
    }
    
}

2.读取Excel表格

  • 需要引入Excel.dll和ICSharpCode.SharpZipLib库文件,放到Plugins文件夹下。

        下载地址:Unity读取和写入Excel表格所需dll-Unity3D文档类资源-CSDN文库

  • 在Editor文件夹下创建cs文件去实现读取Excel操作。       

        1.定义一个类,里面存放我们需要的字段:

using UnityEngine;

namespace SmipleLocalization
{
    public class LocalizationExcelConfig : MonoBehaviour
    {
        /// <summary>
        /// 存放将Excel表格数据转化CS文件的文件夹路径
        /// </summary>
        public static readonly string assetFolderPath = "Assets/Resources/Datas/";

        /// <summary>
        /// 存放将Excel表格数据转化CS文件的路径
        /// </summary>
        public static readonly string assetPath = string.Format("{0}{1}.asset", assetFolderPath, "LanguageLocalizationData");

        /// <summary>
        /// 加载文件的路径
        /// </summary>
        public static readonly string loadAssetPath = "Datas/LanguageLocalizationData";
    }
}

        2.定义一个类,用来读取Excel表格的数据,并解析数据存放到Asset中

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

namespace SmipleLocalization
{
    public class LocalizationExcelTool
    {
        /// <summary>
        /// 读取表格数据,生成对应的数据
        /// </summary>
        public static List<TextLocalization> CreateDataWithExcel(string filePath)
        {
            List<TextLocalization> localizationTxts = new List<TextLocalization>();
            //获得表格数据
            int row = 0, column = 0;
            DataRowCollection collection = ReadExcel(filePath, ref row, ref column);
            //根据Excel表的定义,第二行开始才是数据
            for (int i = 1; i < row; i++)
            {
                string[] language = new string[column - 1];
                for (int j = 1; j < column; j++)
                {
                    language[j - 1] = collection[i][j].ToString();
                }
                TextLocalization tl = new TextLocalization(collection[i][0].ToString(), language);
                localizationTxts.Add(tl);
            }
            return localizationTxts;
        }

        /// <summary>
        /// 读取Excel文件内容
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="row">行数</param>
        /// <param name="column">列数</param>
        private static DataRowCollection ReadExcel(string filePath, ref int row, ref int column)
        {
            FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            IExcelDataReader edReader = ExcelReaderFactory.CreateOpenXmlReader(stream);

            DataSet result = edReader.AsDataSet();
            //Tables[0]下标0表示Excel文件中第一张表的数据
            row = result.Tables[0].Rows.Count;
            column = result.Tables[0].Columns.Count;
            return result.Tables[0].Rows;
        }
    }
}

        3.将拿到的数据存放在LocalizationData中,并生成一个Asset文件:

using System.IO;
using UnityEditor;
using UnityEngine;

namespace SmipleLocalization
{
    public class BuildExcelDataEditor : Editor
    {
        [MenuItem("CustomEditor/CreateLocalizationData")]
        public static void CreateLocalizationData()
        {
            //打开Excel
            string filePath = EditorUtility.OpenFilePanel("Open Localization Excel", "", "xlsx");
            //获取Excel数据
            LocalizationData data = ScriptableObject.CreateInstance<LocalizationData>();
            data.localizationTxts = LocalizationExcelTool.CreateDataWithExcel(filePath);
            //判断文件夹是否存在
            if (!Directory.Exists(LocalizationExcelConfig.assetFolderPath))
            {
                Directory.CreateDirectory(LocalizationExcelConfig.assetFolderPath);
            }
            string assetPath = LocalizationExcelConfig.assetPath;
            //判断文件是否已经存在
            //若已经存在,则删除该文件
            if (File.Exists(assetPath))
            {
                AssetDatabase.DeleteAsset(assetPath);
            }
            //生成一个Asset文件
            AssetDatabase.CreateAsset(data, assetPath);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
    }
}

        这样我们就可以在Unity中选择我们自定义的菜单CustromEditor->CreateLocalizationData,然后选择我们需要的Excel表格,即可生成Asset了。

3.处理Unity中需要本地化的Text

  • 创建一个类,在游戏运行时,加载数据,为目标提供本地化方法
using UnityEngine;
using System;

namespace SmipleLocalization
{
    public static class LocalizationSys
    {
        private static LocalizationData localizationDatas;
        public static Action ChangeCurLanguageTypeEvent;//监听语言类型变化事件,在事件触发时设置内容,主要是方便用来游戏运行中测试本地化
        private static LanguageType mCurLanguageType;

        static LocalizationSys()
        {
            InitDatas();
            InitLanguageType();
        }

        /// <summary>
        /// 加载数据
        /// </summary>
        private static void InitDatas()
        {
            localizationDatas = Resources.Load<LocalizationData>(LocalizationExcelConfig.loadAssetPath);
        }

        /// <summary>
        /// 根据系统语言设置默认语言
        /// </summary>
        private static void InitLanguageType()
        {
            SystemLanguage languageStr = Application.systemLanguage;
            if (languageStr == SystemLanguage.Chinese ||
                languageStr == SystemLanguage.ChineseSimplified)
            {
                CurLanguageType = LanguageType.Chinese;
            }
            else
            {
                CurLanguageType = LanguageType.English;
            }
        }

        /// <summary>
        /// 设置当前语言类型
        /// </summary>
        public static LanguageType CurLanguageType
        {
            set
            {
                if (mCurLanguageType != value)
                {
                    mCurLanguageType = value;
                    ChangeCurLanguageTypeEvent?.Invoke();
                }
            }
        }

        /// <summary>
        /// 对应的语言本地化
        /// </summary>
        /// <param name="textKey">需要本地化的文本</param>
        public static string Localize(TextKey textKey)
        {
            return Localize(textKey, mCurLanguageType);
        }

        /// <summary>
        /// 获取对应的语言本地化
        /// </summary>
        /// <param name="textKey">需要本地化的文本</param>
        /// <param name="languageType">语言类型</param>
        /// <returns></returns>
        private static string Localize(TextKey textKey, LanguageType languageType)
        {
            string value = string.Empty;
            TextLocalization textInfo = localizationDatas.GetTextInfo(textKey);
            if (textInfo != null)
            {
                ItemData typeInfo = textInfo.GetType(languageType);
                //如果没找到对应语言本地化信息,则设置默认英文信息
                if (typeInfo == null)
                {
                    typeInfo = textInfo.GetType(LanguageType.English);
                }
                value = typeInfo.content;
            }
            return value;
        }
    }
    
    //这里保证与Excel表顺序一致
    public enum LanguageType
    {
        English = 0,//默认语言类型为英文
        Chinese = 1
    }

    public enum TextKey
    {
        Racing_Bricks,
        Score,
        High_Score,
        Speed,
        Quick,
        Down,
        Left,
        Right,
        Rotate_Direction
    }
}
  • 创建一个类,用来设置文本文字内容
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace SmipleLocalization
{
    public class UITextLocalize : MonoBehaviour
    {
        private Text mText;
        [SerializeField]
        private TextKey textKey;

        private void Awake()
        {
            this.mText = base.GetComponent<Text>();
        }

        private void Start()
        {
            LocalizationSys.ChangeCurLanguageTypeEvent += OnChangeLanguage;
            this.OnChangeLanguage();
        }

        private void OnDestroy()
        {
            LocalizationSys.ChangeCurLanguageTypeEvent -= OnChangeLanguage;
        }

        private void OnChangeLanguage()
        {
            this.StaticSet();
        }

        private void StaticSet()
        {
            this.mText.text = LocalizationSys.Localize(textKey);
        }
    }
}
  • 在需要本地化的文本上挂上UITextLocalize脚本,并配置对应的信息,如下所示:

  • 如果我们需要在游戏中测试多语言功能,可以在画布下创建一个DropDown,设置Options,添加OnValueChanged事件,如下所示:
public void OnChangeLanguage(int value)
{
    LocalizationSys.CurLanguageType = (LanguageType)value;
}

这样我们就可以在游戏运行中查看我们的多语言本地化是否正确了。

三、扩展功能

这款游戏中我只用到了静态文本,但是有时候我们还需要简单的动态修改文本内容。这个时候我们修改一下UITextLocalize脚本即可,如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace SmipleLocalization
{
    public class UITextLocalize : MonoBehaviour
    {
        private Text mText;
        [SerializeField]
        private TextKey textKey;
        [SerializeField]
        private LocalizeType localizeType = LocalizeType.Static;
        private object[] args;
        private void Awake()
        {
            this.mText = base.GetComponent<Text>();
        }

        private void Start()
        {
            LocalizationSys.ChangeCurLanguageTypeEvent += OnChangeLanguage;
            this.OnChangeLanguage();
        }

        private void OnDestroy()
        {
            LocalizationSys.ChangeCurLanguageTypeEvent -= OnChangeLanguage;
        }

        private void OnChangeLanguage()
        {
            if (this.localizeType == LocalizeType.Static)
            {
                this.StaticSet();
            }
            else if(this.args != null)
            {
                this.DynamicSet(this.args);
            }
        }

        private void StaticSet()
        {
            this.mText.text = LocalizationSys.Localize(textKey);
        }

        /// <summary>
        /// 动态设置文本内容
        /// </summary>
        public void DynamicSet(params object[] _args)
        {
            string format = LocalizationSys.Localize(textKey);
            this.mText.text = string.Format(format, _args);
            this.args = _args;
        }
    }
}

然后在Inspector面板上设置UITextLocalize的LocalizeType为Dynamic。

接着在需要动态修改Text内容时,不是调用GetComponent<Text>().text去设置,而是调用GetComponent<UITextLocalize>().DynamicSet(),传入需要修改的数据即可。参考如下:


总结

大家也可以试试导入EPPlus.dll,用来向Excel写入数据。后续会给大家整理一下。今天就到这里啦~

以上就是今天要讲的内容,如果有哪里不懂的可以留言交流一下。

猜你喜欢

转载自blog.csdn.net/yokixiang/article/details/127224066
今日推荐