您的当前位置:首页正文

Unity I2 Localization实现多语言切换

来源:华佗健康网


前言:

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

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

目录结构

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

EditorTool:

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

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;
    }
}

压缩包内文件结构为:

JSON文件内容结构为:

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

因篇幅问题不能全部显示,请点此查看更多更全内容