429 lines
16 KiB
C#
429 lines
16 KiB
C#
/*
|
||
* @Author: 白哉
|
||
* @Description:
|
||
* @Date: 2025年05月26日 星期一 17:05:14
|
||
* @Modify:
|
||
*/
|
||
|
||
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using Logic.Multilingual;
|
||
using TMPro;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using Debug = UnityEngine.Debug;
|
||
|
||
|
||
namespace Logic.Editor
|
||
{
|
||
public class MultilingualEditorWindow : EditorWindow
|
||
{
|
||
// 滑条
|
||
private Vector2 _barPosition;
|
||
|
||
// 背景
|
||
private GUIStyle _redBoxStyle;
|
||
private GUIStyle _whiteBoxStyle;
|
||
|
||
private MultilingualData _asset;
|
||
|
||
private Dictionary<string, uint> _zhStrDict = new Dictionary<string, uint>();
|
||
private uint _idIndex;
|
||
|
||
|
||
[MenuItem("Tools/多语言编辑器")]
|
||
private static void ShowWindow()
|
||
{
|
||
var window = CreateWindow<MultilingualEditorWindow>();
|
||
window.titleContent = new GUIContent("多语言编辑器");
|
||
window.Show();
|
||
window.minSize = new Vector2(500, 600);
|
||
}
|
||
|
||
protected virtual void OnEnable()
|
||
{
|
||
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
private void OnGUI()
|
||
{
|
||
if (!_asset)
|
||
{
|
||
var path = $"Assets/Resources/Export/Multilingual.asset";
|
||
_asset = AssetDatabase.LoadAssetAtPath<MultilingualData>(path);
|
||
if (!_asset)
|
||
{
|
||
_asset = CreateInstance<MultilingualData>();
|
||
AssetDatabase.CreateAsset(_asset, path);
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
}
|
||
|
||
if (_redBoxStyle == null)
|
||
{
|
||
_redBoxStyle = InspectorUtils.GetHelpBoxStyle();
|
||
InspectorUtils.AddBorder(_redBoxStyle, new Color(0.5f, 0.4f, 0.4f, 0.6f));
|
||
}
|
||
|
||
if (_whiteBoxStyle == null)
|
||
{
|
||
_whiteBoxStyle = InspectorUtils.GetHelpBoxStyle();
|
||
InspectorUtils.AddBorder(_whiteBoxStyle, new Color(1f, 1f, 1f, 0.2f));
|
||
}
|
||
|
||
GUI.skin.button.wordWrap = true;
|
||
_barPosition = EditorGUILayout.BeginScrollView(_barPosition);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (InspectorUtils.InspectorButtonWithTextWidth("保存"))
|
||
{
|
||
EditorUtility.SetDirty(_asset);
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
if (InspectorUtils.InspectorButtonWithTextWidth("清空"))
|
||
{
|
||
_asset.Items.Clear();
|
||
_asset.RefreshDict();
|
||
}
|
||
|
||
if (InspectorUtils.InspectorButtonWithTextWidth("导出 Excel"))
|
||
{
|
||
AssetExportToExcel();
|
||
}
|
||
|
||
if (InspectorUtils.InspectorButtonWithTextWidth("Excel 导回"))
|
||
{
|
||
ExcelExportToAsset();
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (InspectorUtils.InspectorButtonWithTextWidth("添加字体组"))
|
||
{
|
||
_asset.FontGroups.Add(new MultilingualFontGroup());
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
|
||
var deleteSet = new HashSet<MultilingualFontGroup>();
|
||
for (int i = 0; i < _asset.FontGroups.Count; i++)
|
||
{
|
||
_asset.FontGroups[i].FontID = (uint)i + 1;
|
||
if (!ShowFontGroup(_asset.FontGroups[i])) continue;
|
||
deleteSet.Add(_asset.FontGroups[i]);
|
||
}
|
||
foreach (var deleteGroup in deleteSet)_asset.FontGroups.Remove(deleteGroup);
|
||
|
||
foreach (var item in _asset.Items) ShowMultilingualItem(item);
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
private bool ShowFontGroup(MultilingualFontGroup fontGroup)
|
||
{
|
||
var isDelete = false;
|
||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||
EditorGUILayout.BeginHorizontal();
|
||
InspectorUtils.InspectorTextWidthRich($"<b>ID : {fontGroup.FontID}</b>");
|
||
if (InspectorUtils.InspectorButtonWithTextWidth("x")) isDelete = true;
|
||
EditorGUILayout.EndHorizontal();
|
||
fontGroup.ZHFont = (TMP_FontAsset)EditorGUILayout.ObjectField(fontGroup.ZHFont, typeof(TMP_FontAsset), false);
|
||
fontGroup.ENFont = (TMP_FontAsset)EditorGUILayout.ObjectField(fontGroup.ENFont, typeof(TMP_FontAsset), false);
|
||
fontGroup.JPFont = (TMP_FontAsset)EditorGUILayout.ObjectField(fontGroup.JPFont, typeof(TMP_FontAsset), false);
|
||
fontGroup.KRFont = (TMP_FontAsset)EditorGUILayout.ObjectField(fontGroup.KRFont, typeof(TMP_FontAsset), false);
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space();
|
||
return isDelete;
|
||
}
|
||
|
||
private void ShowMultilingualItem(MultilingualItem item)
|
||
{
|
||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||
InspectorUtils.InspectorTextWidthRich($"<b>{item.ID} : </b>");
|
||
InspectorUtils.InspectorTextWidthRich($" <b>中文:</b> {item.ZH}");
|
||
if (!string.IsNullOrEmpty(item.EN))
|
||
InspectorUtils.InspectorTextWidthRich($" <b>英语:</b> {item.EN}");
|
||
if (!string.IsNullOrEmpty(item.JP))
|
||
InspectorUtils.InspectorTextWidthRich($" <b>日语:</b> {item.JP}");
|
||
if (!string.IsNullOrEmpty(item.KR))
|
||
InspectorUtils.InspectorTextWidthRich($" <b>韩语:</b> {item.KR}");
|
||
var unicode = "";
|
||
foreach (var c in item.ZH) unicode+= $"{(int)c:X4} ";
|
||
InspectorUtils.InspectorTextWidthRich($" <b>Unicode:</b> {unicode}");
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space();
|
||
}
|
||
|
||
private void DuplicateRemoval()
|
||
{
|
||
// 排序 asset.items 保证id从小到大
|
||
_asset.Items = _asset.Items.OrderBy(i => i.ID).ToList();
|
||
_zhStrDict.Clear();
|
||
var deleteItem = new HashSet<MultilingualItem>();
|
||
foreach (var item in _asset.Items)
|
||
{
|
||
if (_zhStrDict.ContainsKey(item.ZH))
|
||
{
|
||
deleteItem.Add(item);
|
||
continue;
|
||
}
|
||
|
||
_zhStrDict[item.ZH] = item.ID;
|
||
}
|
||
|
||
foreach (var item in deleteItem) _asset.Items.Remove(item);
|
||
_asset.RefreshDict();
|
||
}
|
||
|
||
private string RemoveCsvQuotes(string field)
|
||
{
|
||
if (string.IsNullOrEmpty(field)) return field;
|
||
// 去除首尾空格和换行符
|
||
field = field.Trim();
|
||
// 若字段以引号开头和结尾,则去除引号并处理内部转义
|
||
if (field.Length >= 2 && field.StartsWith("\"") && field.EndsWith("\""))
|
||
{
|
||
field = field.Substring(1, field.Length - 2) // 去除首尾引号
|
||
.Replace("\"\"", "\""); // 将转义引号还原为单个引号
|
||
}
|
||
return field;
|
||
}
|
||
|
||
private void ExcelExportToAsset()
|
||
{
|
||
GetExcelData();
|
||
_asset.RefreshDict();
|
||
|
||
string context;
|
||
using (var reader = new StreamReader("../Tools/MultilingualTxt.txt", Encoding.Default, true))
|
||
{
|
||
context = reader.ReadToEnd();
|
||
}
|
||
var lines = context.Split("!@#$%");
|
||
foreach (string line in lines)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(line)) continue; // 跳过空行
|
||
string[] cells = line.Split("%$#@!");
|
||
if (cells.Length == 0) continue;
|
||
|
||
var id = uint.Parse(cells[0]);
|
||
MultilingualItem item;
|
||
if (_asset.ItemDict.TryGetValue(id, out var value)) item = value;
|
||
else
|
||
{
|
||
item = new MultilingualItem();
|
||
_asset.Items.Add(item);
|
||
}
|
||
|
||
item.ID = id;
|
||
if (cells.Length >= 2) item.ZH = RemoveCsvQuotes(cells[1]);
|
||
if (cells.Length >= 3) item.EN = RemoveCsvQuotes(cells[2]);
|
||
if (cells.Length >= 4) item.JP = RemoveCsvQuotes(cells[3]);
|
||
if (cells.Length >= 5) item.KR = RemoveCsvQuotes(cells[4]);
|
||
}
|
||
}
|
||
|
||
private void AssetExportToExcel()
|
||
{
|
||
_zhStrDict.Clear();
|
||
DuplicateRemoval();
|
||
foreach (var item in _asset.Items) _zhStrDict[item.ZH] = item.ID;
|
||
if (_asset.Items.Count != 0) _idIndex = _asset.Items[^1].ID + 1;
|
||
else _idIndex = 1;
|
||
|
||
var uiObj = GameObject.Find("UICanvas");
|
||
if (!uiObj)
|
||
{
|
||
Debug.LogError($"找不到UI根节点");
|
||
return;
|
||
}
|
||
|
||
var coms = uiObj.GetComponentsInChildren<TextMeshProUGUI>(true).ToList();
|
||
foreach (var com in coms)
|
||
{
|
||
if (!Regex.IsMatch(com.text, @"[\u4E00-\u9FFF\u3400-\u4DBFa-zA-Z]")) continue;
|
||
// 去除首尾空格和换行符
|
||
com.text = com.text.Trim();
|
||
var textCom = com.gameObject.GetComponent<MultilingualTextMono>();
|
||
if (!textCom) textCom = com.gameObject.AddComponent<MultilingualTextMono>();
|
||
|
||
if (_zhStrDict.ContainsKey(com.text))
|
||
{
|
||
textCom.ID = _zhStrDict[com.text];
|
||
}
|
||
else
|
||
{
|
||
textCom.ID = _idIndex;
|
||
_zhStrDict[com.text] = _idIndex;
|
||
_idIndex++;
|
||
}
|
||
}
|
||
|
||
var path = $"Assets/Resources/DataAssets/";
|
||
string[] assetPaths = Directory.GetFiles(path, "*.asset", SearchOption.AllDirectories);
|
||
foreach (var assetPath in assetPaths)
|
||
{
|
||
var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(assetPath);
|
||
if (!asset) continue;
|
||
var targetAsset = GetExportAsset(asset);
|
||
TraverseObject(targetAsset);
|
||
}
|
||
|
||
_asset.RefreshDict();
|
||
foreach (var kv in _zhStrDict)
|
||
{
|
||
if (_asset.ItemDict.ContainsKey(kv.Value)) continue;
|
||
var item = new MultilingualItem();
|
||
item.ID = kv.Value;
|
||
item.ZH = kv.Key;
|
||
_asset.Items.Add(item);
|
||
}
|
||
// 排序 asset.items 保证id从小到大
|
||
_asset.Items = _asset.Items.OrderBy(i => i.ID).ToList();
|
||
|
||
string filePath = "../Tools/MultilingualTxt.txt";
|
||
if (!File.Exists(filePath))
|
||
{
|
||
using (File.Create(filePath)) { } // 立即释放句柄
|
||
}
|
||
|
||
using (StreamWriter sw = new StreamWriter(filePath, false, Encoding.UTF8))
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
foreach (var item in _asset.Items)
|
||
{
|
||
sb.Append($"{item.ID}%$#@!{item.ZH}%$#@!{item.EN}%$#@!{item.JP}%$#@!{item.KR}!@#$%");
|
||
}
|
||
sw.Write(sb.ToString());
|
||
}
|
||
|
||
WriteToExcel();
|
||
}
|
||
|
||
private ScriptableObject GetExportAsset(ScriptableObject origin)
|
||
{
|
||
var target = $"Assets/Resources/Export/{origin.name}.asset";
|
||
if (File.Exists(target)) AssetDatabase.DeleteAsset(target);
|
||
bool success = AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(origin), target);
|
||
if (!success)
|
||
{
|
||
Debug.LogError($"拷贝失败!!!");
|
||
return null;
|
||
}
|
||
|
||
return AssetDatabase.LoadAssetAtPath<ScriptableObject>(target);
|
||
}
|
||
|
||
private void TraverseObject(object asset)
|
||
{
|
||
if (asset == null) return;
|
||
|
||
var fields = asset.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||
foreach (var field in fields)
|
||
{
|
||
var attr = field.GetCustomAttribute<MultilingualFieldAttribute>();
|
||
if (attr != null)
|
||
{
|
||
var str = ((string)field.GetValue(asset)).Trim();
|
||
if (!string.IsNullOrEmpty(str))
|
||
{
|
||
if (_zhStrDict.ContainsKey(str))
|
||
{
|
||
field.SetValue(asset, _zhStrDict[str].ToString());
|
||
}
|
||
else
|
||
{
|
||
_zhStrDict[str] = _idIndex;
|
||
field.SetValue(asset, _zhStrDict[str].ToString());
|
||
_idIndex++;
|
||
}
|
||
}
|
||
}
|
||
|
||
var son = field.GetValue(asset);
|
||
if (son == null) continue;
|
||
|
||
// 如果是集合(如 List<T>),遍历元素
|
||
if (son is IEnumerable enumerable && !(son is string))
|
||
{
|
||
foreach (object item in enumerable)
|
||
{
|
||
TraverseObject(item); // 递归处理集合项
|
||
}
|
||
}
|
||
// 如果是自定义对象(非基础类型),递归处理
|
||
else if (!son.GetType().IsPrimitive && son.GetType() != typeof(string))
|
||
{
|
||
TraverseObject(son);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void WriteToExcel()
|
||
{
|
||
var pythonScript = $"../Tools/ExportStringToExcel.py";
|
||
|
||
ProcessStartInfo start = new ProcessStartInfo
|
||
{
|
||
FileName = "py",
|
||
Arguments = $"\"{pythonScript}\"",
|
||
RedirectStandardOutput = true,
|
||
RedirectStandardError = true,
|
||
UseShellExecute = false,
|
||
CreateNoWindow = true
|
||
};
|
||
|
||
using (var process = Process.Start(start))
|
||
{
|
||
string output = process.StandardOutput.ReadToEnd();
|
||
string error = process.StandardError.ReadToEnd(); // 获取错误信息
|
||
process.WaitForExit();
|
||
|
||
Debug.Log($"Exit Code: {process.ExitCode}"); // 打印退出码
|
||
Debug.Log($"Output: {output}");
|
||
Debug.Log($"Error: {error}"); // 打印错误信息
|
||
}
|
||
}
|
||
|
||
public void GetExcelData()
|
||
{
|
||
var pythonScript = $"../Tools/PrintExcelString.py";
|
||
|
||
ProcessStartInfo start = new ProcessStartInfo
|
||
{
|
||
FileName = "py",
|
||
Arguments = $"\"{pythonScript}\"",
|
||
RedirectStandardOutput = true,
|
||
RedirectStandardError = true,
|
||
UseShellExecute = false,
|
||
CreateNoWindow = true
|
||
};
|
||
|
||
using (var process = Process.Start(start))
|
||
{
|
||
string output = process.StandardOutput.ReadToEnd();
|
||
string error = process.StandardError.ReadToEnd(); // 获取错误信息
|
||
process.WaitForExit();
|
||
|
||
Debug.Log($"Exit Code: {process.ExitCode}"); // 打印退出码
|
||
Debug.Log($"Output: {output}");
|
||
Debug.Log($"Error: {error}"); // 打印错误信息
|
||
}
|
||
}
|
||
}
|
||
} |