218 lines
8.9 KiB
C#
218 lines
8.9 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using BaseGames.Localization;
|
||
|
||
namespace BaseGames.Editor.Localization
|
||
{
|
||
/// <summary>
|
||
/// 本地化 CSV 导入/导出工具。
|
||
///
|
||
/// 每个表导出为独立 CSV(含 UTF-8 BOM,Excel 中文不乱码),列顺序:key, ChineseSimplified, English, Japanese, Korean。
|
||
/// 文件存放路径:Assets/_Game/Localization/Export/{TableName}.csv
|
||
///
|
||
/// 导入:读取 CSV → 合并回写 Assets/_Game/Data/Localization/{Language}/{TableName}.json(经 LocalizationFileIO)
|
||
///
|
||
/// 注:日常单表编辑请用「BaseGames / Localization / 表格编辑器」,此窗口用于批量多表导入导出。
|
||
/// 菜单:BaseGames / Localization / CSV 导入导出工具
|
||
/// </summary>
|
||
public class LocalizationCsvTool : EditorWindow
|
||
{
|
||
[MenuItem("BaseGames/Localization/CSV 导入导出工具")]
|
||
private static void Open()
|
||
{
|
||
var win = GetWindow<LocalizationCsvTool>("本地化 CSV 工具");
|
||
win.minSize = new Vector2(480, 360);
|
||
}
|
||
|
||
// ── 状态 ─────────────────────────────────────────────────────────────
|
||
|
||
private static readonly string[] s_allTables =
|
||
{
|
||
LocalizationTable.UI,
|
||
LocalizationTable.Dialogue,
|
||
LocalizationTable.Quest,
|
||
LocalizationTable.Spells,
|
||
LocalizationTable.Skills,
|
||
LocalizationTable.Items,
|
||
LocalizationTable.Character,
|
||
LocalizationTable.Tutorial,
|
||
};
|
||
|
||
private const string ExportDir = "Assets/_Game/Localization/Export";
|
||
private const string DataDir = LocalizationPaths.DataRoot;
|
||
|
||
private readonly bool[] _exportSelected = new bool[s_allTables.Length];
|
||
private readonly bool[] _importSelected = new bool[s_allTables.Length];
|
||
private string _statusMessage = "";
|
||
private MessageType _statusType = MessageType.None;
|
||
private Vector2 _scroll;
|
||
|
||
// ── GUI ───────────────────────────────────────────────────────────────
|
||
|
||
private void OnGUI()
|
||
{
|
||
_scroll = EditorGUILayout.BeginScrollView(_scroll);
|
||
|
||
EditorGUILayout.Space(6);
|
||
GUILayout.Label("📤 导出 → CSV", EditorStyles.boldLabel);
|
||
EditorGUILayout.HelpBox(
|
||
$"将 {DataDir}/ 中的 JSON 表导出为 CSV 文件(UTF-8 BOM,Excel 可直接打开)。\n" +
|
||
$"目标目录:{ExportDir}/",
|
||
MessageType.Info);
|
||
|
||
EditorGUI.indentLevel++;
|
||
for (int i = 0; i < s_allTables.Length; i++)
|
||
_exportSelected[i] = EditorGUILayout.Toggle(s_allTables[i], _exportSelected[i]);
|
||
EditorGUI.indentLevel--;
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("全选")) SetAll(_exportSelected, true);
|
||
if (GUILayout.Button("全不选")) SetAll(_exportSelected, false);
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space(4);
|
||
if (GUILayout.Button("▶ 导出选中表", GUILayout.Height(30)))
|
||
RunExport();
|
||
|
||
EditorGUILayout.Space(16);
|
||
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
|
||
|
||
GUILayout.Label("📥 导入 ← CSV", EditorStyles.boldLabel);
|
||
EditorGUILayout.HelpBox(
|
||
$"读取 {ExportDir}/ 中的 CSV,合并回写至 {DataDir}/ JSON 文件。\n" +
|
||
"已有 Key 覆盖,新增 Key 追加,不删除多余 Key。",
|
||
MessageType.Info);
|
||
|
||
EditorGUI.indentLevel++;
|
||
for (int i = 0; i < s_allTables.Length; i++)
|
||
_importSelected[i] = EditorGUILayout.Toggle(s_allTables[i], _importSelected[i]);
|
||
EditorGUI.indentLevel--;
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("全选")) SetAll(_importSelected, true);
|
||
if (GUILayout.Button("全不选")) SetAll(_importSelected, false);
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space(4);
|
||
if (GUILayout.Button("▶ 导入选中表", GUILayout.Height(30)))
|
||
RunImport();
|
||
|
||
EditorGUILayout.Space(8);
|
||
if (!string.IsNullOrEmpty(_statusMessage))
|
||
EditorGUILayout.HelpBox(_statusMessage, _statusType);
|
||
|
||
EditorGUILayout.Space(6);
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
// ── 导出 ─────────────────────────────────────────────────────────────
|
||
|
||
private void RunExport()
|
||
{
|
||
LocalizationManager.ClearEditorPreviewCache();
|
||
int exported = 0;
|
||
|
||
try
|
||
{
|
||
for (int ti = 0; ti < s_allTables.Length; ti++)
|
||
{
|
||
if (!_exportSelected[ti]) continue;
|
||
string tableName = s_allTables[ti];
|
||
|
||
EditorUtility.DisplayProgressBar("导出 CSV", $"导出 {tableName}…", (float)ti / s_allTables.Length);
|
||
|
||
// 经统一门面读取各语言字典(正确路径),交给 LocalizationCsv 写出(含 BOM)
|
||
var langDicts = new Dictionary<Language, Dictionary<string, string>>();
|
||
bool any = false;
|
||
foreach (var lang in LocalizationFileIO.AllLanguages)
|
||
{
|
||
var dict = LocalizationFileIO.Read(lang, tableName);
|
||
langDicts[lang] = dict;
|
||
if (dict.Count > 0) any = true;
|
||
}
|
||
|
||
if (!any)
|
||
{
|
||
Debug.LogWarning($"[CsvTool] 表「{tableName}」无数据,跳过导出。");
|
||
continue;
|
||
}
|
||
|
||
LocalizationCsv.ExportToFile(tableName, langDicts);
|
||
exported++;
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
AssetDatabase.Refresh();
|
||
SetStatus($"✅ 成功导出 {exported} 个表到 {ExportDir}/", MessageType.Info);
|
||
}
|
||
|
||
// ── 导入 ─────────────────────────────────────────────────────────────
|
||
|
||
private void RunImport()
|
||
{
|
||
int imported = 0;
|
||
int errors = 0;
|
||
|
||
try
|
||
{
|
||
for (int ti = 0; ti < s_allTables.Length; ti++)
|
||
{
|
||
if (!_importSelected[ti]) continue;
|
||
string tableName = s_allTables[ti];
|
||
|
||
EditorUtility.DisplayProgressBar("导入 CSV", $"导入 {tableName}…", (float)ti / s_allTables.Length);
|
||
|
||
try
|
||
{
|
||
// 合并语义 + 正确格式/目录/Addressable 注册,全部由统一门面处理
|
||
int affected = LocalizationCsv.ImportFileToJson(tableName);
|
||
if (affected == 0)
|
||
{
|
||
Debug.LogWarning($"[CsvTool] CSV 文件不存在或为空,跳过:{ExportDir}/{tableName}.csv");
|
||
continue;
|
||
}
|
||
imported++;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"[CsvTool] 导入「{tableName}」失败:{ex.Message}");
|
||
errors++;
|
||
}
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
EditorUtility.ClearProgressBar();
|
||
}
|
||
|
||
AssetDatabase.Refresh();
|
||
LocalizationManager.ClearEditorPreviewCache();
|
||
|
||
string msg = errors == 0
|
||
? $"✅ 成功导入 {imported} 个表。"
|
||
: $"⚠ 导入 {imported} 个表,{errors} 个失败(见控制台)。";
|
||
SetStatus(msg, errors == 0 ? MessageType.Info : MessageType.Warning);
|
||
}
|
||
|
||
// ── 工具函数 ─────────────────────────────────────────────────────────
|
||
|
||
private static void SetAll(bool[] arr, bool v)
|
||
{
|
||
for (int i = 0; i < arr.Length; i++) arr[i] = v;
|
||
}
|
||
|
||
private void SetStatus(string msg, MessageType type)
|
||
{
|
||
_statusMessage = msg;
|
||
_statusType = type;
|
||
Repaint();
|
||
}
|
||
}
|
||
}
|