using TMPro;
using UnityEngine;
using UnityEngine.Localization;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
#endif
namespace Ichni.Localization
{
///
/// 将静态 与 Unity Localization 的
/// 绑定。
///
/// 组件启用时订阅 。因此切换 Locale,
/// 或在 Play Mode 中修改 Inspector 里的 String Reference 后,TMP 文本会自动更新。
///
///
/// Inspector 自带的 String Reference 可预览各 Locale 的文本。Edit Mode 中变更引用时,
/// 本组件只将项目默认 Locale 的文本同步到 TMP_Text,便于直接预览;不保存回退文本,
/// 也不提供额外的编辑器自检功能。
///
///
[DisallowMultipleComponent]
[RequireComponent(typeof(TMP_Text))]
[AddComponentMenu("Localization/Localized TMP Text")]
public sealed class LocalizedTMPText : MonoBehaviour
{
[SerializeField]
[Tooltip("要更新的 TMP 文本。留空时会自动使用当前 GameObject 上的 TMP_Text。")]
private TMP_Text targetText;
[SerializeField]
[Tooltip("在 Inspector 中选择 String Table Collection 和对应的 Entry。")]
private LocalizedString stringReference = new LocalizedString();
private bool _isListening;
/// 当前绑定的 TMP 文本。
public TMP_Text TargetText => targetText;
/// 当前使用的 Unity Localization 字符串引用。
public LocalizedString StringReference => stringReference;
///
/// 在运行时替换当前文本使用的 String Reference。
///
/// 该接口主要供动态生成的 UI 使用,例如 Dropdown 的选项 Item。替换前会先解除旧引用的
/// 订阅,再为新引用建立订阅,因此不会残留
/// 旧语言表的回调,也不会重复监听。
///
///
public void SetStringReference(LocalizedString reference)
{
StopListening();
stringReference = reference ?? new LocalizedString();
EnsureTargetText();
if (isActiveAndEnabled)
{
StartListening();
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
ApplyEditorPreviewText();
}
#endif
}
private void Awake()
{
EnsureTargetText();
}
private void OnValidate()
{
EnsureTargetText();
if (Application.isPlaying)
{
// Play Mode 中通过 Inspector 更换 Table 或 Entry 后立即刷新。
if (_isListening)
{
stringReference?.RefreshString();
}
return;
}
#if UNITY_EDITOR
ApplyEditorPreviewText();
#endif
}
private void OnEnable()
{
EnsureTargetText();
StartListening();
}
private void OnDisable()
{
StopListening();
}
private void OnDestroy()
{
StopListening();
}
///
/// 主动请求重新解析当前 String Reference。
/// 通常仅在运行时修改 Smart String 参数后调用;普通语言切换会自动刷新。
///
public void RefreshText()
{
if (_isListening)
{
stringReference.RefreshString();
}
}
private void StopListening()
{
if (!_isListening || stringReference == null)
{
return;
}
stringReference.StringChanged -= ApplyText;
_isListening = false;
}
///
/// 为当前 String Reference 建立语言变化监听。
/// Unity Localization 会在首次订阅时请求当前 Locale 的文本;这里不使用
/// WaitForCompletion,以避免阻塞主线程或干扰 Addressables 队列。
///
private void StartListening()
{
if (_isListening || stringReference == null || stringReference.IsEmpty)
{
return;
}
stringReference.StringChanged += ApplyText;
_isListening = true;
}
private void EnsureTargetText()
{
if (targetText == null)
{
targetText = GetComponent();
}
}
private void ApplyText(string localizedText)
{
EnsureTargetText();
if (targetText != null)
{
targetText.text = localizedText ?? string.Empty;
}
}
#if UNITY_EDITOR
///
/// 在编辑器中将项目默认 Locale 的已导入文本同步到 TMP_Text。
/// 这里直接读取 String Table 资产,不调用异步 Localization API,因而不会加载 Addressables。
///
private void ApplyEditorPreviewText()
{
if (targetText == null || stringReference == null || stringReference.IsEmpty)
{
return;
}
Locale projectLocale = LocalizationSettings.ProjectLocale;
StringTableCollection tableCollection =
LocalizationEditorSettings.GetStringTableCollection(stringReference.TableReference);
StringTable table = projectLocale == null
? null
: tableCollection?.GetTable(projectLocale.Identifier) as StringTable;
StringTableEntry entry = table?.GetEntryFromReference(stringReference.TableEntryReference);
if (entry == null)
{
return;
}
string previewText = entry.LocalizedValue ?? string.Empty;
if (targetText.text == previewText)
{
return;
}
targetText.text = previewText;
EditorUtility.SetDirty(targetText);
}
#endif
}
}