Files
ichni_Official/Assets/Scripts/Localization/LocalizedTMPText.cs
SoulliesOfficial a34461d31f 阶段性完成
2026-07-25 13:27:53 -04:00

209 lines
6.4 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 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
{
/// <summary>
/// 将静态 <see cref="TMP_Text"/> 与 Unity Localization 的
/// <see cref="LocalizedString"/> 绑定。
/// <para>
/// 组件启用时订阅 <see cref="LocalizedString.StringChanged"/>。因此切换 Locale
/// 或在 Play Mode 中修改 Inspector 里的 String Reference 后TMP 文本会自动更新。
/// </para>
/// <para>
/// Inspector 自带的 String Reference 可预览各 Locale 的文本。Edit Mode 中变更引用时,
/// 本组件只将项目默认 Locale 的文本同步到 TMP_Text便于直接预览不保存回退文本
/// 也不提供额外的编辑器自检功能。
/// </para>
/// </summary>
[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;
/// <summary>当前绑定的 TMP 文本。</summary>
public TMP_Text TargetText => targetText;
/// <summary>当前使用的 Unity Localization 字符串引用。</summary>
public LocalizedString StringReference => stringReference;
/// <summary>
/// 在运行时替换当前文本使用的 String Reference。
/// <para>
/// 该接口主要供动态生成的 UI 使用,例如 Dropdown 的选项 Item。替换前会先解除旧引用的
/// <see cref="LocalizedString.StringChanged"/> 订阅,再为新引用建立订阅,因此不会残留
/// 旧语言表的回调,也不会重复监听。
/// </para>
/// </summary>
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();
}
/// <summary>
/// 主动请求重新解析当前 String Reference。
/// 通常仅在运行时修改 Smart String 参数后调用;普通语言切换会自动刷新。
/// </summary>
public void RefreshText()
{
if (_isListening)
{
stringReference.RefreshString();
}
}
private void StopListening()
{
if (!_isListening || stringReference == null)
{
return;
}
stringReference.StringChanged -= ApplyText;
_isListening = false;
}
/// <summary>
/// 为当前 String Reference 建立语言变化监听。
/// Unity Localization 会在首次订阅时请求当前 Locale 的文本;这里不使用
/// <c>WaitForCompletion</c>,以避免阻塞主线程或干扰 Addressables 队列。
/// </summary>
private void StartListening()
{
if (_isListening || stringReference == null || stringReference.IsEmpty)
{
return;
}
stringReference.StringChanged += ApplyText;
_isListening = true;
}
private void EnsureTargetText()
{
if (targetText == null)
{
targetText = GetComponent<TMP_Text>();
}
}
private void ApplyText(string localizedText)
{
EnsureTargetText();
if (targetText != null)
{
targetText.text = localizedText ?? string.Empty;
}
}
#if UNITY_EDITOR
/// <summary>
/// 在编辑器中将项目默认 Locale 的已导入文本同步到 TMP_Text。
/// 这里直接读取 String Table 资产,不调用异步 Localization API因而不会加载 Addressables。
/// </summary>
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
}
}