2025-03-24 14:42:40 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using BITKit;
|
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
|
|
|
|
namespace Net.BITKit.Localization
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 本地化
|
|
|
|
|
/// </summary>
|
|
|
|
|
public interface ILocalizationService
|
|
|
|
|
{
|
|
|
|
|
public string Prefix { get; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 当前语言
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string CurrentLanguage { get; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 更改回调,通常在此检查并下载语言包
|
|
|
|
|
/// </summary>
|
|
|
|
|
public event Func<string,string,UniTask> OnLanguageChangeAsync;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 语言更改完成回调,UI和World执行更新
|
|
|
|
|
/// </summary>
|
|
|
|
|
public event Action<string,string> OnLanguageChanged;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取翻译文本,返回无复制的引用
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key"></param>
|
|
|
|
|
/// <param name="language">语言,默认为当前CultureInfo.Name</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public string GetLocalizedString(string key,string language=null);
|
|
|
|
|
public UniTask ChangeLanguageAsync(string newLanguage);
|
|
|
|
|
public IDictionary<string, IDictionary<string, string>> LocalizedStrings { get; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class LocalizationService : ILocalizationService,IDisposable
|
|
|
|
|
{
|
|
|
|
|
private static LocalizationService _singleton;
|
|
|
|
|
public string Prefix => "#";
|
|
|
|
|
private char _prefix = '#';
|
|
|
|
|
public string CurrentLanguage { get; private set; }
|
|
|
|
|
|
|
|
|
|
public event Func<string, string, UniTask> OnLanguageChangeAsync;
|
|
|
|
|
public event Action<string, string> OnLanguageChanged;
|
|
|
|
|
|
|
|
|
|
private readonly ILogger<LocalizationService> _logger;
|
|
|
|
|
|
|
|
|
|
public IDictionary<string, IDictionary<string, string>> LocalizedStrings { get; } =
|
|
|
|
|
new Dictionary<string, IDictionary<string, string>>();
|
|
|
|
|
|
|
|
|
|
private readonly ValidHandle _isBusy = new();
|
|
|
|
|
|
|
|
|
|
private readonly HashSet<string> _untranslatedKeys = new();
|
|
|
|
|
|
|
|
|
|
public LocalizationService(ILogger<LocalizationService> logger)
|
|
|
|
|
{
|
|
|
|
|
if (_singleton is not null)
|
|
|
|
|
{
|
|
|
|
|
logger.LogError("LocalizationService can only be one singleton");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public string GetLocalizedString(string key, string language = null)
|
|
|
|
|
{
|
2025-04-30 14:18:13 +08:00
|
|
|
|
if (string.IsNullOrEmpty(key)) return "#ERROR_EMPTY_KEY";
|
|
|
|
|
|
2025-03-24 14:42:40 +08:00
|
|
|
|
language ??= CurrentLanguage; // 默认使用当前语言
|
|
|
|
|
|
|
|
|
|
if (key[0] != _prefix)
|
|
|
|
|
{
|
|
|
|
|
key = _prefix + key;
|
|
|
|
|
}
|
|
|
|
|
if (LocalizedStrings.TryGetValue(language, out var langDict) && langDict.TryGetValue(key, out var value))
|
|
|
|
|
{
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
_untranslatedKeys.Add(key);
|
|
|
|
|
return key.Replace(Prefix,string.Empty).Replace("_"," "); // 如果找不到翻译,就返回 key 本身(常见策略)
|
|
|
|
|
}
|
|
|
|
|
public async UniTask ChangeLanguageAsync(string newLanguage)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentLanguage == newLanguage) return;
|
|
|
|
|
var oldLanguage = CurrentLanguage;
|
|
|
|
|
CurrentLanguage = newLanguage;
|
|
|
|
|
|
|
|
|
|
await _isBusy;
|
|
|
|
|
using var _ = _isBusy.GetHandle();
|
|
|
|
|
|
|
|
|
|
#if UNITY_5_3_OR_NEWER
|
|
|
|
|
await UniTask.SwitchToMainThread();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
await OnLanguageChangeAsync.UniTaskFunc(oldLanguage, newLanguage);
|
|
|
|
|
|
|
|
|
|
// 触发同步事件(例如更新 UI)
|
|
|
|
|
OnLanguageChanged?.Invoke(oldLanguage, newLanguage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
_isBusy?.Dispose();
|
|
|
|
|
_singleton = null;
|
|
|
|
|
_logger.LogInformation("Untranslated keys:\n"+string.Join("\n", _untranslatedKeys));
|
|
|
|
|
_untranslatedKeys.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|