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(); var tmpOrigin = new List(); 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 texts,List origin) { var tmpComponents = rootObj.GetComponents(); //原始有$符号的Text if (rootObj.GetComponent() != null && rootObj.name.StartsWith("$")) { origin.Add(rootObj.GetComponent()); } 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 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 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; } } } /// /// /// /// 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() != 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 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 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()) { Debug.LogError("貌似这个物体已经标记过了" + key); return null; } Text text = gameObject.GetComponent(); 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.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(); if (null == textHelper) { // Debug.Log("这个物体没有被标记过" + gameObject); return; } Text text = gameObject.GetComponent(); 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; } /// /// 递归列出全部含Text组件的GameObject /// /// /// private static List ListAllGameObjectWithText(GameObject rootObject) { List infoArray = new List(); TravelGameObjectWithText(rootObject, infoArray); return infoArray; } private static void TravelGameObjectWithText(GameObject go, List infoArray) { // find Text in self if (go.GetComponent() != 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); } } } }