From be3c8bf7cbef98b3ada4067954ad4286730c727f Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期六, 09 五月 2026 20:58:49 +0800
Subject: [PATCH] 592 多语言适配 提交主干

---
 Main/Component/UI/Core/TextLanguageAdapter.cs       |   41 ++++++
 Main/Component/UI/Core/ImageLanguageAdapter.cs.meta |   11 +
 Main/Component/UI/Core/ImageLanguageAdapter.cs      |  279 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 331 insertions(+), 0 deletions(-)

diff --git a/Main/Component/UI/Core/ImageLanguageAdapter.cs b/Main/Component/UI/Core/ImageLanguageAdapter.cs
new file mode 100644
index 0000000..be81e62
--- /dev/null
+++ b/Main/Component/UI/Core/ImageLanguageAdapter.cs
@@ -0,0 +1,279 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+/// <summary>
+/// 鍥剧墖缁勪欢绫诲瀷鏋氫妇
+/// </summary>
+public enum ImageComponentType
+{
+    None = 0,
+    Image = 1,
+    ImageEx = 2
+}
+
+/// <summary>
+/// 鍗曚釜璇█鐨勫浘鐗囨帓鐗堝拰閫傞厤鏁版嵁鑺傜偣
+/// </summary>
+[Serializable]
+public class ImageLanguageConfigItem
+{
+    [Header("RectTransform 閰嶇疆")]
+    public Vector2 anchoredPosition = Vector2.zero;
+    public Vector2 sizeDelta = new Vector2(100f, 100f);
+    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;
+
+    [Header("Image 閰嶇疆")]
+    public bool enabled = true;
+    public Color color = Color.white;
+    public Image.Type type = Image.Type.Simple;
+    public bool fillCenter = true;
+    public Image.FillMethod fillMethod = Image.FillMethod.Horizontal;
+    public float fillAmount = 1f;
+    public int fillOrigin = 0;
+    public bool preserveAspect = true;
+    public float alphaHitTestMinimumThreshold = 0f;
+    public bool useSpriteMesh = false;
+    public float pixelsPerUnitMultiplier = 1f;
+
+    public void ApplyToRectTransform(RectTransform rt)
+    {
+        if (rt == null) return;
+        rt.anchorMin = anchorMin;
+        rt.anchorMax = anchorMax;
+        rt.pivot = pivot;
+        rt.anchoredPosition = anchoredPosition;
+        rt.sizeDelta = sizeDelta;
+        rt.localScale = localScale;
+        rt.localRotation = Quaternion.Euler(localRotation);
+    }
+
+    public void ReadFromRectTransform(RectTransform rt)
+    {
+        if (rt == null) return;
+        anchorMin = rt.anchorMin;
+        anchorMax = rt.anchorMax;
+        pivot = rt.pivot;
+        anchoredPosition = rt.anchoredPosition;
+        sizeDelta = rt.sizeDelta;
+        localScale = rt.localScale;
+        localRotation = rt.localRotation.eulerAngles;
+    }
+
+    public void ApplyToImage(Image img)
+    {
+        if (img == null) return;
+        img.color = color;
+        img.type = type;
+        img.fillCenter = fillCenter;
+        img.fillMethod = fillMethod;
+        img.fillAmount = fillAmount;
+        img.fillOrigin = fillOrigin;
+        img.preserveAspect = preserveAspect;
+        img.alphaHitTestMinimumThreshold = alphaHitTestMinimumThreshold;
+        img.useSpriteMesh = useSpriteMesh;
+        img.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
+    }
+
+    public void ReadFromImage(Image img)
+    {
+        if (img == null) return;
+        color = img.color;
+        type = img.type;
+        fillCenter = img.fillCenter;
+        fillMethod = img.fillMethod;
+        fillAmount = img.fillAmount;
+        fillOrigin = img.fillOrigin;
+        preserveAspect = img.preserveAspect;
+        alphaHitTestMinimumThreshold = img.alphaHitTestMinimumThreshold;
+        useSpriteMesh = img.useSpriteMesh;
+        pixelsPerUnitMultiplier = img.pixelsPerUnitMultiplier;
+    }
+
+    public ImageLanguageConfigItem Clone() => (ImageLanguageConfigItem)MemberwiseClone();
+}
+
+/// <summary>
+/// 鏀寔 Unity 搴忓垪鍖栫殑瀛楀吀
+/// </summary>
+[Serializable]
+public class ImageLanguageConfigDictionary
+{
+    public List<string> keys = new List<string>();
+    public List<ImageLanguageConfigItem> values = new List<ImageLanguageConfigItem>();
+
+    public ImageLanguageConfigItem Get(string key)
+    {
+        if (string.IsNullOrEmpty(key)) return null;
+        int index = keys.IndexOf(key);
+        return index >= 0 ? values[index] : null;
+    }
+
+    public void Set(string key, ImageLanguageConfigItem value)
+    {
+        int index = keys.IndexOf(key);
+        if (index >= 0) 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);
+            values.RemoveAt(index);
+        }
+    }
+}
+
+/// <summary>
+/// 澶氳瑷�鍥剧墖鎺掔増閫傞厤鍣�
+/// </summary>
+[RequireComponent(typeof(RectTransform))]
+public class ImageLanguageAdapter : MonoBehaviour
+{
+    public const string DefaultLangId = "default";
+
+    [SerializeField, Tooltip("璇█閰嶇疆瀛楀吀")]
+    private ImageLanguageConfigDictionary m_LanguageConfigs = new ImageLanguageConfigDictionary();
+
+    [SerializeField, Tooltip("鐩爣鍥剧墖缁勪欢绫诲瀷")]
+    private ImageComponentType m_TargetImageType = ImageComponentType.None;
+
+    [SerializeField, Tooltip("鍏宠仈鐨勫浘鐗囩粍浠跺紩鐢�")]
+    private Component m_TargetImageComponent;
+
+    private bool m_IsApplied = false;
+
+    public ImageComponentType TargetImageType
+    {
+        get => m_TargetImageType;
+        set => m_TargetImageType = value;
+    }
+    public Component TargetImageComponent
+    {
+        get => m_TargetImageComponent;
+        set => m_TargetImageComponent = value;
+    }
+    public ImageLanguageConfigDictionary LanguageConfigs => m_LanguageConfigs;
+
+    private void Awake() => DetectTargetComponent();
+
+    private void OnEnable()
+    {
+        if (!Application.isPlaying || !m_IsApplied)
+        {
+            string langId = Application.isPlaying ? Language.Id : DefaultLangId;
+            ApplyConfig(langId);
+        }
+    }
+
+#if UNITY_EDITOR
+    private void Reset()
+    {
+        DetectTargetComponent();
+        if (!HasConfig(DefaultLangId)) ReadCurrentToConfig(DefaultLangId);
+    }
+#endif
+
+    public ImageLanguageConfigItem GetConfig(string languageId) => m_LanguageConfigs.Get(languageId);
+    public void SetConfig(string languageId, ImageLanguageConfigItem 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)
+    {
+        var config = GetConfig(languageId);
+        if (config == null) return;
+
+        config.ApplyToRectTransform(GetComponent<RectTransform>());
+
+        if (m_TargetImageComponent != null)
+        {
+            m_TargetImageComponent.gameObject.SetActive(config.enabled);
+        }
+
+        if (m_TargetImageComponent is Image img)
+        {
+            config.ApplyToImage(img);
+        }
+
+        m_IsApplied = true;
+    }
+
+    public void ReadCurrentToConfig(string languageId)
+    {
+        var config = new ImageLanguageConfigItem();
+        config.ReadFromRectTransform(GetComponent<RectTransform>());
+
+        if (m_TargetImageComponent is Image img)
+        {
+            config.ReadFromImage(img);
+        }
+
+        SetConfig(languageId, config);
+    }
+
+    public static string GetLanguageShowName(string languageId)
+    {
+        if (Language.languageShowDict != null && Language.languageShowDict.TryGetValue(languageId, out string showName))
+            return showName;
+        return languageId;
+    }
+
+    private void DetectTargetComponent()
+    {
+        if (m_TargetImageComponent != null)
+        {
+            DetermineImageType(m_TargetImageComponent);
+            return;
+        }
+
+        m_TargetImageComponent = GetComponent<ImageEx>() ?? GetComponentInChildren<ImageEx>(true) as Component
+                                ?? GetComponent<Image>() ?? GetComponentInChildren<Image>(true) as Component;
+
+        DetermineImageType(m_TargetImageComponent);
+    }
+
+    private void DetermineImageType(Component component)
+    {
+        m_TargetImageType = component switch
+        {
+            ImageEx _ => ImageComponentType.ImageEx,
+            Image _ => ImageComponentType.Image,
+            _ => ImageComponentType.None
+        };
+    }
+
+#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/ImageLanguageAdapter.cs.meta b/Main/Component/UI/Core/ImageLanguageAdapter.cs.meta
new file mode 100644
index 0000000..080d21a
--- /dev/null
+++ b/Main/Component/UI/Core/ImageLanguageAdapter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3ca83c0f98acaca459051c33bb4270dd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/Component/UI/Core/TextLanguageAdapter.cs b/Main/Component/UI/Core/TextLanguageAdapter.cs
index 15f14aa..fea97e7 100644
--- a/Main/Component/UI/Core/TextLanguageAdapter.cs
+++ b/Main/Component/UI/Core/TextLanguageAdapter.cs
@@ -156,6 +156,8 @@
 
     private bool m_IsApplied = false;
 
+    private void Awake() => DetectTargetComponent();
+
     // 寮�鏀� set 鏉冮檺锛屽厑璁稿閮ㄦ垨缂栬緫鍣ㄦ墜鍔ㄨ祴鍊�
     public TextComponentType TargetTextType 
     { 
@@ -186,6 +188,32 @@
     public bool HasConfig(string languageId) => m_LanguageConfigs.ContainsKey(languageId);
     public List<string> GetConfiguredLanguages() => new List<string>(m_LanguageConfigs.keys);
 
+    private void DetectTargetComponent()
+    {
+        if (m_TargetTextComponent != null)
+        {
+            DetermineTextType(m_TargetTextComponent);
+            return;
+        }
+
+        m_TargetTextComponent = GetComponent<TextEx>() ?? GetComponentInChildren<TextEx>(true) as Component
+                                ?? GetComponent<GradientText>() ?? GetComponentInChildren<GradientText>(true) as Component
+                                ?? GetComponent<Text>() ?? GetComponentInChildren<Text>(true) as Component;
+
+        DetermineTextType(m_TargetTextComponent);
+    }
+
+    private void DetermineTextType(Component component)
+    {
+        m_TargetTextType = component switch
+        {
+            TextEx _ => TextComponentType.TextEx,
+            GradientText _ => TextComponentType.GradientText,
+            Text _ => TextComponentType.Text,
+            _ => TextComponentType.None
+        };
+    }
+
     public void ApplyConfig(string languageId)
     {
         var config = GetConfig(languageId);
@@ -214,6 +242,19 @@
     }
 
 #if UNITY_EDITOR
+    private void Reset()
+    {
+        DetectTargetComponent();
+        if (!HasConfig(DefaultLangId)) ReadCurrentToConfig(DefaultLangId);
+    }
+
+    [ContextMenu("鍒锋柊缁勪欢妫�娴�")]
+    public void Editor_ForceRefreshDetection()
+    {
+        DetectTargetComponent();
+        UnityEditor.EditorUtility.SetDirty(this);
+    }
+
     [ContextMenu("璇诲彇褰撳墠閰嶇疆")]
     public void Editor_ReadCurrentConfig()
     {

--
Gitblit v1.8.0