Files
zeling_v2/Assets/_Game/Scripts/Localization/LocalizedText.cs
2026-05-25 11:54:37 +08:00

145 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}' OnEnableILocalizationService 尚未注册," +
$"文本将不会随语言切换自动刷新。请确认 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
}
}