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
}
}