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