This commit is contained in:
陈思海 2025-09-23 12:04:39 +08:00
parent 908d3e41f2
commit ba4085356e
39 changed files with 2770 additions and 14 deletions

3
Editor/Localization.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8b833f8e32e7456bb382bc73386a0297
timeCreated: 1758196091

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 98a1fa01bd8596b49a4e4878a3cc0d24
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View 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:

View 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;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 26c8edcdf58c4806a5646a5ec155cc85
timeCreated: 1758196101

View 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;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5647a4aaf19f42bf852a701955e888f5
timeCreated: 1758196661

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f61eb05483ae45343a3667fcb64c8410
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: baaf3e4bb10c71c478bcf4f9ce2d3d23
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a40ca89d13c4a3448bd77e3abcfefaad

View File

@ -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);
// }
// }
// }

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 83da11971febf634ba7c141b42267ae6

View File

@ -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);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8b5ae4bf262d12a4899f2a46672ed072

View File

@ -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();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7ca9d65f294bd6843b5ca8d0a0eb97f6

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3bcfa73f585d7aa4caee8e18fab11c4b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3135dc7bd9a1fd649926188f4aa33fff

View File

@ -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();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6da7bdd80cb1d7b4d86b936f963c932a

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a7371740917c3134bb222528d84dd9c9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a7a69a16aa08dee4b888f239c6203c71

View File

@ -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);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a087b0e0010170b4d89da49884c17106

View File

@ -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();

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c7258e05524f4317b0c98fc4b4f06c6c
timeCreated: 1758264906

View File

@ -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);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0b8003079a7f4c54f9326bd1838f057e

View File

@ -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();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e99433e1eef4a964d98eb541d664ab70

View File

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("AlicizaX.Framework.Editor")]

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 10e6d8633c6c407eb678feb2d6fe564b
timeCreated: 1758525627