Unity I2 Localization实现多语言切换

目录

前言:

目录结构

EditorTool:

MultiLanguageEditor:

Entity:

Extension:

StringExtension:

MultiLanguageManager:

ZipLoader:


前言:

在网上找了找I2 Localization的使用,发现教学都收费!!!

一怒之下自己封装了下,朋友们可以拿来直接使用,话不多说,上代码!

目录结构

大致如下,可自由发挥:(用到了一个JSON工具,可自行替换)

EditorTool:

编辑器工具,当更新了多语言文件时,可手动点击更新下配置

注意:实际项目中需放到unity的Editor目录下

MultiLanguageEditor:
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using MultiLanguage.Entity;
using I2.Loc;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;

public class MultiLanguageEditor : Editor
{
    [MenuItem("MultiLanguage/更新多语言配置")]
    public static async void UpdateAllConfigLanguage()
    {
        byte[] zipData = await ZipLoader.LoadZipFile("Localize/lang.zip");
        using ZipArchive archive = new ZipArchive(new MemoryStream(zipData));
        ZipArchiveEntry manifestEntry = archive.GetEntry("manifest.json");
        if (manifestEntry == null)
        {
            return;
        }
        using StreamReader reader = new StreamReader(manifestEntry.Open(), Encoding.UTF8);
        LangManifest manifest = JsonConvert.DeserializeObject<LangManifest>(reader.ReadToEnd());
        if (manifest == null)
        {
            return;
        }
        // 更新manifest, 如果没有会动态添加, 已有则不做处理 关闭多语种
        MultiLanguageManager.ApplyManifestLanguages(manifest);
        //更新内置所有语言
        foreach (LangData data in manifest.languages)
        {
            UpdateLangConfig(archive.GetEntry($"{data.code}.csv"));
        }
        LocalizationManager.DoLocalizeAll(true);
    }
    
    private static void UpdateLangConfig(ZipArchiveEntry entry)
    {
        if (entry == null)
        {
            return;
        }
        string code = entry.Name.Replace(".csv", string.Empty);
        using StreamReader reader = new StreamReader(entry.Open(), Encoding.UTF8);
        string dataStr = reader.ReadToEnd();

        List<string[]> csvList = LocalizationReader.ReadCSV(dataStr);
        Dictionary<string, string> multilingual = new Dictionary<string, string>();
        foreach (string[] arr in csvList)
        {
            if (arr.Length > 0)
            {
                string key = arr[0];
                string value = "";
                if (arr.Length > 1)
                {
                    value = arr[1];
                }

                multilingual[key] = value;
            }
        }
        if (multilingual.Count > 0)
        {
            FullRenewalUpdateDicSources(code, multilingual);
        }
    }
    
    private static void FullRenewalUpdateDicSources(string code, Dictionary<string, string> data)
    {
        foreach (var source in LocalizationManager.Sources)
        {
            source.mDictionary.Clear();
            UpdateDictionaryData(source, code, data);
        }
        Debug.Log($"<color=#41d558>语言{code}更新配置成功!</color>");
    }
    
    private static void UpdateDictionaryData(LanguageSourceData sources,string code, Dictionary<string, string> dataList)
    {
        int LangIndex = sources.GetLanguageIndexFromCode(code);
        foreach (KeyValuePair<string, string> valuePair in dataList)
        {
            string key = valuePair.Key;

            if (!sources.mDictionary.TryGetValue(key, out TermData termData))
            {
                termData = sources.AddTerm(key, eTermType.Text, false);
            }

            termData.Languages[LangIndex] = valuePair.Value;
        }
        sources.Editor_SetDirty();
        AssetDatabase.SaveAssets();
    }
}
Entity:

封装了一下用到的对象,也可自由发挥

public class LangData
{
    public string code { get; set; }
    public string name { get; set; }
}

public class LangManifest
{
    public List<LangData> languages { get; set; }
}

public class LanguageUIModel
{
    public bool Selected;
    public string Language;
}
Extension:

拓展,我这里做了一个string的拓展,去掉换行符及空格

StringExtension:
public static class StringExtension
{
    public static string ReplaceLineBreak(this string input)
    {
        return input.Replace("\r", " ").Replace("\"", "").Trim();
    }
}
MultiLanguageManager:

多语言管理类,初始化、切换语言、通过key获取翻译文本

注意:这里需要更改源码中的LocalizationManager.DoLocalizeAll();方法,变为public

using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MultiLanguage.Entity;
using MultiLanguage.Extension;
using I2.Loc;
using Newtonsoft.Json;
using UnityEngine;

public static class MultiLanguageManager
{
    public static LangManifest Manifest { get; private set; }
    private const string PlayerPrefsLanguageKey = "LanguagePlayerPrefs";
    public static List<LanguageUIModel> Languages { get; private set; }
    
    public static async Task InitAsync()
    {
        Languages = new List<LanguageUIModel>();
        byte[] zipData = await ZipLoader.LoadZipFile("Localize/lang.zip");
        if (HasLanguagePrefs())
        {
            LocalizationManager.CurrentLanguageCode = GetLanguagePrefs();
        }
        HandleConfig(zipData);
        var languages = LocalizationManager.GetAllLanguagesCode();

        foreach (var language in languages)
        {
            var languageModel = new LanguageUIModel() { Selected = language == LocalizationManager.CurrentLanguageCode, Language = language };
            Languages.Add(languageModel);
        }
    }

    public static void SelectLanguage(LanguageUIModel language)
    {
        foreach (var languageModel in Languages)
        {
            if(languageModel == language)
            {
                languageModel.Selected = true;
                LocalizationManager.CurrentLanguageCode = languageModel.Language;
                SaveLanguagePrefs(LocalizationManager.CurrentLanguageCode);
            }
            else
            {
                languageModel.Selected = false;
            }
        }
    }

    public static string GetLanguageName(string code)
    {
        if (Manifest == null)
        {
            return string.Empty;
        }

        return Manifest.languages.Find(x => x.code.Equals(code))?.name ?? string.Empty;
    }
    
    public static string GetTranslation(string key)
    {
        return LocalizationManager.GetTranslation(key).ReplaceLineBreak();
    }

    #region Unzip

    public static void HandleConfig(byte[] content)
    {
        if (content == null)
        {
            return;
        }

        using ZipArchive archive = new ZipArchive(new MemoryStream(content));
        ZipArchiveEntry manifestEntry = archive.GetEntry("manifest.json");
        if (manifestEntry == null)
        {
            return;
        }

        using StreamReader reader = new StreamReader(manifestEntry.Open(), Encoding.UTF8);
        Manifest = JsonConvert.DeserializeObject<LangManifest>(reader.ReadToEnd());
        if (Manifest == null)
        {
            return;
        }
        ApplyManifestLanguages(Manifest);
        // 更新当前选择的语言
        string lang = LocalizationManager.CurrentLanguageCode;
        ZipArchiveEntry entry = archive.GetEntry($"{lang}.csv");
        //如果想实现运行时切换语言就生效,需要UpdateLangConfig所有语言,否则只能重启应用生效
        UpdateLangConfig(entry);
        LocalizationManager.DoLocalizeAll(true);
    }

    /// <summary>
    /// 更新manifest, 如果没有会动态添加, 已有则不做处理
    /// </summary>
    public static void ApplyManifestLanguages(LangManifest manifest)
    {
        if (manifest == null)
        {
            return;
        }
        foreach (LanguageSourceData source in LocalizationManager.Sources)
        {
            foreach (string code in manifest.languages.Select(x => x.code))
            {
                string name = GoogleLanguages.GetLanguageName(code).Replace("/", "_");
                source.AddLanguage(name, code);
            }
        }
    }

    private static void UpdateLangConfig(ZipArchiveEntry entry)
    {
        if (entry == null)
        {
            return;
        }
        string code = entry.Name.Replace(".csv", string.Empty);
        using StreamReader reader = new StreamReader(entry.Open(), Encoding.UTF8);
        string dataStr = reader.ReadToEnd();

        List<string[]> csvList = LocalizationReader.ReadCSV(dataStr);
        Dictionary<string, string> multilingual = new Dictionary<string, string>();
        foreach (string[] arr in csvList)
        {
            if (arr.Length > 0)
            {
                string key = arr[0];
                string value = "";
                if (arr.Length > 1)
                {
                    value = arr[1];
                }

                multilingual[key] = value;
            }
        }

        if (multilingual.Count > 0)
        {
            // refresh
            UpdateDicSources(code, multilingual);
        }
    }
    
    private static void UpdateDicSources(string code, Dictionary<string, string> data)
    {
        foreach (var source in LocalizationManager.Sources)
        {
            UpdateDictionaryData(source, code, data);
        }
    }
    
    private static void UpdateDictionaryData(LanguageSourceData source,string code, Dictionary<string, string> dataList)
    {
        int LangIndex = source.GetLanguageIndexFromCode(code);
        foreach (KeyValuePair<string, string> valuePair in dataList)
        {
            string key = valuePair.Key;

            if (!source.mDictionary.TryGetValue(key, out TermData termData))
            {
                termData = source.AddTerm(key, eTermType.Text, false);
            }

            termData.Languages[LangIndex] = valuePair.Value;
        }
    }

    #endregion

    #region PlayerPrefs

    private static void SaveLanguagePrefs(string languageCode)
    {
        PlayerPrefs.SetString(PlayerPrefsLanguageKey, languageCode);
    }

    private static string GetLanguagePrefs()
    {
        return PlayerPrefs.GetString(PlayerPrefsLanguageKey);
    }

    private static bool HasLanguagePrefs()
    {
        return PlayerPrefs.HasKey(PlayerPrefsLanguageKey);
    }

    #endregion
    
}
ZipLoader:

加载zip文件工具

using System.IO;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;

public static class ZipLoader
{
    public static async UniTask<byte[]> LoadZipFile(string zipFileName)
    {
        string zipFilePath = Path.Combine(Application.streamingAssetsPath, zipFileName);

#if UNITY_EDITOR || UNITY_STANDALONE
        string fileUrl = "file://" + zipFilePath;
#elif UNITY_IOS || UNITY_ANDROID
    string fileUrl = zipFilePath;
#endif
        UnityWebRequest www = UnityWebRequest.Get(fileUrl);
        await www.SendWebRequest().ToUniTask();

        if (www.result != UnityWebRequest.Result.Success)
        {
            Debug.LogError("Failed to load zip file: " + www.error);
            return null;
        }

        byte[] zipData = www.downloadHandler.data;
        return zipData;
    }
}

初始化时调用:

MultiLanguageManager.InitAsync();

选择语言调用:

MultiLanguageManager.SelectLanguage();

压缩包内文件结构为:

JSON文件内容结构为:

{
    "languages": [
        {
            "code": "en",
            "name": "English"
        },
        {
            "code": "zh",
            "name": "中文(简体)"
        },
        {
            "code": "ja",
            "name": "日本語"
        }
    ]
}

这是目前Unity最完整的本地化系统。 支持 Unity UI,Unity 2D,TextMesh Pro,NGUI,2D ToolKit,SVG导入器,标准组件 本地化 图像,文本,声音,字体,精灵,地图集,预制件,TextMeshes,下拉列表等。 自动翻译 内置支持Google Translator,可自动将所有标签本地化为任何语言。 Google Spreadsheets和CSV文件 链接外部电子表格,即使在游戏发布后,也会自动下载任何更改。 检测并修复错误 解析场景以查找丢失或未使用的翻译以及重复或类似的术语。 子对象翻译 设置辅助术语不仅可以更改文本和图像,还可以更改每种语言使用的字体和地图集。 回调和参数 可以修改翻译以使用语法语法插入分数,名称和游戏变量。 复数 每种语言的内置规则涵盖那些只需要单数/复数形式的规则,直到那些使用6种变体的规则。 从右到左语言 正确呈现RTL语言,支持换行和对齐。 保存内存 仅使用您正在使用的字符创建字体。链接您的AssetBundles以添加更多本地化内容。 简单高效 在一个位置编辑和预览所有翻译,然后在运行时仅加载当前语言。使用快速查找来访问它。 重复使用翻译 设置文本自动显示为大写,小写,上限,标题大小等。 在游戏动态翻译 Easy API在运行时翻译聊天消息和其他动态文本 应用程序名称和商店本地化 翻译游戏名称所有语言,并在Android / IOS商店中显示您的游戏支持哪些语言 简单但功能强大的编辑器 直观的检查员允许您预览,编辑,分类,创建,解析和过滤甚至大型来源。 编译时间检查 将翻译转换为脚本常量,以避免在访问代码时使用Typos。 Playmaker 可以 快速,轻松,强大地访问本地元素和交换语言并与之交互的几种操作?没问题! 立即获取I2本地化,让您的游戏为世界做好准备!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值