规范化LocalizationModule

This commit is contained in:
Alex-Rachel
2025-03-18 00:20:44 +08:00
parent b68a5a84b9
commit 385de69cb9
10 changed files with 454 additions and 291 deletions

View File

@@ -50,7 +50,8 @@ namespace Procedure
return; return;
} }
Language language = LocalizationModule.Instance.Language; ILocalizationModule localizationModule = ModuleSystem.GetModule<ILocalizationModule>();
Language language = localizationModule.Language;
if (Utility.PlayerPrefs.HasSetting(Constant.Setting.Language)) if (Utility.PlayerPrefs.HasSetting(Constant.Setting.Language))
{ {
try try
@@ -75,7 +76,7 @@ namespace Procedure
Utility.PlayerPrefs.Save(); Utility.PlayerPrefs.Save();
} }
LocalizationModule.Instance.Language = language; localizationModule.Language = language;
Log.Info("Init language settings complete, current language is '{0}'.", language.ToString()); Log.Info("Init language settings complete, current language is '{0}'.", language.ToString());
} }

View File

@@ -66,10 +66,10 @@ namespace TEngine.Localization
} }
Debug.Log("I2LocalizationManager 加载编辑器资源数据"); Debug.Log("I2LocalizationManager 加载编辑器资源数据");
var sourceAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<LanguageSourceAsset>(DefaultLocalizationHelper.I2GlobalSourcesEditorPath); var sourceAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<LanguageSourceAsset>(LocalizationUtility.I2GlobalSourcesEditorPath);
if (sourceAsset == null) if (sourceAsset == null)
{ {
Debug.LogError($"错误 没有找到编辑器下的资源 {DefaultLocalizationHelper.I2GlobalSourcesEditorPath}"); Debug.LogError($"错误 没有找到编辑器下的资源 {LocalizationUtility.I2GlobalSourcesEditorPath}");
return null; return null;
} }

View File

@@ -0,0 +1,60 @@
using Cysharp.Threading.Tasks;
namespace TEngine
{
public interface ILocalizationModule
{
/// <summary>
/// 获取或设置本地化语言。
/// </summary>
public Language Language { get; set; }
/// <summary>
/// 获取系统语言。
/// </summary>
public Language SystemLanguage {get;}
/// <summary>
/// 加载语言总表。
/// </summary>
public UniTask LoadLanguageTotalAsset(string assetName);
/// <summary>
/// 加载语言分表。
/// </summary>
/// <param name="language">语言类型。</param>
/// <param name="setCurrent">是否立刻设置成当前语言。</param>
/// <param name="fromInit">是否初始化Inner语言。</param>
public UniTask LoadLanguage(string language, bool setCurrent = false, bool fromInit = false);
/// <summary>
/// 检查是否存在该语言。
/// </summary>
/// <param name="language">语言。</param>
/// <returns>是否已加载。</returns>
public bool CheckLanguage(string language);
/// <summary>
/// 设置当前语言。
/// </summary>
/// <param name="language">语言名称。</param>
/// <param name="load">是否加载。</param>
/// <returns></returns>
public bool SetLanguage(Language language, bool load = false);
/// <summary>
/// 设置当前语言。
/// </summary>
/// <param name="language">语言名称。</param>
/// <param name="load">是否加载。</param>
/// <returns></returns>
public bool SetLanguage(string language, bool load = false);
/// <summary>
/// 通过语言的Id设置语言。
/// </summary>
/// <param name="languageId">语言ID。</param>
/// <returns>是否设置成功。</returns>
public bool SetLanguage(int languageId);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6803cf6a42a24545aac1c07f7f0ef466
timeCreated: 1742227577

View File

@@ -0,0 +1,312 @@
using UnityEngine;
using Object = UnityEngine.Object;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Cysharp.Threading.Tasks;
using TEngine.Localization;
namespace TEngine
{
/// <summary>
/// 本地化组件。
/// </summary>
[DisallowMultipleComponent]
public sealed class LocalizationManager : MonoBehaviour, IResourceManager_Bundles
{
private string _defaultLanguage = "Chinese";
[SerializeField]
private TextAsset innerLocalizationCsv;
private LanguageSource _languageSource;
private LanguageSourceData _sourceData
{
get
{
if (_languageSource == null)
{
_languageSource = gameObject.AddComponent<LanguageSource>();
}
return _languageSource.SourceData;
}
}
[SerializeField]
private List<string> allLanguage = new List<string>();
/// <summary>
/// 模拟平台运行时 编辑器资源不加载。
/// </summary>
[SerializeField]
private bool useRuntimeModule = true;
private string _currentLanguage;
/// <summary>
/// 获取或设置本地化语言。
/// </summary>
public Language Language
{
get => LocalizationUtility.GetLanguage(_currentLanguage);
set => SetLanguage(LocalizationUtility.GetLanguageStr(value));
}
/// <summary>
/// 获取系统语言。
/// </summary>
public Language SystemLanguage => LocalizationUtility.SystemLanguage;
private IResourceModule _resourceModule;
/// <summary>
/// 游戏框架组件初始化。
/// </summary>
private void Awake()
{
_resourceModule = ModuleSystem.GetModule<IResourceModule>();
if (_resourceModule == null)
{
Log.Fatal("Resource component is invalid.");
return;
}
LocalizationModule localizationModule = new LocalizationModule();
localizationModule.Bind(this);
ModuleSystem.RegisterModule<ILocalizationModule>(localizationModule);
}
private void Start()
{
RootModule rootModule = RootModule.Instance;
if (rootModule == null)
{
Log.Fatal("Base component is invalid.");
return;
}
_defaultLanguage = LocalizationUtility.GetLanguageStr(
rootModule.EditorLanguage != Language.Unspecified ? rootModule.EditorLanguage : SystemLanguage);
AsyncInit().Forget();
}
private async UniTask<bool> AsyncInit()
{
if (string.IsNullOrEmpty(_defaultLanguage))
{
Log.Fatal($"Must set defaultLanguage.");
return false;
}
#if UNITY_EDITOR
if (!useRuntimeModule)
{
Localization.LocalizationManager.RegisterSourceInEditor();
UpdateAllLanguages();
SetLanguage(_defaultLanguage);
}
else
{
_sourceData.Awake();
await LoadLanguage(_defaultLanguage, true, true);
}
#else
_sourceData.Awake();
await LoadLanguage(_defaultLanguage, true, true);
#endif
return true;
}
/// <summary>
/// 加载语言总表。
/// </summary>
public async UniTask LoadLanguageTotalAsset(string assetName)
{
#if UNITY_EDITOR
if (!useRuntimeModule)
{
Log.Warning($"禁止在此模式下 动态加载语言");
return;
}
#endif
TextAsset assetTextAsset = await _resourceModule.LoadAssetAsync<TextAsset>(assetName);
if (assetTextAsset == null)
{
Log.Warning($"没有加载到语言总表");
return;
}
Log.Info($"加载语言总表成功");
UseLocalizationCSV(assetTextAsset.text, true);
}
/// <summary>
/// 加载语言分表。
/// </summary>
/// <param name="language">语言类型。</param>
/// <param name="setCurrent">是否立刻设置成当前语言。</param>
/// <param name="fromInit">是否初始化Inner语言。</param>
public async UniTask LoadLanguage(string language, bool setCurrent = false, bool fromInit = false)
{
#if UNITY_EDITOR
if (!useRuntimeModule)
{
Log.Warning($"禁止在此模式下 动态加载语言 {language}");
return;
}
#endif
TextAsset assetTextAsset;
if (!fromInit)
{
var assetName = GetLanguageAssetName(language);
assetTextAsset = await _resourceModule.LoadAssetAsync<TextAsset>(assetName);
}
else
{
if (innerLocalizationCsv == null)
{
Log.Warning($"请使用I2Localization.asset导出CSV创建内置多语言.");
return;
}
assetTextAsset = innerLocalizationCsv;
}
if (assetTextAsset == null)
{
Log.Warning($"没有加载到目标语言资源 {language}");
return;
}
Log.Info($"加载语言成功 {language}");
UseLocalizationCSV(assetTextAsset.text, !setCurrent);
if (setCurrent)
{
SetLanguage(language);
}
}
private string GetLanguageAssetName(string language)
{
return $"{LocalizationUtility.I2ResAssetNamePrefix}{language}";
}
/// <summary>
/// 检查并初始化所有语言的Id。
/// </summary>
private void UpdateAllLanguages()
{
this.allLanguage.Clear();
List<string> allLanguages = Localization.LocalizationManager.GetAllLanguages();
foreach (var language in allLanguages)
{
var newLanguage = Regex.Replace(language, @"[\r\n]", "");
this.allLanguage.Add(newLanguage);
}
}
/// <summary>
/// 检查是否存在该语言。
/// </summary>
/// <param name="language">语言。</param>
/// <returns>是否已加载。</returns>
public bool CheckLanguage(string language)
{
return allLanguage.Contains(language);
}
/// <summary>
/// 设置当前语言。
/// </summary>
/// <param name="language">语言名称。</param>
/// <param name="load">是否加载。</param>
/// <returns></returns>
public bool SetLanguage(Language language, bool load = false)
{
return SetLanguage(LocalizationUtility.GetLanguageStr(language), load);
}
/// <summary>
/// 设置当前语言。
/// </summary>
/// <param name="language">语言名称。</param>
/// <param name="load">是否加载。</param>
/// <returns></returns>
public bool SetLanguage(string language, bool load = false)
{
if (!CheckLanguage(language))
{
if (load)
{
LoadLanguage(language, true).Forget();
return true;
}
Log.Warning($"当前没有这个语言无法切换到此语言 {language}");
return false;
}
if (_currentLanguage == language)
{
return true;
}
Log.Info($"设置当前语言 = {language}");
Localization.LocalizationManager.CurrentLanguage = language;
_currentLanguage = language;
return true;
}
/// <summary>
/// 通过语言的Id设置语言。
/// </summary>
/// <param name="languageId">语言ID。</param>
/// <returns>是否设置成功。</returns>
public bool SetLanguage(int languageId)
{
if (languageId < 0 || languageId >= allLanguage.Count)
{
Log.Warning($"Error languageIndex. Could not set and check {languageId} Language.Count = {allLanguage.Count}.");
return false;
}
var language = allLanguage[languageId];
return SetLanguage(language);
}
private void UseLocalizationCSV(string text, bool isLocalizeAll = false)
{
_sourceData.Import_CSV(string.Empty, text, eSpreadsheetUpdateMode.Merge, ',');
if (isLocalizeAll)
{
Localization.LocalizationManager.LocalizeAll();
}
UpdateAllLanguages();
}
/// <summary>
/// 语言模块加载资源接口。
/// </summary>
/// <param name="path">资源定位地址。</param>
/// <typeparam name="T">资源类型。</typeparam>
/// <returns>返回资源实例。</returns>
public T LoadFromBundle<T>(string path) where T : Object
{
var assetObject = _resourceModule.LoadAsset<T>(path);
if (assetObject != null)
{
return assetObject;
}
Log.Error($"Localization could not load {path} assetsType :{typeof(T).Name}.");
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 706e6317a59f61044b2805be79f6b284
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,329 +1,113 @@
using UnityEngine; using Cysharp.Threading.Tasks;
using Object = UnityEngine.Object;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Cysharp.Threading.Tasks;
using TEngine.Localization;
using UnityEngine.Serialization;
namespace TEngine namespace TEngine
{ {
/// <summary> /// <summary>
/// 本地化组件 /// 本地化管理模块,负责多语言资源的加载和切换
/// </summary> /// </summary>
[DisallowMultipleComponent] public class LocalizationModule : Module, ILocalizationModule
public sealed class LocalizationModule : MonoBehaviour, IResourceManager_Bundles
{ {
#region GetInstance // 实际的本地化管理器实例。
private LocalizationManager _localizationManager;
private static LocalizationModule _instance = null;
public static LocalizationModule Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<LocalizationModule>();
}
return _instance;
}
}
#endregion
private string _defaultLanguage = "Chinese";
[SerializeField]
private TextAsset innerLocalizationCsv;
private LanguageSource _languageSource;
private LanguageSourceData _sourceData
{
get
{
if (_languageSource == null)
{
_languageSource = gameObject.AddComponent<LanguageSource>();
}
return _languageSource.SourceData;
}
}
[SerializeField]
private List<string> allLanguage = new List<string>();
/// <summary> /// <summary>
/// 模拟平台运行时 编辑器资源不加载 /// 绑定具体的本地化管理器实现
/// </summary> /// </summary>
[SerializeField] /// <param name="localizationManager">要绑定的本地化管理器实例。</param>
private bool useRuntimeModule = true; public void Bind(LocalizationManager localizationManager)
{
private string _currentLanguage; _localizationManager = localizationManager;
}
/// <summary>
/// 模块初始化方法。
/// </summary>
public override void OnInit()
{
}
/// <summary> /// <summary>
/// 获取或设置本地化语言 /// 模块关闭方法
/// </summary>
public override void Shutdown()
{
UnityEngine.Object.Destroy(_localizationManager);
}
/// <summary>
/// 当前使用的语言(可读写)。
/// </summary> /// </summary>
public Language Language public Language Language
{ {
get => DefaultLocalizationHelper.GetLanguage(_currentLanguage); get => _localizationManager.Language;
set => SetLanguage(DefaultLocalizationHelper.GetLanguageStr(value)); set => _localizationManager.Language = value;
} }
/// <summary> /// <summary>
/// 获取系统语言。 /// 获取系统默认语言。
/// </summary> /// </summary>
public Language SystemLanguage => DefaultLocalizationHelper.SystemLanguage; public Language SystemLanguage => _localizationManager.SystemLanguage;
private IResourceModule _resourceModule;
/// <summary> /// <summary>
/// 游戏框架组件初始化 /// 加载完整的语言资源包
/// </summary>
private void Awake()
{
_instance = this;
_resourceModule = ModuleSystem.GetModule<IResourceModule>();
if (_resourceModule == null)
{
Log.Fatal("Resource component is invalid.");
return;
}
}
private void Start()
{
RootModule rootModule = RootModule.Instance;
if (rootModule == null)
{
Log.Fatal("Base component is invalid.");
return;
}
_defaultLanguage = DefaultLocalizationHelper.GetLanguageStr(
rootModule.EditorLanguage != Language.Unspecified ? rootModule.EditorLanguage : SystemLanguage);
AsyncInit().Forget();
}
private async UniTask<bool> AsyncInit()
{
if (string.IsNullOrEmpty(_defaultLanguage))
{
Log.Fatal($"Must set defaultLanguage.");
return false;
}
#if UNITY_EDITOR
if (!useRuntimeModule)
{
LocalizationManager.RegisterSourceInEditor();
UpdateAllLanguages();
SetLanguage(_defaultLanguage);
}
else
{
_sourceData.Awake();
await LoadLanguage(_defaultLanguage, true, true);
}
#else
_sourceData.Awake();
await LoadLanguage(_defaultLanguage, true, true);
#endif
return true;
}
/// <summary>
/// 加载语言总表。
/// </summary> /// </summary>
/// <param name="assetName">要加载的资源包名称</param>
public async UniTask LoadLanguageTotalAsset(string assetName) public async UniTask LoadLanguageTotalAsset(string assetName)
{ {
#if UNITY_EDITOR await _localizationManager.LoadLanguageTotalAsset(assetName);
if (!useRuntimeModule)
{
Log.Warning($"禁止在此模式下 动态加载语言");
return;
}
#endif
TextAsset assetTextAsset = await _resourceModule.LoadAssetAsync<TextAsset>(assetName);
if (assetTextAsset == null)
{
Log.Warning($"没有加载到语言总表");
return;
}
Log.Info($"加载语言总表成功");
UseLocalizationCSV(assetTextAsset.text, true);
} }
/// <summary> /// <summary>
/// 加载语言分表 /// 加载指定语言的本地化资源
/// </summary> /// </summary>
/// <param name="language">语言类型。</param> /// <param name="language">要加载的语言。</param>
/// <param name="setCurrent">是否立刻设置当前语言。</param> /// <param name="setCurrent">是否设置当前语言。</param>
/// <param name="fromInit">是否初始化Inner语言。</param> /// <param name="fromInit">是否来自初始化流程。</param>
public async UniTask LoadLanguage(string language, bool setCurrent = false, bool fromInit = false) public async UniTask LoadLanguage(string language, bool setCurrent = false, bool fromInit = false)
{ {
#if UNITY_EDITOR await _localizationManager.LoadLanguage(language, setCurrent, fromInit);
if (!useRuntimeModule)
{
Log.Warning($"禁止在此模式下 动态加载语言 {language}");
return;
}
#endif
TextAsset assetTextAsset;
if (!fromInit)
{
var assetName = GetLanguageAssetName(language);
assetTextAsset = await _resourceModule.LoadAssetAsync<TextAsset>(assetName);
}
else
{
if (innerLocalizationCsv == null)
{
Log.Warning($"请使用I2Localization.asset导出CSV创建内置多语言.");
return;
}
assetTextAsset = innerLocalizationCsv;
}
if (assetTextAsset == null)
{
Log.Warning($"没有加载到目标语言资源 {language}");
return;
}
Log.Info($"加载语言成功 {language}");
UseLocalizationCSV(assetTextAsset.text, !setCurrent);
if (setCurrent)
{
SetLanguage(language);
}
}
private string GetLanguageAssetName(string language)
{
return $"{DefaultLocalizationHelper.I2ResAssetNamePrefix}{language}";
} }
/// <summary> /// <summary>
/// 检查并初始化所有语言的Id /// 检查指定语言是否可用
/// </summary> /// </summary>
private void UpdateAllLanguages() /// <param name="language">要检查的语言名称。</param>
{ /// <returns>如果语言可用返回true否则false。</returns>
this.allLanguage.Clear();
List<string> allLanguages = LocalizationManager.GetAllLanguages();
foreach (var language in allLanguages)
{
var newLanguage = Regex.Replace(language, @"[\r\n]", "");
this.allLanguage.Add(newLanguage);
}
}
/// <summary>
/// 检查是否存在该语言。
/// </summary>
/// <param name="language">语言。</param>
/// <returns>是否已加载。</returns>
public bool CheckLanguage(string language) public bool CheckLanguage(string language)
{ {
return allLanguage.Contains(language); return _localizationManager.CheckLanguage(language);
} }
/// <summary> /// <summary>
/// 设置当前语言。 /// 设置当前语言(通过枚举值)
/// </summary> /// </summary>
/// <param name="language">语言名称。</param> /// <param name="language">要设置的语言枚举值。</param>
/// <param name="load">是否加载。</param> /// <param name="load">是否立即加载语言资源。</param>
/// <returns></returns> /// <returns>设置是否成功。</returns>
public bool SetLanguage(Language language, bool load = false) public bool SetLanguage(Language language, bool load = false)
{ {
return SetLanguage(DefaultLocalizationHelper.GetLanguageStr(language), load); return _localizationManager.SetLanguage(language, load);
} }
/// <summary> /// <summary>
/// 设置当前语言。 /// 设置当前语言(通过字符串)
/// </summary> /// </summary>
/// <param name="language">语言名称。</param> /// <param name="language">要设置的语言名称。</param>
/// <param name="load">是否加载。</param> /// <param name="load">是否立即加载语言资源。</param>
/// <returns></returns> /// <returns>设置是否成功。</returns>
public bool SetLanguage(string language, bool load = false) public bool SetLanguage(string language, bool load = false)
{ {
if (!CheckLanguage(language)) return _localizationManager.SetLanguage(language, load);
{
if (load)
{
LoadLanguage(language, true).Forget();
return true;
}
Log.Warning($"当前没有这个语言无法切换到此语言 {language}");
return false;
}
if (_currentLanguage == language)
{
return true;
}
Log.Info($"设置当前语言 = {language}");
LocalizationManager.CurrentLanguage = language;
_currentLanguage = language;
return true;
} }
/// <summary> /// <summary>
/// 通过语言的Id设置语言 /// 设置当前语言通过语言ID
/// </summary> /// </summary>
/// <param name="languageId">语言ID。</param> /// <param name="languageId">要设置的语言ID。</param>
/// <returns>是否设置成功。</returns> /// <returns>设置是否成功。</returns>
public bool SetLanguage(int languageId) public bool SetLanguage(int languageId)
{ {
if (languageId < 0 || languageId >= allLanguage.Count) return _localizationManager.SetLanguage(languageId);
{
Log.Warning($"Error languageIndex. Could not set and check {languageId} Language.Count = {allLanguage.Count}.");
return false;
}
var language = allLanguage[languageId];
return SetLanguage(language);
}
private void UseLocalizationCSV(string text, bool isLocalizeAll = false)
{
_sourceData.Import_CSV(string.Empty, text, eSpreadsheetUpdateMode.Merge, ',');
if (isLocalizeAll)
{
LocalizationManager.LocalizeAll();
}
UpdateAllLanguages();
}
/// <summary>
/// 语言模块加载资源接口。
/// </summary>
/// <param name="path">资源定位地址。</param>
/// <typeparam name="T">资源类型。</typeparam>
/// <returns>返回资源实例。</returns>
public T LoadFromBundle<T>(string path) where T : Object
{
var assetObject = _resourceModule.LoadAsset<T>(path);
if (assetObject != null)
{
return assetObject;
}
Log.Error($"Localization could not load {path} assetsType :{typeof(T).Name}.");
return null;
} }
} }
} }

View File

@@ -1,11 +1,3 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 706e6317a59f61044b2805be79f6b284 guid: 4a1b4797d76240fea12088db6216553e
MonoImporter: timeCreated: 1742227941
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -6,7 +6,7 @@ namespace TEngine
/// <summary> /// <summary>
/// 默认本地化辅助器。 /// 默认本地化辅助器。
/// </summary> /// </summary>
public class DefaultLocalizationHelper public class LocalizationUtility
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
public const string I2GlobalSourcesEditorPath = "Assets/Editor/I2Localization/I2Languages.asset"; public const string I2GlobalSourcesEditorPath = "Assets/Editor/I2Localization/I2Languages.asset";
@@ -74,7 +74,7 @@ namespace TEngine
private static readonly Dictionary<Language, string> s_LanguageMap = new Dictionary<Language, string>(); private static readonly Dictionary<Language, string> s_LanguageMap = new Dictionary<Language, string>();
private static readonly Dictionary<string, Language> s_LanguageStrMap = new Dictionary<string, Language>(); private static readonly Dictionary<string, Language> s_LanguageStrMap = new Dictionary<string, Language>();
static DefaultLocalizationHelper() static LocalizationUtility()
{ {
RegisterLanguageMap(Language.English); RegisterLanguageMap(Language.English);
RegisterLanguageMap(Language.ChineseSimplified, "Chinese"); RegisterLanguageMap(Language.ChineseSimplified, "Chinese");