145 lines
6.0 KiB
C#
145 lines
6.0 KiB
C#
using UnityEngine;
|
||
using TMPro;
|
||
using BaseGames.Core;
|
||
|
||
namespace BaseGames.Localization
|
||
{
|
||
/// <summary>
|
||
/// UI 文本本地化自动绑定组件。
|
||
/// 挂载在含 <see cref="TMP_Text"/> 的 GameObject 上,语言切换时自动刷新文本内容。
|
||
///
|
||
/// 用法:
|
||
/// 1. 挂上此组件,填写 <see cref="key"/> 和 <see cref="table"/>(默认 UI 表)。
|
||
/// 2. 运行时 <see cref="ILocalizationService.OnLanguageChanged"/> 触发时自动刷新。
|
||
/// 3. 格式化参数在运行时通过 <see cref="SetFormatArgs"/> 传入后即时更新显示。
|
||
/// 4. (可选)绑定 <see cref="LanguageFontConfigSO"/>,语言切换时自动替换 TMP 字体。
|
||
///
|
||
/// 编辑器预览:Inspector 内实时显示当前 key 对应的本地化文本(使用简体中文表)。
|
||
/// </summary>
|
||
[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<TMP_Text>();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
_svc = ServiceLocator.GetOrDefault<ILocalizationService>();
|
||
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 ──────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 动态更改本地化 Key 并立即刷新文本。
|
||
/// 适用于同一 UI 控件在不同状态下显示不同字段的情况。
|
||
/// </summary>
|
||
public void SetKey(string key, string table = null)
|
||
{
|
||
_key = key;
|
||
if (table != null) _table = table;
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置格式化参数并立即刷新文本。
|
||
/// 本地化字符串中使用标准 {0}、{1}…占位符,例如:
|
||
/// "获得 {0} 灵珠" → <c>SetFormatArgs(amount)</c>
|
||
/// </summary>
|
||
public void SetFormatArgs(params object[] args)
|
||
{
|
||
_formatArgs = args;
|
||
Refresh();
|
||
}
|
||
|
||
/// <summary>强制立即刷新文本(语言切换后由组件自动调用,通常无需手动调用)。</summary>
|
||
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<TMP_Text>();
|
||
if (_label == null || string.IsNullOrEmpty(_key)) return;
|
||
string preview = LocalizationManager.GetEditorPreview(_key, _table);
|
||
// 未找到时显示 key 本身,方便策划确认是否拼写正确
|
||
_label.text = preview ?? $"[{_key}]";
|
||
}
|
||
#endif
|
||
}
|
||
}
|