using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
///
/// 文本组件类型枚举
///
public enum TextComponentType
{
None = 0,
Text = 1,
TextEx = 2,
GradientText = 3
}
///
/// 多语言排版配置项,存储单个语言的布局和适配数据
///
[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;
/// 将配置应用到 RectTransform
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);
}
/// 从 RectTransform 读取配置
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;
}
/// 将配置应用到 Text
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;
}
/// 从 Text 读取配置
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;
}
///
/// 快速克隆配置
/// 由于内部均为值类型或需浅拷贝的引用(Font),可以直接使用 MemberwiseClone
///
public LanguageConfigItem Clone()
{
return (LanguageConfigItem)this.MemberwiseClone();
}
}
///
/// 语言配置字典(Unity 内置序列化支持的双 List 结构)
///
[Serializable]
public class LanguageConfigDictionary
{
public List keys = new List();
public List values = new List();
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);
}
}
}
///
/// 文本多语言排版适配组件
///
[RequireComponent(typeof(RectTransform))]
public class TextLanguageAdapter : MonoBehaviour
{
public const string DefaultLangId = "default"; // 提取常量避免硬编码错误
[SerializeField]
[Tooltip("每种语言的配置,使用语言ID作为key(如zh、en、ft),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 GetConfiguredLanguages() => new List(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()
?? FindComponent()
?? (Component)FindComponent();
DetermineTextType(m_TargetTextComponent);
}
/// 辅助方法:在自身和子物体中查找组件
private T FindComponent() where T : Component
{
T comp = GetComponent();
return comp != null ? comp : GetComponentInChildren(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
}