1
This commit is contained in:
parent
908d3e41f2
commit
ba4085356e
3
Editor/Localization.meta
Normal file
3
Editor/Localization.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b833f8e32e7456bb382bc73386a0297
|
||||||
|
timeCreated: 1758196091
|
||||||
8
Editor/Localization/EditorIcons.meta
Normal file
8
Editor/Localization/EditorIcons.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 98a1fa01bd8596b49a4e4878a3cc0d24
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Editor/Localization/EditorIcons/mask.png
Normal file
BIN
Editor/Localization/EditorIcons/mask.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
117
Editor/Localization/EditorIcons/mask.png.meta
Normal file
117
Editor/Localization/EditorIcons/mask.png.meta
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 74bd1971b983bb84a83b097f4ce40d02
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 1
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
flipGreenChannel: 0
|
||||||
|
isReadable: 0
|
||||||
|
streamingMipmaps: 0
|
||||||
|
streamingMipmapsPriority: 0
|
||||||
|
vTOnly: 0
|
||||||
|
ignoreMipmapLimit: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: 1
|
||||||
|
aniso: 1
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 0
|
||||||
|
wrapV: 0
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 1
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 0
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 0
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
flipbookRows: 1
|
||||||
|
flipbookColumns: 1
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
ignorePngGamma: 0
|
||||||
|
applyGammaDecoding: 0
|
||||||
|
swizzle: 50462976
|
||||||
|
cookieLightType: 0
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: Standalone
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID:
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
35
Editor/Localization/LocalizationConfiguration.cs
Normal file
35
Editor/Localization/LocalizationConfiguration.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
[AlicizaX.Editor.Setting.FilePath("ProjectSettings/LocalizationConfiguration.asset")]
|
||||||
|
public class LocalizationConfiguration : AlicizaX.Editor.Setting.ScriptableSingleton<LocalizationConfiguration>
|
||||||
|
{
|
||||||
|
[SerializeField] private List<string> LanguageTypes = new List<string>()
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 简体中文。
|
||||||
|
/// </summary>
|
||||||
|
"ChineseSimplified",
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 英语。
|
||||||
|
/// </summary>
|
||||||
|
"English",
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日语。
|
||||||
|
/// </summary>
|
||||||
|
"Japanese",
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 俄语。
|
||||||
|
/// </summary>
|
||||||
|
"Russian",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public IReadOnlyList<string> LanguageTypeNames => LanguageTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Editor/Localization/LocalizationConfiguration.cs.meta
Normal file
3
Editor/Localization/LocalizationConfiguration.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 26c8edcdf58c4806a5646a5ec155cc85
|
||||||
|
timeCreated: 1758196101
|
||||||
91
Editor/Localization/LocalizationSettingsProvider.cs
Normal file
91
Editor/Localization/LocalizationSettingsProvider.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using UnityEditorInternal;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
internal class LocalizationSettingsProvider : UnityEditor.SettingsProvider
|
||||||
|
{
|
||||||
|
private SerializedObject _serializedObject;
|
||||||
|
private SerializedProperty _languageTypes;
|
||||||
|
private ReorderableList _languageList;
|
||||||
|
|
||||||
|
public LocalizationSettingsProvider() : base("Project/Localization Settings", SettingsScope.Project) { }
|
||||||
|
|
||||||
|
public override void OnActivate(string searchContext, VisualElement rootElement)
|
||||||
|
{
|
||||||
|
InitGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitGUI()
|
||||||
|
{
|
||||||
|
var setting = LocalizationConfiguration.Instance;
|
||||||
|
_serializedObject?.Dispose();
|
||||||
|
_serializedObject = new SerializedObject(setting);
|
||||||
|
_languageTypes = _serializedObject.FindProperty("LanguageTypes");
|
||||||
|
|
||||||
|
// 自定义 ReorderableList
|
||||||
|
_languageList = new ReorderableList(_serializedObject, _languageTypes,
|
||||||
|
draggable: false, // 禁止拖拽
|
||||||
|
displayHeader: true,
|
||||||
|
displayAddButton: true,
|
||||||
|
displayRemoveButton: true);
|
||||||
|
|
||||||
|
_languageList.drawHeaderCallback = rect =>
|
||||||
|
{
|
||||||
|
EditorGUI.LabelField(rect, "Language Types");
|
||||||
|
};
|
||||||
|
|
||||||
|
_languageList.drawElementCallback = (rect, index, isActive, isFocused) =>
|
||||||
|
{
|
||||||
|
var element = _languageTypes.GetArrayElementAtIndex(index);
|
||||||
|
rect.y += 2;
|
||||||
|
|
||||||
|
// 只显示可编辑的 string 字段
|
||||||
|
element.stringValue = EditorGUI.TextField(
|
||||||
|
new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight),
|
||||||
|
element.stringValue);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(string searchContext)
|
||||||
|
{
|
||||||
|
if (_serializedObject == null || !_serializedObject.targetObject)
|
||||||
|
{
|
||||||
|
InitGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
_serializedObject.Update();
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
// 使用 ReorderableList 绘制
|
||||||
|
_languageList.DoLayoutList();
|
||||||
|
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
_serializedObject.ApplyModifiedProperties();
|
||||||
|
LocalizationConfiguration.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDeactivate()
|
||||||
|
{
|
||||||
|
base.OnDeactivate();
|
||||||
|
LocalizationConfiguration.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
static LocalizationSettingsProvider s_provider;
|
||||||
|
|
||||||
|
[SettingsProvider]
|
||||||
|
public static SettingsProvider CreateMyCustomSettingsProvider()
|
||||||
|
{
|
||||||
|
if (s_provider == null)
|
||||||
|
{
|
||||||
|
s_provider = new LocalizationSettingsProvider();
|
||||||
|
}
|
||||||
|
return s_provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Editor/Localization/LocalizationSettingsProvider.cs.meta
Normal file
3
Editor/Localization/LocalizationSettingsProvider.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5647a4aaf19f42bf852a701955e888f5
|
||||||
|
timeCreated: 1758196661
|
||||||
8
Editor/Localization/LocalizationTableWindow.meta
Normal file
8
Editor/Localization/LocalizationTableWindow.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f61eb05483ae45343a3667fcb64c8410
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: baaf3e4bb10c71c478bcf4f9ce2d3d23
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,194 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
public static class LocalizationExporter
|
||||||
|
{
|
||||||
|
public static void ExportLocalizationToCSV(LocalizationWindowData windowData, string filePath)
|
||||||
|
{
|
||||||
|
if (windowData == null || windowData.Languages.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("Localization window data is empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allKeys = new List<string>();
|
||||||
|
foreach (var section in windowData.TableSheet)
|
||||||
|
{
|
||||||
|
foreach (var item in section.Items)
|
||||||
|
{
|
||||||
|
string key = $"{section.Id}:{item.Id}";
|
||||||
|
allKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageLookups = windowData.Languages.ToDictionary(
|
||||||
|
lang => lang.Entry.LanguageName,
|
||||||
|
lang => lang.TableSheet.SelectMany(section => section.Items)
|
||||||
|
.ToDictionary(item => $"{item.Parent.Id}:{item.Id}", item => item.Value)
|
||||||
|
);
|
||||||
|
|
||||||
|
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
|
||||||
|
writer.Write("Key");
|
||||||
|
foreach (var lang in windowData.Languages)
|
||||||
|
{
|
||||||
|
writer.Write(",");
|
||||||
|
WriteEscapedCsvField(writer, lang.Entry.LanguageName);
|
||||||
|
}
|
||||||
|
writer.WriteLine();
|
||||||
|
|
||||||
|
foreach (var key in allKeys)
|
||||||
|
{
|
||||||
|
WriteEscapedCsvField(writer, key);
|
||||||
|
foreach (var lang in windowData.Languages)
|
||||||
|
{
|
||||||
|
writer.Write(",");
|
||||||
|
var translations = languageLookups[lang.Entry.LanguageName];
|
||||||
|
string value = translations.ContainsKey(key) ? translations[key] : "";
|
||||||
|
WriteEscapedCsvField(writer, value);
|
||||||
|
}
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"Localization exported successfully to: {filePath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void WriteEscapedCsvField(StreamWriter writer, string field)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(field))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r"))
|
||||||
|
{
|
||||||
|
|
||||||
|
writer.Write("\"");
|
||||||
|
writer.Write(field.Replace("\"", "\"\""));
|
||||||
|
writer.Write("\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.Write(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ImportLocalizationFromCSV(LocalizationWindowData windowData, string filePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
Debug.LogError($"CSV file not found: {filePath}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var reader = new StreamReader(filePath, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
|
||||||
|
string headerLine = reader.ReadLine();
|
||||||
|
if (headerLine == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("CSV file is empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var headers = ParseCsvLine(headerLine);
|
||||||
|
var languageNames = headers.Skip(1).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
var existingLanguages = windowData.Languages
|
||||||
|
.Where(l => languageNames.Contains(l.Entry.LanguageName))
|
||||||
|
.ToDictionary(l => l.Entry.LanguageName);
|
||||||
|
|
||||||
|
if (existingLanguages.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("No matching languages in the Localization Window Data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
var fields = ParseCsvLine(line);
|
||||||
|
if (fields.Length == 0) continue;
|
||||||
|
|
||||||
|
string key = fields[0];
|
||||||
|
string[] keyParts = key.Split(':');
|
||||||
|
|
||||||
|
// Get sectionId and itemId
|
||||||
|
if (keyParts.Length != 2 ||
|
||||||
|
!int.TryParse(keyParts[0], out int sectionId) ||
|
||||||
|
!int.TryParse(keyParts[1], out int itemId))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Invalid key format: {key}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < languageNames.Count; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 >= fields.Length) break;
|
||||||
|
|
||||||
|
string langName = languageNames[i];
|
||||||
|
if (existingLanguages.TryGetValue(langName, out var languageData))
|
||||||
|
{
|
||||||
|
string value = fields[i + 1];
|
||||||
|
|
||||||
|
var matchingSection = languageData.TableSheet.FirstOrDefault(sec => sec.Id == sectionId);
|
||||||
|
var matchingItem = matchingSection?.Items.FirstOrDefault(item => item.Id == itemId);
|
||||||
|
if (matchingItem != null) matchingItem.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Localization CSV imported successfully into existing languages.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] ParseCsvLine(string line)
|
||||||
|
{
|
||||||
|
var fields = new List<string>();
|
||||||
|
var currentField = new StringBuilder();
|
||||||
|
bool inQuotes = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < line.Length; i++)
|
||||||
|
{
|
||||||
|
char c = line[i];
|
||||||
|
|
||||||
|
if (c == '"' && (i == 0 || line[i - 1] != '\\'))
|
||||||
|
{
|
||||||
|
if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
|
||||||
|
{
|
||||||
|
currentField.Append('"');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inQuotes = !inQuotes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (c == ',' && !inQuotes)
|
||||||
|
{
|
||||||
|
fields.Add(currentField.ToString());
|
||||||
|
currentField.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentField.Append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.Add(currentField.ToString());
|
||||||
|
|
||||||
|
return fields.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a40ca89d13c4a3448bd77e3abcfefaad
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
// using UnityEditor.Build;
|
||||||
|
// using UnityEditor;
|
||||||
|
// using UnityEngine;
|
||||||
|
// using UHFPS.Runtime;
|
||||||
|
// using UHFPS.Tools;
|
||||||
|
// using ThunderWire.Editors;
|
||||||
|
// using System.Linq;
|
||||||
|
// using System;
|
||||||
|
//
|
||||||
|
// namespace AlicizaX.Localization.Editor
|
||||||
|
// {
|
||||||
|
// [CustomEditor(typeof(GameLocalization))]
|
||||||
|
// public class GameLocalizationEditor : InspectorEditor<GameLocalization>
|
||||||
|
// {
|
||||||
|
// private const string LOCALIZATION_SYMBOL = "UHFPS_LOCALIZATION";
|
||||||
|
//
|
||||||
|
// public override void OnInspectorGUI()
|
||||||
|
// {
|
||||||
|
// EditorDrawing.DrawInspectorHeader(new GUIContent("Game Localization"), Target);
|
||||||
|
// EditorGUILayout.Space();
|
||||||
|
//
|
||||||
|
// serializedObject.Update();
|
||||||
|
// {
|
||||||
|
// string[] languages = new string[0];
|
||||||
|
// if (Target.LocalizationTable != null)
|
||||||
|
// {
|
||||||
|
// languages = Target.LocalizationTable.Languages
|
||||||
|
// .Where(x => x != null)
|
||||||
|
// .Select((x, i) => x.LanguageName.Or("Unknown Language " + i))
|
||||||
|
// .ToArray();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Properties.Draw("LocalizationTable");
|
||||||
|
// DrawDefaultLanguageSelector(languages);
|
||||||
|
// Properties.Draw("ShowWarnings");
|
||||||
|
//
|
||||||
|
// if (Application.isPlaying)
|
||||||
|
// {
|
||||||
|
// EditorGUILayout.Space();
|
||||||
|
// using (new EditorGUI.DisabledGroupScope(languages.Length <= 0))
|
||||||
|
// {
|
||||||
|
// if (GUILayout.Button("Set Language", GUILayout.Height(25f)))
|
||||||
|
// {
|
||||||
|
// Target.ChangeLanguage(Target.DefaultLanguage);
|
||||||
|
// string name = languages[Target.DefaultLanguage];
|
||||||
|
// Debug.Log("Language set to " + name);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// serializedObject.ApplyModifiedProperties();
|
||||||
|
//
|
||||||
|
// if (!Application.isPlaying)
|
||||||
|
// {
|
||||||
|
// EditorGUILayout.Space();
|
||||||
|
// EditorGUILayout.HelpBox("To enable or disable UHFPS localization, click the button below. A scripting symbol will automatically be included in the player settings to allow you to use Game Localization.", MessageType.Info);
|
||||||
|
// EditorGUILayout.Space(1f);
|
||||||
|
//
|
||||||
|
// string toggleText = CheckActivation() ? "Disable" : "Enable";
|
||||||
|
// if (GUILayout.Button($"{toggleText} GLoc Localization", GUILayout.Height(25f)))
|
||||||
|
// {
|
||||||
|
// ToggleScriptingSymbol();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void DrawDefaultLanguageSelector(string[] languages)
|
||||||
|
// {
|
||||||
|
// if (Target.LocalizationTable != null && languages.Length > 0)
|
||||||
|
// {
|
||||||
|
// string selected = languages.Length > 0 && Target.DefaultLanguage >= 0
|
||||||
|
// ? languages[Target.DefaultLanguage]
|
||||||
|
// : string.Empty;
|
||||||
|
//
|
||||||
|
// EditorDrawing.DrawStringSelectPopup(new GUIContent("Default Language"), new GUIContent("Language"), languages, selected, (lang) =>
|
||||||
|
// {
|
||||||
|
// int index = Array.FindIndex(languages, x => lang == x);
|
||||||
|
// Properties["DefaultLanguage"].intValue = index;
|
||||||
|
// serializedObject.ApplyModifiedProperties();
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Properties["DefaultLanguage"].intValue = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private bool CheckActivation()
|
||||||
|
// {
|
||||||
|
// var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
|
||||||
|
// var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
|
||||||
|
// string defines = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget);
|
||||||
|
// return defines.Contains(LOCALIZATION_SYMBOL);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void ToggleScriptingSymbol()
|
||||||
|
// {
|
||||||
|
// var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
|
||||||
|
// var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
|
||||||
|
//
|
||||||
|
// string defines = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget);
|
||||||
|
// string[] definesParts = defines.Split(';');
|
||||||
|
//
|
||||||
|
// if (defines.Contains(LOCALIZATION_SYMBOL))
|
||||||
|
// definesParts = definesParts.Except(new[] { LOCALIZATION_SYMBOL }).ToArray();
|
||||||
|
// else
|
||||||
|
// definesParts = definesParts.Concat(new[] { LOCALIZATION_SYMBOL }).ToArray();
|
||||||
|
//
|
||||||
|
// defines = string.Join(";", definesParts);
|
||||||
|
// PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, defines);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 83da11971febf634ba7c141b42267ae6
|
||||||
@ -0,0 +1,577 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b5ae4bf262d12a4899f2a46672ed072
|
||||||
@ -0,0 +1,642 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7ca9d65f294bd6843b5ca8d0a0eb97f6
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3bcfa73f585d7aa4caee8e18fab11c4b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using AlicizaX.Editor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Callbacks;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(GameLocaizationTable))]
|
||||||
|
internal class GameLocaizationTableEditor : InspectorEditor<GameLocaizationTable>
|
||||||
|
{
|
||||||
|
private List<string> popConfig = new List<string>();
|
||||||
|
|
||||||
|
[OnOpenAsset]
|
||||||
|
public static bool OnOpenAsset(int instanceId, int line)
|
||||||
|
|
||||||
|
{
|
||||||
|
var obj = EditorUtility.InstanceIDToObject(instanceId);
|
||||||
|
var asset = obj as GameLocaizationTable;
|
||||||
|
|
||||||
|
if (asset == null) return false;
|
||||||
|
string path = AssetDatabase.GetAssetPath(asset);
|
||||||
|
EditorPrefs.SetString("LastSelectedGameLocaizationTable", path);
|
||||||
|
OpenLocalizationEditor();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void OnEnable()
|
||||||
|
{
|
||||||
|
base.OnEnable();
|
||||||
|
popConfig.Clear();
|
||||||
|
if (Target.Languages.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var lang in Target.Languages)
|
||||||
|
{
|
||||||
|
string name = lang.LanguageName.Or("Unknown");
|
||||||
|
popConfig.Add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
EditorDrawing.DrawInspectorHeader(new GUIContent("Game Localization Table"), Target);
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
serializedObject.Update();
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("You can edit this language in the Game Localization Table Editor window.", MessageType.Info);
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
using (new EditorDrawing.BorderBoxScope(new GUIContent("GenCode"), roundedBox: false))
|
||||||
|
{
|
||||||
|
Properties.Draw("GenerateScriptCodePath", new GUIContent("File Path"));
|
||||||
|
EditorDrawing.DrawStringSelectPopup(new GUIContent("Gen Lang"), new GUIContent("None"), popConfig.ToArray(), Target.GenerateScriptCodeFirstConfig, (e) => { Target.GenerateScriptCodeFirstConfig = e; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
using (new EditorDrawing.BorderBoxScope(new GUIContent("Languages"), roundedBox: false))
|
||||||
|
{
|
||||||
|
if (Target.Languages.Count > 0)
|
||||||
|
{
|
||||||
|
using (new EditorGUI.DisabledGroupScope(true))
|
||||||
|
{
|
||||||
|
foreach (var lang in Target.Languages)
|
||||||
|
{
|
||||||
|
string name = lang.LanguageName.Or("Unknown");
|
||||||
|
EditorGUILayout.ObjectField(new GUIContent(name), lang, typeof(LocalizationLanguage), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("There are currently no languages available, open the localization editor and add new languages.", MessageType.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
{
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Open Localization Editor", GUILayout.Width(180f), GUILayout.Height(25)))
|
||||||
|
{
|
||||||
|
string path = AssetDatabase.GetAssetPath(target);
|
||||||
|
EditorPrefs.SetString("LastSelectedGameLocaizationTable", path);
|
||||||
|
OpenLocalizationEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Gen Code", GUILayout.Width(180f), GUILayout.Height(25)))
|
||||||
|
{
|
||||||
|
LocalizationWindowUtility.GenerateCode(Target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Tools/AlicizaX/Localization/Open Localization Editor")]
|
||||||
|
private static void OpenLocalizationEditor()
|
||||||
|
{
|
||||||
|
EditorWindow window = EditorWindow.GetWindow<LocalizationTableWindow>(false, "Localization Editor", true);
|
||||||
|
window.minSize = new(1000, 500);
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Tools/AlicizaX/Localization/Create Localization Table")]
|
||||||
|
private static void CreateLocalizationTable()
|
||||||
|
{
|
||||||
|
string path = "Assets/Localization/";
|
||||||
|
string fileName = "LocalizationTable.asset";
|
||||||
|
string finalPath = Path.Combine(path, fileName);
|
||||||
|
|
||||||
|
GameLocaizationTable table = ScriptableObject.CreateInstance<GameLocaizationTable>();
|
||||||
|
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.CreateAsset(table, finalPath);
|
||||||
|
EditorUtility.SetDirty(table);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
table.TableSheet = new List<GameLocaizationTable.TableData>();
|
||||||
|
IReadOnlyList<string> languageTypes = LocalizationConfiguration.Instance.LanguageTypeNames;
|
||||||
|
|
||||||
|
for (int i = 0; i < languageTypes.Count; i++)
|
||||||
|
{
|
||||||
|
LocalizationLanguage asset = ScriptableObject.CreateInstance<LocalizationLanguage>();
|
||||||
|
asset.name = languageTypes[i];
|
||||||
|
asset.LanguageName = languageTypes[i];
|
||||||
|
asset.Strings = new List<LocalizationLanguage.LocalizationString>();
|
||||||
|
table.Languages.Add(asset);
|
||||||
|
AssetDatabase.AddObjectToAsset(asset, table);
|
||||||
|
EditorUtility.SetDirty(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorUtility.SetDirty(table);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3135dc7bd9a1fd649926188f4aa33fff
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
using AlicizaX.Editor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(LocalizationLanguage))]
|
||||||
|
public class LocalizationLanguageEditor : InspectorEditor<LocalizationLanguage>
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
EditorDrawing.DrawInspectorHeader(new GUIContent("Localization Language"), Target);
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
serializedObject.Update();
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("You can edit this language in the Game Localization Table Editor window.", MessageType.Info);
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
Properties.Draw("LanguageName");
|
||||||
|
using (new EditorGUI.DisabledGroupScope(true))
|
||||||
|
{
|
||||||
|
int entries = Target.Strings.Count;
|
||||||
|
EditorGUILayout.TextField("Strings", entries.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6da7bdd80cb1d7b4d86b936f963c932a
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7371740917c3134bb222528d84dd9c9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,174 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AlicizaX.Editor;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
public class LanguagesTreeView : TreeView
|
||||||
|
{
|
||||||
|
private const string k_DeleteCommand = "Delete";
|
||||||
|
private const string k_SoftDeleteCommand = "SoftDelete";
|
||||||
|
private const string k_NewLanguage = "New Language";
|
||||||
|
|
||||||
|
public Action<LocalizationTableWindow.WindowSelection> OnLanguageSelect;
|
||||||
|
|
||||||
|
private readonly LocalizationWindowData windowData;
|
||||||
|
private readonly GameLocaizationTable _table;
|
||||||
|
|
||||||
|
internal class LanguageTreeViewItem : TreeViewItem
|
||||||
|
{
|
||||||
|
public TempLanguageData language;
|
||||||
|
|
||||||
|
public LanguageTreeViewItem(int id, int depth, TempLanguageData language) : base(id, depth, language.Entry.LanguageName)
|
||||||
|
{
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LanguagesTreeView(TreeViewState state, LocalizationWindowData data, GameLocaizationTable table) : base(state)
|
||||||
|
{
|
||||||
|
windowData = data;
|
||||||
|
rowHeight = 20f;
|
||||||
|
_table = table;
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override TreeViewItem BuildRoot()
|
||||||
|
{
|
||||||
|
var root = new TreeViewItem { id = 0, depth = -1, displayName = "Languages" };
|
||||||
|
int id = 1;
|
||||||
|
|
||||||
|
foreach (var lang in windowData.Languages)
|
||||||
|
{
|
||||||
|
root.AddChild(new LanguageTreeViewItem(id++, 1, lang));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.children == null)
|
||||||
|
root.children = new List<TreeViewItem>();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RowGUI(RowGUIArgs args)
|
||||||
|
{
|
||||||
|
var item = args.item;
|
||||||
|
var rect = args.rowRect;
|
||||||
|
|
||||||
|
GUIContent labelIcon = EditorGUIUtility.TrTextContentWithIcon(" " + item.displayName, "BuildSettings.Web.Small");
|
||||||
|
Rect labelRect = new(rect.x + 2f, rect.y, rect.width - 2f, rect.height);
|
||||||
|
EditorGUI.LabelField(labelRect, labelIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(Rect rect)
|
||||||
|
{
|
||||||
|
EditorDrawing.DrawHeaderWithBorder(ref rect, new GUIContent("LANGUAGES"), 20f, false);
|
||||||
|
base.OnGUI(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddNewLanguage()
|
||||||
|
{
|
||||||
|
windowData.AddLanguage(k_NewLanguage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanRename(TreeViewItem item) => false;
|
||||||
|
protected override bool CanMultiSelect(TreeViewItem item) => true;
|
||||||
|
|
||||||
|
protected override void RenameEnded(RenameEndedArgs args)
|
||||||
|
{
|
||||||
|
if (!args.acceptedRename)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var renamedItem = FindItem(args.itemID, rootItem);
|
||||||
|
if (renamedItem == null) return;
|
||||||
|
|
||||||
|
renamedItem.displayName = args.newName;
|
||||||
|
if (renamedItem is LanguageTreeViewItem item)
|
||||||
|
item.language.Entry.LanguageName = args.newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SingleClickedItem(int id)
|
||||||
|
{
|
||||||
|
var selectedItem = FindItem(id, rootItem);
|
||||||
|
if (selectedItem != null)
|
||||||
|
{
|
||||||
|
if (selectedItem is LanguageTreeViewItem item)
|
||||||
|
{
|
||||||
|
OnLanguageSelect?.Invoke(new LocalizationTableWindow.LanguageSelect()
|
||||||
|
{
|
||||||
|
Language = item.language,
|
||||||
|
TreeViewItem = item
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SelectionChanged(IList<int> selectedIds)
|
||||||
|
{
|
||||||
|
if (selectedIds.Count > 1)
|
||||||
|
OnLanguageSelect?.Invoke(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanStartDrag(CanStartDragArgs args)
|
||||||
|
{
|
||||||
|
var firstItem = FindItem(args.draggedItemIDs[0], rootItem);
|
||||||
|
return args.draggedItemIDs.All(id => FindItem(id, rootItem).parent == firstItem.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
|
||||||
|
{
|
||||||
|
DragAndDrop.PrepareStartDrag();
|
||||||
|
DragAndDrop.SetGenericData("IDs", args.draggedItemIDs.ToArray());
|
||||||
|
DragAndDrop.SetGenericData("Type", "Languages");
|
||||||
|
DragAndDrop.StartDrag("Languages");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void HandleCommandEvent(Event uiEvent)
|
||||||
|
{
|
||||||
|
if (uiEvent.type == EventType.ValidateCommand)
|
||||||
|
{
|
||||||
|
switch (uiEvent.commandName)
|
||||||
|
{
|
||||||
|
case k_DeleteCommand:
|
||||||
|
case k_SoftDeleteCommand:
|
||||||
|
if (HasSelection())
|
||||||
|
uiEvent.Use();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (uiEvent.type == EventType.ExecuteCommand)
|
||||||
|
{
|
||||||
|
switch (uiEvent.commandName)
|
||||||
|
{
|
||||||
|
case k_DeleteCommand:
|
||||||
|
case k_SoftDeleteCommand:
|
||||||
|
DeleteSelected();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteSelected()
|
||||||
|
{
|
||||||
|
if (!HasFocus())
|
||||||
|
return;
|
||||||
|
//
|
||||||
|
// var toDelete = GetSelection().OrderByDescending(i => i);
|
||||||
|
// if (toDelete.Count() <= 0) return;
|
||||||
|
//
|
||||||
|
// foreach (var index in toDelete)
|
||||||
|
// {
|
||||||
|
// windowData.Languages.RemoveAt(index - 1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// OnLanguageSelect?.Invoke(null);
|
||||||
|
// SetSelection(new int[0]);
|
||||||
|
// Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7a69a16aa08dee4b888f239c6203c71
|
||||||
@ -0,0 +1,447 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AlicizaX.Editor;
|
||||||
|
using UnityEditor.IMGUI.Controls;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization.Editor
|
||||||
|
{
|
||||||
|
public class TableSheetTreeView : TreeView
|
||||||
|
{
|
||||||
|
private const string k_DeleteCommand = "Delete";
|
||||||
|
private const string k_SoftDeleteCommand = "SoftDelete";
|
||||||
|
private const string k_NewSection = "New Section";
|
||||||
|
private const string k_NewEntry = "New Entry";
|
||||||
|
private const int k_TreeViewStartIndex = 100;
|
||||||
|
|
||||||
|
public Action<LocalizationTableWindow.WindowSelection> OnTableSheetSelect;
|
||||||
|
|
||||||
|
private readonly LocalizationWindowData windowData;
|
||||||
|
|
||||||
|
private bool InitiateContextMenuOnNextRepaint = false;
|
||||||
|
private int ContextSelectedID = -1;
|
||||||
|
|
||||||
|
internal class LstrSectionTreeViewItem : TreeViewItem
|
||||||
|
{
|
||||||
|
public SheetSectionTreeView Section;
|
||||||
|
|
||||||
|
public int Id => Section.Id;
|
||||||
|
|
||||||
|
public LstrSectionTreeViewItem(int id, int depth, SheetSectionTreeView tableData) : base(id, depth, tableData.Name)
|
||||||
|
{
|
||||||
|
Section = tableData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LstrEntryTreeViewItem : TreeViewItem
|
||||||
|
{
|
||||||
|
public SheetItemTreeView Item;
|
||||||
|
|
||||||
|
public int Id => Item.Id;
|
||||||
|
|
||||||
|
public LstrEntryTreeViewItem(int id, int depth, SheetItemTreeView item) : base(id, depth, item.Key)
|
||||||
|
{
|
||||||
|
Item = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableSheetTreeView(TreeViewState state, LocalizationWindowData data) : base(state)
|
||||||
|
{
|
||||||
|
windowData = data;
|
||||||
|
rowHeight = 20f;
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TreeViewItem BuildRoot()
|
||||||
|
{
|
||||||
|
int id = k_TreeViewStartIndex;
|
||||||
|
var root = new TreeViewItem { id = id++, depth = -1, displayName = "TableSheet" };
|
||||||
|
|
||||||
|
foreach (var section in windowData.TableSheet)
|
||||||
|
{
|
||||||
|
string sectionName = section.Name;
|
||||||
|
var sectionItem = new LstrSectionTreeViewItem(id++, 0, section);
|
||||||
|
|
||||||
|
root.AddChild(sectionItem);
|
||||||
|
|
||||||
|
// Add items within each section as children of the section.
|
||||||
|
foreach (var key in section.Items)
|
||||||
|
{
|
||||||
|
sectionItem.AddChild(new LstrEntryTreeViewItem(id++, 1, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.children == null)
|
||||||
|
root.children = new List<TreeViewItem>();
|
||||||
|
|
||||||
|
SetupDepthsFromParentsAndChildren(root);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RowGUI(RowGUIArgs args)
|
||||||
|
{
|
||||||
|
var item = args.item;
|
||||||
|
var rect = args.rowRect;
|
||||||
|
|
||||||
|
GUIContent labelIcon = new GUIContent(item.displayName);
|
||||||
|
if (item is LstrSectionTreeViewItem) labelIcon = EditorGUIUtility.TrTextContentWithIcon(" " + item.displayName, "Folder Icon");
|
||||||
|
|
||||||
|
Rect labelRect = new(rect.x + GetContentIndent(item), rect.y, rect.width - GetContentIndent(item), rect.height);
|
||||||
|
Rect toggleRect = new Rect(rect.xMax - EditorGUIUtility.singleLineHeight - 4f, rect.y, EditorGUIUtility.singleLineHeight, rect.height);
|
||||||
|
|
||||||
|
EditorGUI.LabelField(labelRect, labelIcon);
|
||||||
|
if (item is LstrEntryTreeViewItem entryTreeViewItem)
|
||||||
|
{
|
||||||
|
entryTreeViewItem.Item.isGen = EditorGUI.Toggle(toggleRect, entryTreeViewItem.Item.isGen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(Rect rect)
|
||||||
|
{
|
||||||
|
Rect headerRect = EditorDrawing.DrawHeaderWithBorder(ref rect, new GUIContent("TABLE SHEET"), 20f, false);
|
||||||
|
headerRect.xMin = headerRect.xMax - EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
headerRect.width = EditorGUIUtility.singleLineHeight;
|
||||||
|
headerRect.y += EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
|
||||||
|
if (GUI.Button(headerRect, EditorUtils.Styles.PlusIcon, EditorStyles.iconButton))
|
||||||
|
{
|
||||||
|
OnAddNewSection();
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InitiateContextMenuOnNextRepaint)
|
||||||
|
{
|
||||||
|
InitiateContextMenuOnNextRepaint = false;
|
||||||
|
PopUpContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleCommandEvent(Event.current);
|
||||||
|
base.OnGUI(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopUpContextMenu()
|
||||||
|
{
|
||||||
|
var selectedItem = FindItem(ContextSelectedID, rootItem);
|
||||||
|
var menu = new GenericMenu();
|
||||||
|
|
||||||
|
if (selectedItem is LstrSectionTreeViewItem section)
|
||||||
|
{
|
||||||
|
menu.AddItem(new GUIContent("Add Entry"), false, () =>
|
||||||
|
{
|
||||||
|
OnAddNewSectionEntry(section.Section.Id);
|
||||||
|
ContextSelectedID = -1;
|
||||||
|
Reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Delete"), false, () =>
|
||||||
|
{
|
||||||
|
DeleteLstrSection(section.Section);
|
||||||
|
ContextSelectedID = -1;
|
||||||
|
Reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (selectedItem is LstrEntryTreeViewItem item)
|
||||||
|
{
|
||||||
|
LstrSectionTreeViewItem parentSection = (LstrSectionTreeViewItem)item.parent;
|
||||||
|
menu.AddItem(new GUIContent("Delete"), false, () =>
|
||||||
|
{
|
||||||
|
DeleteLstrEntry(parentSection.Section, item.Item);
|
||||||
|
ContextSelectedID = -1;
|
||||||
|
Reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.ShowAsContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddNewSection()
|
||||||
|
{
|
||||||
|
windowData.AddSection(k_NewSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAddNewSectionEntry(int sectionId)
|
||||||
|
{
|
||||||
|
foreach (var section in windowData.TableSheet)
|
||||||
|
{
|
||||||
|
if (section.Id == sectionId)
|
||||||
|
{
|
||||||
|
windowData.AddItem(section, k_NewEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanRename(TreeViewItem item) => true;
|
||||||
|
protected override bool CanMultiSelect(TreeViewItem item) => true;
|
||||||
|
|
||||||
|
protected override void ContextClickedItem(int id)
|
||||||
|
{
|
||||||
|
InitiateContextMenuOnNextRepaint = true;
|
||||||
|
ContextSelectedID = id;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RenameEnded(RenameEndedArgs args)
|
||||||
|
{
|
||||||
|
if (!args.acceptedRename)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var renamedItem = FindItem(args.itemID, rootItem);
|
||||||
|
if (renamedItem == null) return;
|
||||||
|
|
||||||
|
renamedItem.displayName = args.newName;
|
||||||
|
if (renamedItem is LstrEntryTreeViewItem item)
|
||||||
|
{
|
||||||
|
item.Item.Key = args.newName;
|
||||||
|
}
|
||||||
|
else if (renamedItem is LstrSectionTreeViewItem section)
|
||||||
|
{
|
||||||
|
section.Section.Name = args.newName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SingleClickedItem(int id)
|
||||||
|
{
|
||||||
|
var selectedItem = FindItem(id, rootItem);
|
||||||
|
if (selectedItem != null)
|
||||||
|
{
|
||||||
|
if (selectedItem is LstrSectionTreeViewItem section)
|
||||||
|
{
|
||||||
|
OnTableSheetSelect?.Invoke(new LocalizationTableWindow.SectionSelect()
|
||||||
|
{
|
||||||
|
Section = section.Section,
|
||||||
|
TreeViewItem = section
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (selectedItem is LstrEntryTreeViewItem entry)
|
||||||
|
{
|
||||||
|
OnTableSheetSelect?.Invoke(new LocalizationTableWindow.ItemSelect()
|
||||||
|
{
|
||||||
|
Item = entry.Item,
|
||||||
|
TreeViewItem = entry
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SelectionChanged(IList<int> selectedIds)
|
||||||
|
{
|
||||||
|
if (selectedIds.Count > 1)
|
||||||
|
OnTableSheetSelect?.Invoke(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanStartDrag(CanStartDragArgs args)
|
||||||
|
{
|
||||||
|
var firstItem = FindItem(args.draggedItemIDs[0], rootItem);
|
||||||
|
return args.draggedItemIDs.All(id => FindItem(id, rootItem).parent == firstItem.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
|
||||||
|
{
|
||||||
|
DragAndDrop.PrepareStartDrag();
|
||||||
|
DragAndDrop.SetGenericData("IDs", args.draggedItemIDs.ToArray());
|
||||||
|
DragAndDrop.SetGenericData("Type", "TableSheet");
|
||||||
|
DragAndDrop.StartDrag("TableSheet");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DragAndDropVisualMode HandleDragAndDrop(DragAndDropArgs args)
|
||||||
|
{
|
||||||
|
int[] draggedIDs = (int[])DragAndDrop.GetGenericData("IDs");
|
||||||
|
string type = (string)DragAndDrop.GetGenericData("Type");
|
||||||
|
|
||||||
|
if (!type.Equals("TableSheet"))
|
||||||
|
return DragAndDropVisualMode.Rejected;
|
||||||
|
|
||||||
|
switch (args.dragAndDropPosition)
|
||||||
|
{
|
||||||
|
case DragAndDropPosition.BetweenItems:
|
||||||
|
if (args.parentItem is LstrSectionTreeViewItem section1)
|
||||||
|
{
|
||||||
|
bool acceptDrag = false;
|
||||||
|
foreach (var draggedId in draggedIDs)
|
||||||
|
{
|
||||||
|
var draggedItem = FindItem(draggedId, rootItem);
|
||||||
|
if (draggedItem != null && draggedItem is LstrEntryTreeViewItem item)
|
||||||
|
{
|
||||||
|
if (args.performDrop)
|
||||||
|
{
|
||||||
|
if (draggedItem.parent == section1)
|
||||||
|
{
|
||||||
|
OnMoveItemWithinSection(section1.Section, item.Item, args.insertAtIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var parentSection = (LstrSectionTreeViewItem)draggedItem.parent;
|
||||||
|
OnMoveItemToSectionAt(parentSection.Section, section1.Section, item.Item, args.insertAtIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptDrag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.performDrop && acceptDrag)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
SetSelection(new int[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptDrag
|
||||||
|
? DragAndDropVisualMode.Move
|
||||||
|
: DragAndDropVisualMode.Rejected;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool acceptDrag = false;
|
||||||
|
foreach (var draggedId in draggedIDs)
|
||||||
|
{
|
||||||
|
var draggedItem = FindItem(draggedId, rootItem);
|
||||||
|
if (draggedItem != null && draggedItem is LstrSectionTreeViewItem section)
|
||||||
|
{
|
||||||
|
if (args.performDrop)
|
||||||
|
OnMoveSection(section.Section, args.insertAtIndex);
|
||||||
|
|
||||||
|
acceptDrag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.performDrop && acceptDrag)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
SetSelection(new int[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptDrag
|
||||||
|
? DragAndDropVisualMode.Move
|
||||||
|
: DragAndDropVisualMode.Rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DragAndDropPosition.UponItem:
|
||||||
|
if (args.parentItem is LstrSectionTreeViewItem section2)
|
||||||
|
{
|
||||||
|
bool acceptDrag = false;
|
||||||
|
foreach (var draggedId in draggedIDs)
|
||||||
|
{
|
||||||
|
var draggedItem = FindItem(draggedId, rootItem);
|
||||||
|
if (draggedItem != null && draggedItem is LstrEntryTreeViewItem item)
|
||||||
|
{
|
||||||
|
if (args.performDrop && draggedItem.parent != section2)
|
||||||
|
{
|
||||||
|
var parentSection = (LstrSectionTreeViewItem)draggedItem.parent;
|
||||||
|
OnMoveItemToSection(parentSection.Section, section2.Section, item.Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptDrag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.performDrop && acceptDrag)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
SetSelection(new int[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptDrag
|
||||||
|
? DragAndDropVisualMode.Move
|
||||||
|
: DragAndDropVisualMode.Rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DragAndDropPosition.OutsideItems:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DragAndDropVisualMode.Rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoveItemWithinSection(SheetSectionTreeView section, SheetItemTreeView item, int position)
|
||||||
|
{
|
||||||
|
windowData.OnMoveItemWithinSection(section, item, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoveItemToSectionAt(SheetSectionTreeView parent, SheetSectionTreeView section, SheetItemTreeView item, int position)
|
||||||
|
{
|
||||||
|
windowData.OnMoveItemToSectionAt(parent, section, item, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoveItemToSection(SheetSectionTreeView parent, SheetSectionTreeView section, SheetItemTreeView item)
|
||||||
|
{
|
||||||
|
windowData.OnMoveItemToSection(parent, section, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoveSection(SheetSectionTreeView section, int position)
|
||||||
|
{
|
||||||
|
windowData.OnMoveSection(section, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleCommandEvent(Event uiEvent)
|
||||||
|
{
|
||||||
|
if (uiEvent.type == EventType.ValidateCommand)
|
||||||
|
{
|
||||||
|
switch (uiEvent.commandName)
|
||||||
|
{
|
||||||
|
case k_DeleteCommand:
|
||||||
|
case k_SoftDeleteCommand:
|
||||||
|
if (HasSelection())
|
||||||
|
uiEvent.Use();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (uiEvent.type == EventType.ExecuteCommand)
|
||||||
|
{
|
||||||
|
switch (uiEvent.commandName)
|
||||||
|
{
|
||||||
|
case k_DeleteCommand:
|
||||||
|
case k_SoftDeleteCommand:
|
||||||
|
DeleteSelected();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteSelected()
|
||||||
|
{
|
||||||
|
var toDelete = GetSelection().OrderByDescending(i => i);
|
||||||
|
if (toDelete.Count() <= 0) return;
|
||||||
|
|
||||||
|
foreach (var index in toDelete)
|
||||||
|
{
|
||||||
|
var selectedItem = FindItem(index, rootItem);
|
||||||
|
if (selectedItem == null) continue;
|
||||||
|
|
||||||
|
if (selectedItem is LstrEntryTreeViewItem item)
|
||||||
|
{
|
||||||
|
var parentSection = (LstrSectionTreeViewItem)selectedItem.parent;
|
||||||
|
DeleteLstrEntry(parentSection.Section, item.Item);
|
||||||
|
}
|
||||||
|
else if (selectedItem is LstrSectionTreeViewItem section)
|
||||||
|
{
|
||||||
|
DeleteLstrSection(section.Section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSelection(new int[0]);
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteLstrEntry(SheetSectionTreeView section, SheetItemTreeView sheetItem)
|
||||||
|
{
|
||||||
|
if (!HasFocus())
|
||||||
|
return;
|
||||||
|
|
||||||
|
windowData.RemoveItem(section, sheetItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteLstrSection(SheetSectionTreeView section)
|
||||||
|
{
|
||||||
|
if (!HasFocus())
|
||||||
|
return;
|
||||||
|
|
||||||
|
windowData.RemoveSection(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a087b0e0010170b4d89da49884c17106
|
||||||
@ -10,7 +10,7 @@ namespace AlicizaX.UI.Editor
|
|||||||
{
|
{
|
||||||
internal class UISettingEditorWindow : OdinEditorWindow
|
internal class UISettingEditorWindow : OdinEditorWindow
|
||||||
{
|
{
|
||||||
[MenuItem("Tools/AlicizaX/UI Setting Window")]
|
[MenuItem("Tools/AlicizaX/UI/Setting Window")]
|
||||||
private static void OpenWindow()
|
private static void OpenWindow()
|
||||||
{
|
{
|
||||||
GetWindow<UISettingEditorWindow>().Show();
|
GetWindow<UISettingEditorWindow>().Show();
|
||||||
|
|||||||
@ -5,14 +5,14 @@ namespace AlicizaX.Localization
|
|||||||
[Prewarm(4)]
|
[Prewarm(4)]
|
||||||
public readonly struct LocalizationChangeEvent : IEventArgs
|
public readonly struct LocalizationChangeEvent : IEventArgs
|
||||||
{
|
{
|
||||||
public readonly Language ChangedLanguage;
|
public readonly string ChangedLanguage;
|
||||||
|
|
||||||
public LocalizationChangeEvent(Language language)
|
public LocalizationChangeEvent(string language)
|
||||||
{
|
{
|
||||||
ChangedLanguage = language;
|
ChangedLanguage = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Publisher(Language language)
|
public static void Publisher(string language)
|
||||||
{
|
{
|
||||||
EventPublisher.Publish(new LocalizationChangeEvent(language));
|
EventPublisher.Publish(new LocalizationChangeEvent(language));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ namespace AlicizaX.Localization.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ILocalizationModule : IModule, IModuleAwake
|
public interface ILocalizationModule : IModule, IModuleAwake
|
||||||
{
|
{
|
||||||
public Language Language { get; }
|
public string Language { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据字典主键获取字典内容字符串。
|
/// 根据字典主键获取字典内容字符串。
|
||||||
@ -17,7 +17,7 @@ namespace AlicizaX.Localization.Runtime
|
|||||||
/// <returns>要获取的字典内容字符串。</returns>
|
/// <returns>要获取的字典内容字符串。</returns>
|
||||||
string GetString(string key);
|
string GetString(string key);
|
||||||
|
|
||||||
void ChangedLanguage(Language language);
|
void ChangedLanguage(string language);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -421,6 +421,16 @@ namespace AlicizaX.Localization.Runtime
|
|||||||
/// <returns>字典值。</returns>
|
/// <returns>字典值。</returns>
|
||||||
string GetRawString(string key);
|
string GetRawString(string key);
|
||||||
|
|
||||||
void AddLocalizationConfig(Dictionary<string, string> config);
|
/// <summary>
|
||||||
|
/// 增量增加多语言配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table"></param>
|
||||||
|
void IncreAddLocalizationConfig(GameLocaizationTable table);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 覆盖增加多语言配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="table"></param>
|
||||||
|
void CoverAddLocalizationConfig(GameLocaizationTable table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,14 +11,14 @@ namespace AlicizaX.Localization.Runtime
|
|||||||
internal sealed partial class LocalizationModule : ILocalizationModule
|
internal sealed partial class LocalizationModule : ILocalizationModule
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, string> Dic = new();
|
private readonly Dictionary<string, string> Dic = new();
|
||||||
private Language _language;
|
private string _language;
|
||||||
|
|
||||||
public Language Language
|
public string Language
|
||||||
{
|
{
|
||||||
get => _language;
|
get => _language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangedLanguage(Language language)
|
public void ChangedLanguage(string language)
|
||||||
{
|
{
|
||||||
if (_language == language) return;
|
if (_language == language) return;
|
||||||
_language = language;
|
_language = language;
|
||||||
@ -712,11 +712,34 @@ namespace AlicizaX.Localization.Runtime
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddLocalizationConfig(Dictionary<string, string> config)
|
public void IncreAddLocalizationConfig(GameLocaizationTable table)
|
||||||
{
|
{
|
||||||
foreach (var item in config)
|
LocalizationLanguage localizationLanguage = table.GetLanguage(_language);
|
||||||
|
if (localizationLanguage == null)
|
||||||
{
|
{
|
||||||
Dic.TryAdd(item.Key, item.Value);
|
Log.Warning($"Can not Find {_language} Strins ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in localizationLanguage.Strings)
|
||||||
|
{
|
||||||
|
Dic.Add(item.Key, item.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CoverAddLocalizationConfig(GameLocaizationTable table)
|
||||||
|
{
|
||||||
|
Dic.Clear();
|
||||||
|
LocalizationLanguage localizationLanguage = table.GetLanguage(_language);
|
||||||
|
if (localizationLanguage == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"Can not Find {_language} Strins ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in localizationLanguage.Strings)
|
||||||
|
{
|
||||||
|
Dic.Add(item.Key, item.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +751,7 @@ namespace AlicizaX.Localization.Runtime
|
|||||||
void IModuleAwake.Awake()
|
void IModuleAwake.Awake()
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
_language = (Language)UnityEditor.EditorPrefs.GetInt(LocalizationComponent.PrefsKey, 1);
|
_language = UnityEditor.EditorPrefs.GetString(LocalizationComponent.PrefsKey, "None");
|
||||||
#else
|
#else
|
||||||
_language = AppBuilderSetting.Instance.Language;
|
_language = AppBuilderSetting.Instance.Language;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
3
Runtime/Localization/ScriptableObject.meta
Normal file
3
Runtime/Localization/ScriptableObject.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7258e05524f4317b0c98fc4b4f06c6c
|
||||||
|
timeCreated: 1758264906
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Serialization;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization
|
||||||
|
{
|
||||||
|
public class GameLocaizationTable : ScriptableObject
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[SerializeField] internal string GenerateScriptCodePath = string.Empty;
|
||||||
|
[SerializeField] internal string GenerateScriptCodeFirstConfig;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public struct SheetItem
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public string Key;
|
||||||
|
public bool IsGen;
|
||||||
|
|
||||||
|
public SheetItem(string key, int id, bool isGen)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Key = key;
|
||||||
|
IsGen = isGen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public struct TableData
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public string SectionName;
|
||||||
|
public List<SheetItem> SectionSheet;
|
||||||
|
|
||||||
|
public TableData(string section, int id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
SectionName = section;
|
||||||
|
SectionSheet = new List<SheetItem>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TableData> TableSheet = new();
|
||||||
|
#endif
|
||||||
|
public List<LocalizationLanguage> Languages = new();
|
||||||
|
|
||||||
|
internal LocalizationLanguage GetLanguage(string languageCode)
|
||||||
|
{
|
||||||
|
return Languages.Find(t => t.LanguageName == languageCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0b8003079a7f4c54f9326bd1838f057e
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace AlicizaX.Localization
|
||||||
|
{
|
||||||
|
public class LocalizationLanguage : ScriptableObject
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public sealed class LocalizationString
|
||||||
|
{
|
||||||
|
public int EntryId;
|
||||||
|
public int SectionId;
|
||||||
|
|
||||||
|
public string Key;
|
||||||
|
public string Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LanguageName;
|
||||||
|
public List<LocalizationString> Strings = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e99433e1eef4a964d98eb541d664ab70
|
||||||
3
Runtime/_InternalVisibleTo.cs
Normal file
3
Runtime/_InternalVisibleTo.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("AlicizaX.Framework.Editor")]
|
||||||
3
Runtime/_InternalVisibleTo.cs.meta
Normal file
3
Runtime/_InternalVisibleTo.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 10e6d8633c6c407eb678feb2d6fe564b
|
||||||
|
timeCreated: 1758525627
|
||||||
Loading…
Reference in New Issue
Block a user