using UnityEngine; using TMPro; using BaseGames.Core; namespace BaseGames.Localization { /// /// UI 文本本地化自动绑定组件。 /// 挂载在含 的 GameObject 上,语言切换时自动刷新文本内容。 /// /// 用法: /// 1. 挂上此组件,填写 (默认 UI 表)。 /// 2. 运行时 触发时自动刷新。 /// 3. 格式化参数在运行时通过 传入后即时更新显示。 /// 4. (可选)绑定 ,语言切换时自动替换 TMP 字体。 /// /// 编辑器预览:Inspector 内实时显示当前 key 对应的本地化文本(使用简体中文表)。 /// [RequireComponent(typeof(TMP_Text))] [AddComponentMenu("BaseGames/Localization/Localized Text")] public class LocalizedText : MonoBehaviour { [Tooltip("本地化 Key,如 \"BTN_START\"、\"HUD_HP\"。")] [SerializeField] private string _key; [Tooltip("所属本地化表。使用 LocalizationTable 中的常量,默认 \"UI\"。")] [SerializeField] private string _table = LocalizationTable.UI; [Tooltip("(可选)语言→字体映射表。填写后语言切换时自动替换 TMP 字体,用于 CJK 等需要独立字体的语言。")] [SerializeField] private LanguageFontConfigSO _fontConfig; // 格式化参数(运行时由 SetFormatArgs 设置,空数组 = 无格式化) private object[] _formatArgs; private TMP_Text _label; private ILocalizationService _svc; // ── 生命周期 ────────────────────────────────────────────────────────── private void Awake() { _label = GetComponent(); } private void OnEnable() { _svc = ServiceLocator.GetOrDefault(); if (_svc != null) _svc.OnLanguageChanged += OnLanguageChanged; #if UNITY_EDITOR || DEVELOPMENT_BUILD else Debug.LogWarning( $"[LocalizedText] '{name}' OnEnable:ILocalizationService 尚未注册," + $"文本将不会随语言切换自动刷新。请确认 LocalizationManager 在此对象激活前已完成 Awake。", this); #endif Refresh(); } private void OnDisable() { if (_svc != null) _svc.OnLanguageChanged -= OnLanguageChanged; _svc = null; } // ── 公开 API ────────────────────────────────────────────────────────── /// /// 动态更改本地化 Key 并立即刷新文本。 /// 适用于同一 UI 控件在不同状态下显示不同字段的情况。 /// public void SetKey(string key, string table = null) { _key = key; if (table != null) _table = table; Refresh(); } /// /// 设置格式化参数并立即刷新文本。 /// 本地化字符串中使用标准 {0}、{1}…占位符,例如: /// "获得 {0} 灵珠" → SetFormatArgs(amount) /// public void SetFormatArgs(params object[] args) { _formatArgs = args; Refresh(); } /// 强制立即刷新文本(语言切换后由组件自动调用,通常无需手动调用)。 public void Refresh() { if (_label == null || string.IsNullOrEmpty(_key)) return; ApplyFont(); _label.text = ResolveText(); } // ── 内部 ────────────────────────────────────────────────────────────── private void OnLanguageChanged(Language _) => Refresh(); private string ResolveText() { // 直接使用缓存的 _svc 实例,避免每次调用 ServiceLocator 字典查找(热路径优化) if (_svc != null) { return (_formatArgs != null && _formatArgs.Length > 0) ? _svc.GetFormat(_key, _table, _formatArgs) : _svc.Get(_key, _table); } // 服务未注册时使用静态方法兜底(保证不崩溃) return (_formatArgs != null && _formatArgs.Length > 0) ? LocalizationManager.GetFormat(_key, _table, _formatArgs) : LocalizationManager.Get(_key, _table); } private void ApplyFont() { if (_fontConfig == null || _label == null) return; var lang = _svc?.CurrentLanguage ?? Language.ChineseSimplified; if (!_fontConfig.TryGetFont(lang, out var font, out var mat)) return; if (font != null) _label.font = font; if (mat != null) _label.fontSharedMaterial = mat; } #if UNITY_EDITOR // 编辑器下 key / table 变化时立即预览(无需进入 Play Mode) private void OnValidate() { if (!Application.isPlaying) UpdateEditorPreview(); } public void UpdateEditorPreview() { if (_label == null) _label = GetComponent(); if (_label == null || string.IsNullOrEmpty(_key)) return; string preview = LocalizationManager.GetEditorPreview(_key, _table); // 未找到时显示 key 本身,方便策划确认是否拼写正确 _label.text = preview ?? $"[{_key}]"; } #endif } }