com.alicizax.unity.framework/Editor/Localization/LocalizationTableWindow/LocalizationWindowUtility.cs

643 lines
22 KiB
C#
Raw Normal View History

2025-09-23 12:04:39 +08:00
using System.Linq;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Localization.Editor
{
public sealed class LanguageEntry
{
public string LanguageName;
public LocalizationLanguage Asset;
public LanguageEntry()
{
}
public LanguageEntry(LocalizationLanguage asset)
{
LanguageName = asset.LanguageName;
Asset = asset;
}
}
public class SheetSectionTreeView
{
public int Id;
public string Name;
public SheetSectionTreeView()
{
Id = Random.Range(10000000, 99999999);
Name = LocalizationWindowUtility.NULL;
}
}
public class SheetItemTreeView
{
public int Id;
public string Key;
public bool isGen;
public SheetSectionTreeView Parent;
public SheetItemTreeView()
{
Id = Random.Range(10000000, 99999999);
Key = LocalizationWindowUtility.NULL;
}
}
public sealed class TempSheetItem
{
// Reference to the original item (shared instance)
public SheetItemTreeView Reference;
// Language-specific localization value
public string Value;
public int Id => Reference.Id;
public string Key
{
get => Reference.Key;
set => Reference.Key = value;
}
public SheetSectionTreeView Parent => Reference.Parent;
public Vector2 Scroll { get; set; }
public bool IsExpanded { get; set; }
}
public sealed class TempSheetSection
{
// Reference to the original section (shared instance)
public SheetSectionData Reference;
// Language-specific items
public List<TempSheetItem> Items = new();
public int Id => Reference.Id;
public string Name
{
get => Reference.Name;
set => Reference.Name = value;
}
}
public sealed class SheetSectionData : SheetSectionTreeView
{
public List<SheetItemTreeView> Items = new();
public bool IsExpanded { get; set; }
}
public sealed class TempLanguageData
{
public LanguageEntry Entry;
public List<TempSheetSection> TableSheet = new();
}
public sealed class LocalizationWindowData
{
public List<TempLanguageData> Languages = new();
public List<SheetSectionData> TableSheet = new();
public int LanguageCount = 0;
public int SectionCount = 0;
public int EntryCount = 0;
}
public static class LocalizationWindowUtility
{
public const string NULL = "null";
public static void BuildWindowData(GameLocaizationTable table, out LocalizationWindowData data)
{
LocalizationWindowData windowData = new();
int languages = 0;
int sections = 0;
int entries = 0;
// 1. Build the main table structure
foreach (var tableData in table.TableSheet)
{
SheetSectionData sectionData = new()
{
Id = tableData.Id,
Name = tableData.SectionName,
Items = new List<SheetItemTreeView>()
};
foreach (var item in tableData.SectionSheet)
{
sectionData.Items.Add(new()
{
Id = item.Id,
Key = item.Key,
Parent = sectionData,
isGen = item.IsGen
});
entries++;
}
windowData.TableSheet.Add(sectionData);
sections++;
}
windowData.SectionCount = sections;
windowData.EntryCount = entries;
// 2. Build language-specific data that references the same sections and items
foreach (var lang in table.Languages)
{
TempLanguageData langData = new()
{
Entry = new(lang)
};
// Assign language name from asset
if (lang != null)
{
string name = lang.LanguageName;
langData.Entry.LanguageName = name;
}
foreach (var globalSection in windowData.TableSheet)
{
int _sectionId = globalSection.Id;
// Create a TempSheetSection reference and assign shared section reference
TempSheetSection tempSection = new()
{
Reference = globalSection
};
// For each item in the global section, we create a TempSheetItem that references it
foreach (var globalItem in globalSection.Items)
{
int _entryId = globalItem.Id;
string value = NULL;
if (lang != null)
{
foreach (var item in lang.Strings)
{
// Try match the localized string from language asset
if (item.SectionId == _sectionId && item.EntryId == _entryId)
{
value = item.Value;
break;
}
}
}
// Add SheetItem to section items list and assign shared item reference
tempSection.Items.Add(new()
{
Reference = globalItem,
Value = value
});
}
langData.TableSheet.Add(tempSection);
}
windowData.Languages.Add(langData);
languages++;
}
windowData.LanguageCount = languages;
data = windowData;
}
/// <summary>
/// Assign a language asset to an existing TempLanguageData.
/// </summary>
public static void AssignLanguage(this LocalizationWindowData data, TempLanguageData languageData, LocalizationLanguage asset)
{
// Assign the asset to the language entry
languageData.Entry.Asset = asset;
if (asset == null)
{
// If no asset is assigned, clear all values
foreach (var tempSection in languageData.TableSheet)
{
foreach (var tempItem in tempSection.Items)
{
tempItem.Value = string.Empty;
}
}
return;
}
else
{
// Assign language name from asset
languageData.Entry.LanguageName = asset.LanguageName;
}
// If we have a valid asset, we try to match each TempSheetItem to a corresponding LocalizationString
foreach (var tempSection in languageData.TableSheet)
{
int sectionId = tempSection.Reference.Id;
foreach (var tempItem in tempSection.Items)
{
int entryId = tempItem.Reference.Id;
string value = string.Empty;
foreach (var item in asset.Strings)
{
// Try match the localized string from language asset
if (item.SectionId == sectionId && item.EntryId == entryId)
{
value = item.Value;
break;
}
}
// Assign the localization value
tempItem.Value = value;
}
}
}
/// <summary>
/// Add a new language.
/// </summary>
public static void AddLanguage(this LocalizationWindowData data, string languageName, LocalizationLanguage asset, bool withIndex = true)
{
if (withIndex)
{
int languageIndex = ++data.LanguageCount;
languageName += " " + languageIndex;
}
var newLanguageEntry = new LanguageEntry()
{
LanguageName = languageName,
Asset = asset
};
TempLanguageData newLangData = new()
{
Entry = newLanguageEntry,
TableSheet = new List<TempSheetSection>()
};
// Reference existing sections and items
foreach (var sectionData in data.TableSheet)
{
TempSheetSection tempSection = new()
{
Reference = sectionData,
Items = new List<TempSheetItem>()
};
foreach (var globalItem in sectionData.Items)
{
tempSection.Items.Add(new()
{
Reference = globalItem,
Value = string.Empty
});
}
newLangData.TableSheet.Add(tempSection);
}
data.Languages.Add(newLangData);
}
/// <summary>
/// Add a new section to the global TableSheet.
/// </summary>
public static SheetSectionData AddSection(this LocalizationWindowData data, string sectionName, bool withIndex = true)
{
if (withIndex)
{
int sectionIndex = ++data.SectionCount;
sectionName += " " + sectionIndex;
}
SheetSectionData newSection = new()
{
Name = sectionName,
Items = new List<SheetItemTreeView>()
};
data.TableSheet.Add(newSection);
// Add corresponding section to each language
foreach (var lang in data.Languages)
{
lang.TableSheet.Add(new()
{
Reference = newSection,
Items = new List<TempSheetItem>()
});
}
return newSection;
}
/// <summary>
/// Remove a section from the global TableSheet by reference.
/// </summary>
public static void RemoveSection(this LocalizationWindowData data, SheetSectionTreeView section)
{
// Remove section
int sectionIndex = data.TableSheet.FindIndex(x => x.Id == section.Id);
if (sectionIndex != -1) data.TableSheet.RemoveAt(sectionIndex);
// Remove the corresponding section from each language
foreach (var lang in data.Languages)
{
var tempSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == section);
if (tempSection != null) lang.TableSheet.Remove(tempSection);
}
}
/// <summary>
/// Add a new item to a given section.
/// </summary>
public static SheetItemTreeView AddItem(this LocalizationWindowData data, SheetSectionData section, string key, bool withIndex = true)
{
if (withIndex)
{
int keyIndex = ++data.EntryCount;
key += " " + keyIndex;
}
SheetItemTreeView newItem = new()
{
Key = key,
Parent = section
};
section.Items.Add(newItem);
// Add corresponding item to each language
foreach (var lang in data.Languages)
{
var tempSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == section);
if (tempSection != null)
{
tempSection.Items.Add(new()
{
Reference = newItem,
Value = string.Empty
});
}
}
data.EntryCount++;
return newItem;
}
/// <summary>
/// Remove an item by reference from the given section.
/// </summary>
public static void RemoveItem(this LocalizationWindowData data, SheetSectionTreeView section, SheetItemTreeView item)
{
// Remove item from section
int sectionIndex = data.TableSheet.FindIndex(x => x.Id == section.Id);
if (sectionIndex != -1) data.TableSheet[sectionIndex].Items.Remove(item);
// Remove corresponding item from each language
foreach (var lang in data.Languages)
{
var tempSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == section);
if (tempSection != null)
{
var tempItem = tempSection.Items.FirstOrDefault(ti => ti.Reference == item);
if (tempItem != null) tempSection.Items.Remove(tempItem);
}
}
}
/// <summary>
/// Moves an item within the same section to a new position.
/// </summary>
public static void OnMoveItemWithinSection(this LocalizationWindowData data, SheetSectionTreeView section, SheetItemTreeView item, int position)
{
// Find the corresponding SheetSectionData in data
var sectionData = data.TableSheet.FirstOrDefault(s => s.Id == section.Id);
if (sectionData == null)
return;
int oldIndex = sectionData.Items.IndexOf(item);
if (oldIndex < 0)
return;
// Clamp position
int insertTo = position > oldIndex ? position - 1 : position;
insertTo = Mathf.Clamp(insertTo, 0, sectionData.Items.Count);
if (oldIndex == insertTo)
return;
sectionData.Items.RemoveAt(oldIndex);
sectionData.Items.Insert(insertTo, item);
// Update in each language
foreach (var lang in data.Languages)
{
// Find the corresponding TempSheetSection
var tempSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == sectionData);
if (tempSection == null) continue;
int langOldIndex = tempSection.Items.FindIndex(i => i.Reference == item);
if (langOldIndex < 0) continue;
// Clamp position
int insertToLang = position > langOldIndex ? position - 1 : position;
insertToLang = Mathf.Clamp(insertToLang, 0, tempSection.Items.Count);
if (langOldIndex == position) continue;
var tempItem = tempSection.Items[langOldIndex];
tempSection.Items.RemoveAt(langOldIndex);
tempSection.Items.Insert(insertToLang, tempItem);
}
}
/// <summary>
/// Moves an item from one section to another section, inserting it at a specific position.
/// </summary>
public static void OnMoveItemToSectionAt(this LocalizationWindowData data, SheetSectionTreeView parent, SheetSectionTreeView section, SheetItemTreeView item, int position)
{
// Find both parent and target sections
var parentSection = data.TableSheet.FirstOrDefault(s => s.Id == parent.Id);
var targetSection = data.TableSheet.FirstOrDefault(s => s.Id == section.Id);
if (parentSection == null || targetSection == null)
return;
// Remove from parent
if (!parentSection.Items.Remove(item))
return;
// Insert into target at specified position
position = Mathf.Max(0, Mathf.Min(position, targetSection.Items.Count));
item.Parent = targetSection;
targetSection.Items.Insert(position, item);
// Update in each language
foreach (var lang in data.Languages)
{
var langParentSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == parentSection);
var langTargetSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == targetSection);
if (langParentSection == null || langTargetSection == null)
continue;
// Remove the corresponding temp item from the parent section
var tempItem = langParentSection.Items.FirstOrDefault(i => i.Reference == item);
if (tempItem == null) continue;
langParentSection.Items.Remove(tempItem);
// Insert into target section
position = Mathf.Max(0, Mathf.Min(position, langTargetSection.Items.Count));
langTargetSection.Items.Insert(position, tempItem);
}
}
/// <summary>
/// Moves an item from one section to another section, adding it to the end.
/// </summary>
public static void OnMoveItemToSection(this LocalizationWindowData data, SheetSectionTreeView parent, SheetSectionTreeView section, SheetItemTreeView item)
{
// Find both parent and target sections
var parentSection = data.TableSheet.FirstOrDefault(s => s.Id == parent.Id);
var targetSection = data.TableSheet.FirstOrDefault(s => s.Id == section.Id);
if (parentSection == null || targetSection == null)
return;
// Remove from parent
if (!parentSection.Items.Remove(item))
return;
item.Parent = targetSection;
targetSection.Items.Add(item);
// Update in each language
foreach (var lang in data.Languages)
{
var langParentSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == parentSection);
var langTargetSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == targetSection);
if (langParentSection == null || langTargetSection == null)
continue;
// Remove the corresponding temp item from the parent section
var tempItem = langParentSection.Items.FirstOrDefault(i => i.Reference == item);
if (tempItem == null) continue;
langParentSection.Items.Remove(tempItem);
langTargetSection.Items.Add(tempItem);
}
}
/// <summary>
/// Moves a section to a new position in the TableSheet list.
/// </summary>
public static void OnMoveSection(this LocalizationWindowData data, SheetSectionTreeView section, int position)
{
var sectionData = data.TableSheet.FirstOrDefault(s => s.Id == section.Id);
if (sectionData == null)
return;
int oldIndex = data.TableSheet.IndexOf(sectionData);
if (oldIndex < 0)
return;
// Clamp position
int insertTo = position > oldIndex ? position - 1 : position;
insertTo = Mathf.Clamp(insertTo, 0, sectionData.Items.Count);
if (oldIndex == insertTo)
return;
data.TableSheet.RemoveAt(oldIndex);
data.TableSheet.Insert(insertTo, sectionData);
// Update in each language
foreach (var lang in data.Languages)
{
var langSection = lang.TableSheet.FirstOrDefault(ts => ts.Reference == sectionData);
if (langSection == null) continue;
int langOldIndex = lang.TableSheet.IndexOf(langSection);
if (langOldIndex < 0) continue;
// Clamp position
int insertToLang = position > langOldIndex ? position - 1 : position;
insertToLang = Mathf.Clamp(insertTo, 0, lang.TableSheet.Count);
if (langOldIndex == position)
continue;
lang.TableSheet.RemoveAt(langOldIndex);
lang.TableSheet.Insert(insertToLang, langSection);
}
}
public static void GenerateCode(GameLocaizationTable table)
{
string filePath = table.GenerateScriptCodePath;
string nameSpace = table.GenerateScriptCodeFirstConfig;
List<LocalizationLanguage.LocalizationString> strings = new List<LocalizationLanguage.LocalizationString>();
var localizationLanguage = table.Languages.Find(t => t.LanguageName == table.GenerateScriptCodeFirstConfig);
StringBuilder sb = new StringBuilder();
sb.AppendLine("/// <summary>");
sb.AppendLine("/// AutoGenerate");
sb.AppendLine("/// </summary>");
sb.AppendLine("public static class LocalizationKey");
sb.AppendLine("{");
for (int i = 0; i < table.TableSheet.Count; i++)
{
var v = table.TableSheet[i];
if (v.SectionSheet.FindIndex(t => t.IsGen) < 0) continue;
sb.AppendLine($"\tpublic sealed class {v.SectionName}");
sb.AppendLine("\t{");
for (int j = 0; j < v.SectionSheet.Count; j++)
{
var item = v.SectionSheet[j];
if (!item.IsGen) continue;
string keyValue = item.Key;
string combineKey=v.SectionName+"."+item.Key;
if (localizationLanguage != null)
{
var localizationString = localizationLanguage.Strings.Find(t => t.Key == combineKey);
keyValue = localizationString != null ? localizationString.Value : combineKey;
}
string varibleName = item.Key.Replace(".", "_").ToUpper();
sb.AppendLine("\t\t/// <summary>");
sb.AppendLine($"\t\t/// {keyValue}");
sb.AppendLine("\t\t/// </summary>");
sb.AppendLine($"\t\tpublic const string {varibleName} = \"{combineKey}\";");
if (j < v.SectionSheet.Count - 1) sb.AppendLine("");
}
sb.AppendLine("\t}");
sb.AppendLine("");
}
sb.AppendLine();
sb.AppendLine("}");
System.IO.File.WriteAllText(filePath, sb.ToString());
AssetDatabase.Refresh();
}
}
}