From dd87bce5f1b7024ffbe2cdc1d03ca41ac2b30095 Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期四, 02 四月 2026 15:22:23 +0800
Subject: [PATCH] 592 多语言适配

---
 Main/Component/UI/Core/TextLanguageAdapter.cs      |  339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Main/Component/UI/Core/TextLanguageAdapter.cs.meta |   11 +
 2 files changed, 350 insertions(+), 0 deletions(-)

diff --git a/Main/Component/UI/Core/TextLanguageAdapter.cs b/Main/Component/UI/Core/TextLanguageAdapter.cs
new file mode 100644
index 0000000..027e39a
--- /dev/null
+++ b/Main/Component/UI/Core/TextLanguageAdapter.cs
@@ -0,0 +1,339 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+/// <summary>
+/// 鏂囨湰缁勪欢绫诲瀷鏋氫妇
+/// </summary>
+public enum TextComponentType
+{
+    None = 0,
+    Text = 1,
+    TextEx = 2,
+    GradientText = 3
+}
+
+/// <summary>
+/// 澶氳瑷�鎺掔増閰嶇疆椤癸紝瀛樺偍鍗曚釜璇█鐨勫竷灞�鍜岄�傞厤鏁版嵁
+/// </summary>
+[Serializable]
+public class LanguageConfigItem
+{
+    // ============ RectTransform 閰嶇疆 ============
+    [Header("RectTransform 閰嶇疆")]
+    public Vector2 anchoredPosition = Vector2.zero;
+    public Vector2 sizeDelta = new Vector2(100f, 30f);
+    public Vector2 anchorMin = new Vector2(0.5f, 0.5f);
+    public Vector2 anchorMax = new Vector2(0.5f, 0.5f);
+    public Vector2 pivot = new Vector2(0.5f, 0.5f);
+    public Vector3 localScale = Vector3.one;
+    public Vector3 localRotation = Vector3.zero;
+
+    // ============ Text 鎺掔増涓庨�傞厤閰嶇疆 ============
+    [Header("Text 鎺掔増涓庨�傞厤閰嶇疆")]
+    public Font font;
+    public FontStyle fontStyle = FontStyle.Normal;
+    public int fontSize = 14;
+    public float lineSpacing = 1f;
+
+    public HorizontalWrapMode horizontalOverflow = HorizontalWrapMode.Wrap;
+    public VerticalWrapMode verticalOverflow = VerticalWrapMode.Truncate;
+
+    public bool resizeTextForBestFit = false;
+    public int resizeTextMinSize = 10;
+    public int resizeTextMaxSize = 40;
+
+    public TextAnchor alignment = TextAnchor.UpperLeft;
+    public bool alignByGeometry = false;
+
+    /// <summary> 灏嗛厤缃簲鐢ㄥ埌 RectTransform </summary>
+    public void ApplyToRectTransform(RectTransform rectTransform)
+    {
+        if (rectTransform == null) return;
+        rectTransform.anchorMin = anchorMin;
+        rectTransform.anchorMax = anchorMax;
+        rectTransform.pivot = pivot;
+        rectTransform.anchoredPosition = anchoredPosition;
+        rectTransform.sizeDelta = sizeDelta;
+        rectTransform.localScale = localScale;
+        rectTransform.localRotation = Quaternion.Euler(localRotation);
+    }
+
+    /// <summary> 浠� RectTransform 璇诲彇閰嶇疆 </summary>
+    public void ReadFromRectTransform(RectTransform rectTransform)
+    {
+        if (rectTransform == null) return;
+        anchorMin = rectTransform.anchorMin;
+        anchorMax = rectTransform.anchorMax;
+        pivot = rectTransform.pivot;
+        anchoredPosition = rectTransform.anchoredPosition;
+        sizeDelta = rectTransform.sizeDelta;
+        localScale = rectTransform.localScale;
+        localRotation = rectTransform.localRotation.eulerAngles;
+    }
+
+    /// <summary> 灏嗛厤缃簲鐢ㄥ埌 Text </summary>
+    public void ApplyToText(Text textComponent)
+    {
+        if (textComponent == null) return;
+        if (font != null) textComponent.font = font;
+        
+        textComponent.fontStyle = fontStyle;
+        textComponent.fontSize = fontSize;
+        textComponent.lineSpacing = lineSpacing;
+        textComponent.horizontalOverflow = horizontalOverflow;
+        textComponent.verticalOverflow = verticalOverflow;
+        textComponent.resizeTextForBestFit = resizeTextForBestFit;
+
+        if (resizeTextForBestFit)
+        {
+            textComponent.resizeTextMinSize = resizeTextMinSize;
+            textComponent.resizeTextMaxSize = resizeTextMaxSize;
+        }
+
+        textComponent.alignment = alignment;
+        textComponent.alignByGeometry = alignByGeometry;
+    }
+
+    /// <summary> 浠� Text 璇诲彇閰嶇疆 </summary>
+    public void ReadFromText(Text textComponent)
+    {
+        if (textComponent == null) return;
+        font = textComponent.font;
+        fontStyle = textComponent.fontStyle;
+        fontSize = textComponent.fontSize;
+        lineSpacing = textComponent.lineSpacing;
+        horizontalOverflow = textComponent.horizontalOverflow;
+        verticalOverflow = textComponent.verticalOverflow;
+        resizeTextForBestFit = textComponent.resizeTextForBestFit;
+        resizeTextMinSize = textComponent.resizeTextMinSize;
+        resizeTextMaxSize = textComponent.resizeTextMaxSize;
+        alignment = textComponent.alignment;
+        alignByGeometry = textComponent.alignByGeometry;
+    }
+
+    /// <summary> 
+    /// 蹇�熷厠闅嗛厤缃� 
+    /// 鐢变簬鍐呴儴鍧囦负鍊肩被鍨嬫垨闇�娴呮嫹璐濈殑寮曠敤(Font)锛屽彲浠ョ洿鎺ヤ娇鐢� MemberwiseClone
+    /// </summary>
+    public LanguageConfigItem Clone()
+    {
+        return (LanguageConfigItem)this.MemberwiseClone();
+    }
+}
+
+/// <summary>
+/// 璇█閰嶇疆瀛楀吀锛圲nity 鍐呯疆搴忓垪鍖栨敮鎸佺殑鍙� List 缁撴瀯锛�
+/// </summary>
+[Serializable]
+public class LanguageConfigDictionary
+{
+    public List<string> keys = new List<string>();
+    public List<LanguageConfigItem> values = new List<LanguageConfigItem>();
+
+    public LanguageConfigItem Get(string key)
+    {
+        if (string.IsNullOrEmpty(key)) return null;
+        int index = keys.IndexOf(key);
+        return (index >= 0 && index < values.Count) ? values[index] : null;
+    }
+
+    public void Set(string key, LanguageConfigItem value)
+    {
+        int index = keys.IndexOf(key);
+        if (index >= 0 && index < values.Count)
+        {
+            values[index] = value;
+        }
+        else
+        {
+            keys.Add(key);
+            values.Add(value);
+        }
+    }
+
+    public bool ContainsKey(string key) => keys.Contains(key);
+
+    public void Remove(string key)
+    {
+        int index = keys.IndexOf(key);
+        if (index >= 0)
+        {
+            keys.RemoveAt(index);
+            if (index < values.Count) values.RemoveAt(index);
+        }
+    }
+}
+
+/// <summary>
+/// 鏂囨湰澶氳瑷�鎺掔増閫傞厤缁勪欢
+/// </summary>
+[RequireComponent(typeof(RectTransform))]
+public class TextLanguageAdapter : MonoBehaviour
+{
+    public const string DefaultLangId = "default"; // 鎻愬彇甯搁噺閬垮厤纭紪鐮侀敊璇�
+
+    [SerializeField]
+    [Tooltip("姣忕璇█鐨勯厤缃紝浣跨敤璇█ID浣滀负key锛堝zh銆乪n銆乫t锛夛紝default涓洪粯璁ら厤缃�")]
+    private LanguageConfigDictionary m_LanguageConfigs = new LanguageConfigDictionary();
+
+    [SerializeField]
+    [Tooltip("鑷姩妫�娴嬪埌鐨勬枃鏈粍浠剁被鍨�")]
+    private TextComponentType m_TargetTextType = TextComponentType.None;
+
+    [SerializeField]
+    [Tooltip("鍏宠仈鐨勬枃鏈粍浠跺紩鐢�")]
+    private Component m_TargetTextComponent;
+
+    private bool m_IsApplied = false;
+
+    #region 鍏叡灞炴��
+    public TextComponentType TargetTextType => m_TargetTextType;
+    public Component TargetTextComponent => m_TargetTextComponent; // 寮�鏀剧粰 Editor 浣跨敤锛岄伩鍏嶅弽灏�
+    public LanguageConfigDictionary LanguageConfigs => m_LanguageConfigs;
+    #endregion
+
+    #region Unity鐢熷懡鍛ㄦ湡
+    protected void Awake()
+    {
+        DetectTargetComponent();
+    }
+
+    protected void OnEnable()
+    {
+        // 淇濊瘉浠呭簲鐢ㄤ竴娆℃帓鐗堜互鑺傜渷鎬ц兘锛岄櫎闈炲湪缂栬緫鍣ㄩ潪杩愯妯″紡涓嬮渶瑕佸己鍒�
+        if (!Application.isPlaying || !m_IsApplied)
+        {
+            string langId = Application.isPlaying ? Language.Id : DefaultLangId;
+            ApplyConfig(langId);
+        }
+    }
+
+#if UNITY_EDITOR
+    protected void Reset()
+    {
+        DetectTargetComponent();
+        if (!HasConfig(DefaultLangId))
+        {
+            ReadCurrentToConfig(DefaultLangId);
+        }
+    }
+#endif
+    #endregion
+
+    #region 鍏叡閰嶇疆鏂规硶
+    public LanguageConfigItem GetConfig(string languageId)
+    {
+        // 1. 灏濊瘯鑾峰彇鐩爣璇█閰嶇疆
+        LanguageConfigItem config = m_LanguageConfigs.Get(languageId);
+        if (config != null) return config;
+
+        // 2. 鎵句笉鍒板垯鍥為��鍒伴粯璁ら厤缃�
+        config = m_LanguageConfigs.Get(DefaultLangId);
+        if (config != null) return config;
+
+        Debug.LogError($"[TextLanguageAdapter] 鎵句笉鍒拌瑷� {languageId} 鐨勯厤缃紝涓斾笉瀛樺湪 default 榛樿閰嶇疆锛�");
+        return null;
+    }
+
+    public void SetConfig(string languageId, LanguageConfigItem config) => m_LanguageConfigs.Set(languageId, config);
+    public void RemoveConfig(string languageId) => m_LanguageConfigs.Remove(languageId);
+    public bool HasConfig(string languageId) => m_LanguageConfigs.ContainsKey(languageId);
+    public List<string> GetConfiguredLanguages() => new List<string>(m_LanguageConfigs.keys);
+
+    public void ApplyConfig(string languageId)
+    {
+        LanguageConfigItem config = GetConfig(languageId);
+        if (config == null) return;
+
+        if (TryGetComponent(out RectTransform rectTransform))
+            config.ApplyToRectTransform(rectTransform);
+
+        if (m_TargetTextComponent is Text textComponent)
+            config.ApplyToText(textComponent);
+
+        m_IsApplied = true;
+    }
+
+    public void ReadCurrentToConfig(string languageId)
+    {
+        LanguageConfigItem config = new LanguageConfigItem();
+
+        if (TryGetComponent(out RectTransform rectTransform))
+            config.ReadFromRectTransform(rectTransform);
+
+        if (m_TargetTextComponent is Text textComponent)
+            config.ReadFromText(textComponent);
+
+        SetConfig(languageId, config);
+    }
+
+    public static string GetLanguageShowName(string languageId)
+    {
+        if (Language.languageShowDict != null && Language.languageShowDict.TryGetValue(languageId, out string showName))
+        {
+            return showName;
+        }
+        return languageId;
+    }
+    #endregion
+
+    #region 绉佹湁缁勪欢妫�娴�
+    private void DetectTargetComponent()
+    {
+        if (m_TargetTextComponent != null)
+        {
+            DetermineTextType(m_TargetTextComponent);
+            return;
+        }
+
+        // 鎸変紭鍏堢骇鏌ユ壘缁勪欢
+        m_TargetTextComponent = FindComponent<GradientText>() 
+                             ?? FindComponent<TextEx>() 
+                             ?? (Component)FindComponent<Text>();
+
+        DetermineTextType(m_TargetTextComponent);
+    }
+
+    /// <summary> 杈呭姪鏂规硶锛氬湪鑷韩鍜屽瓙鐗╀綋涓煡鎵剧粍浠� </summary>
+    private T FindComponent<T>() where T : Component
+    {
+        T comp = GetComponent<T>();
+        return comp != null ? comp : GetComponentInChildren<T>(true);
+    }
+
+    private void DetermineTextType(Component component)
+    {
+        m_TargetTextType = component switch
+        {
+            GradientText _ => TextComponentType.GradientText,
+            TextEx _ => TextComponentType.TextEx,
+            Text _ => TextComponentType.Text,
+            _ => TextComponentType.None
+        };
+    }
+    #endregion
+
+#if UNITY_EDITOR
+    [ContextMenu("鍒锋柊缁勪欢妫�娴�")]
+    public void Editor_ForceRefreshDetection()
+    {
+        DetectTargetComponent();
+        UnityEditor.EditorUtility.SetDirty(this);
+    }
+
+    [ContextMenu("璇诲彇褰撳墠閰嶇疆")]
+    public void Editor_ReadCurrentConfig()
+    {
+        ReadCurrentToConfig(DefaultLangId);
+        UnityEditor.EditorUtility.SetDirty(this);
+    }
+
+    [ContextMenu("搴旂敤榛樿閰嶇疆")]
+    public void Editor_ApplyDefaultConfig()
+    {
+        ApplyConfig(DefaultLangId);
+    }
+#endif
+}
\ No newline at end of file
diff --git a/Main/Component/UI/Core/TextLanguageAdapter.cs.meta b/Main/Component/UI/Core/TextLanguageAdapter.cs.meta
new file mode 100644
index 0000000..10a3b48
--- /dev/null
+++ b/Main/Component/UI/Core/TextLanguageAdapter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8cf0191d06fb2924fb79d005580c1918
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

--
Gitblit v1.8.0