Files
MRCS/Assets/Plugins/XPlugins/Localization/XLocalization/Editor/TextScanner.cs
2025-10-31 12:25:48 +08:00

337 lines
13 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 System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using Object = System.Object;
namespace XLocalization {
public class TextScanner : MonoBehaviour {
[MenuItem("XPlugin/本地化Text/标记 选中文件夹中Prefab的Text")]
static void MarkPrefab() {
MarkTextInPrefab(AssetDatabase.GetAssetPath(Selection.activeObject));
}
[MenuItem("XPlugin/本地化Text/还原 选中文件夹中Prefab的Text")]
static void UnmarkPrefab() {
UnmarkTextInPrefab(AssetDatabase.GetAssetPath(Selection.activeObject));
}
#region $
[MenuItem("XPlugin/本地化Text/为所有script赋值的Text添加$符号")]
private static void AddSymbol()
{
AddSymbolToScriptText(AssetDatabase.GetAssetPath(Selection.activeObject));
}
[MenuItem("XPlugin/本地化Text/移除所有Text的$符号")]
private static void RemoveSymbol()
{
RemoveSymbolToScriptText(AssetDatabase.GetAssetPath(Selection.activeObject));
}
private static void AddSymbolToScriptText(string path)
{
var prefabArray = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);
try
{
for (var i = 0; i < prefabArray.Length; i++)
{
var tmpTexts = new List<Text>();
var tmpOrigin = new List<Text>();
var tmpPrefab =
AssetDatabase.LoadAssetAtPath(prefabArray[i], typeof(GameObject)) as
GameObject;
AddSymbolWithLoop(tmpPrefab, tmpTexts, tmpOrigin);
//遍历原始带$的Text判断是否有被脚本引用
foreach (var origin in tmpOrigin)
{
if (!tmpTexts.Contains(origin))
{
origin.name = origin.name.Replace("$", "");
}
}
EditorUtility.DisplayCancelableProgressBar("添加$符号", $"{tmpPrefab.name}",
(float) i / prefabArray.Length);
//EditorUtility.SetDirty(tmpPrefab);
Debug.Log($"处理了{tmpPrefab.name}预制体");
//每次修改完了就保存。最后一次性保存会有修改未应用的情况
AssetDatabase.SaveAssets();
}
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
}
catch (Exception e)
{
Console.WriteLine(e);
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
throw;
}
}
private static void AddSymbolWithLoop(GameObject rootObj,List<Text> texts,List<Text> origin)
{
var tmpComponents = rootObj.GetComponents<Component>();
//原始有$符号的Text
if (rootObj.GetComponent<Text>() != null && rootObj.name.StartsWith("$"))
{
origin.Add(rootObj.GetComponent<Text>());
}
foreach (var component in tmpComponents)
{
var fieldInfos = component.GetType().GetFields();
SearchTextWithLoop(fieldInfos,texts,rootObj,component);
// foreach (var info in fieldInfos)
// {
// //不是.net原生类则继续向下递归
// if (info.FieldType.IsSerializable && info.FieldType.IsClass && !info.FieldType.IsPrimitive)
// {
// var tmpInfo = info.GetValue(component).GetType().GetFields();
// foreach (var item in tmpInfo)
// {
// AddSymbolToText(item, texts, rootObj, component);
// }
// }
// else
// {
// AddSymbolToText(info, texts, rootObj, component);
// }
// }
}
foreach (Transform child in rootObj.transform)
{
AddSymbolWithLoop(child.gameObject,texts,origin);
}
}
private static void SearchTextWithLoop(FieldInfo[] fieldInfos,List<Text> texts,GameObject rootObj,object component)
{
if (component == null) return;
foreach (var info in fieldInfos)
{
//不是.net原生类则继续向下递归
if (info.FieldType.IsSerializable && info.FieldType.IsClass &&
!info.FieldType.IsPrimitive)
{
var tmpInfo = info.GetValue(component)?.GetType().GetFields();
if(tmpInfo==null) continue;
var fi = component.GetType().GetField(info.Name);
var tmpCo = fi.GetValue(component);
// SearchTextWithLoop(tmpInfo,texts,rootObj,fi.GetValue(component));
foreach (var item in tmpInfo)
{
AddSymbolToText(item, texts, rootObj, tmpCo);
}
}
else
{
AddSymbolToText(info, texts, rootObj, component);
}
}
}
private static void AddSymbolToText(FieldInfo info,List<Text> texts,GameObject rootObj,object component)
{
if (component == null) return;
if (info.FieldType.IsArray && info.FieldType == typeof(Text[]))
{
var tmpInfo = (Text[])info.GetValue(component);
foreach (var text in tmpInfo)
{
texts.Add(text);
if(text == null) Debug.LogError(rootObj.name + " " + info + " 这个Text[]字段没有赋值");
if (text != null && !text.name.StartsWith("$"))
{
text.name = $"${text.name}";
}
}
}
else if (info.FieldType == typeof(Text))
{
var tmpObj = info.GetValue(component) as Text;
//记录有被引用的Text
texts.Add(tmpObj);
if(tmpObj == null) Debug.LogError(rootObj.name + " " + info + " 这个Text字段没有赋值");
if (tmpObj != null && !tmpObj.name.StartsWith("$"))
{
tmpObj.name = "$" + tmpObj.name;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
private static void RemoveSymbolToScriptText(string path)
{
var prefabArray = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);
for (var i = 0; i < prefabArray.Length; i++)
{
var tmpPrefab =
AssetDatabase.LoadAssetAtPath(prefabArray[i], typeof(GameObject)) as GameObject;
RemoveSymbolWithLoop(tmpPrefab);
EditorUtility.DisplayCancelableProgressBar("移除$符号", $"{tmpPrefab.name}",
(float) i / prefabArray.Length);
//EditorUtility.SetDirty(tmpPrefab);
Debug.Log($"移除了{tmpPrefab.name}预制体");
//每次修改完了就保存。最后一次性保存会有修改未应用的情况
AssetDatabase.SaveAssets();
}
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
}
private static void RemoveSymbolWithLoop(GameObject rootObj)
{
if (rootObj.GetComponent<Text>() != null && rootObj.name.StartsWith("$"))
{
rootObj.name = rootObj.name.Replace("$", "");
}
foreach (Transform child in rootObj.transform)
{
RemoveSymbolWithLoop(child.gameObject);
}
}
#endregion
private static void MarkTextInPrefab(string path) {
string txtPath = path.Replace("/", "_") + ".txt";
LocalizationMap locMap = new LocalizationMap();
string[] prefabArray = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);
for (int i = 0; i < prefabArray.Length; i++) {
GameObject prefab = AssetDatabase.LoadAssetAtPath(prefabArray[i], typeof(GameObject)) as GameObject;
List<GameObjectInfo> infoArray = ListAllGameObjectWithText(prefab);
foreach (GameObjectInfo info in infoArray) {
GameObject gameObject = info.gameObject;
TextHelper textHelper = MarkText(gameObject, info.key);
if (null != textHelper && !string.IsNullOrEmpty(info.key)) {
string newKey = locMap.Insert(textHelper.Key, textHelper.OriginStringValue);
textHelper.Key = newKey; //value重复
}
}
EditorUtility.DisplayCancelableProgressBar("标记Text", "", (float) i / prefabArray.Length);
AssetDatabase.SaveAssets();
}
EditorUtility.DisplayCancelableProgressBar("写入" + txtPath, "", 100);
locMap.WriteToTxt(txtPath);
EditorUtility.ClearProgressBar();
}
private static void UnmarkTextInPrefab(string path) {
string[] prefabArray = Directory.GetFiles(path, "*.prefab", SearchOption.AllDirectories);
for (int i = 0; i < prefabArray.Length; i++) {
GameObject prefab = AssetDatabase.LoadAssetAtPath(prefabArray[i], typeof(GameObject)) as GameObject;
List<GameObjectInfo> infoArray = ListAllGameObjectWithText(prefab);
foreach (GameObjectInfo info in infoArray) {
UnmarkText(info.gameObject);
}
EditorUtility.DisplayCancelableProgressBar("还原Text", "", (float) i / prefabArray.Length);
AssetDatabase.SaveAssets();
}
EditorUtility.ClearProgressBar();
}
public static TextHelper MarkText(GameObject gameObject, string key) {
if (null != gameObject.GetComponent<TextHelper>()) {
Debug.LogError("貌似这个物体已经标记过了" + key);
return null;
}
Text text = gameObject.GetComponent<Text>();
if (null == text) {
Debug.LogError("没有在这个物体上找到Text " + key);
return null;
}
if (!string.IsNullOrEmpty(text.text) && text.text.ContainChineseOrEnglish() && !string.IsNullOrEmpty(key) && !text.gameObject.name.StartsWith("$")) {
TextHelper textHelper = gameObject.AddComponent<TextHelper>();
textHelper.Key = key;
textHelper.OriginStringValue = LocalizedUtil.ReplaceNewLine(text.text);
textHelper.OriginStringValue = textHelper.OriginStringValue.Replace("\t", " ");
return textHelper;
}
return null;
}
// 删除GameObject上的TextHelper组件
public static void UnmarkText(GameObject gameObject) {
TextHelper textHelper = gameObject.GetComponent<TextHelper>();
if (null == textHelper) {
// Debug.Log("这个物体没有被标记过" + gameObject);
return;
}
Text text = gameObject.GetComponent<Text>();
if (null == text) {
Debug.LogError("这个物体上没有Text");
return;
}
text.text = LocalizedUtil.RecoverNewLine(textHelper.OriginStringValue);
DestroyImmediate(textHelper, true);
Debug.Log("成功恢复 - " + " - " + gameObject.name);
}
private class GameObjectInfo {
public GameObject gameObject;
public string key;
}
/// <summary>
/// 递归列出全部含Text组件的GameObject
/// </summary>
/// <param name="rootObject"></param>
/// <returns></returns>
private static List<GameObjectInfo> ListAllGameObjectWithText(GameObject rootObject) {
List<GameObjectInfo> infoArray = new List<GameObjectInfo>();
TravelGameObjectWithText(rootObject, infoArray);
return infoArray;
}
private static void TravelGameObjectWithText(GameObject go, List<GameObjectInfo> infoArray) {
// find Text in self
if (go.GetComponent<Text>() != null && !go.name.StartsWith("$")) {
GameObjectInfo info = new GameObjectInfo();
info.gameObject = go;
info.key = AnimationUtility.CalculateTransformPath(go.transform, null)
.Replace(" ", "_")
.Replace("/", "_")
.Replace("(", "")
.Replace(")", ""); //取路径作为key
infoArray.Add(info);
}
// travel children
foreach (Transform child in go.transform) {
TravelGameObjectWithText(child.gameObject, infoArray);
}
}
}
}