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本地化,让您的游戏为世界做好准备!
This is currently the most complete localization system available for Unity. Supports Unity UI, Unity 2D, TextMesh Pro, NGUI, 2D ToolKit, SVG Importer, Standard Components Localizes Image, Text, Sounds, Fonts, Sprites, Atlases, Prefabs, TextMeshes, Dropdowns and more. Auto Translation Built-in support for Google Translator to automatically localize all your labels into any language. Google Spreadsheets and CSV files Link external Spreadsheets and any change will be automatically downloaded, even after the game is released. Detect and Fix Errors Parse scenes to find missing or unused translations as well as duplicated or similar terms. Sub-Object Translations Set Secondary terms to change not just text and images, but also which Fonts and Atlases are used per language. Callbacks and Parameters Translations can be modified to insert Scores, Names and Game Variables using the languages grammar. Plurals Built-in rules for each language covering those that just need singular/plural forms up to those that use 6 variants. Right to Left Languages Correct rendering of RTL languages with support for line wrapping and alignment. Save Memory Create Fonts with only the characters you are using. Link your AssetBundles to add more localized content. Easy and Efficient Edit and Preview all your translations in one location, then, at runtime only the current language is loaded. Using fast lookups to access it. Reuse Translations Set texts to be automatically shown as UPPER CASE, lower case, Upper first, Title Case, etc. In Game Dynamic Translations Easy API to translate chat messages and other dynamic texts at runtime App Name and Store Localization Translate the name of your game to all languages and show in the Android/IOS store which languages your game support Simple yet Powerful Editors Intuitive inspectors allow you Preview, Edit, Categorize, Create, Parse and Filter even large sources. Compile Time Checking Bake translations into script constants to avoid Typo
Unity I2 LocalizationUnity I2 国际化)是Unity引擎上的一种插件,用于帮助开发者实现游戏或应用的多语言支持。 Unity I2 Localization提供了一种简单易用的方式来管理和应用多语言文本。它允许开发者轻松地在游戏中添加多个语言,并根据用户的语言设置自动切换文本。开发者只需按照插件的文档指引在Unity编辑器中设置好本地化文本,然后在游戏中使用相应的API来获取和显示正确的语言文本。 通过Unity I2 Localization,开发者可以更方便地进行游戏的本地化工作。不同的语言文本可以按照对应的语种进行分类和组织,使得管理和更新翻译变得更加高效。开发者可以在Unity编辑器中直接修改和更新本地化文本,而无需重新编译代码,从而加快开发和调试的速度。 另外,Unity I2 Localization还提供了一些额外的功能,可以帮助开发者更好地适应各种语言环境。例如,插件支持复数形式的处理和变量替换,以便在不同语言中正确显示复数个数和动态内容。 总之,Unity I2 LocalizationUnity引擎上一款实用而强大的插件,可以帮助开发者轻松实现游戏或应用的多语言支持。它提供了一种简单易用的方式来管理和应用多语言文本,并且附带了一些额外的功能,使得本地化工作更加高效和灵活。无论是为了吸引更多的用户,还是为了扩大游戏或应用的市场,Unity I2 Localization都是一个很好的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值