using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using AlicizaX; using UnityEditor; using UnityEngine; public static class LocalizationCSVUtility { const string MainPath = "Assets/Scripts/Editor/Localization/Localization.csv"; const string HotfixPath = "Assets/Scripts/Editor/Localization/HotFixLocalization.csv"; const string MainOut = "Assets/Resources/Localization/"; const string HotfixOut = "Assets/Bundles/Configs/Localization/"; [MenuItem("Tools/Localization/Create CSV Template")] private static void CreateCSVTemplateMenu() { CreateLocalizationTemplate(MainPath); CreateLocalizationTemplate(HotfixPath); } public static void CreateLocalizationTemplate(string path) { if (string.IsNullOrEmpty(path)) return; using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8)) { // 写入标题行 var sb = new StringBuilder("Key"); foreach (Language lang in System.Enum.GetValues(typeof(Language))) { if (lang == Language.Unspecified) continue; sb.Append($",{lang}"); } sw.WriteLine(sb.ToString()); } AssetDatabase.Refresh(); // 刷新Unity资源目录[3](@ref) } [MenuItem("Tools/Localization/Export Localization Files")] private static void ExportLocalizationMenu() { ExportLocalizationFiles(MainPath, MainOut); ExportLocalizationFiles(HotfixPath, HotfixOut); AssetDatabase.Refresh(); } private const int KEY_COLUMN = 0; private const int FIRST_LANG_COLUMN = 1; public static void ExportLocalizationFiles(string csvPath, string outputDir) { var rawData = ParseCSV(csvPath); if (rawData.Count == 0) { Debug.LogError("CSV文件内容为空"); return; } var missingValues = new List(); // 解析首行获取语言列表 string[] header = rawData[0]; List langs = new List(); for (int i = FIRST_LANG_COLUMN; i < header.Length; i++) { langs.Add(header[i]); } // 初始化语言字典 Dictionary> langDict = new Dictionary>(); foreach (string lang in langs) { langDict[lang] = new Dictionary(); } // 处理数据行(从第二行开始) for (int rowIndex = 1; rowIndex < rawData.Count; rowIndex++) { string[] row = rawData[rowIndex]; if (row.Length == 0) continue; string key = row[KEY_COLUMN]; if (string.IsNullOrEmpty(key)) continue; for (int col = FIRST_LANG_COLUMN; col < row.Length; col++) { int langIndex = col - FIRST_LANG_COLUMN; if (langIndex >= langs.Count) break; string lang = langs[langIndex]; string value = row[col]; if (string.IsNullOrEmpty(value)) { missingValues.Add($"[Missing] Key:{key} Lang:{lang}"); continue; } // 覆盖重复key的警告 if (langDict[lang].ContainsKey(key)) { Debug.LogWarning($"Key '{key}' already exists in {lang}, will be overwritten"); } langDict[lang][key] = value; } } if (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } // 导出JSON文件 foreach (var lang in langDict) { if (lang.Value.Count == 0) continue; // 手动构建JSON格式确保兼容性 var entries = lang.Value.Select(kvp => $" \"{EscapeJson(kvp.Key)}\": \"{EscapeJson(kvp.Value)}\""); string json = "{\n" + string.Join(",\n", entries) + "\n}"; string outputPath = Path.Combine(outputDir, $"{lang.Key}.json"); File.WriteAllText(outputPath, json, Encoding.UTF8); } // 显示缺失值警告 if (missingValues.Count > 0) { Debug.LogWarning($"Missing {missingValues.Count} translations:\n" + string.Join("\n", missingValues)); } } private static List ParseCSV(string path) { List data = new List(); using (StreamReader sr = new StreamReader(path, Encoding.UTF8)) { while (!sr.EndOfStream) { string line = sr.ReadLine(); if (string.IsNullOrEmpty(line)) continue; data.Add(line.Split(',')); } } return data; } private static string EscapeJson(string input) { return input.Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\n", "\\n") .Replace("\r", "\\r"); } }