Unity 多语言系统实现

为什么需要多语言?

|---------------|--------------------------------------------|
| 核心商业价值 | 使游戏进入海外市场,显著提升收入和用户基数 |
| 用户体验优化 | 降低游戏理解和操作门槛,增强玩家留存,增强玩家沉浸感和满意度 |
| 文化适应和合规需求 | 文本的翻译显示需要符合当地的法律法规、对文化敏感性进行调整,要符合当地宗教和社会规范 |
| 方便内容管理和维护 | 方便策划统一管理和维护游戏中的文本内容 |

基础流程

实际流程

1、在每个的地区发布的游戏版本中,会设置对应的默认语言,例如在中国发行的版本就默认中文、在美国就默认英语、在日本默认日语等。

2、一般在游戏的设置界面,会提供给玩家切换语言的功能,此时多语言系统就起作用了。

3、点击切换之后,会弹出提示,提醒玩家进行确认,然后重启游戏,切换到新的语言系统

开发流程

1、配置阶段

策划整理功能中需要的中文文本,并给定唯一多语言id,根据中文翻译多国语言并配在表中。

程序根据系统中的语言类型字段(地区版本默认字段或者玩家自定义)和多语言Id,就可以确定唯一的文本内容进行显示。

(根据自己项目需求设定语言类型个数)

|------|----------------|
| 简体中文 | Chinese_S |
| 英文 | English |
| 繁体中文 | Chinese_T |
| 日语 | Japanese |
| 韩语 | Korean |
| 德语 | German |
| 法语 | French |
| 西班牙语 | Spain |
| 葡萄牙语 | Portuguese |
| 意大利语 | Italian |
| 罗马尼亚 | Romanian |
| 荷兰 | Dutch |
| 瑞典 | Swedish |
| 马来西亚 | Malaysian |
| 俄语 | Russian |
| 印尼 | Indonesian |

2、实现多语言管理器

(1)、管理器一般是单例类实现

**(**2)、注意:图中数据字典LanguageDic写法只是用于测试,实际开发中,需要借助项目导表工具将Excel表中配置数据转到项目中可使用的数据,具体实现根据各自的项目而定。

cs 复制代码
using System.Collections.Generic;
using UnityEngine;

public enum LanguageType
{
    Chinese_S, // 简体中文	
    English, // 英文	
    Chinese_T, // 繁体中文	
    Japanese, // 日语	
    Korean, // 韩语	
    German, // 德语	
    French, // 法语	
    Spain, // 西班牙语	
    Portuguese, // 葡萄牙语	
    Italian, // 意大利语	
    Romanian, // 罗马尼亚	
    Dutch, // 荷兰	
    Swedish, // 瑞典	
    Malaysian, // 马来西亚	
    Russian, // 俄语	
    Indonesian, // 印尼	
}

public class LanguageManager
{
    private static LanguageManager _instance = new LanguageManager();
    private LanguageManager() { }

    public static LanguageManager Instance => _instance;

    private LanguageType _languageType = LanguageType.Japanese;
    public LanguageType CurLanguageType => _languageType;
    
    private Dictionary<int, Dictionary<LanguageType, string>> LanguageDic =
        new Dictionary<int, Dictionary<LanguageType, string>>()
        {
            {
                6, new Dictionary<LanguageType, string>()
                {
                    { LanguageType.Chinese_S, "领取" },
                    { LanguageType.English, "Claim" },
                    { LanguageType.Chinese_T, "領取" },
                    { LanguageType.Japanese, "受取" },
                    { LanguageType.Korean, "수령" },
                    { LanguageType.German, "Einfordern" },
                    { LanguageType.French, "Obtenir" },
                    { LanguageType.Spain, "Reclamar" },
                    { LanguageType.Portuguese, "Reclamar" },
                    { LanguageType.Italian, "Richiedi" },
                    { LanguageType.Romanian, "Cerere" },
                    { LanguageType.Dutch, "Claim" },
                    { LanguageType.Swedish, "Krav" },
                    { LanguageType.Malaysian, "Tuntut" },
                    { LanguageType.Russian, "Klaim" },
                    { LanguageType.Indonesian, "Забрать" },
                }
            }
        };
    
    /// <summary>
    /// 切换多语言并更新文本显示
    /// </summary>
    /// <param name="type"></param>
    public void SetLanguageType(LanguageType type)
    {
        if (_languageType == type) return;
        _languageType = type;

        RefreshAllMultiLanguage();
    }
    
    /// <summary>
    /// 获取多语言文本
    /// </summary>
    /// <param name="langId">多语言唯一id</param>
    /// <returns></returns>
    public string GetLangStr(int langId)
    {
        if (LanguageDic.TryGetValue(langId, out var dic))
        {
            if (dic.TryGetValue(_languageType, out var result))
            {
                return result;
            }
        }

        return "";
    }

    //更新所有的多语言文本组件显示
    public void RefreshAllMultiLanguage()
    {
        var root = GameObject.Find("UIRoot");
        var txts = root.GetComponentsInChildren<MultiLanguage>(true);
        if (txts.Length > 0)
        {
            foreach (var txt in txts)
            {
                txt.ShowStr();
            }
        }
    }
    
    /// <summary>
    /// 获取含参数多语言文本
    /// </summary>
    /// <param name="langId">多语言唯一id</param>
    /// <param name="values">键值对列表</param>
    /// <returns></returns>
    public string GetLangFormatStr(int langId,params (string key, object value)[] values)
    {
        var result = GetLangStr(langId);
        if (!string.IsNullOrEmpty(result))
        {
            return ReplaceNamedPlaceholders(result, values);
        }
        return "";
    }
    
    private string ReplaceNamedPlaceholders(string format, params (string key, object value)[] values)
    {
        string result = format;
        foreach (var (key, value) in values)
        {
            result = result.Replace($"{{{key}}}", value.ToString());
        }
        return result;
    }
}

(3)实现Multilanguage挂载脚本,直接将该脚本挂载到UI文本组件上,比较方便,策划也可以操作(挂载脚本工作可以交给策划,节省开发时间),只需要在面板填上多语言id即可。

cs 复制代码
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class MultiLanguage : MonoBehaviour
{
    [SerializeField] private int LanguageId;

    private void Start()
    {
        ShowStr();
    }
    
    public void ShowStr()
    {
        var str = LanguageManager.Instance.GetLangStr(6);
        var txt = transform.GetComponent<Text>();
        if (txt != null)
        {
            txt.text = str;
        }
        else
        {
            var tmpTxt = transform.GetComponent<TMP_Text>();
            if (tmpTxt != null)
            {
                tmpTxt.text = str;
            }
        }
    }
}

3、补充

含参数的多语言

有时候多语言文本中需要包含参数,这些参数灵活性较大,比如"升级x个英雄到x级别(x/y)",有的数据来自于配表,有的数据来自于服务器。对于这类包含参数的多语言,一般不适用挂载脚本,需要在多语言管理器中实现公共接口。同时需要注意的是有时某种语言翻译后会出现主谓宾出现倒置的情况,为了避免出现误会,不建议使用这种占位符,使用指定字符

"升级{value1}个英雄到{value2}级 ({value3}/{value4})"。

cs 复制代码
    /// <summary>
    /// 获取含参数多语言文本
    /// </summary>
    /// <param name="langId">多语言唯一id</param>
    /// <param name="values">键值对列表</param>
    /// <returns></returns>
    public string GetLangFormatStr(int langId,params (string key, object value)[] values)
    {
        var result = GetLangStr(langId);
        if (!string.IsNullOrEmpty(result))
        {
            return ReplaceNamedPlaceholders(result, values);
        }
        return "";
    }
    
    private string ReplaceNamedPlaceholders(string format, params (string key, object value)[] values)
    {
        string result = format;
        foreach (var (key, value) in values)
        {
            result = result.Replace($"{{{key}}}", value.ToString());
        }
        return result;
    }

切换后更新方式

1、不需要重启客户端的情况

这种操作是切换语言后,界面中的所有文本都切换成对应语言,不需要重启客户端。

好处:直接、迅速、体验感好

缺点:需要对界面中所有的文本组件进行更新文本操作,可能会出现卡顿情况,当然也可以通过一些方法优化。

2、需要重启客户端的情况

这种操作是切换语言后,会提示类似"切换语言后,需要重启客户端"的提示,确认后,系统重启加载。

好处:不会卡顿,出错率小,能够保证文本正确显示到切换后的语言。

缺点:每次切换都需要重启,体验感不好。

总结:具体使用哪种方案,请根据策划需求确定。

相关推荐
CreasyChan9 小时前
Unity中C#状态模式详解
unity·c#·状态模式
鹿野素材屋9 小时前
动作游戏网游:帧同步下的动画同步
unity·游戏引擎
世洋Blog9 小时前
数据驱动与MVC
unity·mvc
WMX10129 小时前
Unity添加近身菜单-MRTK
unity·游戏引擎
在路上看风景1 天前
15. 纹理尺寸是4的倍数
unity
AT~1 天前
unity 使用Socket和protobuf实现网络连接
unity·游戏引擎
怣疯knight1 天前
Cocos creator判断节点是否能用的方法
unity·cocos2d
tealcwu1 天前
Google Play的Keystore不可用时的解决方法
unity
呼呼突突1 天前
Unity使用TouchSocket的RPC
unity·rpc·游戏引擎