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
|
||||
{
|
||||
[MenuItem("Tools/AlicizaX/UI Setting Window")]
|
||||
[MenuItem("Tools/AlicizaX/UI/Setting Window")]
|
||||
private static void OpenWindow()
|
||||
{
|
||||
GetWindow<UISettingEditorWindow>().Show();
|
||||
|
||||
@ -5,14 +5,14 @@ namespace AlicizaX.Localization
|
||||
[Prewarm(4)]
|
||||
public readonly struct LocalizationChangeEvent : IEventArgs
|
||||
{
|
||||
public readonly Language ChangedLanguage;
|
||||
public readonly string ChangedLanguage;
|
||||
|
||||
public LocalizationChangeEvent(Language language)
|
||||
public LocalizationChangeEvent(string language)
|
||||
{
|
||||
ChangedLanguage = language;
|
||||
}
|
||||
|
||||
public static void Publisher(Language language)
|
||||
public static void Publisher(string language)
|
||||
{
|
||||
EventPublisher.Publish(new LocalizationChangeEvent(language));
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ namespace AlicizaX.Localization.Runtime
|
||||
/// </summary>
|
||||
public interface ILocalizationModule : IModule, IModuleAwake
|
||||
{
|
||||
public Language Language { get; }
|
||||
public string Language { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 根据字典主键获取字典内容字符串。
|
||||
@ -17,7 +17,7 @@ namespace AlicizaX.Localization.Runtime
|
||||
/// <returns>要获取的字典内容字符串。</returns>
|
||||
string GetString(string key);
|
||||
|
||||
void ChangedLanguage(Language language);
|
||||
void ChangedLanguage(string language);
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -421,6 +421,16 @@ namespace AlicizaX.Localization.Runtime
|
||||
/// <returns>字典值。</returns>
|
||||
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
|
||||
{
|
||||
private readonly Dictionary<string, string> Dic = new();
|
||||
private Language _language;
|
||||
private string _language;
|
||||
|
||||
public Language Language
|
||||
public string Language
|
||||
{
|
||||
get => _language;
|
||||
}
|
||||
|
||||
public void ChangedLanguage(Language language)
|
||||
public void ChangedLanguage(string language)
|
||||
{
|
||||
if (_language == language) return;
|
||||
_language = language;
|
||||
@ -712,11 +712,34 @@ namespace AlicizaX.Localization.Runtime
|
||||
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()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
_language = (Language)UnityEditor.EditorPrefs.GetInt(LocalizationComponent.PrefsKey, 1);
|
||||
_language = UnityEditor.EditorPrefs.GetString(LocalizationComponent.PrefsKey, "None");
|
||||
#else
|
||||
_language = AppBuilderSetting.Instance.Language;
|
||||
#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