578 lines
21 KiB
C#
578 lines
21 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using AlicizaX.Editor;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEditor.IMGUI.Controls;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace AlicizaX.Localization.Editor
|
|
{
|
|
public class LocalizationTableWindow : EditorWindow
|
|
{
|
|
private List<GameLocaizationTable> allTables = new List<GameLocaizationTable>(); // 存储所有找到的GameLocaizationTable
|
|
private string[] tableDisplayNames; // 用于下拉框显示的名称数组
|
|
private int selectedTableIndex = 0; // 当前选中的索引
|
|
private string selectString = string.Empty;
|
|
private GameLocaizationTable currentTable; // 当前选中的GameLocaizationTable
|
|
|
|
private const float k_LanguagesWidth = 200f;
|
|
private const float k_TableSheetWidth = 200f;
|
|
private float Spacing => EditorGUIUtility.standardVerticalSpacing * 2;
|
|
|
|
private GUIStyle miniLabelButton => new GUIStyle(EditorStyles.miniButton)
|
|
{
|
|
font = EditorStyles.miniBoldLabel.font,
|
|
fontSize = EditorStyles.miniBoldLabel.fontSize
|
|
};
|
|
|
|
public class WindowSelection
|
|
{
|
|
public TreeViewItem TreeViewItem;
|
|
}
|
|
|
|
public sealed class LanguageSelect : WindowSelection
|
|
{
|
|
public TempLanguageData Language;
|
|
}
|
|
|
|
public sealed class SectionSelect : WindowSelection
|
|
{
|
|
public SheetSectionTreeView Section;
|
|
}
|
|
|
|
public sealed class ItemSelect : WindowSelection
|
|
{
|
|
public SheetItemTreeView Item;
|
|
}
|
|
|
|
|
|
private LocalizationWindowData windowData;
|
|
|
|
private SearchField searchField;
|
|
private string searchString;
|
|
private Vector2 scrollPosition;
|
|
|
|
[SerializeField] private TreeViewState languagesTreeViewState;
|
|
private LanguagesTreeView languagesTreeView;
|
|
|
|
[SerializeField] private TreeViewState tableSheetTreeViewState;
|
|
private TableSheetTreeView tableSheetTreeView;
|
|
|
|
private WindowSelection selection = null;
|
|
private bool globalExpanded = false;
|
|
|
|
private void CreateGUI()
|
|
{
|
|
searchField = new SearchField();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
SaveSelection();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
RefreshTableList();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
SaveSelection();
|
|
}
|
|
|
|
private void RefreshTableList()
|
|
{
|
|
allTables.Clear();
|
|
string[] guids = AssetDatabase.FindAssets("t:GameLocaizationTable");
|
|
foreach (string guid in guids)
|
|
{
|
|
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
|
GameLocaizationTable table = AssetDatabase.LoadAssetAtPath<GameLocaizationTable>(assetPath);
|
|
if (table != null)
|
|
{
|
|
allTables.Add(table);
|
|
}
|
|
}
|
|
|
|
tableDisplayNames = new string[allTables.Count];
|
|
for (int i = 0; i < allTables.Count; i++)
|
|
{
|
|
tableDisplayNames[i] = allTables[i].name;
|
|
}
|
|
|
|
if (allTables.Count > 0 && selectedTableIndex >= allTables.Count)
|
|
{
|
|
selectedTableIndex = 0;
|
|
}
|
|
|
|
|
|
var selectIndex = selectedTableIndex;
|
|
var lastSelect = EditorPrefs.GetString("LastSelectedGameLocaizationTable", string.Empty);
|
|
if (!string.IsNullOrEmpty(lastSelect))
|
|
{
|
|
for (int i = 0; i < allTables.Count; i++)
|
|
{
|
|
var path = AssetDatabase.GetAssetPath(allTables[i]);
|
|
if (path.Equals(lastSelect))
|
|
{
|
|
selectIndex = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selectIndex <= tableDisplayNames.Length - 1 && selectIndex >= 0)
|
|
{
|
|
selectString = tableDisplayNames[selectIndex];
|
|
}
|
|
|
|
UpdateCurrentTable();
|
|
}
|
|
|
|
private void UpdateCurrentTable()
|
|
{
|
|
if (allTables.Count > 0 && selectedTableIndex >= 0 && selectedTableIndex < allTables.Count)
|
|
{
|
|
currentTable = allTables[selectedTableIndex];
|
|
SaveSelection();
|
|
}
|
|
else
|
|
{
|
|
currentTable = null;
|
|
}
|
|
|
|
InitializeTreeView();
|
|
}
|
|
|
|
private void SaveSelection()
|
|
{
|
|
if (currentTable != null)
|
|
{
|
|
string path = AssetDatabase.GetAssetPath(currentTable);
|
|
EditorPrefs.SetString("LastSelectedGameLocaizationTable", path);
|
|
}
|
|
}
|
|
|
|
private void InitializeTreeView()
|
|
{
|
|
if (!currentTable) return;
|
|
|
|
LocalizationWindowUtility.BuildWindowData(currentTable, out windowData);
|
|
|
|
|
|
foreach (var section in windowData.TableSheet)
|
|
{
|
|
section.IsExpanded = globalExpanded;
|
|
}
|
|
|
|
|
|
languagesTreeViewState = new TreeViewState();
|
|
languagesTreeView = new(languagesTreeViewState, windowData, currentTable)
|
|
{
|
|
OnLanguageSelect = (s) => selection = s
|
|
};
|
|
|
|
|
|
tableSheetTreeViewState = new TreeViewState();
|
|
tableSheetTreeView = new(tableSheetTreeViewState, windowData)
|
|
{
|
|
OnTableSheetSelect = (s) => selection = s
|
|
};
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
Rect toolbarRect = new(0, 0, position.width, 20f);
|
|
GUI.Box(toolbarRect, GUIContent.none, EditorStyles.toolbar);
|
|
|
|
|
|
float buttonWidth = 100f;
|
|
float spacing = 5f;
|
|
|
|
|
|
Rect leftTitle = new(toolbarRect.xMin, 0, 40, 20f);
|
|
Rect leftPop = new(leftTitle.xMin + 40, 0, 200, 20f);
|
|
Rect saveBtn = new(toolbarRect.xMax - buttonWidth - spacing, 0, buttonWidth, 20f);
|
|
Rect genBtn = new(saveBtn.xMin - buttonWidth - spacing, 0, buttonWidth, 20f);
|
|
Rect importBtn = new(genBtn.xMin - buttonWidth - spacing, 0, buttonWidth, 20f);
|
|
Rect exportBtn = new(importBtn.xMin - buttonWidth - spacing, 0, buttonWidth, 20f);
|
|
|
|
EditorGUI.LabelField(leftTitle, "Table", EditorStyles.boldLabel);
|
|
EditorDrawing.DrawStringSelectPopup(leftPop, tableDisplayNames, selectString, (e) =>
|
|
{
|
|
selectString = e;
|
|
selectedTableIndex = allTables.FindIndex(table => table.name == e);
|
|
UpdateCurrentTable();
|
|
});
|
|
|
|
if (currentTable == null) return;
|
|
|
|
|
|
if (GUI.Button(exportBtn, "Export CSV", EditorStyles.toolbarButton))
|
|
{
|
|
string path = EditorUtility.SaveFilePanel("Export CSV", "", "Localization", "csv");
|
|
LocalizationExporter.ExportLocalizationToCSV(windowData, path);
|
|
}
|
|
|
|
|
|
if (GUI.Button(importBtn, "Import CSV", EditorStyles.toolbarButton))
|
|
{
|
|
string path = EditorUtility.OpenFilePanel("Export CSV", "", "csv");
|
|
LocalizationExporter.ImportLocalizationFromCSV(windowData, path);
|
|
}
|
|
|
|
if (GUI.Button(genBtn, "Gen Code", EditorStyles.toolbarButton))
|
|
{
|
|
LocalizationWindowUtility.GenerateCode(currentTable);
|
|
}
|
|
|
|
if (GUI.Button(saveBtn, "Save Asset", EditorStyles.toolbarButton))
|
|
{
|
|
BuildLocalizationTable();
|
|
EditorUtility.SetDirty(currentTable);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
|
|
Rect languagesRect = new Rect(5f, 25f, k_LanguagesWidth, position.height - 35f);
|
|
languagesTreeView.OnGUI(languagesRect);
|
|
|
|
|
|
float tableSheetStartX = languagesRect.xMax + 5f;
|
|
Rect tableSheetRect = new Rect(tableSheetStartX, 25f, k_TableSheetWidth, position.height - 35f);
|
|
tableSheetTreeView.OnGUI(tableSheetRect);
|
|
|
|
|
|
if (selection != null)
|
|
{
|
|
float inspectorStartX = tableSheetRect.xMax + 5f;
|
|
Rect inspectorRect = new Rect(inspectorStartX, 25f, position.width - inspectorStartX - 5f, position.height - 30f);
|
|
|
|
if (selection is LanguageSelect language)
|
|
{
|
|
string title = language.Language.Entry.LanguageName;
|
|
GUIContent inspectorTitle = EditorGUIUtility.TrTextContentWithIcon($" INSPECTOR ({title})", "PrefabVariant On Icon");
|
|
EditorDrawing.DrawHeaderWithBorder(ref inspectorRect, inspectorTitle, 20f, false);
|
|
|
|
Rect inspectorViewRect = inspectorRect;
|
|
inspectorViewRect.y += Spacing;
|
|
inspectorViewRect.yMax -= Spacing;
|
|
inspectorViewRect.xMin += Spacing;
|
|
inspectorViewRect.xMax -= Spacing;
|
|
|
|
GUILayout.BeginArea(inspectorViewRect);
|
|
OnDrawLanguageInspector(language);
|
|
GUILayout.EndArea();
|
|
}
|
|
else if (selection is SectionSelect section)
|
|
{
|
|
string title = section.Section.Name;
|
|
GUIContent inspectorTitle = EditorGUIUtility.TrTextContentWithIcon($" INSPECTOR ({title})", "PrefabVariant On Icon");
|
|
EditorDrawing.DrawHeaderWithBorder(ref inspectorRect, inspectorTitle, 20f, false);
|
|
|
|
Rect inspectorViewRect = inspectorRect;
|
|
inspectorViewRect.y += Spacing;
|
|
inspectorViewRect.yMax -= Spacing;
|
|
inspectorViewRect.xMin += Spacing;
|
|
inspectorViewRect.xMax -= Spacing;
|
|
|
|
GUILayout.BeginArea(inspectorViewRect);
|
|
OnDrawSectionInspector(section);
|
|
GUILayout.EndArea();
|
|
}
|
|
else if (selection is ItemSelect item)
|
|
{
|
|
string title = item.Item.Key;
|
|
GUIContent inspectorTitle = EditorGUIUtility.TrTextContentWithIcon($" INSPECTOR ({title})", "PrefabVariant On Icon");
|
|
EditorDrawing.DrawHeaderWithBorder(ref inspectorRect, inspectorTitle, 20f, false);
|
|
|
|
Rect inspectorViewRect = inspectorRect;
|
|
inspectorViewRect.y += Spacing;
|
|
inspectorViewRect.yMax -= Spacing;
|
|
inspectorViewRect.xMin += Spacing;
|
|
inspectorViewRect.xMax -= Spacing;
|
|
|
|
GUILayout.BeginArea(inspectorViewRect);
|
|
OnDrawSectionItemInspector(item);
|
|
GUILayout.EndArea();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDrawSectionInspector(SectionSelect section)
|
|
{
|
|
// section name change
|
|
EditorGUI.BeginChangeCheck();
|
|
{
|
|
section.Section.Name = EditorGUILayout.TextField("Name", section.Section.Name);
|
|
}
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
section.TreeViewItem.displayName = section.Section.Name;
|
|
}
|
|
|
|
using (new EditorGUI.DisabledGroupScope(true))
|
|
{
|
|
int childerCount = section.TreeViewItem.children?.Count ?? 0;
|
|
EditorGUILayout.IntField(new GUIContent("Keys"), childerCount);
|
|
}
|
|
|
|
EditorGUILayout.Space(2);
|
|
EditorDrawing.Separator();
|
|
EditorGUILayout.Space(1);
|
|
|
|
using (new EditorGUI.DisabledGroupScope(true))
|
|
{
|
|
EditorGUILayout.LabelField("Id: " + section.Section.Id, EditorStyles.miniBoldLabel);
|
|
}
|
|
}
|
|
|
|
private void OnDrawSectionItemInspector(ItemSelect item)
|
|
{
|
|
// item key change
|
|
EditorGUI.BeginChangeCheck();
|
|
{
|
|
item.Item.Key = EditorGUILayout.TextField("Key", item.Item.Key);
|
|
}
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
item.TreeViewItem.displayName = item.Item.Key;
|
|
}
|
|
|
|
EditorGUILayout.Space(2);
|
|
EditorDrawing.Separator();
|
|
EditorGUILayout.Space(1);
|
|
|
|
using (new EditorGUI.DisabledGroupScope(true))
|
|
{
|
|
string parentName = item.Item.Parent.Name;
|
|
string parentText = item.Item.Parent.Id + $" ({parentName})";
|
|
EditorGUILayout.LabelField("Parent Id: " + parentText, EditorStyles.miniBoldLabel);
|
|
EditorGUILayout.LabelField("Id: " + item.Item.Id, EditorStyles.miniBoldLabel);
|
|
}
|
|
}
|
|
|
|
private void OnDrawLanguageInspector(LanguageSelect selection)
|
|
{
|
|
var language = selection.Language;
|
|
var entry = language.Entry;
|
|
var treeView = selection.TreeViewItem;
|
|
|
|
// using (new EditorDrawing.BorderBoxScope(false))
|
|
// {
|
|
// // language name change
|
|
// Rect nameRect = EditorGUILayout.GetControlRect();
|
|
//
|
|
// Rect renameAssetRect = nameRect;
|
|
// renameAssetRect.xMin = nameRect.xMax + 2f;
|
|
// renameAssetRect.width = EditorGUIUtility.singleLineHeight;
|
|
//
|
|
// using (new EditorGUI.DisabledGroupScope(entry.Asset == null))
|
|
// {
|
|
// GUIContent editIcon = EditorGUIUtility.IconContent("editicon.sml", "Rename");
|
|
// if (GUI.Button(renameAssetRect, editIcon, EditorStyles.iconButton))
|
|
// {
|
|
// string assetPath = AssetDatabase.GetAssetPath(entry.Asset);
|
|
// string newName = "(Language) " + entry.LanguageName;
|
|
// AssetDatabase.RenameAsset(assetPath, newName);
|
|
// }
|
|
// }
|
|
//
|
|
// }
|
|
|
|
using (new EditorGUI.DisabledGroupScope(entry.Asset == null))
|
|
{
|
|
// Draw search field
|
|
EditorGUILayout.Space();
|
|
|
|
GUIContent expandText = new GUIContent("Expand");
|
|
float expandWidth = miniLabelButton.CalcSize(expandText).x;
|
|
|
|
var searchRect = EditorGUILayout.GetControlRect();
|
|
searchRect.xMax -= (expandWidth + 2f);
|
|
searchString = searchField.OnGUI(searchRect, searchString);
|
|
|
|
Rect expandRect = new Rect(searchRect.xMax + 2f, searchRect.y, expandWidth, searchRect.height);
|
|
expandRect.y -= 1f;
|
|
|
|
using (new EditorDrawing.BackgroundColorScope("#F7E987"))
|
|
{
|
|
if (GUI.Button(expandRect, expandText, miniLabelButton))
|
|
{
|
|
globalExpanded = !globalExpanded;
|
|
foreach (var section in language.TableSheet)
|
|
{
|
|
section.Reference.IsExpanded = globalExpanded;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entry.Asset != null)
|
|
{
|
|
// Draw localization data
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
|
{
|
|
foreach (var section in GetSearchResult(language, searchString))
|
|
{
|
|
DrawLocalizationKey(section);
|
|
}
|
|
}
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.HelpBox("To begin editing localization data, you must first assign a localization asset.", MessageType.Warning);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawLocalizationKey(TempSheetSection section)
|
|
{
|
|
if (section.Items == null || section.Items.Count == 0)
|
|
return;
|
|
|
|
using (new EditorDrawing.BorderBoxScope(false))
|
|
{
|
|
string sectionName = section.Name.Replace(" ", "");
|
|
section.Reference.IsExpanded = EditorGUILayout.Foldout(section.Reference.IsExpanded, new GUIContent(sectionName), true, EditorDrawing.Styles.miniBoldLabelFoldout);
|
|
|
|
// Show section keys when expanded
|
|
if (section.Reference.IsExpanded)
|
|
{
|
|
foreach (var item in section.Items)
|
|
{
|
|
string keyName = item.Key.Replace(" ", "");
|
|
string key = sectionName + "." + keyName;
|
|
|
|
if (IsMultiline(item.Value))
|
|
key += " (Multiline)";
|
|
|
|
using (new EditorGUILayout.VerticalScope(GUI.skin.box))
|
|
{
|
|
// Display the expandable toggle
|
|
using (new EditorGUILayout.HorizontalScope(GUI.skin.box))
|
|
{
|
|
item.IsExpanded = EditorGUILayout.Foldout(item.IsExpanded, new GUIContent(key), true, EditorDrawing.Styles.miniBoldLabelFoldout);
|
|
}
|
|
|
|
if (item.IsExpanded)
|
|
{
|
|
// Show TextArea when expanded
|
|
float height = (EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.singleLineHeight) * 3;
|
|
height += EditorGUIUtility.standardVerticalSpacing;
|
|
|
|
item.Scroll = EditorGUILayout.BeginScrollView(item.Scroll, GUILayout.Height(height));
|
|
item.Value = EditorGUILayout.TextArea(item.Value, GUILayout.ExpandHeight(true));
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
else
|
|
{
|
|
// Show TextField when collapsed
|
|
item.Value = EditorGUILayout.TextField(item.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.Space(1f);
|
|
}
|
|
|
|
private IEnumerable<TempSheetSection> GetSearchResult(TempLanguageData languageData, string search)
|
|
{
|
|
if (!string.IsNullOrEmpty(search))
|
|
{
|
|
List<TempSheetSection> searchResult = new();
|
|
|
|
foreach (var section in languageData.TableSheet)
|
|
{
|
|
List<TempSheetItem> sectionItems = new();
|
|
string sectionName = section.Name.Replace(" ", "");
|
|
|
|
foreach (var item in section.Items)
|
|
{
|
|
string keyName = item.Key.Replace(" ", "");
|
|
string key = sectionName + "." + keyName;
|
|
|
|
if (key.Contains(search))
|
|
sectionItems.Add(item);
|
|
}
|
|
|
|
searchResult.Add(new TempSheetSection()
|
|
{
|
|
Items = sectionItems,
|
|
Reference = section.Reference
|
|
});
|
|
}
|
|
|
|
return searchResult;
|
|
}
|
|
|
|
return languageData.TableSheet;
|
|
}
|
|
|
|
private bool IsMultiline(string text)
|
|
{
|
|
return text.Contains("\n") || text.Contains("\r");
|
|
}
|
|
|
|
private void BuildLocalizationTable()
|
|
{
|
|
// 1. build table sheet
|
|
currentTable.TableSheet = new();
|
|
foreach (var section in windowData.TableSheet)
|
|
{
|
|
GameLocaizationTable.TableData tableData = new GameLocaizationTable.TableData(section.Name, section.Id);
|
|
|
|
foreach (var item in section.Items)
|
|
{
|
|
GameLocaizationTable.SheetItem sheetItem = new GameLocaizationTable.SheetItem(item.Key, item.Id, item.isGen);
|
|
tableData.SectionSheet.Add(sheetItem);
|
|
}
|
|
|
|
currentTable.TableSheet.Add(tableData);
|
|
}
|
|
|
|
// 2. build table sheet for each language
|
|
IList<LocalizationLanguage> languages = new List<LocalizationLanguage>();
|
|
foreach (var language in windowData.Languages)
|
|
{
|
|
if (language.Entry.Asset == null)
|
|
continue;
|
|
|
|
LocalizationLanguage asset = language.Entry.Asset;
|
|
IList<LocalizationLanguage.LocalizationString> strings = new List<LocalizationLanguage.LocalizationString>();
|
|
|
|
foreach (var section in language.TableSheet)
|
|
{
|
|
string sectionKey = section.Name.Replace(" ", "");
|
|
foreach (var item in section.Items)
|
|
{
|
|
string itemKey = item.Key.Replace(" ", "");
|
|
string key = sectionKey + "." + itemKey;
|
|
|
|
strings.Add(new()
|
|
{
|
|
SectionId = section.Id,
|
|
EntryId = item.Id,
|
|
Key = key,
|
|
Value = item.Value
|
|
});
|
|
}
|
|
}
|
|
|
|
asset.LanguageName = language.Entry.LanguageName;
|
|
asset.Strings = new(strings);
|
|
|
|
languages.Add(asset);
|
|
EditorUtility.SetDirty(asset);
|
|
}
|
|
|
|
currentTable.Languages = new(languages);
|
|
}
|
|
}
|
|
}
|