TH1/My project/Assets/Scripts/Logic/Editor/MultilingualEditorWindow.cs
2025-05-28 21:29:27 +08:00

429 lines
16 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.

/*
* @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}"); // 打印错误信息
}
}
}
}