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();
}
}
}