337 lines
13 KiB
C#
337 lines
13 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
} |