// Assets/Scripts/Localization/LocalizationManager.cs // 本地化管理器(运行时 JSON 文件驱动)。 // // 数据格式(放在 Resources/Localization/{Language}/{TableName}.json): // { // "entries": [ // { "key": "ui_start", "value": "开始游戏" }, // { "key": "ui_settings", "value": "设置" } // ] // } // // 用法(静态 Facade,保持调用兼容): // LocalizationManager.Get("ui_start") → "开始游戏" // LocalizationManager.Get("dlg_hero", "Dialogue") → Dialogue 表中的对应文本 // // 服务调用(通过 ServiceLocator): // ServiceLocator.GetOrDefault()?.SetLanguage(Language.English) using System; using System.Collections.Generic; using UnityEngine; using BaseGames.Core; using BaseGames.Core.Save; namespace BaseGames.Localization { /// /// 本地化管理器(MonoBehaviour,挂在 Persistent 场景)。 /// 实现 ILocalizationService + ISaveable,通过 ServiceLocator 注册。 /// 语言偏好持久化到 SaveData.Settings.Language,不使用 PlayerPrefs。 /// 保留静态 Get() Facade,现有调用方无需修改。 /// public class LocalizationManager : MonoBehaviour, ILocalizationService, ISaveable { // 默认语言:回退链:当前语言 → 英语 → 直接返回 key private Language _currentLanguage = Language.ChineseSimplified; private readonly Language _fallbackLanguage = Language.English; // 双层缓存:languageKey("ChineseSimplified/UI") → (key → value) private readonly Dictionary> _cache = new(); // ── 静态事件代理(向后兼容静态订阅方式)───────────────────────────────── /// 语言切换时触发(静态代理,可通过 LocalizationManager.OnLanguageChanged += 订阅)。 public static event Action OnLanguageChanged; // ILocalizationService 显式实现:委托给静态事件 event Action ILocalizationService.OnLanguageChanged { add { OnLanguageChanged += value; } remove { OnLanguageChanged -= value; } } // ── 生命周期 ────────────────────────────────────────────────────────── private void Awake() { if (ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; } ServiceLocator.Register(this); } private void OnEnable() { ServiceLocator.GetOrDefault()?.Register(this); } private void OnDisable() { ServiceLocator.GetOrDefault()?.Unregister(this); } private void OnDestroy() { ServiceLocator.Unregister(this); } // ── ILocalizationService ────────────────────────────────────────────── public Language CurrentLanguage => _currentLanguage; /// 切换游戏语言并通知所有订阅者刷新文本。 public void SetLanguage(Language language) { if (_currentLanguage == language) return; _currentLanguage = language; OnLanguageChanged?.Invoke(language); } /// /// 获取本地化字符串(实例方法)。 /// 查找顺序:当前语言 → 回退语言(English)→ 直接返回 key。 /// public string Get(string key, string table = "UI") { if (string.IsNullOrEmpty(key)) return string.Empty; // 1. 尝试当前语言 if (TryGetFromTable(_currentLanguage, table, key, out string text)) return text; // 2. 回退到 English if (_currentLanguage != _fallbackLanguage && TryGetFromTable(_fallbackLanguage, table, key, out text)) return text; // 3. 最终回退:原始 key return key; } // ── ISaveable ───────────────────────────────────────────────────────── public void OnSave(SaveData data) { if (data?.Settings == null) return; data.Settings.Language = _currentLanguage.ToString(); } public void OnLoad(SaveData data) { if (data?.Settings == null || string.IsNullOrEmpty(data.Settings.Language)) return; if (Enum.TryParse(data.Settings.Language, out var lang)) SetLanguage(lang); } // ── 静态 Facade(保持现有调用方不变)──────────────────────────────────── /// /// 静态快捷获取本地化字符串。委托给 ILocalizationService 实例;服务未注册时直接返回 key。 /// public static string Get(string key, string table = "UI") => ServiceLocator.GetOrDefault()?.Get(key, table) ?? key; // ── 内部 ───────────────────────────────────────────────────────────── private bool TryGetFromTable(Language language, string table, string key, out string value) { var cacheKey = $"{language}/{table}"; if (!_cache.TryGetValue(cacheKey, out var dict)) { dict = LoadTable(language, table); _cache[cacheKey] = dict; // 即使加载失败也存入空字典,避免每帧重试 } return dict != null && dict.TryGetValue(key, out value); } /// /// 从 Resources/Localization/{language}/{table}.json 加载字符串表。 /// 返回 null 表示文件不存在。 /// private static Dictionary LoadTable(Language language, string table) { string path = $"Localization/{language}/{table}"; var asset = Resources.Load(path); if (asset == null) return null; var parsed = JsonUtility.FromJson(asset.text); if (parsed?.entries == null) return null; var dict = new Dictionary(parsed.entries.Count, StringComparer.Ordinal); foreach (var entry in parsed.entries) if (!string.IsNullOrEmpty(entry.key)) dict[entry.key] = entry.value ?? string.Empty; return dict; } // ── 序列化辅助类型 ──────────────────────────────────────────────────── [Serializable] private class StringTableJson { public List entries; } [Serializable] private class StringEntry { public string key; public string value; } } }