Unity游戲多語言工具包

????????由于一開始的代碼沒有考慮多語言場景,導致代碼中提示框和UI顯示直接用了中文,最近開始提取代碼的中文,提取起來太麻煩,所以拓展了之前的多語言包,降低了操作復雜度。最后把工具代碼提取出來到單獨項目里面,做一個分享,項目資源放到最后。

? ? ? ? 接入方案和使用步驟

  1. 導入package
  2. 修改EnumMessageTxt文件內容,根據自己的需求新增或刪除語言,如刪除繁體中文,則刪除第一行中的,繁體中文,刪除第三行的繁體中文縮寫|Cht,刪除多余的繁體語言包內容(可以把下面所有行都刪除),刪除后內容為
    S,Key,簡體中文
    //Area|
    1|Area|Cn
    //Common2~500|Common
    2|CommonTip|公共提示
    3|CommonTip2|公共提示2
    4|CommonTip3|公共提示
    5|CommonFormatTip|提示{0}
    //PanelA501~601|PanelA
    501|PanelABtn|按鈕

    若要添加其他語言,務必要記得第三行中加入對應的縮寫

  3. 點擊拓展工具Tools/MessageDataTool/EnumMessageEditor,打開界面,點擊加載按鈕,選擇文件進行修改,修改完畢點擊生成代碼按鈕,等待生成完成,編譯完成即可,加載代碼在Panel腳本中,需要先初始化語言類型(縮寫),再進行加載

  4. 語言包加載邏輯在MessageManager.InitCyData中,根據自己項目的資源加載方式進行修改

  5. 由于只是簡單的分享, 所以工具可能缺少部分合法驗證,如遇到報錯等情況,可以查看堆棧信息,或者私信咨詢

? ? ? ? 邏輯介紹

? ? ? ? 項目目錄結構

? ? ? ? 1.生成的Asset資源,繁體多語言數據

? ? ? ? 2.生成的Asset資源,簡體多語言數據,路徑可以通過代碼修改,測試需要,放在Resources下加載

? ? ? ? 3.Demo場景

? ? ? ? 4.Unity Console日志重定向腳本,方便根據日志跳轉代碼中文處

? ? ? ? 5.匹配代碼中文的腳本

? ? ? ? 6.多語言工具窗口腳本

? ? ? ? 7.Demo界面

? ? ? ? 8.生成的多語言key腳本,枚舉類型

? ? ? ? 9.1,2中Asset資源對應的源Scriptable腳本

? ? ? ? 10.多語言管理器,加載多語言內容

? ? ? ? 11.多語言源txt文件,存放源內容

? ? ? 編輯器拓展功能

? ? ? ? 1.導入txt源文件,生成腳本,最初始的多語言工具拓展功能,沒有刪除,保留了下來,如果不使用預覽工具時,可使用該功能

? ? ? ? 2.多語言工具預覽和操作界面

? ? ? ? 3.匹配代碼中文?功能

?多語言txt源文件

S,Key,簡體中文,繁體中文
//Area|
1|Area|Cn|Cht
//Common2~500|Common
2|CommonTip|公共提示|公共提示繁體
3|CommonTip2|公共提示2|
4|CommonTip3|公共提示|
5|CommonFormatTip|提示{0}|
//PanelA501~601|PanelA
501|PanelABtn|按鈕|按鈕繁體

? ? ? ? ?1.首行格式固定為 S,Key,語言A,語言B,語言C 代碼會根據該行后面的語言類型數量創建對應的數據

? ? ? ? 2.第二行及后續所有以//開頭的內容,表示為注釋,不參與邏輯預算,主要功能是提示,以|分割,|后內容表示為標頭,無邏輯意義,主要功能是方面添加Key,后面結合工具介紹

? ? ? ? 3.除去首行及注釋行,其他行內容均為多語言內容,格式為 枚舉整型值|枚舉名稱|語言A文本|語言B文本...第三行這個枚舉整型值為1的是特例,表示上面各個語言的縮寫,也表示了生成的資源文件名稱,不能重復。注意并不是1中有多少種語言,這里就要添加多少種語言文本,但是至少多語言A的要有值,如果語言文本沒有定義,那么會取最前面的語言文本,也就是假如這里沒有定義語言C的多內容語言,那么語言C對應的多語言內容會取語言A的。配合生成的枚舉腳本和數據可以更直觀理清,這里實例用了兩種簡體中文和繁體中文。

? ? ? ? 生成的枚舉腳本

namespace Message
{public enum EnumMessage{//Area|/// <summary>/// Cn/// </summary>Area = 1,//Common2~500|Common/// <summary>/// 公共提示/// </summary>CommonTip = 2,/// <summary>/// 公共提示2/// </summary>CommonTip2 = 3,/// <summary>/// 公共提示/// </summary>CommonTip3 = 4,/// <summary>/// 提示{0}/// </summary>CommonFormatTip = 5,//PanelA501~601|PanelA/// <summary>/// 按鈕/// </summary>PanelABtn = 501,MaxNum,}
}

? ? ? ? ?生成的簡體中文多語言資源內容,注意名稱和上面源文件第三行的關系

????????生成的繁體中文多語言資源內容,注意名稱和上面源文件第三行的關系和空值填充?

?多語言工具預覽和操作界面

? ? ? ? 1.?選擇的多語言txt源文件路徑

? ? ? ? 2.加載:拉起選擇框,選擇要加載的源文件。重載:依據1中的路徑,重新刷新當前界面的數據。保存:保存界面數據到1中的路徑。生成腳本:先保存到源文件,再根據當前界面的數據生成枚舉腳本和Asset數據

? ? ? ? 3.輸入要搜索的多語言內容,內容為多語言A的內容

? ? ? ? 4.粘貼+搜索:粘貼剪貼板內容到3的輸入框,并進行搜索。搜索:搜索3的輸入框內容。取消搜索:隱藏搜索結果

? ? ? ? 5.搜索到的結果,可能有多條。內容從左到右依次為所在組名枚舉整型值枚舉名稱

? ? ? ? 6.根據當前條目生成獲取多語言值的代碼,代碼內容為

Message.MessageManager.I.GetMessage(Message.EnumMessage.PanelABtn)

代碼生成格式可以自行修改,邏輯在腳本文件MessageDataToolGenerateMessage函數中

? ? ? ? 7.根據當前條目生成獲取Format多語言值的代碼,代碼內容為

Message.MessageManager.I.GetMessageFormat(Message.EnumMessage.PanelABtn)

注意要配合format類型的多語言使用,代碼生成格式可以修改,邏輯在腳本文件MessageDataToolGenerateFormatMessage函數中

? ? ? ? 8.組列表,所有 // 開頭的行,選中的為綠色高亮

? ? ? ? 9.要添加的組名稱,格式需要以//開頭

? ? ? ? 10.添加新組,組名為9中內容

? ? ? ? 11.當前選中的組,可以在此處修改組名稱

? ? ? ? 12.修改組名稱按鈕,必須點擊按鈕才能修改,同時為了保證源文件的組順序不變化,這里會自動保存文件

? ? ? ? 13.組的標頭,可以理解為當前組下所有內容的枚舉名稱前綴,方面增加前綴的,可以為空

? ? ? ? 14.枚舉整型值,全局唯一索引,可以手動修改,同一組內添加條目默認遞增1,組內的第一個新建條目需要手動修改索引值為合適值

? ? ? ? 15.枚舉名稱,全局唯一,可以手動修改

? ? ? ? 16.給枚舉名稱添加標頭前綴,保證格式統一

? ? ? ? 17.多語言內容,在這里修改

? ? ? ? 18.將當前剪貼板內容粘貼到17的輸入框內

? ? ? ? 20.同6

? ? ? ? 21.同7

? ? ? ? 22.刪除當前條目,為了防止舊數據出錯,刪除條目不會修正索引,比如之前索引列表為2,3,4,刪除了3之后,索引為4不會修正為3,而是留空

? ? ? ? 23.添加新的條目,索引遞增1,若13有標頭,會自動給15輸入框中賦值標頭內容

? ? ? ? 界面的操作的邏輯都在腳本里面,因為涉及到布局代碼過多,這里不再解讀,可以搜索按鈕名稱查看對應的邏輯

? ? ? ? 匹配代碼中文腳本

using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;/// <summary>
/// 匹配代碼中的中文
/// </summary>
public class MatchCnInCode : Editor
{// 正則表達式匹配中文字符static Regex chineseCharRegex = new Regex("[\u4e00-\u9fa5]");//忽略的文件夾static string[] IgnoreDir = new string[] { @"/Message/", @"/ProtoBuffer/", @"/ThirdPlugins/" };//忽略的代碼行static string[] IgnoreCode = new string[] { "[Header(", "Debugger.Log", "Debug.Log","#region" };//搜索的腳本路徑static string searchScriptPath = "Assets/HotUpdate/Scripts";[MenuItem("Tools/MatchCnInCode")]static void Method(){if (EditorUtility.DisplayDialog("提示", "開始匹配代碼中的中文", "確定", "取消")){Do();}}static void Do(){// 搜索所有的 .cs 文件var csFiles = AssetDatabase.FindAssets("t:script", new string[] { searchScriptPath });for (int i = 0; i < csFiles.Length; i++){var file = AssetDatabase.GUIDToAssetPath(csFiles[i]);bool hit = false;for (int j = 0; j < IgnoreDir.Length; j++){if (file.Contains(IgnoreDir[j])){hit = true;break;}}if (hit){continue;}var obj = AssetDatabase.LoadAssetAtPath<Object>(file);using (StreamReader sr = new StreamReader(file)){//匹配到中文bool matchCn = false;//在多行注釋中bool inMulAnnotation = false;int lineIndex = 0;while (!sr.EndOfStream){lineIndex++;//要匹配的內容bool matched = false;var line = sr.ReadLine();if (string.IsNullOrEmpty(line)){continue;}//多行注釋開始的索引int mulAnnotationStartIndex = line.IndexOf("/*");if (mulAnnotationStartIndex != -1){//在多行注釋中inMulAnnotation = true;var indexBeforeStr = line.Substring(0, mulAnnotationStartIndex);var match = chineseCharRegex.Match(indexBeforeStr);if (match.Success){Debug.LogError($"{file}=>{lineIndex}=> {match.Value} => {line} ", obj);matched = true;}}//在多行注釋中if (inMulAnnotation){//不含多行注釋開始標識的匹配串string noAnnotationStartStr = string.Empty;//該行沒有開始標識,直接找結束標識if (mulAnnotationStartIndex == -1){noAnnotationStartStr = line;}else//有開始標識,從開始標識后找結束標識防止/*/的情況{noAnnotationStartStr = line.Substring(mulAnnotationStartIndex + 2);}var mulAnnotationEndIndex = noAnnotationStartStr.IndexOf("*/");//有結尾符if (mulAnnotationEndIndex != -1){inMulAnnotation = false;var indexAfterStr = noAnnotationStartStr.Substring(mulAnnotationEndIndex);var match = chineseCharRegex.Match(indexAfterStr);if (match.Success){Debug.LogError($"{file}=>{lineIndex}=> {match.Value} => {line} ", obj);matched = true;}}}else{//查找是否有單行注釋var singleAnnotationIndex = line.IndexOf("//");if (singleAnnotationIndex == -1){bool hitIgnore = false;for (int j = 0; j < IgnoreCode.Length; j++){var logIndex = line.IndexOf(IgnoreCode[j]);if (logIndex != -1){hitIgnore = true;}}if (!hitIgnore){var match = chineseCharRegex.Match(line);if (match.Success){Debug.LogError($"{file}=>{lineIndex}=> {match.Value} => {line} ", obj);matched = true;}}}else{var match = chineseCharRegex.Match(line.Substring(0, singleAnnotationIndex));if (match.Success){Debug.LogError($"{file}=>{lineIndex}=> {match.Value} => {line} ", obj);matched = true;}}}//匹配所有行,還是匹配到一行后返回//if (matched)//{//    break;//}}}}}
}

? ? ? ? ?主要邏輯就是根據正則匹配中文,同時要忽略掉注釋以及特殊字符開頭的行,注意上面的Debug.LogError輸出格式為腳本路徑=>行數=>匹配到的首字=>整行內容,下面日志重定向要用。這幾處輸出可以封裝,這里省略了封裝代碼,下面是實際運行時的日志輸出,注意這里的堆棧信息,也是日志重定向需要使用的

? ? ? ? 日志重定向腳本

using System;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditorInternal;namespace TEngine.Editor
{/// <summary>/// 日志重定向相關的實用函數。/// </summary>internal static class LogRedirection{[OnOpenAsset(0)]private static bool OnOpenAsset(int instanceID, int line){if (line <= 0){return false;}// 獲取資源路徑string assetPath = AssetDatabase.GetAssetPath(instanceID);// 判斷資源類型if (!assetPath.EndsWith(".cs")){return false;}var stackTrace = GetStackTrace();if (!string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("MatchCnInCode.cs") && stackTrace.StartsWith("Assets")){//中文匹配的輸出var arr = stackTrace.Split("=>");//0:路徑 ,1: 行數var path = arr[0];var lineNum = int.Parse(arr[1]);var fullPath = UnityEngine.Application.dataPath.Substring(0, UnityEngine.Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal));fullPath = $"{fullPath}{path}";// 跳轉到目標代碼的特定行InternalEditorUtility.OpenFileAtLineExternal(fullPath.Replace('/', '\\'), lineNum);return true;}return false;}/// <summary>/// 獲取當前日志窗口選中的日志的堆棧信息。/// </summary>/// <returns>選中日志的堆棧信息實例。</returns>private static string GetStackTrace(){// 通過反射獲取ConsoleWindow類var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");// 獲取窗口實例var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow",BindingFlags.Static |BindingFlags.NonPublic);if (fieldInfo != null){var consoleInstance = fieldInfo.GetValue(null);if (consoleInstance != null)if (EditorWindow.focusedWindow == (EditorWindow)consoleInstance){// 獲取m_ActiveText成員fieldInfo = consoleWindowType.GetField("m_ActiveText",BindingFlags.Instance |BindingFlags.NonPublic);// 獲取m_ActiveText的值if (fieldInfo != null){var activeText = fieldInfo.GetValue(consoleInstance).ToString();return activeText;}}}return null;}}
}

? ? ? ? 主要邏輯在函數OnOpenAsset中,var stackTrace = GetStackTrace();這里獲取了整個輸出的所有信息,然后下一行代碼判斷信息中是否有“MatchCnInCode.cs”以及是否以“Assets”開頭,如果滿足這兩個條件,我們需要對該日志輸出進行重定向。再根據我們上面的輸出格式腳本路徑=>行數=>匹配到的首字=>整行內容,可以取到路徑和行數,就可以實現點擊該行輸出,直接定位到日志中標記的行數了,注意這里的路徑不能多空格,結尾的空格也不行,否則會無法定位到腳本編輯工具中,所以日志輸出的時候要檢查是否有多余的空格。

資源地址?

https://download.csdn.net/download/a598211757/90628215

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/79826.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/79826.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/79826.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

.NET MCP 文檔

MCP 概述 MCP&#xff08;Model Context Protocol&#xff09;是由 Anthropic 推出的一種開放協議&#xff0c;類似 AI 的 USB-C 擴展塢&#xff0c;用于在大模型和數據源之間建立安全的通信&#xff08;授權&#xff09;&#xff0c;讓 AI 應用能夠安全地訪問和操作本地或遠程…

【Linux】vim配置----超詳細

目錄 一、插件管理器準備 二、目錄準備 三、安裝插件 一、插件管理器準備 Vim-plug 是一個Vim插件管理器&#xff0c;利用異步并行可以快速地安裝、更新和卸載插件。它的安裝和配置都非常簡單&#xff0c;而且在操作過程中會給出很多易讀的反饋信息&#xff0c;是一個自由、…

PHP實現圖片自動添加水印效果

<?php // 設置原始圖片路徑和水印圖片路徑 $original_image original.jpg; $watermark_image watermark.png;// 創建圖片資源 $original imagecreatefromjpeg($original_image); $watermark imagecreatefrompng($watermark_image);// 獲取圖片尺寸 $original_width im…

檢查新接手LINUX服務器應用的部署情況和正在運行的服務

當接手一臺新的 Linux 服務器時&#xff0c;第一要務就是摸清系統上已經安裝部署了哪些應用和服務。 本文將以 CentOS7為例&#xff0c;詳細介紹如何系統地排查已安裝的應用和服務&#xff0c;包括它們的安裝方式和安裝位置。 1.查看系統基本信息 首先獲取系統整體信息&…

使用注解方式整合ssm時,啟動tomcat掃描不到resource下面的xxxmapper.xml問題,解決方法

解決org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.xxx.mapper.方法 在Spring與Mybatis整合時&#xff0c;可能會遇到這樣的報錯 原因&#xff1a; 其原因為mapper路徑的映射錯誤&#xff0c;表示在嘗試執行某個 Mapper 接口的方法時…

C++11特性補充

目錄 lambda表達式 定義 捕捉的方式 可變模板參數 遞歸函數方式展開參數包 數組展開參數包 移動構造和移動賦值 包裝器 綁定bind 智能指針 RAII auto_ptr unique_ptr shared_ptr 循環引用 weak_ptr 補充 總結 特殊類的設計 不能被拷貝的類 只能在堆上創建…

My SQL 索引

核心目標&#xff1a; 理解 mysql 索引的工作原理、類型、優缺點&#xff0c;并掌握創建、管理和優化索引的方法&#xff0c;以顯著提升數據庫查詢性能。 什么是索引&#xff1f; 索引是一種特殊的數據庫結構&#xff0c;它包含表中一列或多列的值以及指向這些值所在物理行的指…

極狐GitLab 注冊限制如何設置?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 注冊限制 (BASIC SELF) 您可以對注冊實施以下限制&#xff1a; 禁用新注冊。新注冊需要管理員批準。需要用戶電子郵件確認。…

10.(vue3.x+vite)div實現tooltip功能(css實現)

1:效果截圖 2:代碼實現 <template><div><div class="tooltip" style="margin-top: 20%; margin-left: 20%; background-color: blueviolet; color: white;

Linux下 文件的查找、復制、移動和解壓縮

1、在/var/log目錄下創建一個hehe.log的文件&#xff0c;其文件內容是&#xff1a; myhostname ghl mydomain localdomain relayhost [smtp.qq.com]:587 smtp_use_tls yes smtp_sasl_auth_enable yes smtp_sasl_security_options noanonymous smtp_sasl_tls_security_opt…

Ubuntu 安裝 Docker 教程(官方推薦方式)

? 步驟 1&#xff1a;卸載舊版本&#xff08;如果有&#xff09; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done---### ? 步驟 2&#xff1a;更新 APT 索引并安裝依賴項bash sudo a…

計算機視覺與深度學習 | Transformer原理,公式,代碼,應用

Transformer 詳解 Transformer 是 Google 在 2017 年提出的基于自注意力機制的深度學習模型,徹底改變了序列建模的范式,解決了 RNN 和 LSTM 在長距離依賴和并行計算上的局限性。以下是其原理、公式、代碼和應用的詳細解析。 一、原理 核心架構 Transformer 由 編碼器(Encod…

計算機基礎 | 常見進制與單位簡介 / 表示 / 描述

注&#xff1a;本文為 “進制與常見單位應用” 相關文章合輯。 原文為繁體&#xff0c;注意術語描述差異。 略作重排。 進制簡介&#xff08;二進制、八進制、十進制、十六進制&#xff09; 發表于 2017-01-20 鄭中勝 數字系統&#xff08;Numeral system&#xff09;&#…

門面模式與適配器模式

一、門面模式 門面模式&#xff1a;提供統一接口訪問子系統接口 1、包含角色 外觀系統對外的統一接口子系統類的集合&#xff1b;并不知道外觀角色的存在&#xff0c;需要為了配合外觀角色而做特殊處理或修改 2、舉例 原本開關燈要分別操作各個房間的燈&#xff0c;現在設置總…

SpringBoot Actuator指標收集:Micrometer與Prometheus集成

文章目錄 引言一、Spring Boot Actuator基礎二、Micrometer簡介與集成三、基本指標收集與配置四、自定義業務指標實現五、與Prometheus集成六、實戰案例&#xff1a;API性能監控總結 引言 在現代微服務架構中&#xff0c;監控應用程序的健康狀況和性能指標變得至關重要。Sprin…

【Android面試八股文】Android應用進程的啟動流程【二】

應用進程 1.1 Android系統進程的啟動過程&#xff1a; 1、init進程fork出Zygote進程后&#xff0c;Zygote進程會創建一個服務端socket&#xff0c;等待AMS發起socket請求。 同時&#xff0c;由Zygote進程fork出的SystemServer進程會啟動各項系統服務&#xff0c;其中就包含了A…

基于Django的AI客服租車分析系統

基于Django的AI客服租車分析系統 【包含內容】 【一】項目提供完整源代碼及詳細注釋 【二】系統設計思路與實現說明 【三】AI智能客服與用戶交互指導手冊 【技術棧】 ①&#xff1a;系統環境&#xff1a;Python 3.8&#xff0c;Django 4.2框架 ②&#xff1a;開發環境&a…

全同態加密醫療數據分析集python實現

目錄 摘要一、前言二、全同態加密與醫療數據分析概述2.1 全同態加密(FHE)簡介2.2 醫療數據分析需求三、數據生成與預處理四、系統架構與流程4.1 系統架構圖五、核心數學公式六、異步任務調度與(可選)GPU 加速七、PyQt6 GUI 設計八、完整代碼實現九、自查測試與總結十、展望…

linux 搭建 dvwa 滲透測試環境

linux 安裝 dvwa 1、分為4個部分&#xff0c;搭建dvwa滲透測試環境2、安裝centos 7.63、安裝apache http server4、安裝mysql5、安裝php6、運行dvwa 1、分為4個部分&#xff0c;搭建dvwa滲透測試環境 本文基于centos 7.6 搭建 dvwa 滲透測試環境 安裝一個linux系統安裝apache…

stm32(gpio的四種輸出)

其實GPIO這個片上外設的功能&#xff1a; 用于控制IO引腳。 CPU就如同大腦&#xff0c;而這些片上外設就如同四肢一樣的關系 如圖 —————————————————————————————— OK類比了以上 其實GPIO是有 八種工作模式的 這八種工作模式 因為GPIO是面向IO…