using System; using System.Collections.Generic; using Newtonsoft.Json; using TMPro; using UnityEngine; using UnityEngine.UI; #if UNITY_EDITOR using UnityEditor; #endif namespace AppUI.Localization { /// /// AppUI 独立多语言表。查词时优先读本表,缺失则回退 。 /// 旧 BowArrow 资源与逻辑不做任何修改。 /// public static class AppUILocalization { const string ResourcesPrefix = "AppUILocalization/"; static LanguageEnum currentLanguageEnum = LanguageEnum.Chinese; static Dictionary languageDictionary; static bool inited; public static event Action OnLanguageChanged; public static void Init() { if (inited && languageDictionary != null) return; int id = PlayerPrefs.GetInt("Language", 0); LoadLanguage((LanguageEnum)id, notifyListeners: false); } /// 强制按 PlayerPrefs 当前语言重载 json(Editor 预览改 json 后点刷新用)。 public static void Reload() { inited = false; languageDictionary = null; int id = PlayerPrefs.GetInt("Language", 0); LoadLanguage((LanguageEnum)id, notifyListeners: false); } /// /// 与旧系统同步切换语言。应在设置页等入口调用,保证 TextAutoLanguage / TextAutoLanguage2 / ImageAutoLanguage 一并更新。 /// public static void ChangeAllLanguages(LanguageEnum languageEnum) { TextAutoLanguage.ChangeLanguage(languageEnum); TextAutoLanguage2.ChangeLanguage(languageEnum); ImageAutoLanguage.ChangeLanguage(languageEnum); ChangeLanguage(languageEnum); } /// 仅加载 AppUI 表并刷新 组件。 public static void ChangeLanguage(LanguageEnum languageEnum) { LoadLanguage(languageEnum, notifyListeners: true); } static void LoadLanguage(LanguageEnum languageEnum, bool notifyListeners) { string fileName = ResolveFileName(languageEnum); languageDictionary = LoadDictionary(fileName); currentLanguageEnum = languageEnum; inited = true; if (!notifyListeners) return; foreach (var item in AppUITextAutoLanguage.RegisteredComponents) { try { item.ApplyToText(); } catch (Exception e) { Debug.LogError(e.Message); } } OnLanguageChanged?.Invoke(languageEnum); } public static LanguageEnum GetLanguage() { Init(); return currentLanguageEnum; } public static string GetTextByKey(string textKey) { return ResolveText(textKey); } /// /// 查词后按 可用宽度插入 \u200B,便于窄容器内逐字/逐字母换行。 /// / 为 0 时使用 的 RectTransform 尺寸。 /// public static string GetTextByKey(string textKey, TMP_Text tmpText, float maxWidth = 0f, float maxHeight = 0f, params object[] formatArgs) { return GetTextWithWidthBreak(ResolveText(textKey), tmpText, null, maxWidth, maxHeight, formatArgs); } /// Legacy 版本。 public static string GetTextByKey(string textKey, Text legacyText, float maxWidth = 0f, float maxHeight = 0f, params object[] formatArgs) { return GetTextWithWidthBreak(ResolveText(textKey), null, legacyText, maxWidth, maxHeight, formatArgs); } /// 显式指定布局矩形与 TMP 组件。 public static string GetTextByKey(string textKey, RectTransform rect, TMP_Text tmpText, float maxWidth = 0f, float maxHeight = 0f, params object[] formatArgs) { string text = FormatResolvedText(ResolveText(textKey), formatArgs); if (tmpText == null || string.IsNullOrEmpty(text)) return text; maxWidth = AppUILocalizationTextBreak.ResolveMaxWidth(rect != null ? rect : tmpText.rectTransform, maxWidth); maxHeight = AppUILocalizationTextBreak.ResolveMaxHeight(rect != null ? rect : tmpText.rectTransform, maxHeight); return AppUILocalizationTextBreak.InsertWidthBreaks(text, tmpText, maxWidth, maxHeight); } /// 对已解析文案按宽度插入 \u200B public static string InsertWidthBreaks(string text, TMP_Text tmpText, float maxWidth = 0f, float maxHeight = 0f) { return AppUILocalizationTextBreak.InsertWidthBreaks(text, tmpText, maxWidth, maxHeight); } /// Legacy 版本。 public static string InsertWidthBreaks(string text, Text legacyText, float maxWidth = 0f, float maxHeight = 0f) { return AppUILocalizationTextBreak.InsertWidthBreaks(text, legacyText, maxWidth, maxHeight); } static string GetTextWithWidthBreak(string text, TMP_Text tmpText, Text legacyText, float maxWidth, float maxHeight, object[] formatArgs) { text = FormatResolvedText(text, formatArgs); if (string.IsNullOrEmpty(text)) return text; if (tmpText != null) return AppUILocalizationTextBreak.InsertWidthBreaks(text, tmpText, maxWidth, maxHeight); if (legacyText != null) return AppUILocalizationTextBreak.InsertWidthBreaks(text, legacyText, maxWidth, maxHeight); return text; } static string FormatResolvedText(string text, object[] formatArgs) { if (string.IsNullOrEmpty(text)) return text; if (formatArgs != null && formatArgs.Length > 0) return string.Format(text, formatArgs); return text; } public static string GetTextByCNKey(string textKey) { Init(); if (currentLanguageEnum == LanguageEnum.Chinese) return textKey; if (TryGetLocal(textKey, out string localText)) return localText; return TextAutoLanguage2.GetTextByCNKey(textKey); } /// 新表优先,旧表回退;两边都没有时返回 key 并打 Warning。 public static string ResolveText(string textKey) { Init(); if (TryGetLocal(textKey, out string localText)) return localText; try { return TextAutoLanguage2.GetTextByKey(textKey); } catch (KeyNotFoundException) { Debug.LogWarning($"[AppUILocalization] Missing key: {textKey}"); return textKey; } } static bool TryGetLocal(string textKey, out string text) { text = null; if (languageDictionary == null || string.IsNullOrEmpty(textKey)) return false; return languageDictionary.TryGetValue(textKey, out text) && text != null; } static string ResolveFileName(LanguageEnum languageEnum) { if (languageEnum == LanguageEnum.English) return "en"; if (languageEnum == LanguageEnum.Japan) return "Japan"; return "cn"; } static Dictionary LoadDictionary(string fileName) { TextAsset asset = null; #if UNITY_EDITOR if (!Application.isPlaying) { asset = AssetDatabase.LoadAssetAtPath( $"Assets/AppUI/Resources/AppUILocalization/{fileName}.json"); } #endif if (asset == null) asset = Resources.Load(ResourcesPrefix + fileName); if (asset == null) { Debug.LogError($"[AppUILocalization] Missing Resources/{ResourcesPrefix}{fileName}.json"); return new Dictionary(); } return JsonConvert.DeserializeObject>(asset.text) ?? new Dictionary(); } } }