diff --git a/.gitignore b/.gitignore index 9ca9cc147..9b8ac9be3 100644 --- a/.gitignore +++ b/.gitignore @@ -333,3 +333,4 @@ Test/Test - Backup.csproj UndertaleModLib/UndertaleModLib - Backup.csproj UndertaleModTests/UndertaleModTests - Backup.csproj UndertaleModLib/gitversion.txt +/UndertaleModTool/Localization/Strings.Designer.cs diff --git a/UndertaleModTool/Controls/AudioFileReference.xaml b/UndertaleModTool/Controls/AudioFileReference.xaml index 4659c643e..b6c4a5a05 100644 --- a/UndertaleModTool/Controls/AudioFileReference.xaml +++ b/UndertaleModTool/Controls/AudioFileReference.xaml @@ -1,14 +1,15 @@ - - + @@ -32,7 +33,7 @@ @@ -60,14 +61,14 @@ - + - + - + - + - + - + @@ -147,9 +148,8 @@ - - Hint: You can click on any tile region below to highlight its ID above. - + diff --git a/UndertaleModTool/Editors/UndertaleBackgroundEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleBackgroundEditor.xaml.cs index ac07d94f1..ad5a375e6 100644 --- a/UndertaleModTool/Editors/UndertaleBackgroundEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleBackgroundEditor.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; @@ -7,6 +7,7 @@ using System.Windows.Controls; using System.Windows.Input; using UndertaleModLib.Models; +using UndertaleModTool.Localization; using UndertaleModTool.Windows; namespace UndertaleModTool @@ -25,7 +26,7 @@ public UndertaleBackgroundEditor() var item = new MenuItem() { - Header = "Find all references of this tile" + Header = LocalizationSource.GetString("Menu_FindAllReferencesOfTile") }; item.Click += FindAllTileReferencesItem_Click; tileContextMenu.Items.Add(item); @@ -122,8 +123,7 @@ private void FindAllTileReferencesItem_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_ObjectReferencesWindowError"), ex)); dialog?.Close(); } } @@ -146,7 +146,7 @@ private bool ScrollTileIntoView(int tileIndex) ScrollViewer tileListViewer = MainWindow.FindVisualChild(TileIdList); if (tileListViewer is null) { - mainWindow.ShowError("Cannot find the tile ID list scroll viewer."); + mainWindow.ShowError(LocalizationSource.GetString("Msg_CannotFindTileIdListScrollViewer")); return false; } tileListViewer.ScrollToVerticalOffset(tileIndex + 1 - (tileListViewer.ViewportHeight / 2)); // DataGrid offset is logical diff --git a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml index b5974be3d..b369b0c1e 100644 --- a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml @@ -1,10 +1,11 @@ - @@ -31,23 +32,23 @@ - Name + - Locals count + - Arguments count + - Offset + - Parent entry + - + - + (); - return "Failed to parse language file: " + e.Message; + return string.Format(LocalizationSource.GetString("Msg_FailedToParseLangFile"), e.Message); } return null; } @@ -731,7 +732,7 @@ private async Task DecompileCode(UndertaleCode code, bool first, LoaderDialog ex if (code.ParentEntry != null) { - DecompiledEditor.Text = "// This code entry is a reference to an anonymous function within " + code.ParentEntry.Name.Content + ", view it there."; + DecompiledEditor.Text = "// " + string.Format(LocalizationSource.GetString("Msg_CodeEntryReference"), code.ParentEntry.Name.Content); DecompiledChanged = false; CurrentDecompiled = code; existingDialog?.TryClose(); @@ -742,11 +743,11 @@ private async Task DecompileCode(UndertaleCode code, bool first, LoaderDialog ex if (existingDialog != null) { dialog = existingDialog; - dialog.Message = "Decompiling, please wait... This can take a while on complex scripts."; + dialog.Message = LocalizationSource.GetString("Msg_DecompilingPleaseWait"); } else { - dialog = new LoaderDialog("Decompiling", "Decompiling, please wait... This can take a while on complex scripts."); + dialog = new LoaderDialog(LocalizationSource.GetString("Dialog_Decompileing"), LocalizationSource.GetString("Msg_DecompilingPleaseWait")); dialog.Owner = Window.GetWindow(this); try { @@ -982,7 +983,7 @@ private async Task DecompiledLostFocusBody(object sender, RoutedEventArgs e) } // Create compiling dialog - LoaderDialog dialog = new("Compiling", "Compiling, please wait...") + LoaderDialog dialog = new(LocalizationSource.GetString("Dialog_Compiling"), LocalizationSource.GetString("Msg_CompilingPleaseWait")) { Owner = Window.GetWindow(this) }; @@ -1023,14 +1024,14 @@ private async Task DecompiledLostFocusBody(object sender, RoutedEventArgs e) if (rootException is not null) { dialog.TryClose(); - mainWindow.ShowError(Truncate(rootException, 512), "Compiler error"); + mainWindow.ShowError(Truncate(rootException, 512), LocalizationSource.GetString("Dialog_CompilerError")); return; } if (!compileResult.Successful) { dialog.TryClose(); - mainWindow.ShowError(Truncate(compileResult.PrintAllErrors(false), 512), "Compiler error"); + mainWindow.ShowError(Truncate(compileResult.PrintAllErrors(false), 512), LocalizationSource.GetString("Dialog_CompilerError")); return; } @@ -1108,7 +1109,7 @@ private void DisassemblyEditor_LostFocus(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError(ex.ToString(), "Assembler error"); + mainWindow.ShowError(ex.ToString(), LocalizationSource.GetString("Dialog_AssemblerError")); return; } @@ -1127,10 +1128,7 @@ private void DisassemblyEditor_LostFocus(object sender, RoutedEventArgs e) if (mainWindow.Project is ProjectContext project && project.TryGetCodeSource(code, out _)) { // The user really shouldn't be editing disassembly - warn them about this in detail - mainWindow.ShowWarning("Editing disassembly while in an open project (even through scripts) can cause " + - "desyncs with source code in the project.\n\n" + - "The source code will not change unless you directly modify it, " + - "or if you remove the code asset from the project entirely."); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_EditingDisassemblyProjectWarning")); } if (!DisassemblyEditor.IsReadOnly) @@ -1291,7 +1289,7 @@ public override VisualLineElement ConstructElement(int offset) if (id > 0x00050000) { MenuItemDark item = new(); - item.Header = "0x" + id.ToString("X6") + " (color)"; + item.Header = "0x" + id.ToString("X6") + " " + LocalizationSource.GetString("Editor_Color"); item.Click += (sender2, ev2) => { if (!((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)) @@ -1308,7 +1306,7 @@ public override VisualLineElement ConstructElement(int offset) if (myKey != null) { MenuItemDark item = new(); - item.Header = myKey.Replace("_", "__") + " (constant)"; + item.Header = myKey.Replace("_", "__") + " " + LocalizationSource.GetString("Editor_Constant"); item.Click += (sender2, ev2) => { if (!((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)) @@ -1320,7 +1318,7 @@ public override VisualLineElement ConstructElement(int offset) }; contextMenu.Items.Add(item); } - contextMenu.Items.Add(new MenuItemDark() { Header = id + " (number)", IsEnabled = false }); + contextMenu.Items.Add(new MenuItemDark() { Header = id + " " + LocalizationSource.GetString("Editor_Number"), IsEnabled = false }); contextMenu.IsOpen = true; } @@ -1356,7 +1354,7 @@ public NameGenerator(UndertaleCodeEditor codeEditorInst, TextArea textAreaInst) var menuItem = new MenuItemDark() { - Header = "Open in new tab" + Header = LocalizationSource.GetString("Menu_OpenInNewTab") }; menuItem.Click += (sender, _) => { diff --git a/UndertaleModTool/Editors/UndertaleCodeLocalsEditor.xaml b/UndertaleModTool/Editors/UndertaleCodeLocalsEditor.xaml index 3846adde4..bfbd370b0 100644 --- a/UndertaleModTool/Editors/UndertaleCodeLocalsEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleCodeLocalsEditor.xaml @@ -1,11 +1,12 @@ - @@ -17,10 +18,10 @@ - Name + - Local variables + @@ -39,7 +40,7 @@ - Double click to add + @@ -48,14 +49,14 @@ - + - + @@ -65,4 +66,4 @@ - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml b/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml index 7430a53cb..15015e237 100644 --- a/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml @@ -1,11 +1,12 @@ - @@ -40,11 +41,11 @@ - - + + - Play - Stop + + diff --git a/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml.cs index 7ea31f94c..ce4d149a2 100644 --- a/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleEmbeddedAudioEditor.xaml.cs @@ -1,4 +1,4 @@ -using Microsoft.Win32; +using Microsoft.Win32; using NAudio.Vorbis; using NAudio.Wave; using System; @@ -6,6 +6,7 @@ using System.IO; using System.Windows; using UndertaleModLib.Models; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -26,7 +27,7 @@ public string FileType { if (DataContext is not UndertaleEmbeddedAudio target) { - return "Unknown"; + return LocalizationSource.GetString("Common_Unknown"); } if (IsWav(target.Data)) @@ -37,7 +38,7 @@ public string FileType { return "OGG"; } - return "Unknown"; + return LocalizationSource.GetString("Common_Unknown"); } } @@ -62,20 +63,20 @@ private void Import_Click(object sender, RoutedEventArgs e) OpenFileDialog dlg = new(); - if (IsWav(target.Data)) + if (IsWav(target.Data)) { dlg.DefaultExt = ".wav"; - dlg.Filter = "WAV files|*.wav|All files|*"; - } - else if (IsOgg(target.Data)) + dlg.Filter = LocalizationSource.GetString("FileFilter_WAV") + "|*.wav|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; + } + else if (IsOgg(target.Data)) { dlg.DefaultExt = ".ogg"; - dlg.Filter = "OGG files|*.ogg|All files|*"; - } - else + dlg.Filter = LocalizationSource.GetString("FileFilter_OGG") + "|*.ogg|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; + } + else { dlg.DefaultExt = ""; - dlg.Filter = "All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; } if (dlg.ShowDialog() == true) @@ -83,9 +84,9 @@ private void Import_Click(object sender, RoutedEventArgs e) try { byte[] data = File.ReadAllBytes(dlg.FileName); - if (!IsWav(data) && !IsOgg(data)) + if (!IsWav(data) && !IsOgg(data)) { - if (mainWindow.ShowQuestionWithCancel("Warning: File being imported is not a WAV or OGG. Import anyway?\r\n\r\nThis may corrupt the sound.", MessageBoxImage.Warning, "Unknown format") != MessageBoxResult.Yes) + if (mainWindow.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_ImportNonAudioWarning"), MessageBoxImage.Warning, LocalizationSource.GetString("Dialog_UnknownFormat")) != MessageBoxResult.Yes) { return; } @@ -93,18 +94,17 @@ private void Import_Click(object sender, RoutedEventArgs e) else if ((IsWav(target.Data) && IsOgg(data)) || (IsOgg(target.Data) && IsWav(data))) { if (mainWindow.ShowQuestionWithCancel( - "Warning: Filetype being imported does not match existing filetype. Import anyway?\r\n\r\n" + - "This may corrupt the sound, unless sound asset compression settings are adjusted as well.", MessageBoxImage.Warning, "Format mismatch") != MessageBoxResult.Yes) + LocalizationSource.GetString("Msg_ImportFormatMismatchWarning"), MessageBoxImage.Warning, LocalizationSource.GetString("Dialog_FormatMismatch")) != MessageBoxResult.Yes) { return; } - } + } target.Data = data; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FileType))); } catch (Exception ex) { - mainWindow.ShowError("Failed to import file: " + ex.Message, "Failed to import file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToImportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToImportFile")); } } } @@ -115,20 +115,20 @@ private void Export_Click(object sender, RoutedEventArgs e) SaveFileDialog dlg = new SaveFileDialog(); - if (IsWav(target.Data)) + if (IsWav(target.Data)) { dlg.DefaultExt = ".wav"; - dlg.Filter = "WAV files|*.wav|All files|*"; - } - else if (IsOgg(target.Data)) + dlg.Filter = LocalizationSource.GetString("FileFilter_WAV") + "|*.wav|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; + } + else if (IsOgg(target.Data)) { dlg.DefaultExt = ".ogg"; - dlg.Filter = "OGG files|*.ogg|All files|*"; - } - else + dlg.Filter = LocalizationSource.GetString("FileFilter_OGG") + "|*.ogg|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; + } + else { dlg.DefaultExt = ""; - dlg.Filter = "All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; } if (dlg.ShowDialog() == true) @@ -139,7 +139,7 @@ private void Export_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportFile")); } } } @@ -178,13 +178,13 @@ private void Play_Click(object sender, RoutedEventArgs e) } else { - mainWindow.ShowError("Failed to play audio!\r\nNot a WAV or OGG.", "Audio failure"); + mainWindow.ShowError(LocalizationSource.GetString("Msg_FailedToPlayAudioNotWavOgg"), LocalizationSource.GetString("Dialog_AudioFailure")); } } catch (Exception ex) { waveOut = null; - mainWindow.ShowError("Failed to play audio!\r\n" + ex.Message, "Audio failure"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToPlayAudio"), ex.Message), LocalizationSource.GetString("Dialog_AudioFailure")); } } diff --git a/UndertaleModTool/Editors/UndertaleEmbeddedImageEditor.xaml b/UndertaleModTool/Editors/UndertaleEmbeddedImageEditor.xaml index 5dc59173f..502beddf4 100644 --- a/UndertaleModTool/Editors/UndertaleEmbeddedImageEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleEmbeddedImageEditor.xaml @@ -1,11 +1,12 @@ - @@ -17,10 +18,10 @@ - Name + - Texture + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml b/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml index eee2f883e..01684c186 100644 --- a/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml @@ -1,100 +1,100 @@ - - - - - - - - - - - - - - - - - - - - - - - - - Scaled - - - - - - Size - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hint: This image is mouse interactable (3 mouse buttons and mouse wheel). - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml.cs index d0c0192e4..27225433c 100644 --- a/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleEmbeddedTextureEditor.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Globalization; using System.IO; @@ -14,6 +14,7 @@ using Microsoft.Win32; using UndertaleModLib.Models; using UndertaleModLib.Util; +using UndertaleModTool.Localization; using UndertaleModTool.Windows; namespace UndertaleModTool @@ -42,12 +43,12 @@ public UndertaleEmbeddedTextureEditor() var newTabItem = new MenuItem() { - Header = "Open in new tab" + Header = LocalizationSource.GetString("Menu_OpenInNewTab") }; newTabItem.Click += OpenInNewTabItem_Click; var referencesItem = new MenuItem() { - Header = "Find all references to this page item" + Header = LocalizationSource.GetString("Menu_FindAllReferencesToPageItem") }; referencesItem.Click += FindAllItemReferencesItem_Click; pageContextMenu.Items.Add(newTabItem); @@ -137,8 +138,7 @@ private void FindAllItemReferencesItem_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_ObjectReferencesWindowError"), ex)); } finally { @@ -203,7 +203,7 @@ private void Import_Click(object sender, RoutedEventArgs e) OpenFileDialog dlg = new OpenFileDialog(); dlg.DefaultExt = ".png"; - dlg.Filter = "PNG files (.png)|*.png|All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_PNG") + "|*.png|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; if (dlg.ShowDialog() == true) { @@ -234,7 +234,7 @@ private void Import_Click(object sender, RoutedEventArgs e) uint width = (uint)image.Width, height = (uint)image.Height; if ((width & (width - 1)) != 0 || (height & (height - 1)) != 0) { - mainWindow.ShowWarning("WARNING: Texture page dimensions are not powers of 2. Sprite blurring is very likely in-game.", "Unexpected texture dimensions"); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_TexturePageDimensionsNotPowerOf2"), LocalizationSource.GetString("Dialog_UnexpectedTextureDimensions")); } var previousFormat = target.TextureData.Image?.Format; @@ -247,7 +247,7 @@ private void Import_Click(object sender, RoutedEventArgs e) // If texture was DDS, warn user that texture has been converted to PNG if (previousFormat == GMImage.ImageFormat.Dds && currentFormat == GMImage.ImageFormat.Png) { - mainWindow.ShowMessage($"{target} was converted into PNG format since we don't support converting images into DDS format. This might have performance issues in the game."); + mainWindow.ShowMessage(string.Format(LocalizationSource.GetString("Msg_TextureConvertedToPNG"), target)); } // Update width/height properties in the UI @@ -256,7 +256,7 @@ private void Import_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to import file: " + ex.Message, "Failed to import file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToImportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToImportFile")); } } } @@ -268,7 +268,7 @@ private void Export_Click(object sender, RoutedEventArgs e) SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = ".png"; - dlg.Filter = "PNG files (.png)|*.png|All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_PNG") + "|*.png|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; if (dlg.ShowDialog() == true) { @@ -279,7 +279,7 @@ private void Export_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportFile")); } } } diff --git a/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml b/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml index 78e5a5be1..2a761ae85 100644 --- a/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml @@ -1,12 +1,13 @@ - @@ -34,22 +35,22 @@ - Name + - - Class Name + - Files + - + @@ -58,9 +59,9 @@ - + - - + - + @@ -112,7 +113,7 @@ - + - - Product ID + - THINGS TO NOTE: + - - Keep in mind that .gml files are either not present, or will be empty. (e.g. no functions) - - Because they are prebaked into GML scripts. That is completely normal. + + + - diff --git a/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml.cs index 2fa9cacfc..e26816f72 100644 --- a/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleExtensionEditor.xaml.cs @@ -16,6 +16,7 @@ using System.Windows.Shapes; using UndertaleModLib; using UndertaleModLib.Models; +using UndertaleModTool.Localization; using static UndertaleModLib.Models.UndertaleExtensionOption; namespace UndertaleModTool @@ -77,7 +78,7 @@ private void NewFileButton_Click(object sender, RoutedEventArgs e) UndertaleExtensionFile obj = new() { Kind = UndertaleExtensionKind.Dll, - Filename = mainWindow.Data.Strings.MakeString($"NewExtensionFile{lastItem}.dll"), + Filename = mainWindow.Data.Strings.MakeString(string.Format(LocalizationSource.GetString("Editor_NewExtensionFile"), lastItem)), Functions = new UndertalePointerList() }; extension.Files.Add(obj); @@ -91,7 +92,7 @@ private void NewOptionButton_Click(object sender, RoutedEventArgs e) UndertaleExtensionOption obj = new() { - Name = mainWindow.Data.Strings.MakeString($"extensionOption{lastItem}"), + Name = mainWindow.Data.Strings.MakeString(string.Format(LocalizationSource.GetString("Editor_NewExtensionOption"), lastItem)), Value = mainWindow.Data.Strings.MakeString("", true) }; extension.Options.Add(obj); @@ -112,9 +113,9 @@ private void KindComboBox_SelectionChanged(object sender, SelectionChangedEventA case OptionKind.Boolean: if (option.Value.Content.ToLowerInvariant() == "true") - option.Value.Content = "True"; + option.Value.Content = LocalizationSource.GetString("Common_True"); else - option.Value.Content = "False"; + option.Value.Content = LocalizationSource.GetString("Common_False"); break; case OptionKind.Number: @@ -177,13 +178,13 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu { case "boolean": if (value is not bool b) - return new ValidationResult(false, "Invalid boolean value"); - return (b ? "True" : "False"); - + return new ValidationResult(false, LocalizationSource.GetString("Msg_InvalidBooleanValue")); + return (b ? LocalizationSource.GetString("Common_True") : LocalizationSource.GetString("Common_False")); + case "number": if (value is string s && Double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out double _)) return s; - return new ValidationResult(false, "Invalid number string"); + return new ValidationResult(false, LocalizationSource.GetString("Msg_InvalidNumberString")); default: return null; diff --git a/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml b/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml index 75df129d2..787901900 100644 --- a/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml @@ -1,4 +1,4 @@ - @@ -28,21 +29,21 @@ - FileName + - Cleanup function name + - Init function name + - Kind + - Functions + @@ -61,7 +62,7 @@ - Double click to add + @@ -80,4 +81,4 @@ - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml.cs index 797ad2a54..efe1e06dc 100644 --- a/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,6 +14,7 @@ using System.Windows.Shapes; using UndertaleModLib; using UndertaleModLib.Models; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -34,8 +35,8 @@ private void DataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e) UndertaleExtensionFunction obj = new UndertaleExtensionFunction() { - Name = (Application.Current.MainWindow as MainWindow).Data.Strings.MakeString($"new_extension_function_{lastItem}"), - ExtName = (Application.Current.MainWindow as MainWindow).Data.Strings.MakeString($"new_extension_function_{lastItem}_ext"), + Name = (Application.Current.MainWindow as MainWindow).Data.Strings.MakeString(string.Format(LocalizationSource.GetString("Editor_NewExtensionFunction"), lastItem)), + ExtName = (Application.Current.MainWindow as MainWindow).Data.Strings.MakeString(string.Format(LocalizationSource.GetString("Editor_NewExtensionFunctionExt"), lastItem)), RetType = UndertaleExtensionVarType.Double, Arguments = new UndertaleSimpleList(), Kind = 11, // ??? diff --git a/UndertaleModTool/Editors/UndertaleExtensionFunctionEditor.xaml b/UndertaleModTool/Editors/UndertaleExtensionFunctionEditor.xaml index 7e452c637..b582f4a46 100644 --- a/UndertaleModTool/Editors/UndertaleExtensionFunctionEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleExtensionFunctionEditor.xaml @@ -1,11 +1,12 @@ - @@ -30,23 +31,23 @@ - Name + - External Name + - ID + - Kind + - Return Type + - Argument Types + @@ -65,7 +66,7 @@ - Double click to add + @@ -85,11 +86,7 @@ - - Runner literally doesn't care about the 'Kind' value. - - The 'ID' does matter though, so make sure you don't have any overlaps. - + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleFontEditor/EditGlyphRectangleWindow.xaml b/UndertaleModTool/Editors/UndertaleFontEditor/EditGlyphRectangleWindow.xaml index 600c0eb8e..ed26fe752 100644 --- a/UndertaleModTool/Editors/UndertaleFontEditor/EditGlyphRectangleWindow.xaml +++ b/UndertaleModTool/Editors/UndertaleFontEditor/EditGlyphRectangleWindow.xaml @@ -1,4 +1,4 @@ - @@ -21,7 +22,7 @@ + Content="{loc:Loc Glyph_GetHelp}" Click="HelpButton_Click" Focusable="False"/> - + @@ -102,7 +102,6 @@ - @@ -155,7 +154,7 @@ - + @@ -178,6 +177,6 @@ Save changes + FontSize="18" Content="{loc:Loc Common_Save}"/> - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml b/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml index a19f18743..6c6e8b382 100644 --- a/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml @@ -1,10 +1,11 @@ - @@ -49,29 +50,29 @@ - Name + - Display name + - Font size + - + - Bold + - Italic + - Range + @@ -79,20 +80,20 @@ - + - Charset + - Anti Aliasing + - Texture + - Scale + @@ -102,29 +103,28 @@ - - - - - - Hint: You can click on any glyph here to highlight it in the "Glyphs". - + @@ -221,15 +221,15 @@ @@ -238,10 +238,10 @@ - + - Glyphs: + - Double click to add + @@ -273,17 +273,17 @@ - + - + @@ -323,9 +323,9 @@ - - + ToolTip="{loc:Loc Editor_ShiftTooltip}"> + + @@ -338,9 +338,9 @@ - - + ToolTip="{loc:Loc Editor_OffsetTooltip}"> + + @@ -350,7 +350,7 @@ - + @@ -382,7 +382,7 @@ - + - Double click to add + @@ -416,7 +416,7 @@ - + - + @@ -448,18 +448,15 @@ - Note that the glyphs need to be specified in ascending order. - Press the button below to sort them appropriately. + + Content="{loc:Loc Editor_SortGlyphs}" Width="200" FontSize="14" Style="{StaticResource glyphsOperationButtonStyle}"/> - Also, if you have added new characters or changed the character - of an existing glyph, you should update the font range. - Press the button below to do that. + + Content="{loc:Loc Editor_UpdateRange}" Width="200" FontSize="14" Style="{StaticResource glyphsOperationButtonStyle}"/> - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml.cs index d3b0a2b23..e7471878e 100644 --- a/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleFontEditor/UndertaleFontEditor.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -15,6 +15,7 @@ using System.Windows.Shapes; using UndertaleModLib.Models; using UndertaleModTool.Editors.UndertaleFontEditor; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -42,7 +43,7 @@ private void Button_Sort_Click(object sender, RoutedEventArgs e) foreach (var glyph in copy) font.Glyphs.Add(glyph); - mainWindow.ShowMessage("The glyphs were sorted successfully."); + mainWindow.ShowMessage(LocalizationSource.GetString("Msg_GlyphsSortedSuccessfully")); } private void Button_UpdateRange_Click(object sender, RoutedEventArgs e) { @@ -53,7 +54,7 @@ private void Button_UpdateRange_Click(object sender, RoutedEventArgs e) font.RangeStart = characters.Min(); font.RangeEnd = characters.Max(); - mainWindow.ShowMessage("The range was updated successfully."); + mainWindow.ShowMessage(LocalizationSource.GetString("Msg_RangeUpdatedSuccessfully")); } private void Grid_MouseDown(object sender, MouseButtonEventArgs e) @@ -91,7 +92,7 @@ private void ScrollGlyphIntoView(int glyphIndex) ScrollViewer glyphListViewer = MainWindow.FindVisualChild(GlyphsGrid); if (glyphListViewer is null) { - mainWindow.ShowError("Cannot find the glyphs table scroll viewer."); + mainWindow.ShowError(LocalizationSource.GetString("Msg_CannotFindGlyphsScrollViewer")); return; } glyphListViewer.ScrollToVerticalOffset(glyphIndex + 1 - (glyphListViewer.ViewportHeight / 2)); // DataGrid offset is logical @@ -111,13 +112,13 @@ private void EditRectangleButton_Click(object sender, RoutedEventArgs e) { if (GlyphsGrid.SelectedItem is not UndertaleFont.Glyph glyph) { - mainWindow.ShowError("No glyph selected."); + mainWindow.ShowError(LocalizationSource.GetString("Msg_NoGlyphSelected")); return; } if (DataContext is UndertaleFont font && font.Texture is null) { - mainWindow.ShowError("The font has no texture."); + mainWindow.ShowError(LocalizationSource.GetString("Msg_FontHasNoTexture")); return; } @@ -133,8 +134,7 @@ private void EditRectangleButton_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("An error occurred in the glyph rectangle editor window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_GlyphRectEditorError"), ex)); } finally { @@ -152,8 +152,7 @@ private void CreateGlyphButton_Click(object sender, RoutedEventArgs e) if (font.Glyphs[index].SourceWidth == 0 || font.Glyphs[index].SourceHeight == 0) { - mainWindow.ShowWarning("The last glyph has zero size.\n" + - "You can use the button on the left to fix that."); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_LastGlyphZeroSize")); return; } } @@ -180,7 +179,7 @@ private void EditKerningButton_Click(object sender, RoutedEventArgs e) char? ch = (char?)CharConverter.Instance.Convert(glyph.Character, null, null, null); ch ??= default; - GlyphsLabel.Text = $"Kerning of glyph '{ch}' (code - {glyph.Character}):"; + GlyphsLabel.Text = string.Format(LocalizationSource.GetString("Editor_KerningOfGlyph"), ch, glyph.Character); } private void KerningBackButton_Click(object sender, RoutedEventArgs e) @@ -192,7 +191,7 @@ private void KerningBackButton_Click(object sender, RoutedEventArgs e) GlyphsGrid.Visibility = Visibility.Visible; GlyphsGrid.IsEnabled = true; - GlyphsLabel.Text = "Glyphs:"; + GlyphsLabel.Text = LocalizationSource.GetString("Editor_Glyphs"); } private void Command_GoBack(object sender, ExecutedRoutedEventArgs e) { @@ -211,7 +210,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn if (value is not ushort charNum) { if (value is not short charNum1) - return "(error)"; + return LocalizationSource.GetString("Common_Error"); try { @@ -219,7 +218,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn } catch { - return "(error)"; + return LocalizationSource.GetString("Common_Error"); } } @@ -236,7 +235,7 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu uint charNum = charStr[0]; if (charNum > ushort.MaxValue) { - mainWindow.ShowError("The character code is greater than the maximum (65535)"); + mainWindow.ShowError(LocalizationSource.GetString("Msg_CharCodeExceedsMax")); return new ValidationResult(false, null); } diff --git a/UndertaleModTool/Editors/UndertaleFunctionEditor.xaml b/UndertaleModTool/Editors/UndertaleFunctionEditor.xaml index d006fec48..327c4023c 100644 --- a/UndertaleModTool/Editors/UndertaleFunctionEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleFunctionEditor.xaml @@ -1,11 +1,12 @@ - @@ -17,10 +18,10 @@ - Name + - Name string ID + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleGameEndEditor.xaml b/UndertaleModTool/Editors/UndertaleGameEndEditor.xaml index 351fd6570..53a76abc0 100644 --- a/UndertaleModTool/Editors/UndertaleGameEndEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleGameEndEditor.xaml @@ -1,15 +1,16 @@ - - + @@ -29,7 +30,7 @@ - Double click to add + @@ -48,4 +49,4 @@ - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleGameObjectEditor.xaml b/UndertaleModTool/Editors/UndertaleGameObjectEditor.xaml index 2790a87f0..2046a8ee2 100644 --- a/UndertaleModTool/Editors/UndertaleGameObjectEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleGameObjectEditor.xaml @@ -1,12 +1,13 @@ - @@ -47,110 +48,6 @@ - - @@ -188,10 +85,10 @@ - Name + - Sprite + @@ -212,65 +109,62 @@ - Visible + - Solid + - Depth + - Persistent + - Parent + - Texture mask id + - Uses physics + - Is sensor + - Collision shape + - Physics + - Density + - Restitution + - Group + - Linear damping + - Angular damping + - - - Friction + - Is awake + - Is kinematic + - Physics shape vertices + - Double click to add + @@ -302,14 +196,14 @@ - + - + @@ -322,9 +216,8 @@ - Events + - @@ -353,7 +246,7 @@ - Double click to add + @@ -361,12 +254,11 @@ - + @@ -413,7 +305,7 @@ - n/a + @@ -422,7 +314,7 @@ - n/a + @@ -480,12 +372,12 @@ - + - + @@ -20,7 +21,7 @@ - + @@ -53,35 +54,35 @@ - Disable GMS debugger + - Bytecode version + - FileName + - Config + - Last object ID + - Last tile ID + - Game ID + - DirectPlay GUID + - Name + - Version + @@ -101,7 +102,7 @@ - Default window size + @@ -109,20 +110,20 @@ - + - Flags + - License MD5 + - License CRC32 + - Timestamp + - - Display name + - Active targets + - Function classifications + - Steam AppID + - Debugger port + - Room order - + + - Double click to add + @@ -196,7 +197,7 @@ - + @@ -210,20 +211,20 @@ - FPS + - Allow statistics + - Game GUID + - + @@ -249,49 +250,49 @@ - Shader Extension Flag + - Shader Extension Version + - Flags + - Scale + - Window color + - Color depth + - Resolution + - Frequency + - Vertex sync - + + - Priority + - Back image + - Front image + - Load image + - Load alpha + - Constants + @@ -310,7 +311,7 @@ - Double click to add + @@ -319,14 +320,14 @@ - + - + @@ -340,7 +341,7 @@ - + @@ -354,19 +355,19 @@ - Unknown + - Language count + - Entry count + - Note: the languages currently don't seem used, and so the editor for them has not yet been made. + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleGeneralInfoEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleGeneralInfoEditor.xaml.cs index ea84c7aee..30cca3a0e 100644 --- a/UndertaleModTool/Editors/UndertaleGeneralInfoEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleGeneralInfoEditor.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -15,6 +15,7 @@ using System.Windows.Shapes; using UndertaleModLib; using UndertaleModLib.Models; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -48,8 +49,7 @@ private void DebuggerCheckBox_PreviewMouseLeftButtonDown(object sender, MouseBut return; e.Handled = true; - var result = mainWindow.ShowQuestion("Are you sure that you want to enable GMS debugger?\n" + - "If you want to enable a debug mode in some game, then you need to use one of the scripts."); + var result = mainWindow.ShowQuestion(LocalizationSource.GetString("Msg_EnableGMSDebugger")); if (result == MessageBoxResult.Yes) checkBox.IsChecked = false; } @@ -60,10 +60,10 @@ public class TimestampDateTimeConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is not ulong timestamp) - return "(error)"; + return LocalizationSource.GetString("Common_Error"); DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds((long)timestamp); if (parameter is string par && par == "GMT") - return "GMT+0: " + dateTimeOffset.UtcDateTime.ToString(); + return LocalizationSource.GetString("Editor_GMT0") + " " + dateTimeOffset.UtcDateTime.ToString(); else return dateTimeOffset.LocalDateTime.ToString(); } @@ -71,9 +71,9 @@ public object Convert(object value, Type targetType, object parameter, CultureIn public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value is not string dateTimeStr) - return new ValidationResult(false, "The value is not a string."); + return new ValidationResult(false, LocalizationSource.GetString("Msg_ValueIsNotString")); if (!DateTime.TryParse(dateTimeStr, out DateTime dateTime)) - return new ValidationResult(false, "Invalid date time format."); + return new ValidationResult(false, LocalizationSource.GetString("Msg_InvalidDateTimeFormat")); return (ulong)(new DateTimeOffset(dateTime).ToUnixTimeSeconds()); } diff --git a/UndertaleModTool/Editors/UndertaleGlobalInitEditor.xaml b/UndertaleModTool/Editors/UndertaleGlobalInitEditor.xaml index f687440ed..4697a5d89 100644 --- a/UndertaleModTool/Editors/UndertaleGlobalInitEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleGlobalInitEditor.xaml @@ -1,15 +1,16 @@ - - + - Double click to add + @@ -52,4 +53,4 @@ - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleParticleSystemEditor.xaml b/UndertaleModTool/Editors/UndertaleParticleSystemEditor.xaml index 0880d2992..4c0f2da25 100644 --- a/UndertaleModTool/Editors/UndertaleParticleSystemEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleParticleSystemEditor.xaml @@ -1,11 +1,12 @@ - @@ -19,10 +20,10 @@ - Name + - Origin + @@ -32,11 +33,11 @@ - Draw order + - Emitters - + + - Double click to add + diff --git a/UndertaleModTool/Editors/UndertaleParticleSystemEmitterEditor.xaml b/UndertaleModTool/Editors/UndertaleParticleSystemEmitterEditor.xaml index 19fe5c8ba..e4e0beab8 100644 --- a/UndertaleModTool/Editors/UndertaleParticleSystemEmitterEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleParticleSystemEmitterEditor.xaml @@ -1,12 +1,13 @@ - @@ -87,20 +88,20 @@ - Name + Enabled + Text="{loc:Loc Editor_Enabled}"/> - Emit mode + - Emit count + @@ -116,10 +117,10 @@ - Emit relative + - Delay min|max + @@ -129,11 +130,11 @@ - Delay measured in: + - Interval min|max + @@ -143,20 +144,20 @@ - Interval measured in: + - Distribution + - Shape + - Region X|Y|Width|Height + @@ -170,10 +171,10 @@ - Rotation + - Sprite + @@ -193,7 +194,7 @@ - Sprite frame index + @@ -207,33 +208,33 @@ - Animate sprite + - Stretch sprite + - Random + - Texture + - Start color + - Middle color + - End color + - Additive color blend + - Lifetime min|max + @@ -243,7 +244,7 @@ - Scale X|Y + @@ -253,13 +254,9 @@ - - Hint: Prior to GM 2023.8, these "Size" values were not split by x and y. - UndertaleModTool represents this by averaging the values. - For best results, enter the same value in both fields. - + - Size min + @@ -269,7 +266,7 @@ - Size max + @@ -279,7 +276,7 @@ - Size increase + @@ -289,7 +286,7 @@ - Size wiggle + @@ -299,7 +296,7 @@ - Speed min|max + @@ -309,7 +306,7 @@ - Speed increase|wiggle + @@ -319,7 +316,7 @@ - Gravity force|direction + @@ -329,7 +326,7 @@ - Direction min|max + @@ -339,7 +336,7 @@ - Direction increase|wiggle + @@ -349,7 +346,7 @@ - Orientation min|max + @@ -359,7 +356,7 @@ - Orientation increase|wiggle + @@ -369,19 +366,19 @@ - Is orientation relative + - Spawn on death + - Spawn on death count + - Spawn on update + - Spawn on update count + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertalePathEditor.xaml b/UndertaleModTool/Editors/UndertalePathEditor.xaml index ef42b50e0..a9b258e7e 100644 --- a/UndertaleModTool/Editors/UndertalePathEditor.xaml +++ b/UndertaleModTool/Editors/UndertalePathEditor.xaml @@ -1,17 +1,18 @@ - - + @@ -27,19 +28,19 @@ - Name + - Smooth + - Closed + - - Precision + + - Points + @@ -58,7 +59,7 @@ - Double click to add + @@ -67,21 +68,21 @@ - + - + - + @@ -92,7 +93,7 @@ - TODO: The preview doesn't update live, I know, just close and reopen the object for now + diff --git a/UndertaleModTool/Editors/UndertaleRoomEditor/UndertaleRoomEditor.xaml b/UndertaleModTool/Editors/UndertaleRoomEditor/UndertaleRoomEditor.xaml index ccd1d9bf9..f3f360e02 100644 --- a/UndertaleModTool/Editors/UndertaleRoomEditor/UndertaleRoomEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleRoomEditor/UndertaleRoomEditor.xaml @@ -1,4 +1,4 @@ - @@ -112,8 +113,8 @@ - - + + @@ -128,8 +129,8 @@ - - + + @@ -144,8 +145,8 @@ - - + + @@ -160,8 +161,8 @@ - - + + @@ -169,10 +170,10 @@ - + - + @@ -194,15 +195,15 @@ - + - + - + - + @@ -254,8 +255,8 @@ - - + + @@ -263,18 +264,18 @@ - - + + - + - - - - + + + + @@ -299,9 +300,9 @@ - - - + + + @@ -312,7 +313,7 @@ - + @@ -336,10 +337,10 @@ - - - - + + + + @@ -350,7 +351,7 @@ - + @@ -361,7 +362,7 @@ - + @@ -377,10 +378,10 @@ Height="Auto" VerticalAlignment="Top" Margin="-3,0,0,0"> @@ -421,13 +422,13 @@ - + diff --git a/UndertaleModTool/Editors/UndertaleSoundEditor.xaml b/UndertaleModTool/Editors/UndertaleSoundEditor.xaml index cd76c5958..83b94373a 100644 --- a/UndertaleModTool/Editors/UndertaleSoundEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleSoundEditor.xaml @@ -1,11 +1,12 @@ - @@ -27,34 +28,34 @@ - Name + - Flags + - Type + - File + - Effects + - Volume + - Pitch + - Audio group + - Audio file + - Preload (old audio system) + @@ -65,8 +66,8 @@ - Play - Stop + + diff --git a/UndertaleModTool/Editors/UndertaleSoundEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleSoundEditor.xaml.cs index 722fc914a..01f08f9da 100644 --- a/UndertaleModTool/Editors/UndertaleSoundEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleSoundEditor.xaml.cs @@ -1,4 +1,4 @@ -#pragma warning disable CA1416 // Validate platform compatibility +#pragma warning disable CA1416 // Validate platform compatibility using System; using System.Collections.Generic; @@ -20,6 +20,7 @@ using UndertaleModLib; using UndertaleModLib.Models; using UndertaleModLib.Util; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -140,7 +141,7 @@ private void Play_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { waveOut = null; - mainWindow.ShowError("Failed to play audio!\r\n" + ex.Message, "Audio failure"); + mainWindow.ShowError(LocalizationSource.GetString("Msg_FailedPlayAudio") + "\r\n" + ex.Message, LocalizationSource.GetString("Msg_AudioFailure")); } return; } @@ -177,11 +178,11 @@ private void Play_Click(object sender, RoutedEventArgs e) target = audioGroupData.EmbeddedAudio[sound.AudioID]; } else - throw new Exception("Failed to find audio group file."); + throw new Exception(LocalizationSource.GetString("Msg_FailedFindAudioGroupFile")); } catch (Exception ex) { waveOut = null; - mainWindow.ShowError("Failed to play audio!\r\n" + ex.Message, "Audio failure"); + mainWindow.ShowError(LocalizationSource.GetString("Msg_FailedPlayAudio") + "\r\n" + ex.Message, LocalizationSource.GetString("Msg_AudioFailure")); return; } } else @@ -218,7 +219,7 @@ private void Play_Click(object sender, RoutedEventArgs e) } } else - mainWindow.ShowError("Failed to play audio!\r\nNo options for playback worked.", "Audio failure"); + mainWindow.ShowError(LocalizationSource.GetString("Msg_FailedPlayAudioNoPlayback"), LocalizationSource.GetString("Msg_AudioFailure")); } diff --git a/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml b/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml index ee33f635b..99bb7ba5e 100644 --- a/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml @@ -1,4 +1,4 @@ - @@ -56,10 +57,10 @@ - Name + - Size + @@ -67,13 +68,13 @@ - Width: + - Height: + - Margin + @@ -85,33 +86,33 @@ - Left: + - Right: + - Bottom: + - Top: + - Transparent + - Smooth + - Preload + - Bounding box mode + - SepMasks + - Origin + @@ -119,13 +120,13 @@ - X: + - Y: + - Textures + - Double click to add + @@ -178,9 +179,9 @@ - + - Collision masks + - Double click to add + @@ -220,8 +221,8 @@ - - + + @@ -251,7 +252,7 @@ - + @@ -264,8 +265,8 @@ - - + + @@ -291,7 +292,7 @@ - Is special type? + @@ -305,10 +306,10 @@ - Version + - Type + @@ -322,13 +323,13 @@ - Playback speed + - Playback speed type + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml.cs index d1c2cc35d..01061ea9b 100644 --- a/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleSpriteEditor.xaml.cs @@ -1,4 +1,4 @@ -using Microsoft.Win32; +using Microsoft.Win32; using System; using System.Collections.ObjectModel; using System.Collections.Specialized; @@ -10,6 +10,7 @@ using System.Windows.Input; using UndertaleModLib.Models; using UndertaleModLib.Util; +using UndertaleModTool.Localization; using static UndertaleModLib.Models.UndertaleSprite; namespace UndertaleModTool @@ -125,8 +126,7 @@ private void OnAssetUpdated() private void ExportAllSpine(SaveFileDialog dlg, UndertaleSprite sprite) { - mainWindow.ShowWarning("This seems to be a Spine sprite, .json and .atlas files will be exported together with the frames. " + - "PLEASE EDIT THEM CAREFULLY! SOME MANUAL EDITING OF THE JSON MAY BE REQUIRED! THE DATA IS EXPORTED AS-IS.", "Spine warning"); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_SpineSpriteWarning"), LocalizationSource.GetString("Dialog_SpineWarning")); if (dlg.ShowDialog() == true) { @@ -152,7 +152,7 @@ private void ExportAllSpine(SaveFileDialog dlg, UndertaleSprite sprite) } catch (Exception ex) { - mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportFile")); } } } @@ -164,7 +164,7 @@ private void ExportAllSpine(SaveFileDialog dlg, UndertaleSprite sprite) } catch (Exception ex) { - mainWindow.ShowError("Failed to export: " + ex.Message, "Failed to export sprite"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportSprite"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportSprite")); } } } @@ -177,7 +177,7 @@ private void ExportAll_Click(object sender, RoutedEventArgs e) dlg.FileName = sprite.Name.Content + ".png"; dlg.DefaultExt = ".png"; - dlg.Filter = "PNG files (.png)|*.png|All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_PNG") + "|*.png|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; if (sprite.IsSpineSprite) { @@ -190,7 +190,7 @@ private void ExportAll_Click(object sender, RoutedEventArgs e) { try { - bool includePadding = (mainWindow.ShowQuestion("Include padding?") == MessageBoxResult.Yes); + bool includePadding = (mainWindow.ShowQuestion(LocalizationSource.GetString("Msg_IncludePadding")) == MessageBoxResult.Yes); using TextureWorker worker = new(); if (sprite.Textures.Count > 1) @@ -209,7 +209,7 @@ private void ExportAll_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportFile")); } } } @@ -221,17 +221,17 @@ private void ExportAll_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportFile")); } } else { - mainWindow.ShowError("No frames to export", "Failed to export sprite"); + mainWindow.ShowError(LocalizationSource.GetString("Msg_NoFramesToExport"), LocalizationSource.GetString("Dialog_FailedToExportSprite")); } } catch (Exception ex) { - mainWindow.ShowError("Failed to export: " + ex.Message, "Failed to export sprite"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportSprite"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportSprite")); } } } @@ -248,7 +248,7 @@ private void MaskImport_Click(object sender, RoutedEventArgs e) OpenFileDialog dlg = new OpenFileDialog(); dlg.DefaultExt = ".png"; - dlg.Filter = "PNG files (.png)|*.png|All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_PNG") + "|*.png|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; if (dlg.ShowDialog() == true) { @@ -263,7 +263,7 @@ private void MaskImport_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to import file: " + ex.Message, "Failed to import file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToImportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToImportFile")); } } } @@ -276,7 +276,7 @@ private void MaskExport_Click(object sender, RoutedEventArgs e) SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = ".png"; - dlg.Filter = "PNG files (.png)|*.png|All files|*"; + dlg.Filter = LocalizationSource.GetString("FileFilter_PNG") + "|*.png|" + LocalizationSource.GetString("FileFilter_AllFiles") + "|*"; if (dlg.ShowDialog() == true) { @@ -287,7 +287,7 @@ private void MaskExport_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToExportFile"), ex.Message), LocalizationSource.GetString("Dialog_FailedToExportFile")); } } } @@ -298,9 +298,9 @@ private void UndertaleObjectReference_Loaded(object sender, RoutedEventArgs e) objRef.ClearRemoveClickHandler(); objRef.RemoveButton.Click += Remove_Click_Override; - objRef.RemoveButton.ToolTip = "Remove texture entry"; + objRef.RemoveButton.ToolTip = LocalizationSource.GetString("Editor_RemoveTextureEntry"); objRef.RemoveButton.IsEnabled = true; - objRef.DetailsButton.ToolTip = "Open texture entry"; + objRef.DetailsButton.ToolTip = LocalizationSource.GetString("Editor_OpenTextureEntry"); objRef.ObjectText.PreviewKeyDown += ObjectText_PreviewKeyDown; } private void ObjectText_PreviewKeyDown(object sender, KeyEventArgs e) diff --git a/UndertaleModTool/Editors/UndertaleStringEditor.xaml b/UndertaleModTool/Editors/UndertaleStringEditor.xaml index f8611efc7..75fd0d61c 100644 --- a/UndertaleModTool/Editors/UndertaleStringEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleStringEditor.xaml @@ -1,11 +1,12 @@ - @@ -16,7 +17,7 @@ - Content + diff --git a/UndertaleModTool/Editors/UndertaleTextureGroupInfoEditor.xaml b/UndertaleModTool/Editors/UndertaleTextureGroupInfoEditor.xaml index bb7db11c5..f53226b50 100644 --- a/UndertaleModTool/Editors/UndertaleTextureGroupInfoEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleTextureGroupInfoEditor.xaml @@ -1,10 +1,11 @@ - @@ -18,7 +19,7 @@ - + @@ -52,13 +53,13 @@ - Group Name + - Texture Pages - + + - Double click to add + @@ -102,8 +103,8 @@ - Sprites - + + - Double click to add + @@ -147,8 +148,8 @@ - Spine Sprites - + + - Double click to add + @@ -192,8 +193,8 @@ - Fonts - + + - Double click to add + @@ -237,8 +238,8 @@ - Tilesets - + + - Double click to add + @@ -289,4 +290,4 @@ TODO: Implement the Texture Group Info editor --> - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml b/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml index f8d32cec5..bb3cadbd6 100644 --- a/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml @@ -1,11 +1,12 @@ - @@ -30,7 +31,7 @@ - Source position/size + @@ -44,7 +45,7 @@ - Target position/size + @@ -58,7 +59,7 @@ - Bounding size + @@ -68,11 +69,11 @@ - Texture + Find all references to this page item + Click="FindReferencesButton_Click" Content="{loc:Loc Editor_TexPageItem_FindAllReferences}"/> @@ -87,8 +88,8 @@ - - + + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs index 7a0c1b640..9e15423a9 100644 --- a/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls; @@ -9,6 +9,7 @@ using Microsoft.Win32; using UndertaleModLib.Models; using UndertaleModLib.Util; +using UndertaleModTool.Localization; using UndertaleModTool.Windows; namespace UndertaleModTool @@ -219,8 +220,7 @@ private void FindReferencesButton_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_ObjectReferencesWindowError"), ex)); } finally { diff --git a/UndertaleModTool/Editors/UndertaleTimelineEditor.xaml b/UndertaleModTool/Editors/UndertaleTimelineEditor.xaml index b380794ad..a45e5f6af 100644 --- a/UndertaleModTool/Editors/UndertaleTimelineEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleTimelineEditor.xaml @@ -1,11 +1,12 @@ - @@ -17,10 +18,10 @@ - Name + - Moments + @@ -39,7 +40,7 @@ - Double click to add + @@ -48,19 +49,19 @@ - + - + - + diff --git a/UndertaleModTool/Editors/UndertaleVariableChunkEditor.xaml b/UndertaleModTool/Editors/UndertaleVariableChunkEditor.xaml index 6d1b738d7..63c0284db 100644 --- a/UndertaleModTool/Editors/UndertaleVariableChunkEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleVariableChunkEditor.xaml @@ -1,12 +1,13 @@ - @@ -21,16 +22,16 @@ - + - Instance/global variable count + - Instance/global variable count (again) + - Max local variable count + - + \ No newline at end of file diff --git a/UndertaleModTool/Editors/UndertaleVariableEditor.xaml b/UndertaleModTool/Editors/UndertaleVariableEditor.xaml index d54bdf854..ab595c797 100644 --- a/UndertaleModTool/Editors/UndertaleVariableEditor.xaml +++ b/UndertaleModTool/Editors/UndertaleVariableEditor.xaml @@ -1,12 +1,13 @@ - @@ -15,7 +16,7 @@ - + @@ -28,16 +29,16 @@ - Name + - Instance type + - VarID + - Name string ID + - + \ No newline at end of file diff --git a/UndertaleModTool/Localization/LocExtension.cs b/UndertaleModTool/Localization/LocExtension.cs new file mode 100644 index 000000000..3d50e191b --- /dev/null +++ b/UndertaleModTool/Localization/LocExtension.cs @@ -0,0 +1,26 @@ +using System; +using System.Windows.Data; +using System.Windows.Markup; + +namespace UndertaleModTool.Localization +{ + public class LocExtension : MarkupExtension + { + public string Key { get; } + + public LocExtension(string key) + { + Key = key; + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + var binding = new Binding($"[{Key}]") + { + Source = LocalizationSource.Instance, + Mode = BindingMode.OneWay + }; + return binding.ProvideValue(serviceProvider); + } + } +} diff --git a/UndertaleModTool/Localization/LocalizationSource.cs b/UndertaleModTool/Localization/LocalizationSource.cs new file mode 100644 index 000000000..babc8f3a5 --- /dev/null +++ b/UndertaleModTool/Localization/LocalizationSource.cs @@ -0,0 +1,43 @@ +using System.ComponentModel; +using System.Globalization; +using System.Resources; + +namespace UndertaleModTool.Localization +{ + public class LocalizationSource : INotifyPropertyChanged + { + private static readonly LocalizationSource _instance = new(); + public static LocalizationSource Instance => _instance; + + private readonly ResourceManager _manager; + + public event PropertyChangedEventHandler PropertyChanged; + + private CultureInfo _currentCulture = CultureInfo.CurrentUICulture; + + public CultureInfo CurrentCulture + { + get => _currentCulture; + set + { + if (_currentCulture.Name != value.Name) + { + _currentCulture = value; + CultureInfo.DefaultThreadCurrentUICulture = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null)); + } + } + } + + public string this[string key] => _manager.GetString(key, _currentCulture) ?? key; + + public static string GetString(string key) => Instance[key]; + + public static string FallbackNoGameLoaded => Instance["Main_FallbackNoGameLoaded"]; + + public LocalizationSource() + { + _manager = new ResourceManager("UndertaleModTool.Localization.Strings", typeof(LocalizationSource).Assembly); + } + } +} diff --git a/UndertaleModTool/Localization/Strings.resx b/UndertaleModTool/Localization/Strings.resx new file mode 100644 index 000000000..76c9e4558 --- /dev/null +++ b/UndertaleModTool/Localization/Strings.resx @@ -0,0 +1,4952 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Name + + + Close + + + Add + + + Delete + + + Save + + + Open + + + Export + + + Import + + + Play + + + Stop + + + None + + + Done + + + Cancel + + + OK + + + Yes + + + No + + + Error + + + Warning + + + Double click to add + + + Filter by name... + + + Search + + + Copy + + + Copy all + + + Open in new tab + + + Transparent + + + Smooth + + + Preload + + + Texture + + + Scale + + + List + + + Code + + + Flags + + + Type + + + Size + + + Position + + + Definition + + + Enabled + + + Visible + + + Color + + + Speed + + + Depth + + + Rotation + + + Kind + + + Edit + + + Auto + + + n/a + + + _File + + + _New + + + _Open + + + _Save + + + _Temp run game + + + Run game under GMS _debugger + + + Run game with other runner + + + Generate o_ffset map + + + S_ettings + + + _Close + + + _Scripts + + + (...loading...) + + + _Find + + + _Search in code + + + Find unreferenced _assets + + + _Project + + + _New project + + + _Open project + + + _Save project + + + _View unexported assets + + + _Close project + + + _Help + + + _GitHub + + + _About + + + Data + + + General info + + + Global init + + + Game End scripts + + + Audio groups + + + Sounds + + + Sprites + + + Backgrounds & Tile sets + + + Tile sets + + + Paths + + + Scripts + + + Shaders + + + Fonts + + + Timelines + + + Game objects + + + Rooms + + + Extensions + + + Texture page items + + + Code + + + Variables + + + Functions + + + Code locals + + + Strings + + + Embedded textures + + + Embedded audio + + + Texture group information + + + Embedded images + + + Particle systems + + + Particle system emitters + + + Welcome! + + + Close + + + Close other tabs + + + Close all tabs + + + Settings + + + GameMaker: Studio 1.4 path: + + + Required only if you want to use the Studio runner rather than the .exe or run the game under debugger. + + + GameMaker Studio 2 runtimes path: + + + Required only if you want to run GMS2 games using the Studio runner rather than the .exe + + + Enable asset order swapping + + + Toggles dragging & dropping assets in the asset tabs to different positions in the list. Disabled by default. + + + Automatically associate GameMaker data files + + + Automatic file association. Enabled by default. + + + Show "Run game under GMS debugger" file option + + + Whether to show the option in the "File" menu. Disabled by default. + + + Remember last window position and size + + + Whether to restore window position and size when launching a new tool instance. Disabled by default. + + + Warn about saving before closing + + + Warn about saving before closing. Enabled by default. + + + Warn about temp running + + + Warn about temp running. Enabled by default. + + + Show null entries in resource tree + + + Whether to show null entries in the resource tree, which in GM 2024.11+ games usually were unused assets removed by the compiler. Disabled by default due to limited support in the GUI. + + + Recompile all source GML when saving a project data file + + + Whether to recompile all GML source code included in the currently-loaded project, if one is loaded, whenever saving a data file (including temp runs). Disabled by default. + + + Enable dark mode + + + Makes the program interface dark. Disabled by default. + + + Transparency grid colors: + + + GML compiler/decompiler settings + + + Global grid width + + + This option globally overrides the automatic assignment of a room's grid width based on the most used tile's width in that room. + + + Global grid height + + + This option globally overrides the automatic assignment of a room's grid height based on the most used tile's height in that room. + + + Global grid thickness + + + This option globally overrides the automatic assignment of a room's grid thickness. + + + Open application data folder + + + Update app to latest commit + + + Settings - GML compiler/decompiler + + + General settings (compiler & decompiler) + + + Settings that apply to both the compiler and decompiler, to make editing GML code simpler. + + + Use CSS hex color literals + + + If enabled, color constants can be formatted in the #RRGGBB hex format. + + + Named instance ID prefix: + + + Prefix used for named instance IDs when found by decompiler, and for compilation. Change this value if it conflicts with variable names. + + + Decompiler settings + + + Settings that solely apply to the GML decompiler, and will not affect GML compilation. These do not apply to all scripts. + + + Always use semicolons + + + If enabled, all statements that can have a semicolon after them will. + + + Indentation style: + + + Style of indentation to be used when printing decompiled code. + + + Open block braces on same line + + + If enabled, the curly braces used to open a block will be placed on the same line as the statement it belongs to, rather than placing the brace on its own line. + + + Remove block braces if single line + + + If enabled, blocks that contain a single line will have its braces removed, where possible. For if/else chains, all blocks must be single lines. + + + Create enum declarations + + + If enabled, enum declarations identified during decompilation will always be printed to decompiled output. + + + Enum/macro declarations at top of code + + + If enabled, enums and macros will appear at the start (top) of decompiled output, rather than at the end (bottom). + + + Print warnings + + + If enabled, warnings generated during decompilation will be printed as comments. + + + Allow leftover data on VM stack + + + If enabled, data left over on the VM stack will be treated as a warning rather than an error. + + + Add empty lines: + + + Select specific syntax to add empty lines before, after, or around. + + + Around branch statements + + + If enabled, adds an empty line before and after branch statements. + + + After local variable declarations + + + If enabled, adds an empty line after block-scoped local variable declarations. + + + Before switch cases + + + If enabled, adds an empty line before (a chain) of "case" entries in a switch statement. + + + After switch cases + + + If enabled, adds an empty line after (a chain) of "case" entries in a switch statement. + + + Around function declarations + + + If enabled, adds an empty line before and after a function declaration, even if it is anonymous. + + + Around static initialization + + + If enabled, adds an empty line before and after static initialization, grouping together consecutive statics if possible. + + + Perform cleanups: + + + Select specific cleanup operations for the decompiler to perform. Disabling these may make code more ugly or "incorrect," but can show underlying logic. + + + Try/catch/finally rewriting + + + If enabled, compiler-generated code and logic for try/catch/finally statements is rewritten to (attempt to) look like the original code. + + + Transform else into continue + + + If enabled, certain usages of "else" inside of a loop can be transformed into "continue" statements, which can make code easier to read. This only happens if accuracy can be maintained. + + + Default argument values + + + If enabled, syntax for default named function arguments will be used. Compiler-generated if statements are transformed to do this, maintaining accuracy. + + + Builtin array variable syntax + + + If enabled, certain builtin globals (such as "view_xview"), when detected to be used without an array accessor, will be printed as such, rather than using compiler-generated syntax. + + + Scope local variable declarations + + + If enabled, local variable declarations will be placed according to their scopes, which are detected based on usage in the code. + + + Restore to defaults + + + General info + + + Options + + + Language (?) + + + Disable GMS debugger + + + Bytecode version + + + FileName + + + Config + + + Last object ID + + + Last tile ID + + + Game ID + + + DirectPlay GUID + + + Version + + + Default window size + + + License MD5 + + + License CRC32 + + + Timestamp + + + (local time, hover to see GMT+0) + + + Display name + + + Active targets + + + Function classifications + + + Steam AppID + + + Debugger port + + + Room order + + + Sync with room list + + + FPS + + + Allow statistics + + + Game GUID + + + Shader Extension Flag + + + Shader Extension Version + + + Window color + + + Color depth + + + Resolution + + + Frequency + + + Vertex sync + + + Priority + + + Back image + + + Front image + + + Load image + + + Load alpha + + + Constants + + + Unknown + + + Language count + + + Entry count + + + Note: the languages currently don't seem used, and so the editor for them has not yet been made. + + + Width: + + + Height: + + + Margin + + + Left: + + + Right: + + + Bottom: + + + Top: + + + Bounding box mode + + + SepMasks + + + Origin + + + Textures + + + Export all frames (or Spine data if this is a Spine sprite) + + + Collision masks + + + Invalid mask data! + + + Import mask + + + Export mask + + + Is special type? + + + Version + + + Playback speed + + + Playback speed type + + + File + + + Effects + + + Volume + + + Pitch + + + Audio group + + + Audio file + + + Preload (old audio system) + + + Tile Width + + + Tile Height + + + Tile Separation X + + + Tile Separation Y + + + Output Border X + + + Output Border Y + + + Tile Columns + + + Items/frames per tile + + + Tile Count + + + Exported Sprite (GM 2023.8+) + + + Frame Time (microseconds) + + + Tile IDs + + + GM Tileset Version + + + Hint: You can click on any tile region below to highlight its ID above. + + + Locals count + + + Arguments count + + + Offset + + + Parent entry + + + Decompiled + + + Disassembly + + + Sprite + + + Visible + + + Solid + + + Depth + + + Persistent + + + Parent + + + Texture mask id + + + Uses physics + + + Is sensor + + + Collision shape + + + Physics + + + Density + + + Restitution + + + Group + + + Linear damping + + + Angular damping + + + Friction + + + Is awake + + + Is kinematic + + + Physics shape vertices + + + Events + + + Subtype ID + + + Actions + + + Display name + + + Font size + + + Bold + + + Italic + + + Range + + + Charset + + + Anti Aliasing + + + Ascender + + + Ascender offset + + + SDF spread value + + + Line height + + + Hint: You can click on any glyph here to highlight it in the "Glyphs". + + + Edit a selected glyph rectangle + + + Select a region of an empty glyph + + + Create an empty glyph + + + Glyphs: + + + Char + + + Source position/size + + + Shift + + + Offset + + + Kerning + + + Preceeding char + + + Add. shift + + + Note that the glyphs need to be specified in ascending order. +Press the button below to sort them appropriately. + + + Sort glyphs + + + Also, if you have added new characters or changed the character +of an existing glyph, you should update the font range. +Press the button below to do that. + + + Update range + + + Go back + + + Smooth + + + Closed + + + Precision + + + Points + + + TODO: The preview doesn't update live, I know, just close and reopen the object for now + + + Code + + + GLSL ES vertex source + + + GLSL ES fragment source + + + GLSL vertex source + + + GLSL fragment source + + + HLSL9 vertex source + + + HLSL9 fragment source + + + Vertex shader attributes + + + Moments + + + Step + + + Actions (Code Entries) + + + Instance type + + + VarID + + + Name string ID + + + Name string ID + + + Content + + + Source position/size + + + Target position/size + + + Bounding size + + + Find all references to this page item + + + Scaled + + + Generated mips + + + Warning: Texture failed to load! + + + Hint: This image is mouse interactable (3 mouse buttons and mouse wheel). + + + {0} data, {1} bytes + + + Texture + + + Class Name + + + Files + + + Add new extension file + + + Product ID + + + THINGS TO NOTE: +- Keep in mind that .gml files are either not present, or will be empty. (e.g. no functions) + Because they are prebaked into GML scripts. That is completely normal. + + + Add new extension option + + + Cleanup function name + + + Init function name + + + Functions + + + External Name + + + ID + + + Return Type + + + Argument Types + + + Runner literally doesn't care about the 'Kind' value. +The 'ID' does matter though, so make sure you don't have any overlaps. + + + Path (GM 2024.14+) + + + Texture Group + + + Group Name + + + Texture Pages + + + Sprites + + + Spine Sprites + + + Fonts + + + Tilesets + + + Backgrounds + + + Views + + + Game objects + + + Tiles + + + Layers + + + Caption + + + Persistent + + + Background color + + + Draw background color + + + Creation Code + + + World + + + Top/Left/Right/Bottom + + + Gravity + + + Grid Width + + + Grid Height + + + Grid Thickness + + + Foreground + + + Tiling + + + Stretch + + + View + + + Pos/size + + + Port pos/size + + + Border + + + Follows object + + + Instance ID + + + Creation code + + + Pre Create code + + + Frame index + + + Image speed + + + Source + + + Hint: You can select a tile by clicking on it. +To select a region of tile, hold down ALT. + + + Layer name + + + Layer id + + + Layer type + + + Depth + + + Offset + + + Empty layer data. + + + Contains GameObject instances, listed in the tree on the left + + + Contains legacy tiles and sprites, listed in the tree on the left + + + Tile set + + + Tile data + + + First frame + + + Animation speed + + + Save as PNG + + + Copy object + + + Delete object + + + Copy tile + + + Delete tile + + + Copy sprite instance + + + Delete sprite instance + + + Copy particle system instance + + + Delete particle system instance + + + Delete background + + + Disable view + + + New object instance + + + Paste object instance + + + New tile + + + Paste tile + + + New tile + + + New Instances layer + + + New Tiles layer + + + New Background layer + + + New Assets layer + + + New object instance + + + Delete layer + + + New legacy tile + + + New sprite instance + + + Paste asset + + + Move item up (-) + + + Move item down (+) + + + Tile {0} of {1} + + + Sprite {0} of {1} + + + Particle system {0} of {1} + + + Tile {0} of {1} (UndertaleRoom+Tile) + + + Welcome to UndertaleModTool! + + + Open a data.win file to get started, then double click on the items on the left to view them. + + + Double click on the items on the left to view them! + + + Open data.win file to get started, then double click on the items on the left to view them + + + New file created, have fun making a game out of nothing +I TOLD YOU to open a data.win, not create a new file! :P + + + Expand the list on the left to edit items + + + Load data.win file first + + + No game loaded + + + Marked for export: + + + Whether the currently open asset is marked to be exported to the currently open project, the next time it is saved. + + + This is an ID (index) of the currently open asset, item, or object. It starts from 0. + + + First-time setup: Would you like to associate GameMaker data files (like .win) with UndertaleModTool? + + + File associations + + + Loading + + + Loading, please wait... + + + Saving + + + Saving, please wait... + + + Decompiling, please wait... This can take a while on complex scripts. + + + Compiling, please wait... + + + Generating debugger data... + + + Recompiling GML code, please wait... + + + Saving data file, please wait... + + + Script in progress... + + + Please wait... + + + An error occurred while trying to load: +{0} + + + An error occurred while trying to save: +{0} + + + An error occurred while trying to recompile code sources: +{0} + + + The changes in code editor weren't saved due to some error in "SaveCodeChanges()". + + + Errors occurred during loading. High chance of data loss! Proceed at your own risk. + + + Failed to load settings.json! Using default values. +{0} + + + Failed to save settings.json! +{0} + + + Loading warning + + + Loading problems + + + Warnings occurred during loading. Data loss will likely occur when trying to save! + + + Only bytecode versions 13 to 17 are supported for now, you are trying to load {0}. A lot of code is disabled and will likely break something. Saving/exporting is disabled. + + + This game uses YYC (YoYo Compiler), which means the code is embedded into the game executable. This configuration is currently not fully supported; continue at your own risk. + + + This game is set to run with the GameMaker Studio debugger and the normal runtime will simply hang after loading if the debugger is not running. You can turn this off in General Info by checking the "Disable Debugger" box and saving. + + + This game contains texture(s) with unknown or unsupported image formats. These will save/load, but will not display in editors, and many operations will fail regarding them. Proceed with caution. + + + You are saving the game in GameMaker Studio debug mode. Unless the debugger is running, the normal runtime will simply hang after loading. You can turn this off in General Info by checking the "Disable Debugger" box and saving. + + + UndertaleModToolUpdater app didn't exit. +Can't delete its temp folder. + + + The updater temp folder can't be deleted. +Error - {0}. + + + Item name is null. + + + Save failed, cannot run. + + + The file must be saved in order to be run. + + + You must save your changes to run. + + + Use the "Run game using debugger" option to run this game. + + + Nothing to run! + + + Null game executable name or location + + + Failed to find valid game executable path; escaped directory + + + Cannot find game executable path, expected: {0} + + + Cannot find data file path, expected: {0} + + + Cannot find game path, expected to find it at: {0} + + + Failed to open child data file; escaped directory. + + + The selected runtime does not support debugging. + + + Failed to open browser! +{0} + + + Failed to open folder! +{0} + + + Your operating system is 32-bit. + + + + Not enough space on the system drive {0} - at least 500 MB is required. + + + Failed to fetch latest build! +{0} + + + Failed to find latest build! +Detected action name - {0} + + + UndertaleModTool is already up to date. +Update anyway? + + + Detected 32-bit (x86) version of UndertaleModTool on an 64-bit operating system. + + + + Failed to find the artifact! + + + Updater not found! Aborting update, report this to the devs! +Location checked: {0} + + + Can't copy the updater app to the temporary folder. +{0} + + + Failed to download new version of UndertaleModTool. +Error - {0}. + + + Can't copy the item name to clipboard due to this error: +{0} + + + An error occurred in the object references related window. + + + + An error occurred while trying to add the menu item. No action has been taken. + +Error: + +{0} + + + The script file doesn't exist. + + + Can't find code entry "{0}". +(probably, different game data was loaded) + + + Stopping the progress bar updater task is failed. +It's highly recommended to restart the application. + + + Warning: you currently have a project open. +Are you sure you want to make a new project? + + + Run {0} as a script? + + + Open {0} as a data file? + + + Save to the project's designated data file for saving? + + + A project is currently open - open another data file and discard all unsaved changes? + + + There are assets marked to be exported in the current project. +Really quit? + + + Save changes before quitting? + + + Script still runs. Save anyway? +It can corrupt the data file that you'll save. + + + Delete {0}? + + + Delete {0}? + +Note that the code often references objects by ID, so this operation is likely to break stuff because other items will shift up! + + + Asset name "{0}" is not a valid identifier. Add a new asset using an auto-generated name instead? + + + Add the new room to the end of the room order list? + + + The game has the debugger enabled. Would you like to disable it so the game will run? + + + Save changes first? + + + Are you sure that you want to run the game with GMS debugger? + + + + Confirmation + + + Load error + + + Save error + + + Recompile error + + + Script compile error + + + Script error + + + Script warning + + + Script Question + + + Run error + + + Unsupported bytecode version + + + YYC + + + GMS Debugger + + + Unsupported textures + + + Project currently open + + + Settings + + + Debug data + + + String reference changed + + + Runtime picker + + + Search in code + + + Unexported project assets + + + The types of references + + + The references of game object "" + + + GameMaker data files (.win, .unx, .ios, .droid, audiogroup*.dat) + + + GameMaker main data files (.win, .unx, .ios, .droid) + + + Close current tab + + + Close all tabs + + + Restore last closed tab + + + Switch to the next tab + + + Switch to the previous tab + + + Search in code + + + New project + + + Open project + + + Save project + + + View project assets + + + Close project + + + Compile code + + + Alternate paste command + + + Mirror the brush + + + Flip the brush + + + Rotate the brush 90 degrees clockwise + + + Rotate the brush 90 degrees counterclockwise + + + Toggle the tile grid + + + Toggle the "tiling" behavior on multi-tile brushes + + + Toggle the room preview + + + Undoes actions + + + Redoes actions + + + Back + + + Forward + + + Remove + + + The debugger is enabled. Do you want to generate debugger data? + + + Decompiled + + + Decompile whole source code when saving for display in the debugger + + + NOTE: no breakpoint support yet! Except for the script entry point + + + Disassembly, partial breakpoints + + + Full disassembly, but you can set breakpoints only on B, BR, BT, POP, POPZ, RET and EXIT. This is still a lot more than that poor GM:S debugger was designed to handle, so allow up to 5 minutes for it to load the data file (yes, seriously) + + + Disassembly, full breakpoints + + + Gave up after like 15 minutes of waiting, don't even try this + + + No debug data + + + You edited a string reference! What do you want to do? + + + Change only this one value + + + Change all occurrences of this value + + + Abort the change + + + Select the runtime you want to use + + + Version + + + Path + + + Continue + + + Case sensitive + + + Regex search + + + Multiline regex + + + In assembly + + + Filter by code name + + + Code + + + Line + + + Text + + + ClickableTextOutput + + + Displaying the results, please wait... + + + Default text with hyperlinks. + + + Below is a list of all assets currently waiting to be exported to disk for the current project, once it is next saved. + + + To quickly add or remove assets from being part of the project, you can drag and drop assets here, or delete them. + + + Name + + + Asset Type + + + Unmark for export + + + Where to search the references? + + + Select all + + + Deselect all + + + The search results for the game object +"". + + + Copy name to clipboard + + + Find all references + + + Note: the results may be not 100% complete. +Use one of the search scripts in the "Find" menu if necessary. + + + Export results + + + Back + + + Forward + + + Width + + + Height + + + X + + + Y + + + Unknown + + + UndertaleModTool + + + Create + + + Open referenced object + + + Remove reference + + + This is an object reference. Drag and drop an object of matching type from the tree on the left to change it! + + + Find all references + + + Copy name to clipboard + + + (null) + + + (empty) + + + (empty string) + + + #AABBGGRR + + + #BBGGRR + + + Invalid color string + + + Game objects + + + Game objects (from all layers) + + + New Instances Layer + + + New Tiles Layer + + + New Background Layer + + + New Assets Layer + + + NewExtensionFile{0}.dll + + + extensionOption{0} + + + Remove texture entry + + + Open texture entry + + + Constant + + + Number + + + GMT+0: {0} + + + Kerning of glyph '{0}' (code - {1}): + + + Instances layer + + + {0} (color) + + + {0} (constant) + + + {0} (number) + + + Horizontal + + + Vertical + + + PNG files (.png) + + + WAV files + + + OGG files + + + CSV table + + + All files + + + Text files (.txt) + + + Decompiling + + + Compiling + + + Compiler error + + + Assembler error + + + Failed to export file + + + Failed to export sprite + + + Failed to import file + + + Format mismatch + + + Spine warning + + + Unexpected texture dimensions + + + Unknown format + + + Audio failure + + + Choose source data file + + + Choose destination data file + + + Open project file + + + Choose project name + + + Script in progress... + + + An error occurred while trying to load: +{0} + + + An error occurred while trying to save: +{0} + + + An error occurred while trying to recompile code sources: +{0} + + + Failed to export file: {0} + + + Failed to export: {0} + + + Failed to import file: {0} + + + No frames to export + + + Include padding? + + + This seems to be a Spine sprite, .json and .atlas files will be exported together with the frames. PLEASE EDIT THEM CAREFULLY! SOME MANUAL EDITING OF THE JSON MAY BE REQUIRED! THE DATA IS EXPORTED AS-IS. + + + WARNING: Texture page dimensions are not powers of 2. Sprite blurring is very likely in-game. + + + {0} was converted into PNG format since we don't support converting images into DDS format. This might have performance issues in the game. + + + Failed to play audio! +{0} + + + Failed to play audio! +Not a WAV or OGG. + + + Warning: File being imported is not a WAV or OGG. Import anyway? + +This may corrupt the sound. + + + Warning: Filetype being imported does not match existing filetype. Import anyway? + +This may corrupt the sound, unless sound asset compression settings are adjusted as well. + + + Invalid boolean value + + + Invalid number string + + + Are you sure that you want to enable GMS debugger? +If you want to enable a debug mode in some game, then you need to use one of the scripts. + + + This code entry is a reference to an anonymous function within {0}, view it there. + + + There is a duplicate key in textdata_en, being {0}. This may cause errors in the comment display of text. + + + Unknown error in textdata_en. This may cause errors in the comment display of text. + + + Failed to parse language file: {0} + + + <localization fetch error> + + + Decompiling, please wait... This can take a while on complex scripts. + + + Compiling, please wait... + + + Editing disassembly while in an open project (even through scripts) can cause desyncs with source code in the project. + +The source code will not change unless you directly modify it, or if you remove the code asset from the project entirely. + + + The glyphs were sorted successfully. + + + The range was updated successfully. + + + Cannot find the glyphs table scroll viewer. + + + No glyph selected. + + + The font has no texture. + + + An error occurred in the glyph rectangle editor window. +Please report this on GitHub. + +{0} + + + The last glyph has zero size. +You can use the button on the left to fix that. + + + The character code is greater than the maximum (65535) + + + The value is not a string. + + + Invalid date time format. + + + Find all references of this tile + + + An error occurred in the object references related window. +Please report this on GitHub. + +{0} + + + Cannot find the tile ID list scroll viewer. + + + Find all references to this page item + + + The instances list of layer "{0}" is empty, but the layer has the instances ID. + + + No empty room backgrounds. + + + Please select a layer. + + + Please select an assets layer. + + + Please select an instance layer. + + + Room is null. + + + Warning - the maximum layer depth is reached. +You probably should change the depth of the new layer. + + + Layers don't support this feature currently, change the layer depths instead. + + + Views don't support this feature. + + + Backgrounds don't support this feature. + + + Can't change the object position - no list for the selected object was found. + + + Tile data is empty. + + + Error while saving the file - "{0}". + + + Exported file path: {0} + + + Tile data size can't be zero. + + + Error while opening file - "{0}". + + + Error - selected file line count doesn't match tile layer height. + + + Was the data exported from "Tiled"? + + + The file has invalid data. + + + Error while parsing line {0} - "{1}". + + + Length of line {0} is not equal to the tile data width. + + + Imported successfully. + + + The layer must have a tileset set! + + + The layer's horizontal and vertical size must be larger than 0 tiles! +(Use the Auto button to set the tilemap size based on the room size.) + + + The layer's horizontal size must be larger than 0 tiles! +(Use the Auto button to set the tilemap size based on the room size.) + + + The layer's vertical size must be larger than 0 tiles! +(Use the Auto button to set the tilemap size based on the room size.) + + + Room flags of GMS 2+ games must contain the "IsGMS2" flag, otherwise the game will crash when loading that room. + + + Room flags of GM 2024.13+ games must contain the "IsGM2024_13" flag, otherwise the game will crash when loading that room. + + + An error occurred while generating "Rectangles" for tile layer {0}. + +{1} + + + {0} does not exist! + + + An error of type "{0}" occurred. The error is: + +{1} + + + Script compile error + + + WARNING: +Temp running the game does not permanently +save your changes. Please "Save" the game +to save your changes. Closing UndertaleModTool +without using the "Save" option can +result in loss of work. + + + No data file is currently loaded! + + + Can't highlight the object - it's null or isn't an UndertaleResource. + + + Can't highlight the object "{0}". +Error - {1} + + + Can't highlight the object "{0}" - element with object list not found. + + + HTTP error - {0}. + + + Check your internet connection. + + + Downloaded MB: {0} + + + UndertaleModTool will now close to finish the update. + + + Currently, the destination data file's directory is empty. You will likely want to copy all other game files to the destination directory, so that external assets can be loaded correctly (both in-game and in this tool), and so the game can be started. + + + The destination data file is in the same directory as the source data file. This may permanently overwrite external data files. Proceed? + + + Destination in same directory + + + Choose destination data file + + + // This code entry is a reference to an anonymous function within "{0}", decompile that instead. + + + ; This code entry is a reference to an anonymous function within "{0}", disassemble that instead. + + + /* +DECOMPILER FAILED! + +{0} +*/ + + + /* +DISASSEMBLY FAILED! + +{0} +*/ + + + Cannot find game executable path, expected: {0} + + + Cannot find data file path, expected: {0} + + + Cannot find game path, expected to find it at: {0} + + + Save failed, cannot run. + + + The game has the debugger enabled. Would you like to disable it so the game will run? + + + You must save your changes to run. + + + Use the "Run game using debugger" option to run this game. + + + The selected runtime does not support debugging. + + + UndertaleModToolUpdater app didn't exit. +Can't delete its temp folder. + + + (error message is missing) + + + Warning: you currently have a project open. +Are you sure you want to make a new project? + + + Asset name "{0}" is not a valid identifier. Add a new asset using an auto-generated name instead? + + + Add to room order list + + + Delete {0}? + + + Note that the code often references objects by ID, so this operation is likely to break stuff because other items will shift up! + + + Save changes before quitting? + + + Script still runs. Save anyway? +It can corrupt the data file that you'll save. + + + There are assets marked to be exported in the current project. +Really quit? + + + There are assets marked to be exported in the current project. Really close? + + + Run {0} as a script? + + + Open {0} as a data file? + + + A project is currently open - open another data file and discard all unsaved changes? + + + There are assets marked to be exported in the current project - create a new project and discard all unexported changes? + + + There are assets marked to be exported in the current project - open another new project and discard all unexported changes? + + + Can't copy the item name to clipboard due to this error: +{0}. +You probably should try again. + + + An error occurred while trying to add the menu item. No action has been taken. + +Error: + +{0} + + + The script file doesn't exist. + + + Running {0} ... + + + {0} finished! + + + Can't find code entry "{0}". +(probably, different game data was loaded) + + + Failed to fetch latest build! +{0} + + + Failed to find latest build! +Detected action name - {0} + + + Failed to download new version of UndertaleModTool. +Error - {0}. + + + Null game executable name or location + + + The changes in code editor weren't saved due to some error in "SaveCodeChanges()". + + + Only bytecode versions 13 to 17 are supported for now, you are trying to load {0}. A lot of code is disabled and will likely break something. Saving/exporting is disabled. + + + This game uses YYC (YoYo Compiler), which means the code is embedded into the game executable. This configuration is currently not fully supported; continue at your own risk. + + + This game is set to run with the GameMaker Studio debugger and the normal runtime will simply hang after loading if the debugger is not running. You can turn this off in General Info by checking the "Disable Debugger" box and saving. + + + You are saving the game in GameMaker Studio debug mode. Unless the debugger is running, the normal runtime will simply hang after loading. You can turn this off in General Info by checking the "Disable Debugger" box and saving. + + + Failed to create new project + + + Error occurred when creating new project: +{0} + + + Error occurred when loading project: +{0} + + + Error occurred when saving project: +{0} + + + Failed to load project + + + Failed to save project + + + Project "{0}" created successfully! + + + Opened project "{0}". + + + Saved project successfully. + + + Project creation failed. + + + Project failed to open. + + + Project failed to save. + + + Project closed. + + + Cancelled new project creation. + + + Recompiling GML code, please wait... + + + Saving data file, please wait... + + + Choose new {0} name + + + Name of new asset: + + + Searching in progress... + + + Assets + + + Cannot determine GameMaker version - "General Info" is null. + + + Cannot get the source types for object of type "{0}". + + + At least one type should be selected. + + + You have selected the "Strings" when there are a lot of strings. +That could make the search process noticeably longer. +Do you want to proceed? + + + Failed to find audio file. + + + Failed to find audio group file. + + + Failed to play audio! +No options for playback worked. + + + Code entry for room already exists; reusing it. + + + Adding not supported in this situation. + + + The selected object is not an "UndertaleResource". + + + Tileset of "{0}" tile layer has wrong parameters (tile size, output border, etc.). +It can't be displayed. + + + An error occurred while rendering tile layer "{0}". + +{1} + + + The type of this object doesn't have an editor/viewer. + + + Unable to find game EXE or any installed Studio runtime + + + Run error + + + Game EXE + + + Generating + + + Downloading + + + Downloading new version... + + + Line + + + First-time setup: Would you like to associate GameMaker data files (like .win) with UndertaleModTool? + + + File associations + + + Remove current file associations, if they exist? + + + File associations + + + Unassociation failed + + + The message boxes (like this one) aren't compatible with the dark mode. +This will be fixed in future versions. + + + Welcome! + + + (drag & drop a {0}) + + + (empty {0} reference) + + + This is a string reference. Change the value here to edit this just this value of all referenced values, or drag and drop another string instance from the tree on the left to change the reference + + + (null) + + + (empty) + + + Welcome to UndertaleModTool! + + + Open a data.win file to get started, then double click on the items on the left to view them. + + + New file created, have fun making a game out of nothing +I TOLD YOU to open a data.win, not create a new file! :P + + + Double click on the items on the left to view them + + + Double click on the items on the left to view them! + + + Open data.win file to get started + + + No game loaded + + + Marked for export: + + + Whether the currently open asset is marked to be exported to the currently open project, the next time it is saved. + + + This is an ID (index) of the currently open asset, item, or object. It starts from 0. + + + Run game under GMS _debugger + + + _View unexported assets + + + Restore last closed tab + + + All files + + + Scripts (.csx) + + + Text files (.txt) + + + UndertaleModTool project files (.json) + + + PageItem {0} + + + EmbeddedSound {0} + + + Texture {0} + + + string{0} + + + New + + + Mod + + + Name + + + Enabled + + + Emit mode + + + Emit count + + + Emit relative + + + Delay min|max + + + Delay measured in: + + + Interval min|max + + + Interval measured in: + + + Distribution + + + Shape + + + Region X|Y|Width|Height + + + Rotation + + + Sprite + + + Sprite frame index + + + Animate sprite + + + Stretch sprite + + + Random + + + Texture + + + Start color + + + Middle color + + + End color + + + Additive color blend + + + Lifetime min|max + + + Scale X|Y + + + Hint: Prior to GM 2023.8, these "Size" values were not split by x and y. +UndertaleModTool represents this by averaging the values. +For best results, enter the same value in both fields. + + + Size min + + + Size max + + + Size increase + + + Size wiggle + + + Speed min|max + + + Speed increase|wiggle + + + Gravity force|direction + + + Direction min|max + + + Direction increase|wiggle + + + Orientation min|max + + + Orientation increase|wiggle + + + Is orientation relative + + + Spawn on death + + + Spawn on death count + + + Spawn on update + + + Spawn on update count + + + Local variables + + + Index + + + Variables + + + Instance/global variable count + + + Instance/global variable count (again) + + + Max local variable count + + + External Name + + + ID + + + Return Type + + + Argument Types + + + Runner literally doesn't care about the 'Kind' value. +The 'ID' does matter though, so make sure you don't have any overlaps. + + + Cleanup function name + + + Init function name + + + Functions + + + Global initialization scripts + + + Game End scripts + + + Edit Tile Data + + + Mirror the brush around the X axis (X) + + + Flip the brush around the Y axis (Y/Z) + + + Rotate the brush 90 degrees (Q, R) + + + Show the rest of the room behind the tilemap (P) + + + Enable the "tiling" behavior when using multi-tile brushes (B) + + + Show the tile grid (G) + + + Undo (Ctrl+Z) + + + Redo (Ctrl+Y, Ctrl+Shift+Z) + + + Tileset columns + + + Number of columns to use for the tile palette. Adjust if the tileset looks broken. +Persists when the tile editor is closed, but not when the file is saved and reopened. + + + Ctrl+LMB: Fill (+Shift: Global) Alt+LMB: Pick MMB: Scroll/Pick + + + Cancel + + + Apply + + + Preview + + + Brush Tiling + + + Grid + + + Edit a font glyph rectangle + + + Get help + + + Open referenced object + + + This is an object reference. Drag and drop an object of matching type from the tree on the left to change it! + + + Create new code entry + + + Remove reference + + + Add in place (unimplemented) + + + Sprites + + + Backgrounds + + + Embedded textures + + + Texture page items + + + Strings + + + Game objects + + + Code entries + + + Code entries (name & contents) + + + Functions + + + Variables + + + Embedded audio + + + Audio groups + + + Sounds + + + Fonts + + + Tile sets + + + Paths + + + Scripts + + + Shaders + + + Timelines + + + Extensions + + + Extension options + + + Extension files + + + Extension functions + + + General info + + + Game options constants + + + Rooms + + + Code + + + Code locals + + + Languages + + + Texture groups + + + Embedded images + + + Room tiles + + + Room sprite instances + + + Room background layers + + + Room backgrounds + + + Room tile layers + + + Room tile layer + + + Room layers + + + Room object instance + + + Room sequence instances + + + Room particle system instances + + + Room particle system instance + + + Room effect properties + + + Animation curves + + + Animation curve channels + + + Sequences + + + Sequence tracks + + + Sequence string keyframes + + + Sequence text keyframes + + + Sequence broadcast messages + + + Sequence moments + + + Sequence instance keyframes + + + Sequence object instance keyframes + + + Particle systems + + + Particle system emitters + + + Filter effects + + + Global init scripts + + + Game End scripts + + + Global init & Game End scripts + + + Assets + + + Please wait... + + + Game EXE + + + 1.4.xxx + + + Unable to find game EXE or any installed Studio runtime + + + {0}/{1} + + + {0}/{1}: {2} + + + The type of this object doesn't have an editor/viewer. + + + Paste + + + Continue + + + Value + + + Remove + + + _Temp run game + + + _Close project + + + Untitled + + + Audio Group + + + Sound + + + Sprite + + + Background + + + Path + + + Script + + + Shader + + + Font + + + Timeline + + + Game Object + + + Room + + + Extension + + + Texture Page Item + + + Code + + + Variable + + + Function + + + Code Locals + + + Embedded Texture + + + Embedded Audio + + + Texture Group info + + + Embedded Image + + + Sequence + + + Animation Curve + + + Particle System + + + Particle System Emitter + + + String + + + Extension File + + + Extension Function + + + Variables overview + + + General info + + + Global init + + + Game End + + + (empty string) + + + Open in new tab + + + Enable asset order swapping + + + Toggles dragging & dropping assets in the asset tabs to different positions in the list. Disabled by default. + + + Warn about temp running + + + Warn about temp running. Enabled by default. + + + Recompile all source GML when saving a project data file + + + Whether to recompile all GML source code included in the currently-loaded project, if one is loaded, whenever saving a data file (including temp runs). Disabled by default. + + + GML compiler/decompiler settings + + + Update app to latest commit + + + Settings - GML compiler/decompiler + + + Use CSS hex color literals + + + If enabled, color constants can be formatted in the #RRGGBB hex format. + + + Named instance ID prefix: + + + Prefix used for named instance IDs when found by decompiler, and for compilation. Change this value if it conflicts with variable names. + + + Open block braces on same line + + + If enabled, the curly braces used to open a block will be placed on the same line as the statement it belongs to, rather than placing the brace on its own line. + + + Remove block braces if single line + + + If enabled, blocks that contain a single line will have its braces removed, where possible. For if/else chains, all blocks must be single lines. + + + Around branch statements + + + If enabled, adds an empty line before and after branch statements. + + + After local variable declarations + + + If enabled, adds an empty line after block-scoped local variable declarations. + + + Before switch cases + + + If enabled, adds an empty line before (a chain) of "case" entries in a switch statement. + + + After switch cases + + + If enabled, adds an empty line after (a chain) of "case" entries in a switch statement. + + + Around function declarations + + + If enabled, adds an empty line before and after a function declaration, even if it is anonymous. + + + Around static initialization + + + If enabled, adds an empty line before and after static initialization, grouping together consecutive statics if possible. + + + Try/catch/finally rewriting + + + If enabled, compiler-generated code and logic for try/catch/finally statements is rewritten to (attempt to) look like the original code. + + + Transform else into continue + + + If enabled, certain usages of "else" inside of a loop can be transformed into "continue" statements, which can make code easier to read. This only happens if accuracy can be maintained. + + + Default argument values + + + If enabled, syntax for default named function arguments will be used. Compiler-generated if statements are transformed to do this, maintaining accuracy. + + + Builtin array variable syntax + + + If enabled, certain builtin globals (such as "view_xview"), when detected to be used without an array accessor, will be printed as such, rather than using compiler-generated syntax. + + + Scope local variable declarations + + + If enabled, local variable declarations will be placed according to their scopes, which are detected based on usage in the code. + + + Name + + + Locals count + + + Arguments count + + + Offset + + + Parent entry + + + Decompiled + + + Disassembly + + + Visible + + + Solid + + + Persistent + + + Parent + + + Texture mask id + + + Uses physics + + + Is sensor + + + Collision shape + + + Physics + + + Density + + + Restitution + + + Group + + + Linear damping + + + Angular damping + + + Friction + + + Is awake + + + Is kinematic + + + Physics shape vertices + + + Events + + + Subtype ID + + + Actions + + + N/A + + + Width: + + + Height: + + + Margin + + + Left: + + + Right: + + + Bottom: + + + Top: + + + Transparent + + + Smooth + + + Preload + + + Bounding box mode + + + SepMasks + + + Origin + + + X + + + Y + + + Textures + + + Export all frames (or Spine data if this is a Spine sprite) + + + Collision masks + + + Invalid mask data! + + + Import mask + + + Export mask + + + Is special type? + + + Playback speed + + + Playback speed type + + + Flags + + + File + + + Effects + + + Volume + + + Pitch + + + Audio group + + + Audio file + + + Preload (old audio system) + + + Play + + + Stop + + + Font size + + + Bold + + + Italic + + + Range + + + Charset + + + Anti Aliasing + + + Scale + + + Ascender + + + Ascender offset + + + SDF spread value + + + Line height + + + Hint: You can click on any glyph here to highlight it in the "Glyphs". + + + Edit a selected glyph rectangle + + + Select a region of an empty glyph + + + Create an empty glyph + + + Glyphs: + + + Char + + + Source position/size + + + Shift + + + Kerning + + + Go back + + + Preceeding char + + + Add. shift + + + Sort glyphs + + + Note that the glyphs need to be specified in ascending order. +Press the button below to sort them appropriately. + + + Update range + + + Also, if you have added new characters or changed the character +of an existing glyph, you should update the font range. +Press the button below to do that. + + + Version + + + List + + + GM Tileset Version + + + Tile Width + + + Tile Height + + + Tile Separation X + + + Tile Separation Y + + + Output Border X + + + Output Border Y + + + Tile Columns + + + Items/frames per tile + + + Tile Count + + + Exported Sprite (GM 2023.8+) + + + Frame Time (microseconds) + + + Tile IDs + + + Hint: You can click on any tile region below to highlight its ID above. + + + New sprite + + + Creation code + + + Rotation + + + Hint: You can select a tile by clicking on it. +To select a region of tile, hold down ALT. + + + Speed + + + Visible + + + Auto + + + Automatically determined + + + (no name) + + + Do you want to associate UndertaleModTool with GameMaker data files? + + + Warning + + + Error + + + UndertaleModTool + + + Settings - GML compiler/decompiler + + + Clickable text output + + + GameMaker data files (.win, .unx, .ios, .droid, audiogroup*.dat) + + + GameMaker main data files (.win, .unx, .ios, .droid) + + + None + + + Back + + + Forward + + + Search + + + Filter by name... + + + Marked for export: + + + Whether the currently open asset is marked to be exported to the currently open project, the next time it is saved. + + + This is an ID (index) of the currently open asset, item, or object. It starts from 0. + + + Case sensitive + + + Regex search + + + Multiline regex + + + In assembly + + + Filter by code name + + + Search + + + Code + + + Line + + + Text + + + Copy all + + + String reference edited + + + You edited a string reference! What do you want to do? + + + Runtime picker + + + Debug data + + + The debugger is enabled. Do you want to generate debugger data? + + + Decompiled + + + Decompile whole source code when saving for display in the debugger + + + NOTE: no breakpoint support yet! Except for the script entry point + + + Disassembly, partial breakpoints + + + Full disassembly, but you can set breakpoints only on B, BR, BT, POP, POPZ, RET and EXIT. This is still a lot more than that poor GM:S debugger was designed to handle, so allow up to 5 minutes for it to load the data file (yes, seriously) + + + Disassembly, full breakpoints + + + Gave up after like 15 minutes of waiting, don't even try this + + + Copy all + + + Search + + + References of + + + The search results for the game object +"". + + + + Edit + + + Export + + + Import + + + Edit glyph rectangle + + + False + + + Welcome! + + + 2 spaces + + + 4 spaces + + + Actions (Code Entries) + + + Add new extension file + + + Class Name + + + Hint: You can click on any tile region below to select it and edit its properties. + + + Code + + + Collision masks + + + Content + + + Depth + + + Draw order + + + Emitters + + + Export all frames (or Spine data if this is a Spine sprite) + + + Files + + + Fonts + + + Frame Time (microseconds) + + + GLSL ES fragment source + + + GLSL ES vertex source + + + GLSL fragment source + + + GLSL vertex source + + + Hint: You can click on any glyph rectangle below to select it and edit its properties, or double-click to add a new glyph. + + + Group Name + + + HLSL9 fragment source + + + HLSL9 vertex source + + + Items/frames per tile + + + Kind + + + (local time, hover to see GMT+0) + + + Moments + + + new_extension_function_{0} + + + new_extension_function_{0}_ext + + + The number of pixels to horizontally offset the rendering of the glyph. +It's highlighed in blue on the texture above. + + + Path (GM 2024.14+) + + + Product ID + + + (?) + + + - + + + Scaled + + + Size + + + Speed + + + Spine Sprites + + + Sprites + + + Step + + + Tabs + + + Texture Group + + + Update range + + + Updates the range of the currently displayed glyphs to match the font texture. + + + Scripts (.csx) + + + About + + + Backgrounds & Tile sets + + + Downloading + + + Generating + + + Loading + + + Running {0} ... + + + Tile sets + + + Your operating system is 32-bit. +The 32-bit (x86) version of UndertaleModTool is obsolete. + + + Can't copy the item name to clipboard due to this error: +{0} + + + Cannot find data file path, expected: {0} + + + Cannot find game executable path, expected: {0} + + + Cannot find game executable path, expected to find it at: {0} + + + Cannot find game path, expected to find it at: {0} + + + Can't highlight the object "{0}" - element with object list not found. + + + Choose project name + + + Choose source data file + + + #{0} + + + Confirmation + + + Delete {0}? + + + (empty {0} reference) + + + Error while opening file - "{0}". + + + Failed to play audio! +No options for playback worked. + + + Failed to find the artifact! + + + Failed to load project + + + Failed to open browser! +{0} + + + Failed to open folder! +{0} + + + Failed to save project + + + File "{0}" already exists. Overwrite? + + + Failed to find valid game executable path; escaped directory + + + Cannot determine GameMaker version - "General Info" is null. + + + Invalid name + + + The search term is not a valid regex pattern. + + + The instances list of layer "{0}" is empty, but the layer has the instances ID. + + + Line {0}: {1} + + + Load data.win file first + + + Warnings occurred during loading. Data loss will likely occur when trying to save! + + + You have selected the "Strings" when there are a lot of strings. +That could make the search process noticeably longer. +Do you want to proceed? + + + No references found. + + + No unreferenced assets found. + + + One code entry matched the filter. + + + Path {0} does not exist, cannot search for files! + + + A project is currently open - open another data file and discard all unsaved changes? + + + Warning: you currently have a project open. +Are you sure you want to make a new project? + + + A project is currently open - open another data file and discard all unsaved changes? + + + The references of game object "{0}" + + + Results in code entries + + + Results saved successfully. + + + Run other script... + + + Script message + + + Script Question + + + Script still runs. Save anyway? +It can corrupt the data file that you'll save. + + + ({0} errors) + + + Found {2} result{1} in {0} code entries. + The {1} is used for 's' suffix + + + Tileset of "{0}" tile layer has wrong parameters (tile size, output border, etc.). +It can't be displayed. + + + There are assets marked to be exported in the current project - open another new project and discard all unexported changes? + + + Unhandled exception + + + Unknown indent style + + + Unreferenced game assets + + + Unsupported bytecode version + + + This game contains texture(s) with unknown or unsupported image formats. These will save/load, but will not display in editors, and many operations will fail regarding them. Proceed with caution. + + + This game uses YYC (YoYo Compiler), which means the code is embedded into the game executable. This configuration is currently not fully supported; continue at your own risk. + + + Color + + + Definition + + + Depth + + + Enabled + + + Position + + + Speed + + + Sprite + + + Hint: You can click on any tile region below to select it and edit its properties. + + + Visible + + + Whether to allow changing the order of assets by dragging and dropping them in the tree view. + + + GameMaker: Studio 1.4 path: + + + The path to the GameMaker Studio 2 runtimes directory. + + + Whether to recompile all GML code sources when saving a project data file. + + + Animation Curve + + + Audio Group + + + Code + + + Code Locals + + + Embedded Image + + + Embedded Texture + + + Extension + + + Extension file + + + Extension function + + + Font + + + Function + + + Game End + + + Game Object + + + General Info + + + Global Init + + + Particle System Emitter + + + Room + + + Script + + + Sequence + + + Shader + + + Sound + + + Sprite + + + String + + + Timeline + + + Variables Overview + + + True + + + Untitled + + + Version + + + Compiler error + + + The references of game object + + + Searching + + + Unreferenced game assets + + + Add new extension option + + + Closed + + + em + + + Exported Sprite (GM 2023.8+) + + + - Keep in mind that .gml files are either not present, or will be empty. (e.g. no functions) + + + Because they are prebaked into GML scripts. That is completely normal. + + + THINGS TO NOTE: + + + Generated mips + + + Note that the glyphs need to be specified in ascending order. + + + Sort glyphs + + + TODO: The preview doesn't update live, but it will after switching to another tab and back. + + + Points + + + Precision + + + Select a region of an empty glyph + + + The number of pixels to shift right when advancing to the next character. +It's highlighed in green on the texture above. + + + Warning: Texture failed to load! + + + Hint: This image is mouse interactable. You can click on it to select the corresponding texture page item. + + + Texture Pages + + + Tilesets + + + Type + + + Note: the glyphs need to be specified in ascending order for this to work correctly. + + + Value + + + Vertex shader attributes + + + x + + + Downloading new version... + + + Expand the list on the left to edit items + + + ID: {0} + + + Loading... + + + Loading, please wait... + + + New file created, have fun making a game out of nothing +I TOLD YOU to open a data.win, not create a new file! :P + + + Saving + + + Saving, please wait... + + + {0} finished! + + + Find all references + + + Find all references of this tile + + + Find all references to this page item + + + Open in new tab + + + Detected 32-bit (x86) version of UndertaleModTool on an 64-bit operating system. +It's highly recommended to use the 64-bit version instead. +Do you wish to download it? + + + Audio failure + + + Can't open the results while the search is still in progress. + + + Can't start a new search while one is still in progress. + + + Cannot search in YYC compiled games. + + + Failed to open child data file; escaped directory. + + + Choose a name for the new project + + + Code entries + + + Delete {0}? +Note that the code often references objects by ID, so this operation is likely to break stuff because other items will shift up! + + + The game has the debugger enabled. Would you like to disable it so the game will run? + + + (drag & drop a {0}) + + + Error while parsing line {0} - "{1}". + + + Error while saving the file - "{0}". + + + Failed to play audio! +{0} + + + Failed to choose the output file name. + + + Failed to create new project + + + Failed to fetch latest build! +{0} + + + Failed to find latest build! +Detected action name - {0} + + + Generating the result list... + + + You are saving the game in GameMaker Studio debug mode. Unless the debugger is running, the normal runtime will simply hang after loading. You can turn this off in General Info by checking the "Disable Debugger" box and saving. + + + GMS Debugger + + + This game is set to run with the GameMaker Studio debugger and the normal runtime will simply hang after loading if the debugger is not running. You can turn this off in General Info by checking the "Disable Debugger" box and saving. + + + Invalid color string + + + Invalid regex + + + Line + + + Multiple code entries matched the filter. + + + No code entries matched the filter. + + + No data file is currently loaded! + + + No results to export. + + + No scripts found! + + + The reference of game object "{0}" is stale (the referenced object was deleted). + + + The type of this object doesn't have an editor/viewer. + + + The references of game object "{0}" are stale (some referenced objects were deleted). + + + An error occurred in the object references related window. +Please report this on GitHub. + +{0} + + + Opened project "{0}". + + + Open project file + + + Stopping the progress bar updater task is failed. +It's highly recommended to restart the application. + + + Project already open + + + Results in + + + {0}: {1}x{2} + + + The selected runtime does not support debugging. + + + Are you sure that you want to run the game with GMS debugger? +If you want to enable a debug mode in some game, then you need to use one of the scripts. + + + Saved project successfully. + + + Script error + + + Script warning + + + The selected object is not an "UndertaleResource". + + + Stopping the progress bar updater task is failed. +It's highly recommended to restart the application. + + + The last tab content state can't be restored - "UserControl" is not found. + + + The last tab content state can't be restored - "ScrollViewer" is not found. + + + The last tab content state can't be saved - "UserControl" is not found. + + + Tile {0} of {1} + + + Too many code lines to display. + + + There are assets marked to be exported in the current project - create a new project and discard all unexported changes? + + + Unsupported textures + + + YYC + + + Automatically set the tilemap size based on the room size + + + Invalid mask data! + + + Whether to automatically associate GameMaker data file types with UndertaleModTool. + + + Whether to enable the dark mode theme. + + + GML compiler/decompiler settings + + + The path to the GameMaker: Studio 1.4 installation directory. + + + GameMaker Studio 2 runtimes path: + + + Whether to override the global grid height for the room editor. + + + Whether to override the global grid thickness for the room editor. + + + Whether to override the global grid width for the room editor. + + + Language: + + + Select the display language for the application. + + + Recompile all source GML when saving a project data file + + + Whether to remember the last window position and size. + + + Whether to show the "Run game under GMS debugger" option in the File menu. + + + Whether to show null entries in the resource tree view. + + + Whether to show a warning when closing with unsaved changes. + + + Whether to show a warning when temporarily running the game. + + + Settings + + + Background + + + Embedded Audio + + + Particle System + + + Path + + + Texture Group Info + + + Texture Page Item + + + Variable + + + The language of open tabs will only take effect after restarting the application. + + \ No newline at end of file diff --git a/UndertaleModTool/Localization/Strings.zh-CN.resx b/UndertaleModTool/Localization/Strings.zh-CN.resx new file mode 100644 index 000000000..b21cd5cb5 --- /dev/null +++ b/UndertaleModTool/Localization/Strings.zh-CN.resx @@ -0,0 +1,4953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 名称 + + + 关闭 + + + 添加 + + + 删除 + + + 保存 + + + 打开 + + + 导出 + + + 导入 + + + 播放 + + + 停止 + + + + + + 完成 + + + 取消 + + + 确定 + + + + + + + + + 错误 + + + 警告 + + + 双击以添加 + + + 按名称筛选... + + + 搜索 + + + 复制 + + + 全部复制 + + + 在新标签页中打开 + + + 透明 + + + 平滑 + + + 预加载 + + + 纹理 + + + 缩放 + + + 列表 + + + 代码 + + + 标志 + + + 类型 + + + 大小 + + + 位置 + + + 定义 + + + 启用 + + + 可见 + + + 颜色 + + + 速度 + + + 深度 + + + 旋转 + + + 类型 + + + 编辑 + + + 自动 + + + 不适用 + + + 文件(_F) + + + 新建(_N) + + + 打开(_O) + + + 保存(_S) + + + 临时运行游戏(_T) + + + 在 GMS 调试器下运行游戏(_D) + + + 使用其他运行器运行游戏 + + + 生成偏移映射(_O) + + + 设置(_E) + + + 关闭(_C) + + + 脚本(_S) + + + (...加载中...) + + + 查找(_F) + + + 在代码中搜索(_S) + + + 查找未引用的资源(_A) + + + 项目(_P) + + + 新建项目(_N) + + + 打开项目(_O) + + + 保存项目(_S) + + + 查看未导出的资源(_V) + + + 关闭项目(_C) + + + 帮助(_H) + + + GitHub(_G) + + + 关于(_A) + + + 数据 + + + 常规信息 + + + 全局初始化 + + + 游戏结束脚本 + + + 音频组 + + + 声音 + + + 精灵图 + + + 背景 & 图块集 + + + 图块集 + + + 路径 + + + 脚本 + + + 着色器 + + + 字体 + + + 时间轴 + + + 游戏对象 + + + 房间 + + + 扩展 + + + 纹理页项 + + + 代码 + + + 变量 + + + 函数 + + + 代码局部变量 + + + 字符串 + + + 嵌入纹理 + + + 嵌入音频 + + + 纹理组信息 + + + 嵌入图像 + + + 粒子系统 + + + 粒子系统发射器 + + + 欢迎! + + + 关闭 + + + 关闭其他标签页 + + + 关闭所有标签页 + + + 设置 + + + GameMaker: Studio 1.4 路径: + + + 仅在使用 Studio 运行器而非 .exe 或在调试器下运行游戏时需要。 + + + GameMaker Studio 2 运行时路径: + + + 仅在使用 Studio 运行器而非 .exe 运行 GMS2 游戏时需要 + + + 启用资源顺序交换 + + + 切换在资源标签页中拖放资源到列表中不同位置的功能。默认禁用。 + + + 自动关联 GameMaker 数据文件 + + + 自动文件关联。默认启用。 + + + 显示"在 GMS 调试器下运行游戏"文件选项 + + + 是否在"文件"菜单中显示该选项。默认禁用。 + + + 记住上次窗口位置和大小 + + + 启动新工具实例时是否恢复窗口位置和大小。默认禁用。 + + + 关闭前警告保存 + + + 关闭前警告保存。默认启用。 + + + 警告临时运行 + + + 警告临时运行。默认启用。 + + + 在资源树中显示空条目 + + + 是否在资源树中显示空条目,在 GM 2024.11+ 游戏中这些通常是被编译器移除的未使用资源。由于 GUI 支持有限,默认禁用。 + + + 保存项目数据文件时重新编译所有源 GML + + + 是否在保存数据文件(包括临时运行)时重新编译当前加载项目中包含的所有 GML 源代码。默认禁用。 + + + 启用深色模式 + + + 使程序界面变为深色。默认禁用。 + + + 透明网格颜色: + + + GML 编译器/反编译器设置 + + + 全局网格宽度 + + + 此选项全局覆盖基于房间中最常用图块宽度自动分配的网格宽度。 + + + 全局网格高度 + + + 此选项全局覆盖基于房间中最常用图块高度自动分配的网格高度。 + + + 全局网格粗细 + + + 此选项全局覆盖房间网格粗细的自动分配。 + + + 打开应用程序数据文件夹 + + + 更新应用至最新提交 + + + 设置 - GML 编译器/反编译器 + + + 常规设置(编译器 & 反编译器) + + + 同时适用于编译器和反编译器的设置,以简化 GML 代码编辑。 + + + 使用 CSS 十六进制颜色字面量 + + + 如果启用,颜色常量可以使用 #RRGGBB 十六进制格式。 + + + 命名实例 ID 前缀: + + + 反编译器发现命名实例 ID 时使用的前缀,以及编译时使用的前缀。如果与变量名冲突,请更改此值。 + + + 反编译器设置 + + + 仅适用于 GML 反编译器的设置,不影响 GML 编译。并非适用于所有脚本。 + + + 始终使用分号 + + + 如果启用,所有可以加分号的语句都会加上分号。 + + + 缩进样式: + + + 打印反编译代码时使用的缩进样式。 + + + 在同一行打开块大括号 + + + 如果启用,用于打开块的大括号将放置在与其所属语句相同的行上,而不是将大括号放在单独的行上。 + + + 如果是单行则移除块大括号 + + + 如果启用,包含单行的块将移除其大括号(在可能的情况下)。对于 if/else 链,所有块都必须是单行。 + + + 创建枚举声明 + + + 如果启用,反编译期间识别的枚举声明将始终打印到反编译输出中。 + + + 枚举/宏声明在代码顶部 + + + 如果启用,枚举和宏将出现在反编译输出的开头(顶部),而不是结尾(底部)。 + + + 打印警告 + + + 如果启用,反编译期间生成的警告将作为注释打印。 + + + 允许 VM 栈上的剩余数据 + + + 如果启用,VM 栈上的剩余数据将被视为警告而非错误。 + + + 添加空行: + + + 选择特定语法以在其前后或周围添加空行。 + + + 分支语句周围 + + + 如果启用,在分支语句前后添加空行。 + + + 局部变量声明之后 + + + 如果启用,在块作用域局部变量声明后添加空行。 + + + switch case 之前 + + + 如果启用,在 switch 语句中的 "case" 条目链之前添加空行。 + + + switch case 之后 + + + 如果启用,在 switch 语句中的 "case" 条目链之后添加空行。 + + + 函数声明周围 + + + 如果启用,在函数声明前后添加空行,即使它是匿名的。 + + + 静态初始化周围 + + + 如果启用,在静态初始化前后添加空行,尽可能将连续的静态语句分组。 + + + 执行清理: + + + 选择反编译器执行的特定清理操作。禁用这些可能使代码更难看或"不正确",但可以显示底层逻辑。 + + + try/catch/finally 重写 + + + 如果启用,编译器为 try/catch/finally 语句生成的代码和逻辑将被重写为(尝试)看起来像原始代码。 + + + 将 else 转换为 continue + + + 如果启用,循环内某些 "else" 的用法可以转换为 "continue" 语句,使代码更易读。只有在能保持准确性的情况下才会发生。 + + + 默认参数值 + + + 如果启用,将使用默认命名函数参数的语法。编译器生成的 if 语句被转换为此格式,保持准确性。 + + + 内置数组变量语法 + + + 如果启用,某些内置全局变量(如 "view_xview"),当检测到未使用数组访问器时,将按原样打印,而不是使用编译器生成的语法。 + + + 作用域局部变量声明 + + + 如果启用,局部变量声明将根据其作用域放置,作用域是根据代码中的使用情况检测的。 + + + 恢复默认值 + + + 常规信息 + + + 选项 + + + 语言 (?) + + + 禁用 GMS 调试器 + + + 字节码版本 + + + 文件名 + + + 配置 + + + 最后对象 ID + + + 最后图块 ID + + + 游戏 ID + + + DirectPlay GUID + + + 版本 + + + 默认窗口大小 + + + 许可证 MD5 + + + 许可证 CRC32 + + + 时间戳 + + + 本地时间,悬停查看 GMT+0 时间 + + + 显示名称 + + + 活动目标 + + + 函数分类 + + + Steam AppID + + + 调试器端口 + + + 房间顺序 + + + 与房间列表同步 + + + FPS + + + 允许统计 + + + 游戏 GUID + + + 着色器扩展标志 + + + 着色器扩展版本 + + + 窗口颜色 + + + 颜色深度 + + + 分辨率 + + + 频率 + + + 垂直同步 + + + 优先级 + + + 背景图像 + + + 前景图像 + + + 加载图像 + + + 加载透明度 + + + 常量 + + + 未知 + + + 语言数量 + + + 条目数量 + + + 注意:语言目前似乎未被使用,因此尚未制作其编辑器。 + + + 宽度: + + + 高度: + + + 边距 + + + 左: + + + 右: + + + 下: + + + 上: + + + 边界框模式 + + + 分离遮罩 + + + 原点 + + + 纹理 + + + 导出所有帧(如果是 Spine 精灵图则导出 Spine 数据) + + + 碰撞遮罩 + + + 无效的遮罩数据! + + + 导入遮罩 + + + 导出遮罩 + + + 是特殊类型? + + + 版本 + + + 播放速度 + + + 播放速度类型 + + + 文件 + + + 效果 + + + 音量 + + + 音调 + + + 音频组 + + + 音频文件 + + + 预加载(旧音频系统) + + + 图块宽度 + + + 图块高度 + + + 图块间距 X + + + 图块间距 Y + + + 输出边框 X + + + 输出边框 Y + + + 图块列数 + + + 每图块项/帧数 + + + 图块数量 + + + 导出的精灵图 (GM 2023.8+) + + + 帧时间(微秒) + + + 图块 ID + + + GM 图块集版本 + + + 提示:您可以点击下方任何图块区域以在上方高亮显示其 ID。 + + + 局部变量数 + + + 参数数量 + + + 偏移 + + + 父条目 + + + 反编译 + + + 反汇编 + + + 精灵图 + + + 可见 + + + 实体 + + + 深度 + + + 持久 + + + 父对象 + + + 纹理遮罩 ID + + + 使用物理 + + + 是传感器 + + + 碰撞形状 + + + 物理 + + + 密度 + + + 弹性 + + + + + + 线性阻尼 + + + 角度阻尼 + + + 摩擦力 + + + 是否唤醒 + + + 是运动学 + + + 物理形状顶点 + + + 事件 + + + 子类型 ID + + + 操作 + + + 显示名称 + + + 字体大小 + + + 粗体 + + + 斜体 + + + 范围 + + + 字符集 + + + 抗锯齿 + + + 上升 + + + 上升偏移 + + + SDF 扩散值 + + + 行高 + + + 提示:您可以点击此处的任何字形以在"字形"中高亮显示它。 + + + 编辑选定的字形矩形 + + + 选择空字形的区域 + + + 创建空字形 + + + 字形: + + + 字符 + + + 源位置/大小 + + + 偏移 + + + 偏移 + + + 字距调整 + + + 前导字符 + + + 附加偏移 + + + 注意字形需要按升序指定。 +按下方的按钮进行适当排序。 + + + 排序字形 + + + 此外,如果您添加了新字符或更改了现有字形的字符, +您应该更新字体范围。 +按下方的按钮执行此操作。 + + + 更新范围 + + + 返回 + + + 平滑 + + + 闭合 + + + 精度 + + + + + + 待办:预览不会实时更新,我知道,暂时关闭并重新打开对象即可 + + + 代码 + + + GLSL ES 顶点源 + + + GLSL ES 片段源 + + + GLSL 顶点源 + + + GLSL 片段源 + + + HLSL9 顶点源 + + + HLSL9 片段源 + + + 顶点着色器属性 + + + 时刻 + + + 步骤 + + + 操作(代码条目) + + + 实例类型 + + + 变量 ID + + + 名称字符串 ID + + + 名称字符串 ID + + + 内容 + + + 源位置/大小 + + + 目标位置/大小 + + + 边界大小 + + + 查找此页面项的所有引用 + + + 缩放 + + + 生成的 mips + + + 警告:纹理加载失败! + + + 提示:此图像可鼠标交互(3个鼠标按钮和鼠标滚轮)。 + + + {0} 数据,{1} 字节 + + + 纹理 + + + 类名 + + + 文件 + + + 添加新扩展文件 + + + 产品 ID + + + 注意事项: +- 请注意,.gml 文件可能不存在或为空。(例如没有函数) + 因为它们已预编译为 GML 脚本。这是完全正常的。 + + + 添加新扩展选项 + + + 清理函数名 + + + 初始化函数名 + + + 函数 + + + 外部名称 + + + ID + + + 返回类型 + + + 参数类型 + + + 运行器实际上不关心 'Kind' 值。 +但 'ID' 确实重要,所以确保没有任何重叠。 + + + 路径 (GM 2024.14+) + + + 纹理组 + + + 组名 + + + 纹理页面 + + + 精灵图 + + + Spine 精灵图 + + + 字体 + + + 图块集 + + + 背景 + + + 视图 + + + 游戏对象 + + + 图块 + + + 图层 + + + 标题 + + + 持久 + + + 背景颜色 + + + 绘制背景颜色 + + + 创建代码 + + + 世界 + + + 上/左/右/下 + + + 重力 + + + 网格宽度 + + + 网格高度 + + + 网格粗细 + + + 前景 + + + 平铺 + + + 拉伸 + + + 视图 + + + 位置/大小 + + + 视口位置/大小 + + + 边框 + + + 跟随对象 + + + 实例 ID + + + 创建代码 + + + 预创建代码 + + + 帧索引 + + + 图像速度 + + + + + + 提示:您可以点击选择图块。 +要选择图块区域,请按住 ALT。 + + + 图层名称 + + + 图层 ID + + + 图层类型 + + + 图层深度 + + + 偏移 + + + 空图层数据。 + + + 包含游戏对象实例,列在左侧树中 + + + 包含旧版图块和精灵图,列在左侧树中 + + + 图块集 + + + 图块数据 + + + 第一帧 + + + 动画速度 + + + 保存为 PNG + + + 复制对象 + + + 删除对象 + + + 复制图块 + + + 删除图块 + + + 复制精灵图实例 + + + 删除精灵图实例 + + + 复制粒子系统实例 + + + 删除粒子系统实例 + + + 删除背景 + + + 禁用视图 + + + 新建对象实例 + + + 粘贴对象实例 + + + 新建图块 + + + 粘贴图块 + + + 新图块 + + + 新建实例图层 + + + 新建图块图层 + + + 新建背景图层 + + + 新建资源图层 + + + 新对象实例 + + + 删除图层 + + + 新建旧版图块 + + + 新精灵图实例 + + + 粘贴资源 + + + 上移项目 (-) + + + 下移项目 (+) + + + 图块 {0}/{1} + + + 精灵图 {0}/{1} + + + 粒子系统 {0}/{1} + + + 图块 {0}/{1} (UndertaleRoom+Tile) + + + 欢迎使用 UndertaleModTool! + + + 打开一个 data.win 文件开始,然后双击左侧的项目查看它们。 + + + 双击左侧的项目即可查看! + + + 打开 data.win 文件开始,然后双击左侧的项目查看 + + + 新文件已创建,祝您从无到有制作游戏 +我告诉过你要打开 data.win,而不是创建新文件!:P + + + 展开左侧列表以编辑项目 + + + 请先加载 data.win 文件 + + + 未加载游戏 + + + 标记为导出: + + + 当前打开的资源是否被标记为导出到当前打开的项目,下次保存时。 + + + 这是当前打开的资源、项目或对象的 ID(索引)。从 0 开始。 + + + 首次设置:您是否要将GameMaker数据文件(如 .win)与UndertaleModTool关联? + + + 文件关联 + + + 加载中 + + + 正在加载,请稍候... + + + 保存中 + + + 正在保存,请稍候... + + + 正在反编译,请稍候... 复杂脚本可能需要一些时间。 + + + 正在编译,请稍候... + + + 正在生成调试器数据…… + + + 正在重新编译 GML 代码,请稍候... + + + 正在保存数据文件,请稍候... + + + 脚本正在运行... + + + 请稍候…… + + + 尝试加载时发生错误: +{0} + + + 尝试保存时发生错误: +{0} + + + 尝试重新编译代码源时发生错误: +{0} + + + 代码编辑器中的更改因 "SaveCodeChanges()" 中的某些错误而未保存。 + + + 加载过程中发生错误。数据丢失的可能性很高!风险自负。 + + + 加载 settings.json 失败!使用默认值。 +{0} + + + 保存 settings.json 失败! +{0} + + + 加载警告 + + + 加载问题 + + + 加载过程中出现警告。保存时可能会发生数据丢失! + + + 目前仅支持字节码版本 13 到 17,您正在尝试加载 {0}。大量代码已禁用,可能会导致问题。保存/导出已禁用。 + + + 此游戏使用 YYC(YoYo 编译器),这意味着代码嵌入在游戏可执行文件中。此配置目前未完全支持;风险自负。 + + + 此游戏设置为使用 GameMaker Studio 调试器运行,如果调试器未运行,普通运行时加载后会挂起。您可以在常规信息中勾选"禁用调试器"并保存来关闭此功能。 + + + 此游戏包含未知或不支持图像格式的纹理。这些纹理可以保存/加载,但不会在编辑器中显示,许多操作将会失败。请谨慎操作。 + + + 您正在以 GameMaker Studio 调试模式保存游戏。除非调试器正在运行,否则普通运行时加载后会挂起。您可以在常规信息中勾选"禁用调试器"并保存来关闭此功能。 + + + UndertaleModToolUpdater 应用未退出。 +无法删除其临时文件夹。 + + + 无法删除更新器临时文件夹。 +错误 - {0}。 + + + 项名称为空。 + + + 保存失败,无法运行。 + + + 文件必须保存后才能运行。 + + + 您必须保存更改才能运行。 + + + 使用"使用调试器运行游戏"选项来运行此游戏。 + + + 没有可运行的内容! + + + 游戏可执行文件名称或位置为空 + + + 未能找到有效的游戏可执行文件路径;路径已脱离目录 + + + 无法找到游戏可执行文件路径,预期:{0} + + + 无法找到数据文件路径,预期:{0} + + + 无法找到游戏路径,预期在:{0} + + + 打开子数据文件失败;路径已脱离目录。 + + + 选定的运行时不支持调试。 + + + 无法打开浏览器! +{0} + + + 无法打开文件夹! +{0} + + + 您的操作系统是32位的。 +UndertaleModTool的32位(x86)版本已过时。 +如果您希望继续使用32位版本的UndertaleModTool,请使用GitHub Actions工件,如果没有GitHub账户则使用Nightly构建,或自行编译UTMT。 +如有任何问题或更多信息,请在Underminers Discord服务器中询问。 + + + 系统驱动器 {0} 空间不足 - 至少需要500 MB。 + + + 获取最新构建失败! +{0} + + + 找不到最新构建! +检测到的操作名称 - {0} + + + UndertaleModTool 已是最新版本。 +仍然更新? + + + 在64位操作系统上检测到32位(x86)版本的UndertaleModTool。 +强烈建议改用64位版本。 +您是否要下载? + + + 未能找到工件! + + + 未找到更新器!中止更新,请向开发者报告! +检查的位置:{0} + + + 无法将更新器应用复制到临时文件夹。 +{0} + + + 下载新版本 UndertaleModTool 失败。 +错误 - {0}。 + + + 由于此错误无法将项目名称复制到剪贴板: +{0} + + + 对象引用相关窗口中发生错误。 + + + + 尝试添加菜单项时发生错误。未执行任何操作。 + +错误: + +{0} + + + 脚本文件不存在。 + + + 找不到代码条目 "{0}"。 +(可能加载了不同的游戏数据) + + + 停止进度条更新器任务失败。 +强烈建议重新启动应用程序。 + + + 警告:您当前已打开一个项目。 +确定要创建新项目吗? + + + 将 {0} 作为脚本运行? + + + 将 {0} 作为数据文件打开? + + + 保存到项目指定的保存数据文件? + + + 当前已打开一个项目 - 打开另一个数据文件并丢弃所有未保存的更改? + + + 当前项目中有标记为导出的资源。 +确定退出吗? + + + 退出前保存更改吗? + + + 脚本仍在运行。仍然保存吗? +这可能会损坏您要保存的数据文件。 + + + 删除 {0}? + + + 删除 {0}? + +请注意,代码通常通过 ID 引用对象,因此此操作可能会导致问题,因为其他项目会向上移动! + + + 资源名称 "{0}" 不是有效的标识符。是否使用自动生成的名称添加新资源? + + + 将新房间添加到房间排序列表的末尾? + + + 游戏已启用调试器。您想禁用它以便游戏运行吗? + + + 先保存更改? + + + 您确定要使用GMS调试器运行游戏吗? +如果您想在某个游戏中启用调试模式,则需要使用其中一个脚本。 + + + 确认 + + + 加载错误 + + + 保存错误 + + + 重新编译错误 + + + 脚本编译错误 + + + 脚本错误 + + + 脚本警告 + + + 脚本问题 + + + 运行错误 + + + 不支持的字节码版本 + + + YYC + + + GMS 调试器 + + + 不支持的纹理 + + + 项目已打开 + + + 设置 + + + 调试数据 + + + 字符串引用已更改 + + + 运行时选择器 + + + 在代码中搜索 + + + 未导出的项目资源 + + + 引用类型 + + + 引用结果 + + + GameMaker 数据文件 (.win, .unx, .ios, .droid, audiogroup*.dat) + + + GameMaker 主数据文件 (.win, .unx, .ios, .droid) + + + 关闭当前标签页 + + + 关闭所有标签页 + + + 恢复上次关闭的标签页 + + + 切换到下一个标签页 + + + 切换到上一个标签页 + + + 在代码中搜索 + + + 新建项目 + + + 打开项目 + + + 保存项目 + + + 查看项目资源 + + + 关闭项目 + + + 编译代码 + + + 替代粘贴命令 + + + 镜像画笔 + + + 翻转画笔 + + + 顺时针旋转画笔 90 度 + + + 逆时针旋转画笔 90 度 + + + 切换图块网格 + + + 切换多图块画笔的"平铺"行为 + + + 切换房间预览 + + + 撤销操作 + + + 重做操作 + + + 后退 + + + 前进 + + + 移除 + + + 调试器已启用。您要生成调试器数据吗? + + + 反编译 + + + 保存时反编译整个源代码以在调试器中显示 + + + 注意:尚不支持断点!脚本入口点除外 + + + 反汇编,部分断点 + + + 完整反汇编,但只能在 B、BR、BT、POP、POPZ、RET 和 EXIT 上设置断点。这仍然比 GM:S 调试器设计的要多得多,因此允许最多 5 分钟加载数据文件(是的,认真的) + + + 反汇编,完整断点 + + + 等了大约 15 分钟后放弃了,别尝试这个 + + + 无调试数据 + + + 您编辑了字符串引用!您想怎么做? + + + 仅更改此值 + + + 更改此值的所有出现 + + + 中止更改 + + + 选择要使用的运行时 + + + 版本 + + + 路径 + + + 继续 + + + 区分大小写 + + + 正则搜索 + + + 多行正则 + + + 在汇编中 + + + 按代码名过滤 + + + 代码 + + + + + + 文本 + + + 可点击文本输出 + + + 正在显示结果,请稍候... + + + 带超链接的默认文本。 + + + 以下是当前等待在下次保存时导出到磁盘的所有资源列表。 + + + 要快速添加或移除项目中的资源,您可以在此拖放资源,或删除它们。 + + + 名称 + + + 资源类型 + + + 取消导出标记 + + + 在哪里搜索引用? + + + 全选 + + + 取消全选 + + + 游戏对象的搜索结果 +""。 + + + + 复制名称到剪贴板 + + + 查找所有引用 + + + 注意:结果可能不是 100% 完整。 +如有必要,请使用"查找"菜单中的搜索脚本之一。 + + + 导出结果 + + + 后退 + + + 前进 + + + 宽度 + + + 高度 + + + X + + + Y + + + 未知 + + + UndertaleModTool + + + 创建 + + + 打开引用的对象 + + + 移除引用 + + + 这是一个对象引用。从左侧树中拖放匹配类型的对象来更改它! + + + 查找所有引用 + + + 复制名称到剪贴板 + + + (空) + + + (空) + + + (空字符串) + + + #AABBGGRR + + + #BBGGRR + + + 无效的颜色字符串 + + + 游戏对象 + + + 游戏对象(来自所有图层) + + + 新实例图层 + + + 新图块图层 + + + 新背景图层 + + + 新资产图层 + + + 新扩展文件{0}.dll + + + 扩展选项{0} + + + 移除纹理条目 + + + 打开纹理条目 + + + 常量 + + + 数字 + + + GMT+0: {0} + + + 字形 '{0}' 的字距调整(代码 - {1}): + + + 实例图层 + + + {0} (颜色) + + + {0} (常量) + + + {0} (数字) + + + 水平 + + + 垂直 + + + PNG 文件 (.png) + + + WAV 文件 + + + OGG 文件 + + + CSV 表格 + + + 所有文件 + + + 文本文件 (.txt) + + + 反编译中 + + + 编译中 + + + 编译器错误 + + + 汇编器错误 + + + 导出文件失败 + + + 导出精灵失败 + + + 导入文件失败 + + + 格式不匹配 + + + Spine 警告 + + + 意外的纹理尺寸 + + + 未知格式 + + + 音频失败 + + + 选择源数据文件 + + + 选择目标数据文件 + + + 打开项目文件 + + + 选择项目名称 + + + 脚本正在运行…… + + + 尝试加载时发生错误: +{0} + + + 尝试保存时发生错误: +{0} + + + 尝试重新编译代码源时发生错误: +{0} + + + 导出文件失败:{0} + + + 导出失败:{0} + + + 导入文件失败:{0} + + + 没有可导出的帧 + + + 包含内边距? + + + 这似乎是 Spine 精灵,.json 和 .atlas 文件将与帧一起导出。请谨慎编辑!可能需要手动编辑 JSON!数据按原样导出。 + + + 警告:纹理页面尺寸不是 2 的幂。游戏中精灵模糊的可能性很大。 + + + {0} 已转换为 PNG 格式,因为我们不支持将图像转换为 DDS 格式。这可能会导致游戏中的性能问题。 + + + 播放音频失败! +{0} + + + 播放音频失败! +不是 WAV 或 OGG 文件。 + + + 警告:正在导入的文件不是 WAV 或 OGG 格式。仍然导入吗? + +这可能会损坏声音。 + + + 警告:导入的文件类型与现有文件类型不匹配。仍然导入吗? + +除非同时调整声音资产压缩设置,否则这可能会损坏声音。 + + + 无效的布尔值 + + + 无效的数字字符串 + + + 您确定要启用 GMS 调试器吗? +如果您想在某个游戏中启用调试模式,则需要使用其中一个脚本。 + + + 此代码条目是对 {0} 中匿名函数的引用,请在该处查看。 + + + textdata_en 中存在重复键 {0}。这可能导致文本注释显示错误。 + + + textdata_en 中出现未知错误。这可能导致文本注释显示错误。 + + + 解析语言文件失败:{0} + + + <本地化获取错误> + + + 反编译中,请稍候……复杂脚本可能需要较长时间。 + + + 编译中,请稍候…… + + + 在打开的项目中编辑反汇编代码(即使通过脚本)可能导致与项目中的源代码不同步。 + +除非您直接修改源代码,或完全从项目中移除代码资产,否则源代码不会更改。 + + + 字形已成功排序。 + + + 范围已成功更新。 + + + 找不到字形表滚动查看器。 + + + 未选择字形。 + + + 字体没有纹理。 + + + 字形矩形编辑器窗口中发生错误。 +请在 GitHub 上报告此问题。 + +{0} + + + 最后一个字形大小为零。 +您可以使用左侧的按钮来修复。 + + + 字符代码超过最大值 (65535) + + + 该值不是字符串。 + + + 无效的日期时间格式。 + + + 查找此图块的所有引用 + + + 对象引用相关窗口中发生错误。 +请在 GitHub 上报告此问题。 + +{0} + + + 找不到图块 ID 列表滚动查看器。 + + + 查找此页面项的所有引用 + + + 图层 "{0}" 的实例列表为空,但该图层具有实例 ID。 + + + 没有空的房间背景。 + + + 请选择一个图层。 + + + 请选择一个资产图层。 + + + 请选择一个实例图层。 + + + 房间为空。 + + + 警告 - 已达到最大图层深度。 +您可能应该更改新图层的深度。 + + + 图层目前不支持此功能,请改为更改图层深度。 + + + 视图不支持此功能。 + + + 背景不支持此功能。 + + + 无法更改对象位置 - 未找到所选对象的列表。 + + + 图块数据为空。 + + + 保存文件时出错 - "{0}"。 + + + 导出文件路径:{0} + + + 图块数据大小不能为零。 + + + 打开文件时出错 - "{0}"。 + + + 错误 - 所选文件行数与图块图层高度不匹配。 + + + 数据是否从 "Tiled" 导出? + + + 文件包含无效数据。 + + + 解析第 {0} 行时出错 - "{1}"。 + + + 第 {0} 行的长度不等于图块数据宽度。 + + + 导入成功。 + + + 该图层必须设置图块集! + + + 该图层的水平和垂直尺寸必须大于 0 个图块! +(使用自动按钮根据房间大小设置图块贴图大小。) + + + 该图层的水平尺寸必须大于 0 个图块! +(使用自动按钮根据房间大小设置图块贴图大小。) + + + 该图层的垂直尺寸必须大于 0 个图块! +(使用自动按钮根据房间大小设置图块贴图大小。) + + + GMS 2+ 游戏的房间标志必须包含 "IsGMS2" 标志,否则游戏在加载该房间时会崩溃。 + + + GM 2024.13+ 游戏的房间标志必须包含 "IsGM2024_13" 标志,否则游戏在加载该房间时会崩溃。 + + + 为图块图层 {0} 生成 "Rectangles" 时发生错误。 + +{1} + + + {0} 不存在! + + + 发生类型为 "{0}" 的错误。错误为: + +{1} + + + 脚本编译错误 + + + 警告: +临时运行游戏不会永久保存您的更改。 +请使用"保存"来保存您的更改。 +在不使用"保存"选项的情况下关闭 +UndertaleModTool可能会导致工作丢失。 + + + 当前未加载数据文件! + + + 无法高亮对象 - 它为空或不是 UndertaleResource。 + + + 无法高亮对象"{0}"。 +错误 - {1} + + + 无法高亮对象"{0}" - 未找到对象列表元素。 + + + HTTP 错误 - {0}。 + + + 请检查您的网络连接。 + + + 已下载 MB: {0} + + + UndertaleModTool 现在将关闭以完成更新。 + + + 当前,目标数据文件的目录为空。您可能需要将所有其他游戏文件复制到目标目录,以便外部资源可以正确加载(在游戏中和在此工具中),并且游戏可以启动。 + + + 目标数据文件与源数据文件在同一目录中。这可能会永久覆盖外部数据文件。继续吗? + + + 目标在同一目录 + + + 选择目标数据文件 + + + // 此代码入口是"{0}"中匿名函数的引用,请改为反编译该函数。 + + + ; 此代码入口是"{0}"中匿名函数的引用,请改为反汇编该函数。 + + + /* +反编译失败! + +{0} +*/ + + + /* +反汇编失败! + +{0} +*/ + + + 找不到游戏可执行文件路径,预期:{0} + + + 找不到数据文件路径,预期:{0} + + + 找不到游戏路径,预期位置:{0} + + + 保存失败,无法运行。 + + + 游戏已启用调试器。您是否要禁用它以便游戏运行? + + + 您必须保存更改才能运行。 + + + 请使用"使用调试器运行游戏"选项来运行此游戏。 + + + 所选运行时不支持调试。 + + + UndertaleModToolUpdater 应用未退出。 +无法删除其临时文件夹。 + + + (错误消息缺失) + + + 警告:您当前已打开一个项目。 +确定要创建新项目吗? + + + 资源名称"{0}"不是有效的标识符。是否改用自动生成的名称添加新资源? + + + 添加到房间排序列表 + + + 删除 {0}? + + + 请注意,代码通常通过ID引用对象,因此此操作可能会导致问题,因为其他项会向上移动! + + + 退出前保存更改? + + + 脚本仍在运行。仍然保存吗? +这可能会损坏您要保存的数据文件。 + + + 当前项目中有标记为导出的资源。 +确定退出吗? + + + 当前项目中有标记为导出的资源。确定关闭吗? + + + 将 {0} 作为脚本运行? + + + 将 {0} 作为数据文件打开? + + + 当前已打开一个项目 - 打开另一个数据文件并放弃所有未保存的更改? + + + 当前项目中有标记为导出的资源 - 创建新项目并放弃所有未导出的更改? + + + 当前项目中有标记为导出的资源 - 打开另一个新项目并放弃所有未导出的更改? + + + 由于此错误,无法将项名称复制到剪贴板: +{0}。 +您可能应该再试一次。 + + + 尝试添加菜单项时发生错误。未执行任何操作。 + +错误: + +{0} + + + 脚本文件不存在。 + + + 正在运行 {0} ... + + + {0} 已完成! + + + 找不到代码入口"{0}"。 +(可能加载了不同的游戏数据) + + + 获取最新构建失败! +{0} + + + 未能找到最新构建! +检测到的操作名称 - {0} + + + 下载新版本的UndertaleModTool失败。 +错误 - {0}。 + + + 游戏可执行文件名称或位置为空 + + + 代码编辑器中的更改因"SaveCodeChanges()"中的某些错误而未保存。 + + + 目前仅支持字节码版本13到17,您正在尝试加载 {0}。大量代码已被禁用,可能会导致问题。保存/导出已禁用。 + + + 此游戏使用YYC(YoYo编译器),这意味着代码嵌入在游戏可执行文件中。此配置目前未完全支持;风险自负。 + + + 此游戏设置为使用GameMaker Studio调试器运行,如果调试器未运行,普通运行时将在加载后挂起。您可以在常规信息中勾选"禁用调试器"并保存来关闭此功能。 + + + 您正在以GameMaker Studio调试模式保存游戏。除非调试器正在运行,否则普通运行时将在加载后挂起。您可以在常规信息中勾选"禁用调试器"并保存来关闭此功能。 + + + 创建新项目失败 + + + 创建新项目时发生错误: +{0} + + + 加载项目时发生错误: +{0} + + + 保存项目时发生错误: +{0} + + + 加载项目失败 + + + 保存项目失败 + + + 项目"{0}"创建成功! + + + 已打开项目"{0}"。 + + + 项目保存成功。 + + + 项目创建失败。 + + + 项目打开失败。 + + + 项目保存失败。 + + + 项目已关闭。 + + + 已取消新项目创建。 + + + 正在重新编译GML代码,请稍候…… + + + 正在保存数据文件,请稍候…… + + + 选择新的{0}名称 + + + 新资源名称: + + + 搜索进行中…… + + + 资产 + + + 无法确定 GameMaker 版本 - "常规信息"为空。 + + + 无法获取类型为 "{0}" 的对象的源类型。 + + + 至少应选择一种类型。 + + + 您在选择"字符串"时存在大量字符串。 +这可能会使搜索过程明显变长。 +您是否要继续? + + + 找不到音频文件。 + + + 找不到音频组文件。 + + + 播放音频失败! +没有可用的播放选项。 + + + 房间的代码条目已存在;正在重用。 + + + 此情况不支持添加。 + + + 所选对象不是"UndertaleResource"。 + + + "{0}"图块层的图块集参数错误(图块大小、输出边框等)。 +无法显示。 + + + 渲染图块层"{0}"时发生错误。 + +{1} + + + 此对象类型没有编辑器/查看器。 + + + 无法找到游戏可执行文件或任何已安装的 Studio 运行时 + + + 运行错误 + + + 游戏可执行文件 + + + 生成中 + + + 下载中 + + + 正在下载新版本…… + + + + + + 首次设置:您是否要将GameMaker数据文件(如 .win)与UndertaleModTool关联? + + + 文件关联 + + + 是否移除当前的文件关联(如果存在)? + + + 文件关联 + + + 取消关联失败 + + + 消息框(如此框)与暗色模式不兼容。 +这将在未来版本中修复。 + + + 欢迎! + + + (拖放 {0}) + + + (空 {0} 引用) + + + 这是一个字符串引用。在此处更改值仅编辑所有引用值中的此值,或从左侧树中拖放另一个字符串实例来更改引用 + + + (空) + + + (空) + + + 欢迎使用 UndertaleModTool! + + + 打开 data.win 文件开始使用,然后双击左侧的项来查看它们。 + +简体中文的本地化由 Github:@GenOuka (哔哩哔哩:秋冥散雨_GenOuka) 完成。 + + + 新文件已创建,祝您从零开始制作游戏愉快 +我告诉过你要打开 data.win,而不是创建新文件!:P + + + 双击左侧的项来查看它们 + + + 双击左侧的项来查看它们! + + + 打开 data.win 文件开始使用 + + + 未加载游戏 + + + 标记为导出: + + + 当前打开的资源是否被标记为在下次保存时导出到当前打开的项目。 + + + 这是当前打开的资源、项或对象的ID(索引)。从0开始。 + + + 在GMS调试器下运行游戏(_D) + + + 查看未导出的资源(_V) + + + 恢复上次关闭的标签页 + + + 所有文件 + + + 脚本 (.csx) + + + 文本文件 (.txt) + + + UndertaleModTool 项目文件 (.json) + + + 页面项 {0} + + + 嵌入声音 {0} + + + 纹理 {0} + + + 字符串{0} + + + 新建 + + + 模组 + + + 名称 + + + 启用 + + + 发射模式 + + + 发射数量 + + + 相对发射 + + + 延迟 最小|最大 + + + 延迟度量单位: + + + 间隔 最小|最大 + + + 间隔度量单位: + + + 分布 + + + 形状 + + + 区域 X|Y|宽|高 + + + 旋转 + + + 精灵图 + + + 精灵帧索引 + + + 动画精灵 + + + 拉伸精灵 + + + 随机 + + + 纹理 + + + 起始颜色 + + + 中间颜色 + + + 结束颜色 + + + 加法颜色混合 + + + 生命周期 最小|最大 + + + 缩放 X|Y + + + 提示:在 GM 2023.8 之前,这些"大小"值没有按 x 和 y 分开。 +UndertaleModTool 通过取平均值来表示。 +为获得最佳效果,请在两个字段中输入相同的值。 + + + 大小最小值 + + + 大小最大值 + + + 大小增量 + + + 大小摆动 + + + 速度 最小|最大 + + + 速度增量|摆动 + + + 重力 力|方向 + + + 方向 最小|最大 + + + 方向增量|摆动 + + + 朝向 最小|最大 + + + 朝向增量|摆动 + + + 朝向是否相对 + + + 死亡时生成 + + + 死亡时生成数量 + + + 更新时生成 + + + 更新时生成数量 + + + 局部变量 + + + 索引 + + + 变量 + + + 实例/全局变量数量 + + + 实例/全局变量数量(再次) + + + 最大局部变量数量 + + + 外部名称 + + + ID + + + 返回类型 + + + 参数类型 + + + 运行器实际上不在乎 'Kind' 值。 +但 'ID' 确实重要,请确保没有任何重叠。 + + + 清理函数名 + + + 初始化函数名 + + + 函数 + + + 全局初始化脚本 + + + 游戏结束脚本 + + + 编辑图块数据 + + + 沿 X 轴镜像画笔 (X) + + + 沿 Y 轴翻转画笔 (Y/Z) + + + 旋转画笔 90 度 (Q, R) + + + 显示图块贴图后面的房间其余部分 (P) + + + 在使用多图块画笔时启用"平铺"行为 (B) + + + 显示图块网格 (G) + + + 撤销 (Ctrl+Z) + + + 重做 (Ctrl+Y, Ctrl+Shift+Z) + + + 图块集列数 + + + 用于图块调色板的列数。如果图块集看起来有问题,请调整此值。 +关闭图块编辑器后此设置会保留,但保存文件并重新打开后不会。 + + + Ctrl+左键: 填充 (+Shift: 全局) Alt+左键: 拾取 中键: 滚动/拾取 + + + 取消 + + + 应用 + + + 预览 + + + 画笔平铺 + + + 网格 + + + 编辑字形矩形 + + + 获取帮助 + + + 打开引用的对象 + + + 这是一个对象引用。从左侧树中拖放匹配类型的对象来更改它! + + + 创建新代码条目 + + + 移除引用 + + + 就地添加(未实现) + + + 精灵图 + + + 背景 + + + 嵌入纹理 + + + 纹理页项 + + + 字符串 + + + 游戏对象 + + + 代码条目 + + + 代码条目(名称和内容) + + + 函数 + + + 变量 + + + 嵌入音频 + + + 音频组 + + + 声音 + + + 字体 + + + 图块集 + + + 路径 + + + 脚本 + + + 着色器 + + + 时间轴 + + + 扩展 + + + 扩展选项 + + + 扩展文件 + + + 扩展函数 + + + 常规信息 + + + 游戏选项常量 + + + 房间 + + + 代码 + + + 代码局部变量 + + + 语言 + + + 纹理组 + + + 嵌入图像 + + + 房间图块 + + + 房间精灵图实例 + + + 房间背景图层 + + + 房间背景 + + + 房间图块图层 + + + 房间图块图层 + + + 房间图层 + + + 房间对象实例 + + + 房间序列实例 + + + 房间粒子系统实例 + + + 房间粒子系统实例 + + + 房间效果属性 + + + 动画曲线 + + + 动画曲线通道 + + + 序列 + + + 序列轨道 + + + 序列字符串关键帧 + + + 序列文本关键帧 + + + 序列广播消息 + + + 序列时刻 + + + 序列实例关键帧 + + + 序列对象实例关键帧 + + + 粒子系统 + + + 粒子系统发射器 + + + 滤镜效果 + + + 全局初始化脚本 + + + 游戏结束脚本 + + + 全局初始化和游戏结束脚本 + + + 资源 + + + 请稍候... + + + 游戏 EXE + + + 1.4.xxx + + + 无法找到游戏 EXE 或任何已安装的 Studio 运行时 + + + {0}/{1} + + + {0}/{1}:{2} + + + 此对象类型没有编辑器/查看器。 + + + 粘贴 + + + 继续 + + + + + + 移除 + + + 临时运行游戏(_T) + + + 关闭项目(_C) + + + 未命名 + + + 音频组 + + + 声音 + + + 精灵图 + + + 背景 + + + 路径 + + + 脚本 + + + 着色器 + + + 字体 + + + 时间轴 + + + 游戏对象 + + + 房间 + + + 扩展 + + + 纹理页项 + + + 代码 + + + 变量 + + + 函数 + + + 代码局部变量 + + + 嵌入纹理 + + + 嵌入音频 + + + 纹理组信息 + + + 嵌入图像 + + + 序列 + + + 动画曲线 + + + 粒子系统 + + + 粒子系统发射器 + + + 字符串 + + + 扩展文件 + + + 扩展函数 + + + 变量概览 + + + 常规信息 + + + 全局初始化 + + + 游戏结束 + + + (空字符串) + + + 在新标签页中打开 + + + 启用资源顺序交换 + + + 切换在资源标签页中拖放资源到列表中不同位置的功能。默认禁用。 + + + 警告临时运行 + + + 警告临时运行。默认启用。 + + + 保存项目数据文件时重新编译所有源 GML + + + 在保存数据文件(包括临时运行)时,是否重新编译当前加载项目中包含的所有 GML 源代码。默认禁用。 + + + GML 编译器/反编译器设置 + + + 将应用更新到最新提交 + + + 设置 - GML 编译器/反编译器 + + + 使用 CSS 十六进制颜色字面量 + + + 如果启用,颜色常量可以使用 #RRGGBB 十六进制格式。 + + + 命名实例 ID 前缀: + + + 反编译器发现命名实例 ID 时使用的前缀,以及编译时使用的前缀。如果与变量名冲突,请更改此值。 + + + 在同一行打开块大括号 + + + 如果启用,打开块的大括号将放在语句的同一行,而不是单独一行。 + + + 如果为单行则移除块大括号 + + + 如果启用,包含单行的块将移除大括号(在可能的情况下)。对于 if/else 链,所有块都必须是单行。 + + + 分支语句周围 + + + 如果启用,在分支语句前后添加空行。 + + + 局部变量声明之后 + + + 如果启用,在块作用域局部变量声明之后添加空行。 + + + switch case 之前 + + + 如果启用,在 switch 语句中的"case"条目链之前添加空行。 + + + switch case 之后 + + + 如果启用,在 switch 语句中的"case"条目链之后添加空行。 + + + 函数声明周围 + + + 如果启用,在函数声明前后添加空行,即使是匿名函数。 + + + 静态初始化周围 + + + 如果启用,在静态初始化前后添加空行,尽可能将连续的静态语句分组。 + + + try/catch/finally 重写 + + + 如果启用,编译器为 try/catch/finally 语句生成的代码和逻辑将被重写为(尝试)看起来像原始代码。 + + + 将 else 转换为 continue + + + 如果启用,循环中"else"的某些用法可以转换为"continue"语句,使代码更易读。仅在可以保持准确性的情况下发生。 + + + 默认参数值 + + + 如果启用,将使用默认命名函数参数的语法。编译器生成的 if 语句将被转换以实现此功能,同时保持准确性。 + + + 内置数组变量语法 + + + 如果启用,某些内置全局变量(如"view_xview")在检测到未使用数组访问器时,将按原样打印,而不是使用编译器生成的语法。 + + + 作用域局部变量声明 + + + 如果启用,局部变量声明将根据其作用域放置,作用域是根据代码中的使用检测的。 + + + 名称 + + + 局部变量数 + + + 参数数量 + + + 偏移 + + + 父条目 + + + 反编译 + + + 反汇编 + + + 可见 + + + 实体 + + + 持久 + + + 父对象 + + + 纹理遮罩 ID + + + 使用物理 + + + 是传感器 + + + 碰撞形状 + + + 物理 + + + 密度 + + + 弹性 + + + + + + 线性阻尼 + + + 角度阻尼 + + + 摩擦力 + + + 处于唤醒状态 + + + 是运动学 + + + 物理形状顶点 + + + 事件 + + + 子类型 ID + + + 操作 + + + 不适用 + + + 宽度: + + + 高度: + + + 边距 + + + 左: + + + 右: + + + 下: + + + 上: + + + 透明 + + + 平滑 + + + 预加载 + + + 边界框模式 + + + 分离遮罩 + + + 原点 + + + X: + + + Y: + + + 纹理 + + + 导出所有帧(如果是 Spine 精灵图则导出 Spine 数据) + + + 碰撞遮罩 + + + 无效的遮罩数据! + + + 导入遮罩 + + + 导出遮罩 + + + 是特殊类型? + + + 播放速度 + + + 播放速度类型 + + + 标志 + + + 文件 + + + 效果 + + + 音量 + + + 音调 + + + 音频组 + + + 音频文件 + + + 预加载(旧音频系统) + + + 播放 + + + 停止 + + + 字体大小 + + + 粗体 + + + 斜体 + + + 范围 + + + 字符集 + + + 抗锯齿 + + + 缩放 + + + 上升线 + + + 上升线偏移 + + + SDF 扩散值 + + + 行高 + + + 提示:您可以点击此处的任何字形以在"字形"中高亮显示它。 + + + 编辑选中的字形矩形 + + + 选择空字形的区域 + + + 创建空字形 + + + 字形: + + + 字符 + + + 源位置/大小 + + + 偏移 + + + 字距调整 + + + 返回 + + + 前导字符 + + + 附加偏移 + + + 排序字形 + + + 注意,字形需要按升序指定。 +按下方的按钮可进行适当排序。 + + + 更新范围 + + + 此外,如果您添加了新字符或更改了现有字形的字符,应该更新字体范围。 +按下方的按钮即可完成。 + + + 版本 + + + 列表 + + + GM 图块集版本 + + + 图块宽度 + + + 图块高度 + + + 图块间距 X + + + 图块间距 Y + + + 输出边框 X + + + 输出边框 Y + + + 图块列数 + + + 每图块项/帧数 + + + 图块数量 + + + 导出的精灵图 (GM 2023.8+) + + + 帧时间(微秒) + + + 图块 ID + + + 提示:您可以点击下方的任何图块区域以高亮显示其上方的 ID。 + + + 新建精灵图 + + + 创建代码 + + + 旋转 + + + 提示:您可以通过点击来选择图块。 +要选择图块的<Run FontWeight="DemiBold" Foreground="#FFB23131">区域</Run>,请按住 ALT。 + + + 速度 + + + 可见 + + + 自动 + + + 自动设置大小以匹配房间大小 + + + (无名称) + + + 首次设置:您是否要将 GameMaker 数据文件(如 .win)与 UndertaleModTool 关联? + + + 警告 + + + 错误 + + + UndertaleModTool + + + 设置 - GML 编译器/反编译器 + + + 可点击文本输出 + + + GameMaker 数据文件 (.win, .unx, .ios, .droid, audiogroup*.dat) + + + GameMaker 主数据文件 (.win, .unx, .ios, .droid) + + + + + + 后退 + + + 前进 + + + 搜索 + + + 按名称过滤... + + + 标记为导出: + + + 当前打开的资源是否被标记为在下次保存时导出到当前打开的项目。 + + + 这是当前打开的资源、项或对象的 ID(索引)。从 0 开始。 + + + 区分大小写 + + + 正则表达式搜索 + + + 多行正则 + + + 在汇编中 + + + 按代码名称过滤 + + + 搜索 + + + 代码 + + + + + + 文本 + + + 全部复制 + + + 字符串引用已更改 + + + 您编辑了字符串引用!您想怎么做? + + + 运行时选择器 + + + 调试数据 + + + 调试器已启用。您要生成调试器数据吗? + + + 反编译 + + + 保存时反编译整个源代码以在调试器中显示 + + + 注意:尚不支持断点!脚本入口点除外 + + + 反汇编,部分断点 + + + 完整反汇编,但只能在 B、BR、BT、POP、POPZ、RET 和 EXIT 上设置断点。这仍然比 GM:S 调试器设计处理的要多得多,因此允许最多 5 分钟加载数据文件(是的,真的) + + + 反汇编,完整断点 + + + 等了大约 15 分钟后放弃了,别尝试这个 + + + 全部复制 + + + 搜索 + + + 游戏对象的引用 + + + 游戏对象的搜索结果 + + + 编辑 + + + 导出 + + + 导入 + + + 编辑字形矩形 + + + + + + 欢迎! + + + 2 空格 + + + 4 空格 + + + 操作(代码条目) + + + 添加新扩展文件 + + + 类名 + + + 提示:您可以点击下方任何图块区域来选择并编辑其属性。 + + + 代码 + + + 碰撞遮罩 + + + 内容 + + + 深度 + + + 绘制顺序 + + + 发射器 + + + 导出所有帧(如果是 Spine 精灵则导出 Spine 数据) + + + 文件 + + + 字体 + + + 帧时间(微秒) + + + GLSL ES 片段着色器源码 + + + GLSL ES 顶点着色器源码 + + + GLSL 片段着色器源码 + + + GLSL 顶点着色器源码 + + + 提示:您可以点击下方任何字形矩形来选择并编辑其属性,或双击添加新字形。 + + + 组名 + + + HLSL9 片段着色器源码 + + + HLSL9 顶点着色器源码 + + + 每图块的项/帧数 + + + 类型 + + + (本地时间,悬停查看 GMT+0) + + + 时刻 + + + 新扩展函数_{0} + + + 新扩展函数_{0}_ext + + + 水平偏移字形渲染的像素数。 +上面的纹理以蓝色为亮点。 + + + 路径(GM 2024.14+) + + + 产品 ID + + + (?) + + + - + + + 已缩放 + + + 大小 + + + 速度 + + + Spine 精灵 + + + 精灵 + + + 步骤 + + + 制表符 + + + 纹理组 + + + 更新范围 + + + 更新当前显示字形的范围以匹配字体纹理。 + + + 脚本 (.csx) + + + 关于 + + + 背景与图块集 + + + 下载中 + + + 生成中 + + + 加载中 + + + 正在运行 {0} …… + + + 图块集 + + + 您的操作系统是32位的。 +UndertaleModTool的32位(x86)版本已过时。 + + + 由于此错误,无法将项名称复制到剪贴板: +{0} + + + 找不到数据文件路径,预期:{0} + + + 找不到游戏可执行文件路径,预期:{0} + + + 找不到游戏可执行文件路径,预期位置:{0} + + + 找不到游戏路径,预期位置:{0} + + + 无法高亮对象"{0}" - 未找到对象列表元素。 + + + 选择项目名称 + + + 选择源数据文件 + + + #{0} + + + 确认 + + + 删除 {0}? + + + (空 {0} 引用) + + + 打开文件时出错 - "{0}"。 + + + 播放音频失败! +没有可用的播放选项。 + + + 未能找到工件! + + + 加载项目失败 + + + 无法打开浏览器! +{0} + + + 无法打开文件夹! +{0} + + + 保存项目失败 + + + 文件"{0}"已存在。是否覆盖? + + + 未能找到有效的游戏可执行文件路径;路径已脱离目录 + + + 无法确定 GameMaker 版本 - "常规信息"为空。 + + + 无效名称 + + + 搜索词不是有效的正则表达式模式。 + + + 图层 "{0}" 的实例列表为空,但该图层具有实例 ID。 + + + 第 {0} 行:{1} + + + 请先加载 data.win 文件 + + + 加载过程中出现警告。保存时可能会发生数据丢失! + + + 您在选择"字符串"时存在大量字符串。 +这可能会使搜索过程明显变长。 +您是否要继续? + + + 未找到引用。 + + + 未找到未引用的资源。 + + + 一个代码条目匹配了筛选条件。 + + + 路径 {0} 不存在,无法搜索文件! + + + 当前已打开一个项目 - 打开另一个数据文件并放弃所有未保存的更改? + + + 警告:您当前已打开一个项目。 +确定要创建新项目吗? + + + 当前已打开一个项目 - 打开另一个数据文件并放弃所有未保存的更改? + + + 游戏对象"{0}"的引用 + + + 代码条目中的结果 + + + 结果保存成功。 + + + 运行其他脚本…… + + + 脚本消息 + + + 脚本问题 + + + 脚本仍在运行。仍然保存吗? +这可能会损坏您要保存的数据文件。 + + + ({0} 个错误) + + + 在 {0} 个代码条目中找到 {2} 个结果。 + + + "{0}"图块层的图块集参数错误(图块大小、输出边框等)。 +无法显示。 + + + 当前项目中有标记为导出的资源 - 打开另一个新项目并放弃所有未导出的更改? + + + 未处理的异常 + + + 未知缩进样式 + + + 未引用的游戏资源 + + + 不支持的字节码版本 + + + 此游戏包含未知或不支持图像格式的纹理。这些纹理可以保存/加载,但不会在编辑器中显示,许多操作将会失败。请谨慎操作。 + + + 此游戏使用 YYC(YoYo 编译器),这意味着代码嵌入在游戏可执行文件中。此配置目前未完全支持;风险自负。 + + + 颜色 + + + 定义 + + + 深度 + + + 启用 + + + 位置 + + + 速度 + + + 精灵 + + + 提示:您可以点击下方任何图块区域来选择并编辑其属性。 + + + 可见 + + + 是否允许在树视图中拖放来更改资源的顺序。 + + + GameMaker: Studio 1.4 路径: + + + GameMaker Studio 2 运行时目录的路径。 + + + 保存项目数据文件时是否重新编译所有 GML 代码源。 + + + 动画曲线 + + + 音频组 + + + 代码 + + + 代码局部变量 + + + 嵌入图像 + + + 嵌入纹理 + + + 扩展 + + + 扩展文件 + + + 扩展函数 + + + 字体 + + + 函数 + + + 游戏结束 + + + 游戏对象 + + + 常规信息 + + + 全局初始化 + + + 粒子系统发射器 + + + 房间 + + + 脚本 + + + 序列 + + + 着色器 + + + 声音 + + + 精灵 + + + 字符串 + + + 时间轴 + + + 变量概览 + + + + + + 无标题 + + + 版本 + + + 编译器错误 + + + 游戏对象的引用 + + + 搜索中 + + + 未引用的游戏资源 + + + 添加新扩展选项 + + + 闭合 + + + em + + + 导出的精灵(GM 2023.8+) + + + 请记住,.gml文件要么不存在,要么为空。(例如:不包含函数) + + + 因为它们已被预烘焙到GML脚本中。这完全正常。 + + + 注意事项: + + + 生成 Mipmap + + + 请注意,字形需要按升序排列。 + + + 排序字形 + + + 待办:预览不会实时更新,但切换到其他标签页后返回时会更新。 + + + + + + 精度 + + + 选择空白字形的区域 + + + 前进到下一个字符时向右移动的像素数。 +上面的纹理以绿色为主。 + + + 警告:纹理加载失败! + + + 提示:此图像支持鼠标交互。您可以点击它来选择对应的纹理页项。 + + + 纹理页 + + + 图块集 + + + 类型 + + + 注意:字形需要按升序排列才能正确工作。 + + + + + + 顶点着色器属性 + + + x + + + 正在下载新版本…… + + + 展开左侧列表以编辑项 + + + ID: {0} + + + 加载中…… + + + 正在加载,请稍候…… + + + 新文件已创建,祝您从零开始制作游戏愉快 +我告诉过你要打开 data.win,而不是创建新文件!:P + + + 保存中 + + + 正在保存,请稍候…… + + + {0} 已完成! + + + 查找所有引用 + + + 查找此图块的所有引用 + + + 查找此页面项的所有引用 + + + 在新标签页中打开 + + + 在64位操作系统上检测到32位(x86)版本的UndertaleModTool。 +强烈建议改用64位版本。 +您是否要下载? + + + 音频失败 + + + 搜索仍在进行中,无法打开结果。 + + + 搜索仍在进行中,无法开始新搜索。 + + + 无法在 YYC 编译的游戏中搜索。 + + + 打开子数据文件失败;路径已脱离目录。 + + + 为新项目选择名称 + + + 代码条目 + + + 删除 {0}? +请注意,代码通常通过ID引用对象,因此此操作可能会导致问题,因为其他项会向上移动! + + + 游戏已启用调试器。您是否要禁用它以便游戏运行? + + + (拖放 {0}) + + + 解析第 {0} 行时出错 - "{1}"。 + + + 保存文件时出错 - "{0}"。 + + + 播放音频失败! +{0} + + + 未能选择输出文件名。 + + + 创建新项目失败 + + + 获取最新构建失败! +{0} + + + 未能找到最新构建! +检测到的操作名称 - {0} + + + 正在生成结果列表…… + + + 您正在以 GameMaker Studio 调试模式保存游戏。除非调试器正在运行,否则普通运行时将在加载后挂起。您可以在常规信息中勾选"禁用调试器"并保存来关闭此功能。 + + + GMS 调试器 + + + 此游戏设置为使用 GameMaker Studio 调试器运行,如果调试器未运行,普通运行时将在加载后挂起。您可以在常规信息中勾选"禁用调试器"并保存来关闭此功能。 + + + 无效的颜色字符串 + + + 无效的正则表达式 + + + + + + 多个代码条目匹配了筛选条件。 + + + 没有代码条目匹配筛选条件。 + + + 当前未加载数据文件! + + + 没有可导出的结果。 + + + 未找到脚本! + + + 游戏对象"{0}"的引用已过期(引用的对象已被删除)。 + + + 此对象类型没有编辑器/查看器。 + + + 游戏对象"{0}"的引用已过期(某些引用的对象已被删除)。 + + + 对象引用相关窗口中发生错误。 +请在 GitHub 上报告此问题。 + +{0} + + + 已打开项目"{0}"。 + + + 打开项目文件 + + + 停止进度条更新器任务失败。 +强烈建议重启应用程序。 + + + 项目已打开 + + + 结果位于 + + + {0}: {1}x{2} + + + 所选运行时不支持调试。 + + + 您确定要使用 GMS 调试器运行游戏吗? +如果您想在某个游戏中启用调试模式,则需要使用其中一个脚本。 + + + 项目保存成功。 + + + 脚本错误 + + + 脚本警告 + + + 所选对象不是"UndertaleResource"。 + + + 停止进度条更新器任务失败。 +强烈建议重启应用程序。 + + + 无法恢复上次标签页内容状态 - 未找到"UserControl"。 + + + 无法恢复上次标签页内容状态 - 未找到"ScrollViewer"。 + + + 无法保存上次标签页内容状态 - 未找到"UserControl"。 + + + 图块 {0} / {1} + + + 代码行数过多,无法显示。 + + + 当前项目中有标记为导出的资源 - 创建新项目并放弃所有未导出的更改? + + + 不支持的纹理 + + + YYC + + + 根据房间大小自动设置图块贴图大小 + + + 无效的遮罩数据! + + + 是否自动将 GameMaker 数据文件类型与 UndertaleModTool 关联。 + + + 是否启用暗色模式主题。 + + + GML 编译器/反编译器设置 + + + GameMaker: Studio 1.4 安装目录的路径。 + + + GameMaker Studio 2 运行时路径: + + + 是否覆盖房间编辑器的全局网格高度。 + + + 是否覆盖房间编辑器的全局网格厚度。 + + + 是否覆盖房间编辑器的全局网格宽度。 + + + 语言: + + + 选择应用程序的显示语言。 + + + 保存项目数据文件时重新编译所有源 GML + + + 是否记住上次窗口位置和大小。 + + + 是否在文件菜单中显示"在 GMS 调试器下运行游戏"选项。 + + + 是否在资源树视图中显示空条目。 + + + 关闭时有未保存的更改时是否显示警告。 + + + 临时运行游戏时是否显示警告。 + + + 设置 + + + 背景 + + + 嵌入音频 + + + 粒子系统 + + + 路径 + + + 纹理组信息 + + + 纹理页项 + + + 变量 + + + 已打开的标签页的语言在重启应用程序后才会生效。 + + \ No newline at end of file diff --git a/UndertaleModTool/MainWindow.xaml b/UndertaleModTool/MainWindow.xaml index b55f4d2cc..8583dde92 100644 --- a/UndertaleModTool/MainWindow.xaml +++ b/UndertaleModTool/MainWindow.xaml @@ -1,4 +1,4 @@ - - + @@ -77,10 +78,10 @@ - - - - + + + + - + - + - - + + - - - - + + + + - + - + - - - - - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -609,7 +602,6 @@ - diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index 67c3fb600..c2c0aebdd 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -#pragma warning disable CA1416 // Validate platform compatibility +#pragma warning disable CA1416 // Validate platform compatibility using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Scripting; @@ -31,6 +31,7 @@ using UndertaleModLib.Scripting; using UndertaleModLib.Util; using UndertaleModTool.Windows; +using UndertaleModTool.Localization; using System.IO.Pipes; using Ookii.Dialogs.Wpf; @@ -69,17 +70,17 @@ public partial class MainWindow : Window, INotifyPropertyChanged, IScriptInterfa public string TitleMain { get; set; } - public static readonly RoutedUICommand CloseTabCommand = new RoutedUICommand("Close current tab", "CloseTab", typeof(MainWindow)); - public static readonly RoutedUICommand CloseAllTabsCommand = new RoutedUICommand("Close all tabs", "CloseAllTabs", typeof(MainWindow)); - public static readonly RoutedUICommand RestoreClosedTabCommand = new RoutedUICommand("Restore last closed tab", "RestoreClosedTab", typeof(MainWindow)); - public static readonly RoutedUICommand SwitchToNextTabCommand = new RoutedUICommand("Switch to the next tab", "SwitchToNextTab", typeof(MainWindow)); - public static readonly RoutedUICommand SwitchToPrevTabCommand = new RoutedUICommand("Switch to the previous tab", "SwitchToPrevTab", typeof(MainWindow)); - public static readonly RoutedUICommand SearchInCodeCommand = new("Search in code", "SearchInCode", typeof(MainWindow)); - public static readonly RoutedUICommand NewProjectCommand = new("New project", "NewProject", typeof(MainWindow)); - public static readonly RoutedUICommand OpenProjectCommand = new("Open project", "OpenProject", typeof(MainWindow)); - public static readonly RoutedUICommand SaveProjectCommand = new("Save project", "SaveProject", typeof(MainWindow)); - public static readonly RoutedUICommand ViewProjectAssetsCommand = new("View project assets", "ViewProjectAssets", typeof(MainWindow)); - public static readonly RoutedUICommand CloseProjectCommand = new("Close project", "CloseProject", typeof(MainWindow)); + public static readonly RoutedUICommand CloseTabCommand = new RoutedUICommand(LocalizationSource.GetString("Cmd_CloseCurrentTab"), "CloseTab", typeof(MainWindow)); + public static readonly RoutedUICommand CloseAllTabsCommand = new RoutedUICommand(LocalizationSource.GetString("Cmd_CloseAllTabs"), "CloseAllTabs", typeof(MainWindow)); + public static readonly RoutedUICommand RestoreClosedTabCommand = new RoutedUICommand(LocalizationSource.GetString("Cmd_RestoreClosedTab"), "RestoreClosedTab", typeof(MainWindow)); + public static readonly RoutedUICommand SwitchToNextTabCommand = new RoutedUICommand(LocalizationSource.GetString("Cmd_SwitchToNextTab"), "SwitchToNextTab", typeof(MainWindow)); + public static readonly RoutedUICommand SwitchToPrevTabCommand = new RoutedUICommand(LocalizationSource.GetString("Cmd_SwitchToPrevTab"), "SwitchToPrevTab", typeof(MainWindow)); + public static readonly RoutedUICommand SearchInCodeCommand = new(LocalizationSource.GetString("Cmd_SearchInCode"), "SearchInCode", typeof(MainWindow)); + public static readonly RoutedUICommand NewProjectCommand = new(LocalizationSource.GetString("Cmd_NewProject"), "NewProject", typeof(MainWindow)); + public static readonly RoutedUICommand OpenProjectCommand = new(LocalizationSource.GetString("Cmd_OpenProject"), "OpenProject", typeof(MainWindow)); + public static readonly RoutedUICommand SaveProjectCommand = new(LocalizationSource.GetString("Cmd_SaveProject"), "SaveProject", typeof(MainWindow)); + public static readonly RoutedUICommand ViewProjectAssetsCommand = new(LocalizationSource.GetString("Cmd_ViewProjectAssets"), "ViewProjectAssets", typeof(MainWindow)); + public static readonly RoutedUICommand CloseProjectCommand = new(LocalizationSource.GetString("Cmd_CloseProject"), "CloseProject", typeof(MainWindow)); public ObservableCollection Tabs { get; set; } = new(); public Tab CurrentTab @@ -238,18 +239,18 @@ public void RaiseOnSelectedChanged() }; // Filters for all data files, and for only main data files - private const string DataFileFilter = "GameMaker data files (.win, .unx, .ios, .droid, audiogroup*.dat)|*.win;*.unx;*.ios;*.droid;audiogroup*.dat|All files|*"; - private const string MainDataFileFilter = "GameMaker main data files (.win, .unx, .ios, .droid)|*.win;*.unx;*.ios;*.droid|All files|*"; + private static string DataFileFilter => LocalizationSource.GetString("Filter_DataFiles") + "|*.win;*.unx;*.ios;*.droid;audiogroup*.dat|" + LocalizationSource.GetString("Filter_AllFiles") + "|*"; + private static string MainDataFileFilter => LocalizationSource.GetString("Filter_MainDataFiles") + "|*.win;*.unx;*.ios;*.droid|" + LocalizationSource.GetString("Filter_AllFiles") + "|*"; public MainWindow() { InitializeComponent(); this.DataContext = this; - Highlighted = new DescriptionView("Welcome to UndertaleModTool!", "Open a data.win file to get started, then double click on the items on the left to view them."); + Highlighted = new DescriptionView(LocalizationSource.GetString("Main_WelcomeHeading"), LocalizationSource.GetString("Main_WelcomeDescription")); OpenInTab(Highlighted); - TitleMain = "UndertaleModTool by krzys_h v:" + Version; + TitleMain = LocalizationSource.GetString("Common_UndertaleModTool") + " by krzys_h v:" + Version; CanSave = false; CanSafelySave = false; @@ -352,6 +353,9 @@ private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEve Settings.Load(); + string lang = Settings.Instance.Language ?? "en"; + LocalizationSource.Instance.CurrentCulture = new System.Globalization.CultureInfo(lang); + if (Settings.Instance.RememberWindowPlacements) this.SetPlacement(Settings.Instance.MainWindowPlacement); @@ -385,7 +389,7 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) } if (shouldAssociate && Settings.ShouldPromptForAssociations) { - if (this.ShowQuestion("First-time setup: Would you like to associate GameMaker data files (like .win) with UndertaleModTool?", MessageBoxImage.Question, "File associations") != MessageBoxResult.Yes) + if (this.ShowQuestion(LocalizationSource.GetString("Setup_FileAssociationQuestion"), MessageBoxImage.Question, LocalizationSource.GetString("Setup_FileAssociations")) != MessageBoxResult.Yes) { shouldAssociate = false; } @@ -437,7 +441,7 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) foreach (Process instance in updaterInstances) { if (!instance.WaitForExit(5000)) - this.ShowWarning("UndertaleModToolUpdater app didn't exit.\nCan't delete its temp folder."); + this.ShowWarning(LocalizationSource.GetString("Msg_UpdaterDidntExit")); else updaterClosed = true; } @@ -469,7 +473,7 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) } if (!deleted) - this.ShowWarning($"The updater temp folder can't be deleted.\nError - {exMessage}."); + this.ShowWarning(string.Format(LocalizationSource.GetString("Msg_UpdaterTempFolderError"), exMessage)); } }); } @@ -489,23 +493,23 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) string gameExeName = Data?.GeneralInfo?.FileName?.Content; if (gameExeName is null || FilePath is null) { - ScriptError("Null game executable name or location"); + ScriptError(LocalizationSource.GetString("Msg_NullGameExe")); Environment.Exit(0); } string gameExePath = Paths.TryJoinVerifyWithinDirectory(Path.GetDirectoryName(FilePath), gameExeName + ".exe"); - if (gameExePath is null) - { - ScriptError("Failed to find valid game executable path; escaped directory"); - Environment.Exit(0); - } + if (gameExePath is null) + { + ScriptError(LocalizationSource.GetString("Msg_GameExePathEscaped")); + Environment.Exit(0); + } if (!File.Exists(gameExePath)) { - ScriptError("Cannot find game executable path, expected: " + gameExePath); + ScriptError(string.Format(LocalizationSource.GetString("Msg_CantFindGameExe"), gameExePath)); Environment.Exit(0); } if (!File.Exists(FilePath)) { - ScriptError("Cannot find data file path, expected: " + FilePath); + ScriptError(string.Format(LocalizationSource.GetString("Msg_CantFindDataFile"), FilePath)); Environment.Exit(0); } Process.Start(new ProcessStartInfo(gameExePath, ["-game", FilePath, "-debugoutput", Path.ChangeExtension(FilePath, ".gamelog.txt")])); @@ -546,7 +550,7 @@ public void OpenChildFile(string filename, string chunkName, int itemIndex) string childFilePath = Paths.TryJoinVerifyWithinDirectory(dir, filename); if (childFilePath is null) { - ScriptError("Failed to open child data file; escaped directory."); + ScriptError(LocalizationSource.GetString("Msg_ChildFileEscaped")); return; } Process.Start(new ProcessStartInfo(Environment.ProcessPath, [childFilePath, key])); @@ -692,7 +696,7 @@ public bool MakeNewDataFile() { if (Data != null) { - if (this.ShowQuestion("Warning: you currently have a project open.\nAre you sure you want to make a new project?") == MessageBoxResult.No) + if (this.ShowQuestion(LocalizationSource.GetString("Msg_ProjectOpenNewConfirm")) == MessageBoxResult.No) return false; } this.Dispatcher.Invoke(() => @@ -711,9 +715,9 @@ public bool MakeNewDataFile() OnPropertyChanged("FilePath"); OnPropertyChanged("IsGMS2"); - BackgroundsItemsList.Header = IsGMS2 == Visibility.Visible ? "Tile sets" : "Backgrounds & Tile sets"; + BackgroundsItemsList.Header = IsGMS2 == Visibility.Visible ? LocalizationSource.GetString("Main_TileSets") : LocalizationSource.GetString("Main_BackgroundsTileSets"); - Highlighted = new DescriptionView("Welcome to UndertaleModTool!", "New file created, have fun making a game out of nothing\nI TOLD YOU to open a data.win, not create a new file! :P"); + Highlighted = new DescriptionView(LocalizationSource.GetString("Main_WelcomeHeading"), LocalizationSource.GetString("Main_NewFileDescription")); OpenInTab(Highlighted); CanSave = true; @@ -735,12 +739,12 @@ private async void Window_Drop(object sender, DragEventArgs e) if (fileext == ".csx") { - if (this.ShowQuestion($"Run {filepath} as a script?") == MessageBoxResult.Yes) + if (this.ShowQuestion(string.Format(LocalizationSource.GetString("Msg_RunScriptQuestion"), filepath)) == MessageBoxResult.Yes) await RunScript(filepath); } else if (FileAssociations.Extensions.Contains(fileext) || fileext == ".dat" /* audiogroup */) { - if (this.ShowQuestion($"Open {filepath} as a data file?") == MessageBoxResult.Yes) + if (this.ShowQuestion(string.Format(LocalizationSource.GetString("Msg_OpenDataFileQuestion"), filepath)) == MessageBoxResult.Yes) await LoadFile(filepath, true); } // else, do something? @@ -768,7 +772,7 @@ public async Task DoSaveDialog(bool suppressDebug = false) // If a project is open, save to its save data file specifically if (Project is not null) { - if (this.ShowQuestionWithCancel("Save to the project's designated data file for saving?") == MessageBoxResult.Yes) + if (this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_SaveToProjectDataFile")) == MessageBoxResult.Yes) { await SaveFile(Project.SaveDataPath, suppressDebug); return true; @@ -831,7 +835,7 @@ private void Command_Open(object sender, ExecutedRoutedEventArgs e) { if (Project is not null) { - if (this.ShowQuestionWithCancel("A project is currently open - open another data file and discard all unsaved changes?", MessageBoxImage.Warning, "Project currently open") != MessageBoxResult.Yes) + if (this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_ProjectOpenOpenDataFile"), MessageBoxImage.Warning, LocalizationSource.GetString("Msg_ProjectCurrentlyOpen")) != MessageBoxResult.Yes) { // Abort opening new file return; @@ -846,13 +850,13 @@ private async void Command_Save(object sender, ExecutedRoutedEventArgs e) if (CanSave) { if (!CanSafelySave) - this.ShowWarning("Errors occurred during loading. High chance of data loss! Proceed at your own risk."); + this.ShowWarning(LocalizationSource.GetString("Msg_ErrorsDuringLoading")); var result = await SaveCodeChanges(); if (result == SaveResult.NotSaved) _ = DoSaveDialog(); else if (result == SaveResult.Error) - this.ShowError("The changes in code editor weren't saved due to some error in \"SaveCodeChanges()\"."); + this.ShowError(LocalizationSource.GetString("Msg_CodeEditorSaveError")); } } private async void DataWindow_Closing(object sender, CancelEventArgs e) @@ -865,7 +869,7 @@ private async void DataWindow_Closing(object sender, CancelEventArgs e) if (Project is not null && Project.HasUnexportedAssets) { - MessageBoxResult result = this.ShowQuestionWithCancel("There are assets marked to be exported in the current project.\nReally quit?", MessageBoxImage.Warning, "Warning"); + MessageBoxResult result = this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_UnexportedAssetsQuit"), MessageBoxImage.Warning, LocalizationSource.GetString("Common_Warning")); if (result != MessageBoxResult.Yes) { @@ -874,7 +878,7 @@ private async void DataWindow_Closing(object sender, CancelEventArgs e) } else if (SettingsWindow.WarnOnClose) { - MessageBoxResult result = this.ShowQuestionWithCancel("Save changes before quitting?"); + MessageBoxResult result = this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_SaveBeforeQuit")); if (result == MessageBoxResult.Cancel) { @@ -883,7 +887,7 @@ private async void DataWindow_Closing(object sender, CancelEventArgs e) if (result == MessageBoxResult.Yes) { - if (scriptDialog is null || (this.ShowQuestion("Script still runs. Save anyway?\nIt can corrupt the data file that you'll save.") == MessageBoxResult.Yes)) + if (scriptDialog is null || (this.ShowQuestion(LocalizationSource.GetString("Msg_ScriptStillRunsSave")) == MessageBoxResult.Yes)) save = true; } } @@ -898,7 +902,7 @@ private async void DataWindow_Closing(object sender, CancelEventArgs e) } else if (saveRes == SaveResult.Error) { - this.ShowError("The changes in code editor weren't saved due to some error in \"SaveCodeChanges()\"."); + this.ShowError(LocalizationSource.GetString("Msg_CodeEditorSaveError")); return; } } @@ -943,8 +947,8 @@ private void Command_CloseAllTabs(object sender, ExecutedRoutedEventArgs e) Tabs.Clear(); CurrentTab = null; - OpenInTab(new DescriptionView("Welcome to UndertaleModTool!", - "Open data.win file to get started, then double click on the items on the left to view them")); + OpenInTab(new DescriptionView(LocalizationSource.GetString("Main_WelcomeHeading"), + LocalizationSource.GetString("Main_WelcomeDescription2"))); CurrentTab = Tabs[CurrentTabIndex]; UpdateObjectLabel(CurrentTab.CurrentObject); @@ -1039,7 +1043,7 @@ private void DisposeGameData() } private async Task LoadFile(string filename, bool preventClose = false, bool onlyGeneralInfo = false) { - LoaderDialog dialog = new LoaderDialog("Loading", "Loading, please wait..."); + LoaderDialog dialog = new LoaderDialog(LocalizationSource.GetString("Main_Loading"), LocalizationSource.GetString("Main_LoadingPleaseWait")); dialog.PreventClose = preventClose; this.Dispatcher.Invoke(() => { @@ -1048,7 +1052,7 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl dialog.Owner = this; DisposeGameData(); - Highlighted = new DescriptionView("Welcome to UndertaleModTool!", "Double click on the items on the left to view them!"); + Highlighted = new DescriptionView(LocalizationSource.GetString("Main_WelcomeHeading"), LocalizationSource.GetString("Main_DoubleClickToView")); OpenInTab(Highlighted); GameSpecificResolver.BaseDirectory = ExePath; @@ -1063,7 +1067,7 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl { data = UndertaleIO.Read(stream, (string warning, bool isImportant) => { - this.ShowWarning(warning, "Loading warning"); + this.ShowWarning(warning, LocalizationSource.GetString("Msg_LoadingWarning")); if (isImportant) { hadImportantWarnings = true; @@ -1079,7 +1083,7 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl #if DEBUG Debug.WriteLine(e); #endif - this.ShowError("An error occurred while trying to load:\n" + e.Message, "Load error"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_LoadErrorDetail"), e.Message), LocalizationSource.GetString("Msg_LoadError")); } if (onlyGeneralInfo) @@ -1100,13 +1104,13 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl { if (data.UnsupportedBytecodeVersion) { - this.ShowWarning("Only bytecode versions 13 to 17 are supported for now, you are trying to load " + data.GeneralInfo.BytecodeVersion + ". A lot of code is disabled and will likely break something. Saving/exporting is disabled.", "Unsupported bytecode version"); + this.ShowWarning(string.Format(LocalizationSource.GetString("Msg_UnsupportedBytecodeVersion"), data.GeneralInfo.BytecodeVersion), LocalizationSource.GetString("Msg_UnsupportedBytecodeVersionTitle")); CanSave = false; CanSafelySave = false; } else if (hadImportantWarnings) { - this.ShowWarning("Warnings occurred during loading. Data loss will likely occur when trying to save!", "Loading problems"); + this.ShowWarning(LocalizationSource.GetString("Msg_LoadingWarningsDataLoss"), LocalizationSource.GetString("Msg_LoadingProblems")); CanSave = true; CanSafelySave = false; } @@ -1119,13 +1123,13 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl } if (data.IsYYC()) { - this.ShowWarning("This game uses YYC (YoYo Compiler), which means the code is embedded into the game executable. This configuration is currently not fully supported; continue at your own risk.", "YYC"); + this.ShowWarning(LocalizationSource.GetString("Msg_YYCWarning"), LocalizationSource.GetString("Msg_YYCTitle")); } if (data.GeneralInfo is not null) { if (!data.GeneralInfo.IsDebuggerDisabled) { - this.ShowWarning("This game is set to run with the GameMaker Studio debugger and the normal runtime will simply hang after loading if the debugger is not running. You can turn this off in General Info by checking the \"Disable Debugger\" box and saving.", "GMS Debugger"); + this.ShowWarning(LocalizationSource.GetString("Msg_GMSDebuggerWarning"), LocalizationSource.GetString("Msg_GMSDebuggerTitle")); } } if (data.EmbeddedTextures is not null) @@ -1134,7 +1138,7 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl { if (tex?.TextureData?.Image?.Format == GMImage.ImageFormat.Unknown) { - this.ShowWarning("This game contains texture(s) with unknown or unsupported image formats. These will save/load, but will not display in editors, and many operations will fail regarding them. Proceed with caution.", "Unsupported textures"); + this.ShowWarning(LocalizationSource.GetString("Msg_UnsupportedTexturesWarning"), LocalizationSource.GetString("Msg_UnsupportedTexturesTitle")); break; } } @@ -1156,7 +1160,7 @@ private async Task LoadFile(string filename, bool preventClose = false, bool onl OnPropertyChanged("FilePath"); OnPropertyChanged("IsGMS2"); - BackgroundsItemsList.Header = IsGMS2 == Visibility.Visible ? "Tile sets" : "Backgrounds & Tile sets"; + BackgroundsItemsList.Header = IsGMS2 == Visibility.Visible ? LocalizationSource.GetString("Main_TileSets") : LocalizationSource.GetString("Main_BackgroundsTileSets"); UndertaleCodeEditor.gettext = null; UndertaleCodeEditor.gettextJSON = null; @@ -1183,7 +1187,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) bool isDifferentPath = FilePath != filename; - LoaderDialog dialog = new LoaderDialog("Saving", "Saving, please wait..."); + LoaderDialog dialog = new LoaderDialog(LocalizationSource.GetString("Main_Saving"), LocalizationSource.GetString("Main_SavingPleaseWait")); dialog.PreventClose = true; IProgress> progress = new Progress>(i => { dialog.ReportProgress(i.Item2, i.Item1); }); IProgress setMax = new Progress(i => { dialog.Maximum = i; }); @@ -1197,7 +1201,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) DebugDataDialog.DebugDataMode debugMode = DebugDataDialog.DebugDataMode.NoDebug; if (!suppressDebug && Data.GeneralInfo != null && !Data.GeneralInfo.IsDebuggerDisabled) - this.ShowWarning("You are saving the game in GameMaker Studio debug mode. Unless the debugger is running, the normal runtime will simply hang after loading. You can turn this off in General Info by checking the \"Disable Debugger\" box and saving.", "GMS Debugger"); + this.ShowWarning(LocalizationSource.GetString("Msg_GMSDebuggerSaveWarning"), LocalizationSource.GetString("Msg_GMSDebuggerTitle")); Task t = Task.Run(() => { // Recompile code sources before doing anything, if needed @@ -1207,9 +1211,9 @@ private async Task SaveFile(string filename, bool suppressDebug = false) { if (Project is not null) { - FileMessageEvent?.Invoke("Recompiling GML code, please wait..."); + FileMessageEvent?.Invoke(LocalizationSource.GetString("Msg_RecompilingGML")); Project.RecompileAllCodeSources(); - FileMessageEvent?.Invoke("Saving data file, please wait..."); + FileMessageEvent?.Invoke(LocalizationSource.GetString("Msg_SavingDataFile")); } } catch (ProjectException e) @@ -1217,7 +1221,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) Dispatcher.Invoke(() => { dialog.TryClose(); - this.ShowError(e.Message, "Recompile error"); + this.ShowError(e.Message, LocalizationSource.GetString("Msg_RecompileError")); }); return false; } @@ -1226,7 +1230,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) Dispatcher.Invoke(() => { dialog.TryClose(); - this.ShowError("An error occurred while trying to recompile code sources:\n" + e.Message, "Recompile error"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_RecompileErrorDetail"), e.Message), LocalizationSource.GetString("Msg_RecompileError")); }); return false; } @@ -1246,7 +1250,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) if (debugMode != DebugDataDialog.DebugDataMode.NoDebug) { - FileMessageEvent?.Invoke("Generating debugger data..."); + FileMessageEvent?.Invoke(LocalizationSource.GetString("Msg_GeneratingDebuggerData")); UndertaleDebugData debugData = UndertaleDebugData.CreateNew(); @@ -1333,7 +1337,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) { Dispatcher.Invoke(() => { - this.ShowError("An error occurred while trying to save:\n" + e.Message, "Save error"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_SaveErrorDetail"), e.Message), LocalizationSource.GetString("Msg_SaveError")); }); saveSucceeded = false; @@ -1360,7 +1364,7 @@ private async Task SaveFile(string filename, bool suppressDebug = false) { Dispatcher.Invoke(() => { - this.ShowError("An error occurred while trying to save:\n" + exc.Message, "Save error"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_SaveErrorDetail"), exc.Message), LocalizationSource.GetString("Msg_SaveError")); }); saveSucceeded = false; @@ -1399,27 +1403,28 @@ private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEv { if (e.NewValue is TreeViewItem) { - string item = (e.NewValue as TreeViewItem).Header?.ToString(); + string item = (e.NewValue as TreeViewItem).Name?.ToString(); + string header = (e.NewValue as TreeViewItem).Header?.ToString(); - if (item == "Data") + if (item == "Tree_Data") { - Highlighted = new DescriptionView("Welcome to UndertaleModTool!", Data != null ? "Double click on the items on the left to view them" : "Open data.win file to get started"); + Highlighted = new DescriptionView(LocalizationSource.GetString("Main_WelcomeHeading"), Data != null ? LocalizationSource.GetString("Main_DoubleClickToView2") : LocalizationSource.GetString("Main_OpenDataWinToStart")); return; } if (Data == null) { - Highlighted = new DescriptionView(item, "Load data.win file first"); + Highlighted = new DescriptionView(header, LocalizationSource.GetString("Msg_LoadDataFileFirst")); return; } Highlighted = item switch { - "General info" => new GeneralInfoEditor(Data?.GeneralInfo, Data?.Options, Data?.Language), - "Global init" => new GlobalInitEditor(Data?.GlobalInitScripts), - "Game End scripts" => new GameEndEditor(Data?.GameEndScripts), - "Variables" => Data.FORM.Chunks["VARI"], - _ => new DescriptionView(item, "Expand the list on the left to edit items"), + "Tree_GeneralInfo" => new GeneralInfoEditor(Data?.GeneralInfo, Data?.Options, Data?.Language), + "Tree_GlobalInit" => new GlobalInitEditor(Data?.GlobalInitScripts), + "Tree_GameEndScripts" => new GameEndEditor(Data?.GameEndScripts), + "Tree_Variables" => Data.FORM.Chunks["VARI"], + _ => new DescriptionView(header, LocalizationSource.GetString("Main_ExpandListToEdit")), }; } else @@ -1607,7 +1612,7 @@ internal void DeleteItem(UndertaleObject obj) object source = container.ItemsSource; IList list = ((source as ICollectionView)?.SourceCollection as IList) ?? (source as IList); bool isLast = list.IndexOf(obj) == list.Count - 1; - if (this.ShowQuestion("Delete " + obj + "?" + (!isLast ? "\n\nNote that the code often references objects by ID, so this operation is likely to break stuff because other items will shift up!" : ""), isLast ? MessageBoxImage.Question : MessageBoxImage.Warning, "Confirmation" ) == MessageBoxResult.Yes) + if (this.ShowQuestion(isLast ? string.Format(LocalizationSource.GetString("Msg_DeleteItemConfirm"), obj) : string.Format(LocalizationSource.GetString("Msg_DeleteItemConfirmWithWarning"), obj), isLast ? MessageBoxImage.Question : MessageBoxImage.Warning, LocalizationSource.GetString("Msg_Confirmation") ) == MessageBoxResult.Yes) { // Remove object from list list.Remove(obj); @@ -1684,12 +1689,11 @@ internal void CopyItemName(object obj) } catch (Exception ex) { - this.ShowError("Can't copy the item name to clipboard due to this error:\n" + - ex.Message + ".\nYou probably should try again."); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_ClipboardCopyError"), ex.Message)); } } else - this.ShowWarning("Item name is null."); + this.ShowWarning(LocalizationSource.GetString("Msg_ItemNameNull")); } private void MainTree_KeyDown(object sender, KeyEventArgs e) @@ -1780,8 +1784,7 @@ private void MenuItem_FindUnreferencedAssets_Click(object sender, RoutedEventArg } catch (Exception ex) { - this.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_ReferencesWindowError"), ex)); } finally { @@ -1826,7 +1829,7 @@ private void MenuItem_Add_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - ScriptError("An error occurred while trying to add the menu item. No action has been taken.\r\n\r\nError:\r\n\r\n" + ex.ToString()); + ScriptError(string.Format(LocalizationSource.GetString("Msg_AddMenuItemError"), ex.ToString())); return; } IList list = ((source as ICollectionView)?.SourceCollection as IList) ?? (source as IList); @@ -1854,7 +1857,7 @@ private void MenuItem_Add_Click(object sender, RoutedEventArgs e) { string assetTypeName = obj.GetType().Name.Replace("Undertale", "").Replace("GameObject", "Object").ToLower(); string newName = $"{assetTypeName}{list.Count}"; - string userNewName = ScriptInputDialog($"Choose new {assetTypeName} name", "Name of new asset:", newName, "Cancel", "Create", false, false); + string userNewName = ScriptInputDialog(string.Format(LocalizationSource.GetString("Msg_ChooseNewAssetName"), assetTypeName), LocalizationSource.GetString("Msg_NameOfNewAsset"), newName, LocalizationSource.GetString("Common_Cancel"), LocalizationSource.GetString("Common_Create"), false, false); if (userNewName is null) { // Presume user canceled the action @@ -1866,8 +1869,8 @@ private void MenuItem_Add_Click(object sender, RoutedEventArgs e) } else { - if (this.ShowQuestionWithCancel($"Asset name \"{userNewName}\" is not a valid identifier. Add a new asset using an auto-generated name instead?", - MessageBoxImage.Warning, "Invalid name") != MessageBoxResult.Yes) + if (this.ShowQuestionWithCancel(string.Format(LocalizationSource.GetString("Msg_InvalidAssetName"), userNewName), + MessageBoxImage.Warning, LocalizationSource.GetString("Msg_InvalidName")) != MessageBoxResult.Yes) { return; } @@ -1898,7 +1901,7 @@ private void MenuItem_Add_Click(object sender, RoutedEventArgs e) roomResource.Caption = Data.Strings.MakeString(""); } - if (this.ShowQuestion("Add the new room to the end of the room order list?", MessageBoxImage.Question, "Add to room order list") == MessageBoxResult.Yes) + if (this.ShowQuestion(LocalizationSource.GetString("Msg_AddToRoomOrderList"), MessageBoxImage.Question, LocalizationSource.GetString("Msg_AddToRoomOrderListTitle")) == MessageBoxResult.Yes) { Data.GeneralInfo.RoomOrder.Add(new(roomResource)); } @@ -1993,11 +1996,11 @@ private void MenuItem_RunScript_SubmenuOpened(object sender, RoutedEventArgs e, // exit out early if the path does not exist. if (!directory.Exists) { - item.Items.Add(new MenuItem {Header = $"(Path {folderDir} does not exist, cannot search for files!)", IsEnabled = false}); + item.Items.Add(new MenuItem {Header = string.Format(LocalizationSource.GetString("Msg_PathNotExist"), folderDir), IsEnabled = false}); if (item.Name == "RootScriptItem") { - var otherScripts1 = new MenuItem {Header = "Run _other script..."}; + var otherScripts1 = new MenuItem {Header = LocalizationSource.GetString("Msg_RunOtherScript")}; otherScripts1.Click += MenuItem_RunOtherScript_Click; item.Items.Add(otherScripts1); } @@ -2024,13 +2027,13 @@ private void MenuItem_RunScript_SubmenuOpened(object sender, RoutedEventArgs e, var subDirName = subDirectory.Name; // In addition to the _ comment from above, we also need to add at least one item, so that WPF uses this as a submenuitem - MenuItemDark subItem = new() {Header = subDirName.Replace("_", "__"), Items = {new MenuItem {Header = "(loading...)", IsEnabled = false}}}; + MenuItemDark subItem = new() {Header = subDirName.Replace("_", "__"), Items = {new MenuItem {Header = LocalizationSource.GetString("Main_LoadingEllipsis"), IsEnabled = false}}}; subItem.SubmenuOpened += (o, args) => MenuItem_RunScript_SubmenuOpened(o, args, subDirectory.FullName); item.Items.Add(subItem); } if (item.Items.Count == 0) - item.Items.Add(new MenuItem {Header = "(No scripts found!)", IsEnabled = false}); + item.Items.Add(new MenuItem {Header = LocalizationSource.GetString("Msg_NoScriptsFound"), IsEnabled = false}); } catch (Exception err) { @@ -2051,7 +2054,7 @@ private void MenuItem_RunScript_SubmenuOpened(object sender, RoutedEventArgs e, // If we're at the complete root, we need to add the "Run other script" button as well if (item.Name != "RootScriptItem") return; - var otherScripts = new MenuItem {Header = "Run _other script..."}; + var otherScripts = new MenuItem {Header = LocalizationSource.GetString("Msg_RunOtherScript")}; otherScripts.Click += MenuItem_RunOtherScript_Click; item.Items.Add(otherScripts); } @@ -2065,7 +2068,7 @@ private async void MenuItem_RunBuiltinScript_Item_Click(object sender, RoutedEve if (File.Exists(path)) await RunScript(path); else - this.ShowError("The script file doesn't exist."); + this.ShowError(LocalizationSource.GetString("Msg_ScriptFileNotExist")); } private async void MenuItem_RunOtherScript_Click(object sender, RoutedEventArgs e) @@ -2073,7 +2076,7 @@ private async void MenuItem_RunOtherScript_Click(object sender, RoutedEventArgs OpenFileDialog dlg = new OpenFileDialog(); dlg.DefaultExt = "csx"; - dlg.Filter = "Scripts (.csx)|*.csx|All files|*"; + dlg.Filter = LocalizationSource.GetString("Filter_ScriptFiles") + "|*.csx|" + LocalizationSource.GetString("Filter_AllFiles") + "|*"; if (dlg.ShowDialog() == true) { @@ -2198,7 +2201,7 @@ private void ProgressUpdater() public void StartProgressBarUpdater() { if (cts is not null) - ScriptWarning("Warning - there is another progress bar updater task running (hangs) in the background.\nRestart the application to prevent some unexpected behavior."); + ScriptWarning(LocalizationSource.GetString("Msg_ProgressBarUpdaterRunning")); cts = new CancellationTokenSource(); cToken = cts.Token; @@ -2212,8 +2215,8 @@ public async Task StopProgressBarUpdater() //async because "Wait()" blocks UI th cts.Cancel(); if (await Task.Run(() => !updater.Wait(2000))) //if ProgressUpdater isn't responding - ScriptError("Stopping the progress bar updater task is failed.\nIt's highly recommended to restart the application.", - "Script error", false); + ScriptError(LocalizationSource.GetString("Msg_StopProgressBarFailed"), + LocalizationSource.GetString("Msg_ScriptError"), false); else { cts.Dispose(); @@ -2221,8 +2224,8 @@ public async Task StopProgressBarUpdater() //async because "Wait()" blocks UI th } if (!updater.IsCompleted) - ScriptError("Stopping the progress bar updater task is failed.\nIt's highly recommended to restart the application.", - "Script error", false); + ScriptError(LocalizationSource.GetString("Msg_StopProgressBarFailed"), + LocalizationSource.GetString("Msg_ScriptError"), false); else updater.Dispose(); } @@ -2279,7 +2282,7 @@ public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEdit } else { - this.ShowError($"Can't find code entry \"{name}\".\n(probably, different game data was loaded)"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_CantFindCodeEntry"), name)); } } @@ -2310,7 +2313,7 @@ private async Task RunScriptNow(string path) { string scriptText = $"#line 1 \"{path}\"\n" + File.ReadAllText(path, Encoding.UTF8); - Dispatcher.Invoke(() => CommandBox.Text = "Running " + Path.GetFileName(path) + " ..."); + Dispatcher.Invoke(() => CommandBox.Text = string.Format(LocalizationSource.GetString("Main_RunningScript"), Path.GetFileName(path)) + " ..."); try { if (!scriptSetupTask.IsCompleted) @@ -2324,7 +2327,7 @@ private async Task RunScriptNow(string path) if (FinishedMessageEnabled) { - Dispatcher.Invoke(() => CommandBox.Text = result != null ? result.ToString() : Path.GetFileName(path) + " finished!"); + Dispatcher.Invoke(() => CommandBox.Text = result != null ? result.ToString() : string.Format(LocalizationSource.GetString("Main_ScriptFinished"), Path.GetFileName(path))); } else { @@ -2335,7 +2338,7 @@ private async Task RunScriptNow(string path) { Console.WriteLine(exc.ToString()); Dispatcher.Invoke(() => CommandBox.Text = exc.Message); - this.ShowError(exc.Message, "Script compile error"); + this.ShowError(exc.Message, LocalizationSource.GetString("Msg_ScriptCompileError")); ScriptExecutionSuccess = false; ScriptErrorMessage = exc.Message; ScriptErrorType = "CompilationErrorException"; @@ -2346,7 +2349,7 @@ private async Task RunScriptNow(string path) Console.WriteLine(exc.ToString()); Dispatcher.Invoke(() => CommandBox.Text = exc.Message); - this.ShowError(ScriptingUtil.PrettifyException(in exc), "Script error"); + this.ShowError(ScriptingUtil.PrettifyException(in exc), LocalizationSource.GetString("Msg_ScriptError")); ScriptExecutionSuccess = false; ScriptErrorMessage = exc.Message; ScriptErrorType = "Exception"; @@ -2411,7 +2414,7 @@ public string PromptSaveFile(string defaultExt, string filter) { SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = defaultExt ?? "win"; - dlg.Filter = filter ?? "GameMaker data files (.win, .unx, .ios, .droid, audiogroup*.dat)|*.win;*.unx;*.ios;*.droid;audiogroup*.dat|All files|*"; + dlg.Filter = filter ?? DataFileFilter; return dlg.ShowDialog() == true ? dlg.FileName : null; } @@ -2430,20 +2433,20 @@ public void PlayInformationSound() public void ScriptMessage(string message) { - this.ShowMessage(message, "Script message"); + this.ShowMessage(message, LocalizationSource.GetString("Msg_ScriptMessage")); } public bool ScriptQuestion(string message) { PlayInformationSound(); - return this.ShowQuestion(message, MessageBoxImage.Question, "Script Question") == MessageBoxResult.Yes; + return this.ShowQuestion(message, MessageBoxImage.Question, LocalizationSource.GetString("Msg_ScriptQuestion")) == MessageBoxResult.Yes; } public void ScriptWarning(string message) { - this.ShowWarning(message, "Script warning"); + this.ShowWarning(message, LocalizationSource.GetString("Msg_ScriptWarning")); } - public void ScriptError(string error, string title = "Error", bool SetConsoleText = true) + public void ScriptError(string error, string title = null, bool SetConsoleText = true) { - this.ShowError(error, title); + this.ShowError(error, title ?? LocalizationSource.GetString("Common_Error")); if (SetConsoleText) { SetUMTConsoleText(error); @@ -2549,7 +2552,7 @@ private void MenuItem_GitHub_Click(object sender, RoutedEventArgs e) private void MenuItem_About_Click(object sender, RoutedEventArgs e) { - this.ShowMessage("UndertaleModTool by krzys_h and the Underminers team\nVersion " + Version, "About"); + this.ShowMessage(LocalizationSource.GetString("Common_UndertaleModTool") + " by krzys_h and the Underminers team\nVersion " + Version, LocalizationSource.GetString("Main_About")); } /// From https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml.cs @@ -2584,7 +2587,7 @@ public static void OpenBrowser(string url) } catch (Exception e) { - Application.Current.MainWindow.ShowError("Failed to open browser!\n" + e); + Application.Current.MainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToOpenBrowser"), e)); } } @@ -2604,7 +2607,7 @@ public static void OpenFolder(string folder) } catch (Exception e) { - Application.Current.MainWindow.ShowError("Failed to open folder!\n" + e); + Application.Current.MainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToOpenFolder"), e)); } } @@ -2639,11 +2642,7 @@ public async void UpdateApp(SettingsWindow window) if (!Environment.Is64BitOperatingSystem) { - this.ShowWarning("Your operating system is 32-bit.\n" + - "The 32-bit (x86) version of UndertaleModTool is obsolete.\n" + - "If you wish to continue using the 32-bit version of UndertaleModTool, either use the GitHub Actions Artifacts, " + - "the Nightly builds if you don't have a GitHub account, or compile UTMT yourself.\n" + - "For any questions or more information, ask in the Underminers Discord server."); + this.ShowWarning(LocalizationSource.GetString("Msg_32BitOSWarning")); window.UpdateButtonEnabled = true; return; @@ -2652,7 +2651,7 @@ public async void UpdateApp(SettingsWindow window) string sysDriveLetter = Path.GetTempPath()[0].ToString(); if ((new DriveInfo(sysDriveLetter).AvailableFreeSpace / bytesToMB) < 500) { - this.ShowError($"Not enough space on the system drive {sysDriveLetter} - at least 500 MB is required."); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_NotEnoughSpace"), sysDriveLetter)); window.UpdateButtonEnabled = true; return; } @@ -2671,8 +2670,8 @@ public async void UpdateApp(SettingsWindow window) var result = await HttpGetAsync(baseUrl + "runs?branch=master&status=success&per_page=20"); if (result?.IsSuccessStatusCode != true) { - string errText = $"{(result is null ? "Check your internet connection." : $"HTTP error - {result.ReasonPhrase}.")}"; - this.ShowError($"Failed to fetch latest build!\n{errText}"); + string errText = $"{(result is null ? LocalizationSource.GetString("Msg_CheckInternetConnection") : string.Format(LocalizationSource.GetString("Msg_HTTPError"), result.ReasonPhrase))}"; + this.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToFetchBuild"), errText)); window.UpdateButtonEnabled = true; return; } @@ -2692,7 +2691,7 @@ public async void UpdateApp(SettingsWindow window) } if (action == null) { - this.ShowError($"Failed to find latest build!\nDetected action name - {detectedActionName}"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToFindBuild"), detectedActionName)); window.UpdateButtonEnabled = true; return; } @@ -2700,7 +2699,7 @@ public async void UpdateApp(SettingsWindow window) DateTime currDate = File.GetLastWriteTime(Path.Join(ExePath, "UndertaleModTool.exe")); DateTime lastDate = (DateTime)action["updated_at"]; if (lastDate.Subtract(currDate).TotalMinutes <= 10) - if (this.ShowQuestion("UndertaleModTool is already up to date.\nUpdate anyway?") != MessageBoxResult.Yes) + if (this.ShowQuestion(LocalizationSource.GetString("Msg_AlreadyUpToDate")) != MessageBoxResult.Yes) { window.UpdateButtonEnabled = true; return; @@ -2709,8 +2708,8 @@ public async void UpdateApp(SettingsWindow window) var result2 = await HttpGetAsync($"{baseUrl}runs/{action["id"]}/artifacts"); // Grab information about the artifacts if (result2?.IsSuccessStatusCode != true) { - string errText = $"{(result2 is null ? "Check your internet connection." : $"HTTP error - {result2.ReasonPhrase}.")}"; - this.ShowError($"Failed to fetch latest build!\n{errText}"); + string errText = $"{(result2 is null ? LocalizationSource.GetString("Msg_CheckInternetConnection") : string.Format(LocalizationSource.GetString("Msg_HTTPError"), result2.ReasonPhrase))}"; + this.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToFetchBuild"), errText)); window.UpdateButtonEnabled = true; return; } @@ -2720,9 +2719,7 @@ public async void UpdateApp(SettingsWindow window) if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) { - if (this.ShowQuestion("Detected 32-bit (x86) version of UndertaleModTool on an 64-bit operating system.\n" + - "It's highly recommended to use the 64-bit version instead.\n" + - "Do you wish to download it?") != MessageBoxResult.Yes) + if (this.ShowQuestion(LocalizationSource.GetString("Msg_32BitProcessOn64BitOS")) != MessageBoxResult.Yes) { window.UpdateButtonEnabled = true; return; @@ -2741,7 +2738,7 @@ public async void UpdateApp(SettingsWindow window) } if (artifact is null) { - this.ShowError("Failed to find the artifact!"); + this.ShowError(LocalizationSource.GetString("Msg_FailedToFindArtifact")); window.UpdateButtonEnabled = true; return; } @@ -2757,11 +2754,11 @@ public async void UpdateApp(SettingsWindow window) string downloadOutput = Path.Join(tempFolder, "Update.zip.zip"); // It's time to download; let's use a cool progress bar - scriptDialog = new("Downloading", "Downloading new version...") + scriptDialog = new(LocalizationSource.GetString("Main_Downloading"), LocalizationSource.GetString("Main_DownloadingNewVersion")) { PreventClose = true, Owner = this, - StatusText = "Downloaded MB: 0.00" + StatusText = LocalizationSource.GetString("Msg_DownloadedMB") + " 0.00" }; SetProgressBar(); @@ -2800,7 +2797,7 @@ public async void UpdateApp(SettingsWindow window) if (bytesToProgressCounter >= bytesToUpdateProgress) { bytesToProgressCounter -= bytesToUpdateProgress; - UpdateProgressStatus($"Downloaded MB: {(totalBytesDownloaded / bytesToMB).ToString("F2", CultureInfo.InvariantCulture)}"); + UpdateProgressStatus(LocalizationSource.GetString("Msg_DownloadedMB") + " " + (totalBytesDownloaded / bytesToMB).ToString("F2", CultureInfo.InvariantCulture)); } // Read next bytes @@ -2825,7 +2822,7 @@ public async void UpdateApp(SettingsWindow window) string updaterFolder = Path.Join(ExePath, "Updater"); if (!File.Exists(Path.Join(updaterFolder, "UndertaleModToolUpdater.exe"))) { - this.ShowError("Updater not found! Aborting update, report this to the devs!\nLocation checked: " + updaterFolder); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_UpdaterNotFound"), updaterFolder)); return; } @@ -2842,7 +2839,7 @@ public async void UpdateApp(SettingsWindow window) } catch (Exception ex) { - this.ShowError($"Can't copy the updater app to the temporary folder.\n{ex}"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_CantCopyUpdater"), ex)); return; } File.WriteAllText(Path.Join(updaterFolderTemp, "actualAppFolder"), ExePath); @@ -2861,7 +2858,7 @@ public async void UpdateApp(SettingsWindow window) // Move back to UI thread to perform final actions Dispatcher.Invoke(() => { - this.ShowMessage("UndertaleModTool will now close to finish the update."); + this.ShowMessage(LocalizationSource.GetString("Msg_WillCloseToUpdate")); // Invoke updater Process.Start(new ProcessStartInfo(Path.Join(updaterFolderTemp, "UndertaleModToolUpdater.exe")) @@ -2886,7 +2883,7 @@ public async void UpdateApp(SettingsWindow window) else errMsg = e.Message; - this.ShowError($"Failed to download new version of UndertaleModTool.\nError - {errMsg}."); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_FailedToDownload"), errMsg)); window.UpdateButtonEnabled = true; } } @@ -2895,7 +2892,7 @@ private async void Command_Run(object sender, ExecutedRoutedEventArgs e) { if (Data is null || FilePath is null) { - ScriptError("Nothing to run!"); + ScriptError(LocalizationSource.GetString("Msg_NothingToRun")); return; } @@ -2903,7 +2900,7 @@ private async void Command_Run(object sender, ExecutedRoutedEventArgs e) string gameExeName = Data?.GeneralInfo?.FileName?.Content; if (gameExeName is null) { - ScriptError("Null game executable name or location"); + ScriptError(LocalizationSource.GetString("Msg_NullGameExe")); return; } @@ -2915,14 +2912,14 @@ private async void Command_Run(object sender, ExecutedRoutedEventArgs e) // Project is loaded - try to find game EXE in save directory saveDataFilePath = Project.SaveDataPath; gameExePath = Paths.TryJoinVerifyWithinDirectory(Path.GetDirectoryName(saveDataFilePath), $"{gameExeName}.exe"); - if (gameExePath is null) - { - ScriptError("Failed to find valid game executable path; escaped directory"); - return; + if (gameExePath is null) + { + ScriptError(LocalizationSource.GetString("Msg_GameExePathEscaped")); + return; } if (!File.Exists(gameExePath)) { - ScriptError($"Cannot find game executable path, expected to find it at: {gameExePath}"); + ScriptError(string.Format(LocalizationSource.GetString("Msg_CantFindGameExeAt"), gameExePath)); return; } @@ -2934,25 +2931,20 @@ private async void Command_Run(object sender, ExecutedRoutedEventArgs e) // No project loaded - warn about temp run not permanently saving anything if (!WasWarnedAboutTempRun && SettingsWindow.TempRunMessageShow) { - ScriptMessage(@"WARNING: -Temp running the game does not permanently -save your changes. Please ""Save"" the game -to save your changes. Closing UndertaleModTool -without using the ""Save"" option can -result in loss of work."); + ScriptMessage(LocalizationSource.GetString("Msg_TempRunWarning")); WasWarnedAboutTempRun = true; } // Try to find game EXE gameExePath = Paths.TryJoinVerifyWithinDirectory(Path.GetDirectoryName(FilePath), $"{gameExeName}.exe"); - if (gameExePath is null) - { - ScriptError("Failed to find valid game executable path; escaped directory"); - return; + if (gameExePath is null) + { + ScriptError(LocalizationSource.GetString("Msg_GameExePathEscaped")); + return; } if (!File.Exists(gameExePath)) { - ScriptError($"Cannot find game executable path, expected to find it at: {gameExePath}"); + ScriptError(string.Format(LocalizationSource.GetString("Msg_CantFindGameExeAt"), gameExePath)); return; } @@ -2972,7 +2964,7 @@ to save your changes. Closing UndertaleModTool { if (!File.Exists(saveDataFilePath)) { - ScriptError($"Cannot find game path, expected to find it at: {saveDataFilePath}"); + ScriptError(string.Format(LocalizationSource.GetString("Msg_CantFindGamePath"), saveDataFilePath)); return; } // TODO: possibly have a setting to add debug output via @@ -2981,7 +2973,7 @@ to save your changes. Closing UndertaleModTool } else { - this.ShowWarning("Save failed, cannot run."); + this.ShowWarning(LocalizationSource.GetString("Msg_SaveFailedCannotRun")); } } private async void Command_RunSpecial(object sender, ExecutedRoutedEventArgs e) @@ -2992,41 +2984,41 @@ private async void Command_RunSpecial(object sender, ExecutedRoutedEventArgs e) bool saveOk = true; if (!Data.GeneralInfo.IsDebuggerDisabled) { - if (this.ShowQuestion("The game has the debugger enabled. Would you like to disable it so the game will run?") == MessageBoxResult.Yes) + if (this.ShowQuestion(LocalizationSource.GetString("Msg_DisableDebuggerQuestion")) == MessageBoxResult.Yes) { Data.GeneralInfo.IsDebuggerDisabled = true; if (!await DoSaveDialog()) { - this.ShowError("You must save your changes to run."); + this.ShowError(LocalizationSource.GetString("Msg_MustSaveToRun")); Data.GeneralInfo.IsDebuggerDisabled = false; return; } } else { - this.ShowError("Use the \"Run game using debugger\" option to run this game."); + this.ShowError(LocalizationSource.GetString("Msg_UseDebuggerRunOption")); return; } } else { Data.GeneralInfo.IsDebuggerDisabled = true; - if (this.ShowQuestion("Save changes first?") == MessageBoxResult.Yes) + if (this.ShowQuestion(LocalizationSource.GetString("Msg_SaveChangesFirst")) == MessageBoxResult.Yes) saveOk = await DoSaveDialog(); } if (FilePath == null) { - this.ShowWarning("The file must be saved in order to be run."); + this.ShowWarning(LocalizationSource.GetString("Msg_FileMustBeSavedToRun")); } else if (saveOk) { RuntimePicker picker = new RuntimePicker(); picker.Owner = this; var runtime = picker.Pick(FilePath, Data); - if (runtime is not null) + if (runtime is not null) { - Process.Start(new ProcessStartInfo(runtime.Path, ["-game", FilePath, "-debugoutput", Path.ChangeExtension(FilePath, ".gamelog.txt")])); + Process.Start(new ProcessStartInfo(runtime.Path, ["-game", FilePath, "-debugoutput", Path.ChangeExtension(FilePath, ".gamelog.txt")])); } } } @@ -3036,8 +3028,7 @@ private async void Command_RunDebug(object sender, ExecutedRoutedEventArgs e) if (Data == null) return; - var result = this.ShowQuestion("Are you sure that you want to run the game with GMS debugger?\n" + - "If you want to enable a debug mode in some game, then you need to use one of the scripts."); + var result = this.ShowQuestion(LocalizationSource.GetString("Msg_RunWithDebuggerQuestion")); if (result != MessageBoxResult.Yes) return; @@ -3047,7 +3038,7 @@ private async void Command_RunDebug(object sender, ExecutedRoutedEventArgs e) bool saveOk = await DoSaveDialog(true); if (FilePath == null) { - this.ShowWarning("The file must be saved in order to be run."); + this.ShowWarning(LocalizationSource.GetString("Msg_FileMustBeSavedToRun")); } else if (saveOk) { @@ -3058,7 +3049,7 @@ private async void Command_RunDebug(object sender, ExecutedRoutedEventArgs e) return; if (runtime.DebuggerPath == null) { - this.ShowError("The selected runtime does not support debugging.", "Run error"); + this.ShowError(LocalizationSource.GetString("Msg_RuntimeNoDebugging"), LocalizationSource.GetString("Msg_RunError")); return; } @@ -3111,11 +3102,11 @@ public void UpdateObjectLabel(object obj) string idString; if (foundIndex == -2) { - idString = "None"; - } - else if (foundIndex == -1) + idString = LocalizationSource.GetString("Common_None"); + } + else if (foundIndex == -1) { - idString = "N/A"; + idString = LocalizationSource.GetString("Common_NA"); } else { @@ -3123,7 +3114,7 @@ public void UpdateObjectLabel(object obj) } // Update main label - ObjectLabel.Content = $"ID: {idString}"; + ObjectLabel.Content = string.Format(LocalizationSource.GetString("Main_IDLabelFormat"), idString); // If this object is a project asset (and we're in an open project), then show/update marked for export status if (Project is not null && foundIndex >= 0 && obj is IProjectAsset { ProjectExportable: true } projectAsset) @@ -3175,7 +3166,7 @@ public void HighlightObject(object obj, bool silent = true) UndertaleResource res = obj as UndertaleResource; if (res is null) { - string msg = $"Can't highlight the object - it's null or isn't an UndertaleResource."; + string msg = LocalizationSource.GetString("Msg_CantHighlightNull"); if (silent) Debug.WriteLine(msg); else @@ -3205,7 +3196,7 @@ public void HighlightObject(object obj, bool silent = true) } catch (Exception ex) { - string msg = $"Can't highlight the object \"{objName}\".\nError - {ex.Message}"; + string msg = string.Format(LocalizationSource.GetString("Msg_CantHighlightError"), objName, ex.Message); if (silent) Debug.WriteLine(msg); else @@ -3216,7 +3207,7 @@ public void HighlightObject(object obj, bool silent = true) if (resListView is null) { - string msg = $"Can't highlight the object \"{objName}\" - element with object list not found."; + string msg = string.Format(LocalizationSource.GetString("Msg_CantHighlightListNotFound"), objName); if (silent) Debug.WriteLine(msg); else @@ -3296,7 +3287,7 @@ public void EnsureDataLoaded() { if (Data is null) { - throw new ScriptException("No data file is currently loaded!"); + throw new ScriptException(LocalizationSource.GetString("Msg_NoDataFileLoaded")); } } @@ -3312,12 +3303,12 @@ private async void MenuItem_OffsetMap_Click(object sender, RoutedEventArgs e) SaveFileDialog dlgout = new SaveFileDialog(); dlgout.DefaultExt = "txt"; - dlgout.Filter = "Text files (.txt)|*.txt|All files|*"; + dlgout.Filter = LocalizationSource.GetString("Filter_TextFiles") + "|*.txt|" + LocalizationSource.GetString("Filter_AllFiles") + "|*"; dlgout.FileName = dlg.FileName + ".offsetmap.txt"; if (dlgout.ShowDialog() == true) { - LoaderDialog dialog = new LoaderDialog("Generating", "Loading, please wait..."); + LoaderDialog dialog = new LoaderDialog(LocalizationSource.GetString("Main_Generating"), LocalizationSource.GetString("Main_LoadingPleaseWait")); dialog.Owner = this; Task t = Task.Run(() => { @@ -3337,7 +3328,7 @@ private async void MenuItem_OffsetMap_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - this.ShowError("An error occurred while trying to load:\n" + ex.Message, "Load error"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_LoadErrorDetail"), ex.Message), LocalizationSource.GetString("Msg_LoadError")); } Dispatcher.Invoke(() => @@ -3433,8 +3424,8 @@ public void CloseTab(int tabIndex, bool addDefaultTab = true) if (addDefaultTab) { - OpenInTab(new DescriptionView("Welcome to UndertaleModTool!", - "Open a data.win file to get started, then double click on the items on the left to view them")); + OpenInTab(new DescriptionView(LocalizationSource.GetString("Main_WelcomeHeading"), + LocalizationSource.GetString("Main_WelcomeDescription"))); CurrentTab = Tabs[CurrentTabIndex]; UpdateObjectLabel(CurrentTab.CurrentObject); @@ -3805,7 +3796,7 @@ private string ChooseProjectSaveFile(string sourceFilePath) { DefaultExt = "win", Filter = MainDataFileFilter, - Title = "Choose destination data file" + Title = LocalizationSource.GetString("Msg_ChooseDestinationDataFile") }; if (saveDataDialog.ShowDialog(this) != true) { @@ -3817,7 +3808,7 @@ private string ChooseProjectSaveFile(string sourceFilePath) if (Path.GetFullPath(Path.GetDirectoryName(saveFilePath)).Equals( Path.GetFullPath(Path.GetDirectoryName(sourceFilePath)), StringComparison.OrdinalIgnoreCase)) { - if (this.ShowQuestionWithCancel("The destination data file is in the same directory as the source data file. This may permanently overwrite external data files. Proceed?", MessageBoxImage.Warning, "Destination file in same directory as source file") != MessageBoxResult.Yes) + if (this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_SameDirectoryWarning"), MessageBoxImage.Warning, LocalizationSource.GetString("Msg_SameDirectoryWarningTitle")) != MessageBoxResult.Yes) { // Abort return null; @@ -3829,7 +3820,7 @@ private string ChooseProjectSaveFile(string sourceFilePath) { if (!Directory.EnumerateFileSystemEntries(Path.GetDirectoryName(saveFilePath)).Any()) { - this.ShowWarning("Currently, the destination data file's directory is empty. You will likely want to copy all other game files to the destination directory, so that external assets can be loaded correctly (both in-game and in this tool), and so the game can be started."); + this.ShowWarning(LocalizationSource.GetString("Msg_EmptyDirectoryWarning")); } } catch (Exception) @@ -3844,7 +3835,7 @@ private async void Command_NewProject(object sender, ExecutedRoutedEventArgs e) { if (Project is not null && Project.HasUnexportedAssets) { - if (this.ShowQuestionWithCancel("There are assets marked to be exported in the current project - create a new project and discard all unexported changes?", MessageBoxImage.Warning, "Project already open") != MessageBoxResult.Yes) + if (this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_UnexportedAssetsNewProject"), MessageBoxImage.Warning, LocalizationSource.GetString("Msg_ProjectAlreadyOpen")) != MessageBoxResult.Yes) { // Abort new project creation return; @@ -3858,7 +3849,7 @@ private async void Command_NewProject(object sender, ExecutedRoutedEventArgs e) { DefaultExt = "win", Filter = DataFileFilter, - Title = "Choose source data file" + Title = LocalizationSource.GetString("Msg_ChooseSourceDataFile") }; if (sourceDialog.ShowDialog(this) != true) { @@ -3874,11 +3865,11 @@ private async void Command_NewProject(object sender, ExecutedRoutedEventArgs e) } // Ask for name - string projectName = SimpleTextInput("Choose project name", "Choose a name for the new project", $"{Data.GeneralInfo?.DisplayName?.Content ?? "New"} Mod", false, true); + string projectName = SimpleTextInput(LocalizationSource.GetString("Msg_ChooseProjectName"), LocalizationSource.GetString("Msg_ChooseNewProjectName"), $"{Data.GeneralInfo?.DisplayName?.Content ?? "New"} Mod", false, true); if (projectName is null) { // Name prompt was canceled - SetUMTConsoleText("Cancelled new project creation."); + SetUMTConsoleText(LocalizationSource.GetString("Msg_CancelledNewProject")); return; } projectName = projectName.Trim(); @@ -3888,7 +3879,7 @@ private async void Command_NewProject(object sender, ExecutedRoutedEventArgs e) if (directory is null) { // Directory prompt was canceled - SetUMTConsoleText("Cancelled new project creation."); + SetUMTConsoleText(LocalizationSource.GetString("Msg_CancelledNewProject")); return; } @@ -3908,27 +3899,27 @@ private async void Command_NewProject(object sender, ExecutedRoutedEventArgs e) } catch (ProjectException ex) { - this.ShowError(ex.Message, "Failed to create new project"); - SetUMTConsoleText("Project creation failed."); + this.ShowError(ex.Message, LocalizationSource.GetString("Msg_FailedToCreateProject")); + SetUMTConsoleText(LocalizationSource.GetString("Msg_ProjectCreationFailed")); return; } catch (Exception ex) { - this.ShowError($"Error occurred when creating new project:\n{ex}"); - SetUMTConsoleText("Project creation failed."); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_ErrorCreatingProject"), ex)); + SetUMTConsoleText(LocalizationSource.GetString("Msg_ProjectCreationFailed")); return; } // Start using new project context AssignNewProject(newProjectContext); - SetUMTConsoleText($"Project \"{projectName}\" created successfully!"); + SetUMTConsoleText(string.Format(LocalizationSource.GetString("Msg_ProjectCreated"), projectName)); } private async void Command_OpenProject(object sender, ExecutedRoutedEventArgs e) { if (Project is not null && Project.HasUnexportedAssets) { - if (this.ShowQuestionWithCancel("There are assets marked to be exported in the current project - open another new project and discard all unexported changes?", MessageBoxImage.Warning, "Project already open") != MessageBoxResult.Yes) + if (this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_UnexportedAssetsOpenProject"), MessageBoxImage.Warning, LocalizationSource.GetString("Msg_ProjectAlreadyOpen")) != MessageBoxResult.Yes) { // Abort opening project return; @@ -3939,8 +3930,8 @@ private async void Command_OpenProject(object sender, ExecutedRoutedEventArgs e) OpenFileDialog openProjectDialog = new() { DefaultExt = "json", - Filter = "UndertaleModTool project files (.json)|*.json|All files|*", - Title = "Open project file" + Filter = LocalizationSource.GetString("Filter_ProjectFiles") + "|*.json|" + LocalizationSource.GetString("Filter_AllFiles") + "|*", + Title = LocalizationSource.GetString("Msg_OpenProjectFile") }; if (openProjectDialog.ShowDialog(this) != true) { @@ -3955,7 +3946,7 @@ private async void Command_OpenProject(object sender, ExecutedRoutedEventArgs e) { DefaultExt = "win", Filter = DataFileFilter, - Title = "Choose source data file" + Title = LocalizationSource.GetString("Msg_ChooseSourceDataFile") }; if (sourceDialog.ShowDialog(this) != true) { @@ -4001,12 +3992,12 @@ await Task.Run(() => catch (ProjectException ex) { newProjectContext = null; - this.ShowError(ex.Message, "Failed to load project"); + this.ShowError(ex.Message, LocalizationSource.GetString("Msg_FailedToLoadProject")); } catch (Exception ex) { newProjectContext = null; - this.ShowError($"Error occurred when loading project:\n{ex}"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_ErrorLoadingProject"), ex)); } }); IsEnabled = true; @@ -4014,13 +4005,13 @@ await Task.Run(() => // Don't assign new project context if load failed if (newProjectContext is null) { - SetUMTConsoleText("Project failed to open."); + SetUMTConsoleText(LocalizationSource.GetString("Msg_ProjectFailedToOpen")); return; } // Start using new project context AssignNewProject(newProjectContext); - SetUMTConsoleText($"Opened project \"{newProjectContext.Name}\"."); + SetUMTConsoleText(string.Format(LocalizationSource.GetString("Msg_OpenedProject"), newProjectContext.Name)); } private async void Command_SaveProject(object sender, ExecutedRoutedEventArgs e) { @@ -4047,15 +4038,15 @@ await Task.Run(() => } catch (ProjectException ex) { - this.ShowError(ex.Message, "Failed to save project"); + this.ShowError(ex.Message, LocalizationSource.GetString("Msg_FailedToSaveProject")); } catch (Exception ex) { - this.ShowError($"Error occurred when saving project:\n{ex}"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_ErrorSavingProject"), ex)); } }); IsEnabled = true; - SetUMTConsoleText(success ? "Saved project successfully." : "Project failed to save."); + SetUMTConsoleText(success ? LocalizationSource.GetString("Msg_SavedProjectSuccessfully") : LocalizationSource.GetString("Msg_ProjectFailedToSave")); } private void Command_ViewProjectAssets(object sender, ExecutedRoutedEventArgs e) @@ -4106,7 +4097,7 @@ private void Command_CloseProject(object sender, ExecutedRoutedEventArgs e) // Ensure user really wants to do this if (Project is not null && Project.HasUnexportedAssets) { - MessageBoxResult result = this.ShowQuestionWithCancel("There are assets marked to be exported in the current project. Really close?"); + MessageBoxResult result = this.ShowQuestionWithCancel(LocalizationSource.GetString("Msg_UnexportedAssetsCloseProject")); if (result != MessageBoxResult.Yes) { @@ -4116,7 +4107,7 @@ private void Command_CloseProject(object sender, ExecutedRoutedEventArgs e) UnloadProject(); UpdateObjectLabel(Selected); - SetUMTConsoleText("Project closed."); + SetUMTConsoleText(LocalizationSource.GetString("Msg_ProjectClosed")); } } diff --git a/UndertaleModTool/Program.cs b/UndertaleModTool/Program.cs index c996c600d..52d13f180 100644 --- a/UndertaleModTool/Program.cs +++ b/UndertaleModTool/Program.cs @@ -1,4 +1,4 @@ -#pragma warning disable CA1416 // Validate platform compatibility +#pragma warning disable CA1416 // Validate platform compatibility using System; using System.Collections.Generic; @@ -12,6 +12,7 @@ using System.Threading.Tasks; using System.Windows; using Microsoft.CodeAnalysis; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -39,7 +40,7 @@ public static void Main() catch (Exception e) { File.WriteAllText(Path.Join(GetExecutableDirectory(), "crash.txt"), e.ToString()); - MessageBox.Show(e.ToString()); + MessageBox.Show(string.Format(LocalizationSource.GetString("Msg_UnhandledException"), e.ToString())); } } private static void GlobalUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) diff --git a/UndertaleModTool/ScriptingFunctions.cs b/UndertaleModTool/ScriptingFunctions.cs index c9e5a5193..0efb44999 100644 --- a/UndertaleModTool/ScriptingFunctions.cs +++ b/UndertaleModTool/ScriptingFunctions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.IO; using System.Threading; @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Scripting; using System.IO.Pipes; using System.Threading.Tasks; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -27,19 +28,19 @@ public bool RunUMTScript(string path) // By Grossley if (!File.Exists(path)) { - ScriptError(path + " does not exist!"); + ScriptError(string.Format(LocalizationSource.GetString("Msg_ScriptDoesNotExist"), path)); return false; } RunScript(path).GetAwaiter().GetResult(); if (!ScriptExecutionSuccess) - ScriptError("An error of type \"" + ScriptErrorType + "\" occurred. The error is:\n\n" + ScriptErrorMessage, ScriptErrorType); + ScriptError(string.Format(LocalizationSource.GetString("Msg_ScriptErrorWithType"), ScriptErrorType, ScriptErrorMessage), ScriptErrorType); return ScriptExecutionSuccess; } public void InitializeScriptDialog() { if (scriptDialog == null) { - scriptDialog = new LoaderDialog("Script in progress...", "Please wait..."); + scriptDialog = new LoaderDialog(LocalizationSource.GetString("Dialog_ScriptInProgress"), LocalizationSource.GetString("Msg_ScriptPleaseWait")); scriptDialog.Owner = this; scriptDialog.PreventClose = true; } @@ -49,7 +50,7 @@ public bool LintUMTScript(string path) // By Grossley if (!File.Exists(path)) { - ScriptError(path + " does not exist!"); + ScriptError(string.Format(LocalizationSource.GetString("Msg_ScriptDoesNotExist"), path)); return false; } try @@ -60,7 +61,7 @@ public bool LintUMTScript(string path) } catch (CompilationErrorException exc) { - ScriptError(exc.Message, "Script compile error"); + ScriptError(exc.Message, LocalizationSource.GetString("Dialog_ScriptCompileError")); ScriptExecutionSuccess = false; ScriptErrorMessage = exc.Message; ScriptErrorType = "CompilationErrorException"; diff --git a/UndertaleModTool/Settings.cs b/UndertaleModTool/Settings.cs index 611670140..5188a87d1 100644 --- a/UndertaleModTool/Settings.cs +++ b/UndertaleModTool/Settings.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Windows; using Underanalyzer.Decompiler; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -57,6 +58,8 @@ public class Settings public bool RecompileAllCodeSourcesOnProjectSave { get; set; } = false; + public string Language { get; set; } = "en"; + public static Settings Instance { get; private set; } public static JsonSerializerOptions JsonOptions = new() @@ -125,7 +128,7 @@ public static void Load() } catch (Exception e) { - MessageBox.Show($"Failed to load settings.json! Using default values.\n{e.Message}"); + MessageBox.Show(string.Format(LocalizationSource.GetString("Msg_FailedLoadSettings"), e.Message)); new Settings() { DecompilerSettings = existingDecompilerSettings ?? new() }; } } @@ -141,7 +144,7 @@ public static void Save() } catch (Exception e) { - MessageBox.Show($"Failed to save settings.json!\n{e.Message}"); + MessageBox.Show(string.Format(LocalizationSource.GetString("Msg_FailedSaveSettings"), e.Message)); } } } diff --git a/UndertaleModTool/Tab.cs b/UndertaleModTool/Tab.cs index e8fff7e5f..4d3672541 100644 --- a/UndertaleModTool/Tab.cs +++ b/UndertaleModTool/Tab.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; @@ -15,6 +15,7 @@ using System.Windows; using System.Windows.Media; using System.Windows.Threading; +using UndertaleModTool.Localization; using static UndertaleModLib.Models.UndertaleRoom; namespace UndertaleModTool @@ -56,7 +57,7 @@ public object CurrentObject /// The tab title. /// "Untitled" by default. - public string TabTitle { get; set; } = "Untitled"; + public string TabTitle { get; set; } = LocalizationSource.GetString("Common_Untitled"); /// Whether the title of this tab is autogenerated. public bool IsCustomTitle { get; set; } @@ -109,7 +110,7 @@ public static string GetTitleForObject(object obj) { if (view.Heading.Contains("Welcome")) { - title = "Welcome!"; + title = LocalizationSource.GetString("Common_Welcome"); } else { @@ -122,31 +123,31 @@ public static string GetTitleForObject(object obj) string header = obj switch { - UndertaleAudioGroup => "Audio Group", - UndertaleSound => "Sound", - UndertaleSprite => "Sprite", - UndertaleBackground => "Background", - UndertalePath => "Path", - UndertaleScript => "Script", - UndertaleShader => "Shader", - UndertaleFont => "Font", - UndertaleTimeline => "Timeline", - UndertaleGameObject => "Game Object", - UndertaleRoom => "Room", - UndertaleExtension => "Extension", - UndertaleTexturePageItem => "Texture Page Item", - UndertaleCode => "Code", - UndertaleVariable => "Variable", - UndertaleFunction => "Function", - UndertaleCodeLocals => "Code Locals", - UndertaleEmbeddedTexture => "Embedded Texture", - UndertaleEmbeddedAudio => "Embedded Audio", - UndertaleTextureGroupInfo => "Texture Group Info", - UndertaleEmbeddedImage => "Embedded Image", - UndertaleSequence => "Sequence", - UndertaleAnimationCurve => "Animation Curve", - UndertaleParticleSystem => "Particle System", - UndertaleParticleSystemEmitter => "Particle System Emitter", + UndertaleAudioGroup => LocalizationSource.GetString("TabTitle_AudioGroup"), + UndertaleSound => LocalizationSource.GetString("TabTitle_Sound"), + UndertaleSprite => LocalizationSource.GetString("TabTitle_Sprite"), + UndertaleBackground => LocalizationSource.GetString("TabTitle_Background"), + UndertalePath => LocalizationSource.GetString("TabTitle_Path"), + UndertaleScript => LocalizationSource.GetString("TabTitle_Script"), + UndertaleShader => LocalizationSource.GetString("TabTitle_Shader"), + UndertaleFont => LocalizationSource.GetString("TabTitle_Font"), + UndertaleTimeline => LocalizationSource.GetString("TabTitle_Timeline"), + UndertaleGameObject => LocalizationSource.GetString("TabTitle_GameObject"), + UndertaleRoom => LocalizationSource.GetString("TabTitle_Room"), + UndertaleExtension => LocalizationSource.GetString("TabTitle_Extension"), + UndertaleTexturePageItem => LocalizationSource.GetString("TabTitle_TexturePageItem"), + UndertaleCode => LocalizationSource.GetString("TabTitle_Code"), + UndertaleVariable => LocalizationSource.GetString("TabTitle_Variable"), + UndertaleFunction => LocalizationSource.GetString("TabTitle_Function"), + UndertaleCodeLocals => LocalizationSource.GetString("TabTitle_CodeLocals"), + UndertaleEmbeddedTexture => LocalizationSource.GetString("TabTitle_EmbeddedTexture"), + UndertaleEmbeddedAudio => LocalizationSource.GetString("TabTitle_EmbeddedAudio"), + UndertaleTextureGroupInfo => LocalizationSource.GetString("TabTitle_TextureGroupInfo"), + UndertaleEmbeddedImage => LocalizationSource.GetString("TabTitle_EmbeddedImage"), + UndertaleSequence => LocalizationSource.GetString("TabTitle_Sequence"), + UndertaleAnimationCurve => LocalizationSource.GetString("TabTitle_AnimationCurve"), + UndertaleParticleSystem => LocalizationSource.GetString("TabTitle_ParticleSystem"), + UndertaleParticleSystemEmitter => LocalizationSource.GetString("TabTitle_ParticleSystemEmitter"), _ => null }; @@ -161,7 +162,7 @@ public static string GetTitleForObject(object obj) if (stringFirstLine is not null) { if (stringFirstLine.Length == 0) - stringFirstLine = "(empty string)"; + stringFirstLine = LocalizationSource.GetString("Common_EmptyString"); else { int stringLength = StringTitleConverter.NewLineRegex.Match(stringFirstLine).Index; @@ -170,31 +171,31 @@ public static string GetTitleForObject(object obj) } } - title = "String - " + stringFirstLine; + title = LocalizationSource.GetString("TabTitle_String") + " - " + stringFirstLine; } else if (obj is UndertaleExtensionFile file) { - title = $"Extension file - {file.Filename}"; + title = string.Format(LocalizationSource.GetString("TabTitle_ExtensionFile"), file.Filename); } else if (obj is UndertaleExtensionFunction func) { - title = $"Extension function - {func.Name}"; + title = string.Format(LocalizationSource.GetString("TabTitle_ExtensionFunction"), func.Name); } else if (obj is UndertaleChunkVARI) { - title = "Variables Overview"; + title = LocalizationSource.GetString("TabTitle_VariablesOverview"); } else if (obj is GeneralInfoEditor) { - title = "General Info"; + title = LocalizationSource.GetString("TabTitle_GeneralInfo"); } else if (obj is GlobalInitEditor) { - title = "Global Init"; + title = LocalizationSource.GetString("TabTitle_GlobalInit"); } else if (obj is GameEndEditor) { - title = "Game End"; + title = LocalizationSource.GetString("TabTitle_GameEnd"); } else { @@ -288,7 +289,7 @@ public void SaveTabContentState(bool overwrite = false) } catch { - mainWindow.ShowWarning("The last tab content state can't be saved - \"UserControl\" is not found."); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_TabContentStateSaveFail")); return; } @@ -600,14 +601,14 @@ public void RestoreTabContentState() } catch { - mainWindow.ShowWarning("The last tab content state can't be restored - \"UserControl\" is not found."); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_TabContentStateRestoreFail")); return; } ScrollViewer mainScrollViewer = MainWindow.GetNearestParent(dataEditor); if (mainScrollViewer is null) { - mainWindow.ShowWarning("The last tab content state can't be restored - \"ScrollViewer\" is not found."); + mainWindow.ShowWarning(LocalizationSource.GetString("Msg_TabContentStateRestoreFailScrollViewer")); return; } mainScrollViewer.ScrollToVerticalOffset(LastContentState.MainScrollPosition); diff --git a/UndertaleModTool/UndertaleModTool.csproj b/UndertaleModTool/UndertaleModTool.csproj index 827f50e30..e4c5a8df8 100644 --- a/UndertaleModTool/UndertaleModTool.csproj +++ b/UndertaleModTool/UndertaleModTool.csproj @@ -15,8 +15,8 @@ true UndertaleModTool.Program en - en - en + en;zh-CN + en;zh-CN 1591 app.manifest @@ -35,6 +35,12 @@ + + + PublicResXFileCodeGenerator + Strings.Designer.cs + + @@ -114,4 +120,11 @@ true + + + True + True + Strings.resx + + diff --git a/UndertaleModTool/WindowExtensions.cs b/UndertaleModTool/WindowExtensions.cs index 97b7b4689..58baff550 100644 --- a/UndertaleModTool/WindowExtensions.cs +++ b/UndertaleModTool/WindowExtensions.cs @@ -4,6 +4,7 @@ using System.Text.Json.Serialization; using System.Windows; using System.Windows.Interop; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -20,8 +21,9 @@ public static class MessageBoxExtensions /// A that specifies the title bar caption to display. /// or if /// the was cancelled. - public static MessageBoxResult ShowMessage(this Window window, string messageBoxText, string title = "UndertaleModTool") + public static MessageBoxResult ShowMessage(this Window window, string messageBoxText, string title = null) { + title ??= LocalizationSource.GetString("Common_UndertaleModTool"); return ShowCore(window, messageBoxText, title, MessageBoxButton.OK, MessageBoxImage.Information); } @@ -34,8 +36,9 @@ public static MessageBoxResult ShowMessage(this Window window, string messageBox /// A that specifies the title bar caption to display. /// or depending on the users' answer. /// if the was cancelled. - public static MessageBoxResult ShowQuestion(this Window window, string messageBoxText, MessageBoxImage icon = MessageBoxImage.Question, string title = "UndertaleModTool") + public static MessageBoxResult ShowQuestion(this Window window, string messageBoxText, MessageBoxImage icon = MessageBoxImage.Question, string title = null) { + title ??= LocalizationSource.GetString("Common_UndertaleModTool"); return ShowCore(window, messageBoxText, title, MessageBoxButton.YesNo, icon); } @@ -47,8 +50,9 @@ public static MessageBoxResult ShowQuestion(this Window window, string messageBo /// The to display. /// A that specifies the title bar caption to display. /// , or depending on the users' answer. - public static MessageBoxResult ShowQuestionWithCancel(this Window window, string messageBoxText, MessageBoxImage icon = MessageBoxImage.Question, string title = "UndertaleModTool") + public static MessageBoxResult ShowQuestionWithCancel(this Window window, string messageBoxText, MessageBoxImage icon = MessageBoxImage.Question, string title = null) { + title ??= LocalizationSource.GetString("Common_UndertaleModTool"); return ShowCore(window, messageBoxText, title, MessageBoxButton.YesNoCancel, icon); } @@ -60,8 +64,9 @@ public static MessageBoxResult ShowQuestionWithCancel(this Window window, string /// A that specifies the title bar caption to display. /// or if /// the was cancelled. - public static MessageBoxResult ShowWarning(this Window window, string messageBoxText, string title = "Warning") + public static MessageBoxResult ShowWarning(this Window window, string messageBoxText, string title = null) { + title ??= LocalizationSource.GetString("Common_Warning"); return ShowCore(window, messageBoxText, title, MessageBoxButton.OK, MessageBoxImage.Warning); } @@ -73,8 +78,9 @@ public static MessageBoxResult ShowWarning(this Window window, string messageBox /// A that specifies the title bar caption to display. /// or if /// the was cancelled. - public static MessageBoxResult ShowError(this Window window, string messageBoxText, string title = "Error") + public static MessageBoxResult ShowError(this Window window, string messageBoxText, string title = null) { + title ??= LocalizationSource.GetString("Common_Error"); return ShowCore(window, messageBoxText, title, MessageBoxButton.OK, MessageBoxImage.Error); } diff --git a/UndertaleModTool/Windows/ClickableTextOutput.xaml b/UndertaleModTool/Windows/ClickableTextOutput.xaml index bdb6dd16e..1da561e0b 100644 --- a/UndertaleModTool/Windows/ClickableTextOutput.xaml +++ b/UndertaleModTool/Windows/ClickableTextOutput.xaml @@ -1,15 +1,16 @@ - + Title="{loc:Loc ClickableOutput_WindowTitle}" Height="540" Width="536" MinHeight="160" MinWidth="210"> - + @@ -18,7 +19,7 @@ - diff --git a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs index 37e0395d2..323969d29 100644 --- a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs +++ b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -13,6 +13,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; +using UndertaleModTool.Localization; using static UndertaleModTool.UndertaleCodeEditor; namespace UndertaleModTool.Windows @@ -98,14 +99,14 @@ public void GenerateResults() if (failedCount == 1) { - errorStr = "There is 1 code entry that encountered an error while searching:"; + errorStr = LocalizationSource.GetString("Msg_OneCodeEntryError"); errLines.Add(new Run(errorStr) { FontWeight = FontWeights.Bold }); errLines.Add(new LineBreak()); errLines.Add(new Run(failedList.First())); } else { - errorStr = $"There are {failedCount} code entries that encountered an error while searching:"; + errorStr = string.Format(LocalizationSource.GetString("Msg_MultipleCodeEntriesError"), failedCount); errLines.Add(new Run(errorStr) { FontWeight = FontWeights.Bold }); errLines.Add(new LineBreak()); @@ -131,14 +132,14 @@ public void GenerateResults() } int resCount = resultsDict.Count; - Paragraph headerPara = new(new Run($"{ResultsCount} results in {resCount} code entries for \"{Query}\".")) { FontWeight = FontWeights.Bold }; + Paragraph headerPara = new(new Run(string.Format(LocalizationSource.GetString("Msg_ResultsInCodeEntries"), ResultsCount, resCount, Query))) { FontWeight = FontWeights.Bold }; headerPara.Inlines.Add(new LineBreak()); doc.Blocks.Add(headerPara); int totalLineCount = resultsDict.Select(x => x.Value.Count).Sum(); bool tooManyLines = totalLineCount > 10000; if (tooManyLines) - mainWindow.ShowWarning($"There are too many code lines to display ({totalLineCount}), so the line numbers aren't clickable."); + mainWindow.ShowWarning(string.Format(LocalizationSource.GetString("Msg_TooManyCodeLines"), totalLineCount)); foreach (KeyValuePair> result in resultsDict) { @@ -146,7 +147,7 @@ public void GenerateResults() Paragraph resPara = new(); Underline resHeader = new(); - resHeader.Inlines.Add(new Run("Results in ")); + resHeader.Inlines.Add(new Run(LocalizationSource.GetString("Msg_ResultsIn") + " ")); resHeader.Inlines.Add(new Hyperlink(new Run(result.Key))); resHeader.Inlines.Add(new Run(":")); resHeader.Inlines.Add(new LineBreak()); @@ -157,7 +158,7 @@ public void GenerateResults() { if (!tooManyLines) { - Hyperlink lineLink = new(new Run($"Line {lineNum}") + Hyperlink lineLink = new(new Run(string.Format(LocalizationSource.GetString("Msg_Line"), lineNum)) { Tag = result.Key // code entry name }); @@ -167,7 +168,7 @@ public void GenerateResults() } else { - Run lineRun = new($"Line {lineNum}: {codeLine}"); + Run lineRun = new(string.Format(LocalizationSource.GetString("Msg_LineWithContent"), lineNum, codeLine)); resPara.Inlines.Add(lineRun); } @@ -225,9 +226,10 @@ private void OutTextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) || String.IsNullOrEmpty(linkRun.Text)) return; - if (linkRun.Text.StartsWith("Line ")) + string linePrefix = LocalizationSource.GetString("Msg_LinePrefix"); + if (linkRun.Text.StartsWith(linePrefix)) { - if (!Int32.TryParse(linkRun.Text[5..], out int lineNum)) + if (!Int32.TryParse(linkRun.Text[linePrefix.Length..], out int lineNum)) { e.Handled = true; return; diff --git a/UndertaleModTool/Windows/DebugDataDialog.xaml b/UndertaleModTool/Windows/DebugDataDialog.xaml index feff1b187..cdbd031d3 100644 --- a/UndertaleModTool/Windows/DebugDataDialog.xaml +++ b/UndertaleModTool/Windows/DebugDataDialog.xaml @@ -1,12 +1,13 @@ - + Title="{loc:Loc Dialog_DebugData}" Height="Auto" Width="400" SizeToContent="Height" WindowStartupLocation="CenterOwner"> @@ -19,14 +20,14 @@ - The debugger is enabled. Do you want to generate debugger data? - Decompiled - Decompile whole source code when saving for display in the debugger - NOTE: no breakpoint support yet! Except for the script entry point - Disassembly, partial breakpoints - Full disassembly, but you can set breakpoints only on B, BR, BT, POP, POPZ, RET and EXIT. This is still a lot more than that poor GM:S debugger was designed to handle, so allow up to 5 minutes for it to load the data file (yes, seriously) - Disassembly, full breakpoints - Gave up after like 15 minutes of waiting, don't even try this - No debug data + + + + + + + + + diff --git a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml index 805652d95..e3e75d3fb 100644 --- a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml +++ b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml @@ -1,12 +1,13 @@ - @@ -18,7 +19,7 @@ - + @@ -30,11 +31,11 @@ - - @@ -42,7 +43,7 @@ - @@ -70,9 +71,9 @@ - - - + + + - + diff --git a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml.cs b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml.cs index 08668c282..11b872ad5 100644 --- a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml.cs +++ b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesResults.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -18,6 +18,7 @@ using UndertaleModLib; using UndertaleModLib.Models; using UndertaleModLib.Util; +using UndertaleModTool.Localization; namespace UndertaleModTool.Windows { @@ -52,18 +53,18 @@ public FindReferencesResults(object sourceObj, UndertaleData data, Dictionary tileTuple) - sourceObjName = $"Tile {tileTuple.Item2.ID} of {tileTuple.Item1.Name.Content}"; + sourceObjName = string.Format(LocalizationSource.GetString("Msg_TileOfFormat"), tileTuple.Item2.ID, tileTuple.Item1.Name.Content); else sourceObjName = sourceObj.GetType().Name; this.sourceObjName = sourceObjName; - Title = $"The references of game asset \"{sourceObjName}\""; - label.Text = $"The search results for the game asset\n\"{sourceObjName}\"."; + Title = string.Format(LocalizationSource.GetString("Dialog_ReferencesOfAsset"), sourceObjName); + label.Text = string.Format(LocalizationSource.GetString("Msg_ReferencesOfAsset"), sourceObjName); if (results is null) ResultsTree.Background = new VisualBrush(new Label() { - Content = "No references found.", + Content = LocalizationSource.GetString("Msg_NoReferencesFound"), FontSize = 16 }) { Stretch = Stretch.None }; else @@ -75,13 +76,13 @@ public FindReferencesResults(UndertaleData data, Dictionary this.data = data; - Title = "The unreferenced game assets"; - label.Text = "The search results for the unreferenced game assets."; + Title = LocalizationSource.GetString("Dialog_UnreferencedGameAssets"); + label.Text = LocalizationSource.GetString("Msg_UnreferencedGameAssets"); if (results is null) ResultsTree.Background = new VisualBrush(new Label() { - Content = "No unreferenced assets found.", + Content = LocalizationSource.GetString("Msg_NoUnreferencedAssetsFound"), FontSize = 16 }) { Stretch = Stretch.None }; @@ -159,7 +160,7 @@ private void ExportResults() { if (data.FORM is null) { - this.ShowError($"The object references are stale - a different game data was loaded."); + this.ShowError(LocalizationSource.GetString("Msg_ObjectReferencesAreStale")); return; } @@ -199,7 +200,7 @@ private void ExportResults() if (sb.Length == initContent.Length) { - this.ShowError("No results to export."); + this.ShowError(LocalizationSource.GetString("Msg_NoResultsToExport")); return; } sb.Remove(sb.Length - 2, 2); @@ -215,22 +216,22 @@ private void ExportResults() ? "unreferenced_assets.txt" : $"references_of_asset_{sourceObjName}.txt"); if (filePath is null) { - this.ShowError("Failed to choose good output file name; directory escaped."); + this.ShowError(LocalizationSource.GetString("Msg_FailedToChooseOutputFileName")); return; } if (File.Exists(filePath)) - if (this.ShowQuestion($"File \"{filePath}\" exists.\nOverwrite?") == MessageBoxResult.No) + if (this.ShowQuestion(string.Format(LocalizationSource.GetString("Msg_FileExistsOverwrite"), filePath)) == MessageBoxResult.No) return; File.WriteAllText(filePath, sb.ToString()); - this.ShowMessage($"The results were successfully saved at path\n\"{filePath}\"."); + this.ShowMessage(string.Format(LocalizationSource.GetString("Msg_ResultsSavedSuccessfully"), filePath)); } private void Open(object obj, bool inNewTab = false) { if (data.FORM is null) { - this.ShowError($"The object reference is stale - a different game data was loaded."); + this.ShowError(LocalizationSource.GetString("Msg_ObjectReferenceIsStale")); return; } @@ -251,7 +252,7 @@ private void Open(object obj, bool inNewTab = false) { if (!mainWindow.HasEditorForAsset(inst[^1])) { - this.ShowError("The type of this object reference doesn't have an editor/viewer."); + this.ShowError(LocalizationSource.GetString("Msg_ObjectReferenceNoEditor")); return; } } @@ -260,14 +261,14 @@ private void Open(object obj, bool inNewTab = false) { if (!mainWindow.HasEditorForAsset(obj)) { - this.ShowError("The type of this object reference doesn't have an editor/viewer."); + this.ShowError(LocalizationSource.GetString("Msg_ObjectReferenceNoEditor")); return; } mainWindow.Focus(); mainWindow.ChangeSelection(obj, inNewTab); - } + } } private void MenuItem_ContextMenuOpened(object sender, RoutedEventArgs e) @@ -276,7 +277,7 @@ private void MenuItem_ContextMenuOpened(object sender, RoutedEventArgs e) foreach (var item in menu.Items) { var menuItem = item as MenuItem; - if ((menuItem.Header as string) == "Find all references") + if ((menuItem.Header as string) == LocalizationSource.GetString("Menu_FindAllReferences")) { Type objType = menu.DataContext is object[] inst ? inst[^1].GetType() : menu.DataContext.GetType(); @@ -311,8 +312,7 @@ private void MenuItem_CopyName_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - this.ShowError("Can't copy the item name to clipboard due to this error:\n" + - ex.Message + ".\nYou probably should try again."); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_CantCopyToClipboard"), ex.Message)); } } } @@ -328,7 +328,7 @@ private void MenuItem_FindAllReferences_Click(object sender, RoutedEventArgs e) if (res is null) { - this.ShowError("The selected object is not an \"UndertaleResource\"."); + this.ShowError(LocalizationSource.GetString("Msg_SelectedObjectNotUndertaleResource")); return; } @@ -340,8 +340,7 @@ private void MenuItem_FindAllReferences_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - this.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_ObjectReferencesWindowError"), ex)); } finally { diff --git a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml index 5dd39a301..52657ed4a 100644 --- a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml +++ b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml @@ -1,11 +1,12 @@ - @@ -15,7 +16,7 @@ - diff --git a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml.cs b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml.cs index d42ef67ee..d3f3e5dd8 100644 --- a/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml.cs +++ b/UndertaleModTool/Windows/FindReferencesTypesDialog/FindReferencesTypesDialog.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -14,6 +14,7 @@ using System.Windows.Shapes; using UndertaleModLib; using UndertaleModLib.Models; +using UndertaleModTool.Localization; namespace UndertaleModTool.Windows { @@ -49,7 +50,7 @@ public FindReferencesTypesDialog(UndertaleResource obj, UndertaleData data) if (data.GeneralInfo is null) { - this.ShowError("Cannot determine GameMaker version - \"General Info\" is null."); + this.ShowError(LocalizationSource.GetString("Msg_GeneralInfoNull")); dontShowWindow = true; return; } @@ -57,7 +58,7 @@ public FindReferencesTypesDialog(UndertaleResource obj, UndertaleData data) (Type, string)[] sourceTypes = UndertaleResourceReferenceMap.GetTypeMapForVersion(obj.GetType(), data); if (sourceTypes is null) { - this.ShowError($"Cannot get the source types for object of type \"{obj.GetType()}\"."); + this.ShowError(string.Format(LocalizationSource.GetString("Msg_CannotGetSourceTypes"), obj.GetType())); dontShowWindow = true; return; } @@ -81,7 +82,7 @@ public FindReferencesTypesDialog(UndertaleData data) if (data.GeneralInfo is null) { - this.ShowError("Cannot determine GameMaker version - \"General Info\" is null."); + this.ShowError(LocalizationSource.GetString("Msg_GeneralInfoNull")); dontShowWindow = true; return; } @@ -138,7 +139,7 @@ private async void SearchButton_Click(object sender, RoutedEventArgs e) if (typesList.Count == 0) { - this.ShowError("At least one type should be selected."); + this.ShowError(LocalizationSource.GetString("Msg_AtLeastOneTypeSelected")); return; } @@ -151,8 +152,7 @@ private async void SearchButton_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_ObjectReferencesWindowError"), ex)); dialog?.Close(); } @@ -172,16 +172,14 @@ private async void SearchButton_Click(object sender, RoutedEventArgs e) if (typesDict.Count == 0) { - this.ShowError("At least one type should be selected."); + this.ShowError(LocalizationSource.GetString("Msg_AtLeastOneTypeSelected")); return; } if (typesDict.Count > 1 && typesDict.ContainsKey(typeof(UndertaleString)) && data.Strings.Count > 5000) { - var res = this.ShowQuestion("You have selected the \"Strings\" when there are a lot of strings.\n" + - "That could make the search process noticeably longer.\n" + - "Do you want to proceed?"); + var res = this.ShowQuestion(LocalizationSource.GetString("Msg_ManyStringsSelectedWarning")); if (res != MessageBoxResult.Yes) return; } @@ -196,8 +194,7 @@ private async void SearchButton_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - mainWindow.ShowError("An error occurred in the object references related window.\n" + - $"Please report this on GitHub.\n\n{ex}"); + mainWindow.ShowError(string.Format(LocalizationSource.GetString("Msg_ObjectReferencesWindowError"), ex)); dialog?.Close(); } } diff --git a/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMap.cs b/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMap.cs index f6190144b..1f4d22faa 100644 --- a/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMap.cs +++ b/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMap.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using UndertaleModLib; using UndertaleModLib.Models; +using UndertaleModTool.Localization; namespace UndertaleModTool.Windows { @@ -27,7 +28,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleGameObject), "Game objects") + (typeof(UndertaleGameObject), LocalizationSource.GetString("RefType_GameObjects")) } }, new TypesForVersion @@ -35,9 +36,9 @@ public static class UndertaleResourceReferenceMap Version = (2, 0, 0), Types = new[] { - (typeof(UndertaleRoom.Tile), "Room tiles"), - (typeof(UndertaleRoom.SpriteInstance), "Room sprite instances"), - (typeof(UndertaleRoom.Layer.LayerBackgroundData), "Room background layers") + (typeof(UndertaleRoom.Tile), LocalizationSource.GetString("RefType_RoomTiles")), + (typeof(UndertaleRoom.SpriteInstance), LocalizationSource.GetString("RefType_RoomSpriteInstances")), + (typeof(UndertaleRoom.Layer.LayerBackgroundData), LocalizationSource.GetString("RefType_RoomBackgroundLayers")) } }, new TypesForVersion @@ -45,7 +46,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 2, 1), Types = new[] { - (typeof(UndertaleTextureGroupInfo), "Texture groups") + (typeof(UndertaleTextureGroupInfo), LocalizationSource.GetString("RefType_TextureGroups")) } }, new TypesForVersion @@ -53,7 +54,7 @@ public static class UndertaleResourceReferenceMap Version = (2023, 2, 0), Types = new[] { - (typeof(UndertaleParticleSystemEmitter), "Particle system emitters") + (typeof(UndertaleParticleSystemEmitter), LocalizationSource.GetString("RefType_ParticleSystemEmitters")) } } } @@ -67,8 +68,8 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleRoom.Background), "Room backgrounds"), - (typeof(UndertaleRoom.Tile), "Room tiles") + (typeof(UndertaleRoom.Background), LocalizationSource.GetString("RefType_RoomBackgrounds")), + (typeof(UndertaleRoom.Tile), LocalizationSource.GetString("RefType_RoomTiles")) } }, new TypesForVersion @@ -78,7 +79,7 @@ public static class UndertaleResourceReferenceMap { (typeof(UndertaleRoom.Background), null), (typeof(UndertaleRoom.Tile), null), - (typeof(UndertaleRoom.Layer), "Room tile layers") + (typeof(UndertaleRoom.Layer), LocalizationSource.GetString("RefType_RoomTileLayers")) } }, new TypesForVersion @@ -86,7 +87,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 2, 1), Types = new[] { - (typeof(UndertaleTextureGroupInfo), "Texture groups") + (typeof(UndertaleTextureGroupInfo), LocalizationSource.GetString("RefType_TextureGroups")) } } } @@ -100,7 +101,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleTexturePageItem), "Texture page items") + (typeof(UndertaleTexturePageItem), LocalizationSource.GetString("RefType_TexturePageItems")) } }, new TypesForVersion @@ -108,7 +109,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 2, 1), Types = new[] { - (typeof(UndertaleTextureGroupInfo), "Texture groups") + (typeof(UndertaleTextureGroupInfo), LocalizationSource.GetString("RefType_TextureGroups")) } }, } @@ -122,9 +123,9 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleSprite), "Sprites"), - (typeof(UndertaleBackground), "Backgrounds"), - (typeof(UndertaleFont), "Fonts") + (typeof(UndertaleSprite), LocalizationSource.GetString("RefType_Sprites")), + (typeof(UndertaleBackground), LocalizationSource.GetString("RefType_Backgrounds")), + (typeof(UndertaleFont), LocalizationSource.GetString("RefType_Fonts")) } }, new TypesForVersion() @@ -132,8 +133,8 @@ public static class UndertaleResourceReferenceMap Version = (2, 0, 0), Types = new[] { - (typeof(UndertaleBackground), "Tile sets"), - (typeof(UndertaleEmbeddedImage), "Embedded images") + (typeof(UndertaleBackground), LocalizationSource.GetString("RefType_TileSets")), + (typeof(UndertaleEmbeddedImage), LocalizationSource.GetString("RefType_EmbeddedImages")) } } } @@ -147,26 +148,26 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleBackground), "Backgrounds"), - (typeof(UndertaleCode), "Code entries (name and contents)"), - (typeof(UndertaleVariable), "Variables"), - (typeof(UndertaleFunction), "Functions"), - (typeof(UndertaleSound), "Sounds"), - (typeof(UndertaleAudioGroup), "Audio groups"), - (typeof(UndertaleSprite), "Sprites"), - (typeof(UndertaleExtension), "Extensions"), - (typeof(UndertaleExtensionFile), "Extension files"), - (typeof(UndertaleExtensionOption), "Extension options"), - (typeof(UndertaleExtensionFunction), "Extension functions"), - (typeof(UndertaleFont), "Fonts"), - (typeof(UndertaleGameObject), "Game objects"), - (typeof(UndertaleGeneralInfo), "General info"), - (typeof(UndertaleOptions.Constant), "Game options constants"), - (typeof(UndertalePath), "Paths"), - (typeof(UndertaleRoom), "Rooms"), - (typeof(UndertaleScript), "Scripts"), - (typeof(UndertaleShader), "Shaders"), - (typeof(UndertaleTimeline), "Timelines") + (typeof(UndertaleBackground), LocalizationSource.GetString("RefType_Backgrounds")), + (typeof(UndertaleCode), LocalizationSource.GetString("RefType_CodeEntriesNameAndContents")), + (typeof(UndertaleVariable), LocalizationSource.GetString("RefType_Variables")), + (typeof(UndertaleFunction), LocalizationSource.GetString("RefType_Functions")), + (typeof(UndertaleSound), LocalizationSource.GetString("RefType_Sounds")), + (typeof(UndertaleAudioGroup), LocalizationSource.GetString("RefType_AudioGroups")), + (typeof(UndertaleSprite), LocalizationSource.GetString("RefType_Sprites")), + (typeof(UndertaleExtension), LocalizationSource.GetString("RefType_Extensions")), + (typeof(UndertaleExtensionFile), LocalizationSource.GetString("RefType_ExtensionFiles")), + (typeof(UndertaleExtensionOption), LocalizationSource.GetString("RefType_ExtensionOptions")), + (typeof(UndertaleExtensionFunction), LocalizationSource.GetString("RefType_ExtensionFunctions")), + (typeof(UndertaleFont), LocalizationSource.GetString("RefType_Fonts")), + (typeof(UndertaleGameObject), LocalizationSource.GetString("RefType_GameObjects")), + (typeof(UndertaleGeneralInfo), LocalizationSource.GetString("RefType_GeneralInfo")), + (typeof(UndertaleOptions.Constant), LocalizationSource.GetString("RefType_GameOptionsConstants")), + (typeof(UndertalePath), LocalizationSource.GetString("RefType_Paths")), + (typeof(UndertaleRoom), LocalizationSource.GetString("RefType_Rooms")), + (typeof(UndertaleScript), LocalizationSource.GetString("RefType_Scripts")), + (typeof(UndertaleShader), LocalizationSource.GetString("RefType_Shaders")), + (typeof(UndertaleTimeline), LocalizationSource.GetString("RefType_Timelines")) } }, new TypesForVersion @@ -176,7 +177,7 @@ public static class UndertaleResourceReferenceMap BeforeVersion = (2024, 8, 0), Types = new[] { - (typeof(UndertaleCodeLocals), "Code locals") + (typeof(UndertaleCodeLocals), LocalizationSource.GetString("RefType_CodeLocals")) } }, new TypesForVersion @@ -185,7 +186,7 @@ public static class UndertaleResourceReferenceMap Version = (16, uint.MaxValue, uint.MaxValue), Types = new[] { - (typeof(UndertaleLanguage), "Languages"), + (typeof(UndertaleLanguage), LocalizationSource.GetString("RefType_Languages")), } }, new TypesForVersion @@ -193,10 +194,10 @@ public static class UndertaleResourceReferenceMap Version = (2, 0, 0), Types = new[] { - (typeof(UndertaleBackground), "Tile sets"), - (typeof(UndertaleEmbeddedImage), "Embedded images"), - (typeof(UndertaleRoom.Layer), "Room layers"), - (typeof(UndertaleRoom.SpriteInstance), "Room sprite instances") + (typeof(UndertaleBackground), LocalizationSource.GetString("RefType_TileSets")), + (typeof(UndertaleEmbeddedImage), LocalizationSource.GetString("RefType_EmbeddedImages")), + (typeof(UndertaleRoom.Layer), LocalizationSource.GetString("RefType_RoomLayers")), + (typeof(UndertaleRoom.SpriteInstance), LocalizationSource.GetString("RefType_RoomSpriteInstances")) } }, new TypesForVersion @@ -204,7 +205,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 2, 1), Types = new[] { - (typeof(UndertaleTextureGroupInfo), "Texture groups") + (typeof(UndertaleTextureGroupInfo), LocalizationSource.GetString("RefType_TextureGroups")) } }, new TypesForVersion @@ -212,14 +213,14 @@ public static class UndertaleResourceReferenceMap Version = (2, 3, 0), Types = new[] { - (typeof(UndertaleAnimationCurve), "Animation curves"), - (typeof(UndertaleAnimationCurve.Channel), "Animation curve channels"), - (typeof(UndertaleRoom.SequenceInstance), "Room sequence instances"), - (typeof(UndertaleSequence), "Sequences"), - (typeof(UndertaleSequence.Track), "Sequence tracks"), - (typeof(UndertaleSequence.BroadcastMessage), "Sequence broadcast messages"), - (typeof(UndertaleSequence.Moment), "Sequence moments"), - (typeof(UndertaleSequence.StringKeyframes), "Sequence string keyframes") + (typeof(UndertaleAnimationCurve), LocalizationSource.GetString("RefType_AnimationCurves")), + (typeof(UndertaleAnimationCurve.Channel), LocalizationSource.GetString("RefType_AnimationCurveChannels")), + (typeof(UndertaleRoom.SequenceInstance), LocalizationSource.GetString("RefType_RoomSequenceInstances")), + (typeof(UndertaleSequence), LocalizationSource.GetString("RefType_Sequences")), + (typeof(UndertaleSequence.Track), LocalizationSource.GetString("RefType_SequenceTracks")), + (typeof(UndertaleSequence.BroadcastMessage), LocalizationSource.GetString("RefType_SequenceBroadcastMessages")), + (typeof(UndertaleSequence.Moment), LocalizationSource.GetString("RefType_SequenceMoments")), + (typeof(UndertaleSequence.StringKeyframes), LocalizationSource.GetString("RefType_SequenceStringKeyframes")) } }, new TypesForVersion @@ -227,7 +228,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 3, 6), Types = new[] { - (typeof(UndertaleFilterEffect), "Filter effects") + (typeof(UndertaleFilterEffect), LocalizationSource.GetString("RefType_FilterEffects")) } }, new TypesForVersion @@ -235,7 +236,7 @@ public static class UndertaleResourceReferenceMap Version = (2022, 1, 0), Types = new[] { - (typeof(UndertaleRoom.EffectProperty), "Room effect properties") + (typeof(UndertaleRoom.EffectProperty), LocalizationSource.GetString("RefType_RoomEffectProperties")) } }, new TypesForVersion @@ -243,7 +244,7 @@ public static class UndertaleResourceReferenceMap Version = (2022, 2, 0), Types = new[] { - (typeof(UndertaleSequence.TextKeyframes), "Sequence track text keyframes") + (typeof(UndertaleSequence.TextKeyframes), LocalizationSource.GetString("RefType_SequenceTextKeyframes")) } }, new TypesForVersion @@ -251,9 +252,9 @@ public static class UndertaleResourceReferenceMap Version = (2023, 2, 0), Types = new[] { - (typeof(UndertaleParticleSystem), "Particle systems"), - (typeof(UndertaleParticleSystemEmitter), "Particle system emitters"), - (typeof(UndertaleRoom.ParticleSystemInstance), "Room particle system instances") + (typeof(UndertaleParticleSystem), LocalizationSource.GetString("RefType_ParticleSystems")), + (typeof(UndertaleParticleSystemEmitter), LocalizationSource.GetString("RefType_ParticleSystemEmitters")), + (typeof(UndertaleRoom.ParticleSystemInstance), LocalizationSource.GetString("RefType_RoomParticleSystemInstances")) } } } @@ -267,7 +268,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleRoom.GameObject), "Room object instance") + (typeof(UndertaleRoom.GameObject), LocalizationSource.GetString("RefType_RoomObjectInstance")) } }, new TypesForVersion @@ -275,7 +276,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 3, 0), Types = new[] { - (typeof(UndertaleSequence.InstanceKeyframes), "Sequence instance keyframes") + (typeof(UndertaleSequence.InstanceKeyframes), LocalizationSource.GetString("RefType_SequenceInstanceKeyframes")) } }, } @@ -289,10 +290,10 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleGameObject), "Game objects"), - (typeof(UndertaleRoom), "Rooms"), - (typeof(UndertaleGlobalInit), "Global initialization and game end scripts"), - (typeof(UndertaleScript), "Scripts") + (typeof(UndertaleGameObject), LocalizationSource.GetString("RefType_GameObjects")), + (typeof(UndertaleRoom), LocalizationSource.GetString("RefType_Rooms")), + (typeof(UndertaleGlobalInit), LocalizationSource.GetString("RefType_GlobalInitAndGameEndScripts")), + (typeof(UndertaleScript), LocalizationSource.GetString("RefType_Scripts")) } } } @@ -306,7 +307,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleSound), "Sounds") + (typeof(UndertaleSound), LocalizationSource.GetString("RefType_Sounds")) } } } @@ -320,7 +321,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleSound), "Sounds") + (typeof(UndertaleSound), LocalizationSource.GetString("RefType_Sounds")) } } } @@ -334,7 +335,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleCode), "Code") + (typeof(UndertaleCode), LocalizationSource.GetString("RefType_Code")) } } } @@ -348,7 +349,7 @@ public static class UndertaleResourceReferenceMap Version = (1, 0, 0), Types = new[] { - (typeof(UndertaleCode), "Code") + (typeof(UndertaleCode), LocalizationSource.GetString("RefType_Code")) } } } @@ -362,7 +363,7 @@ public static class UndertaleResourceReferenceMap Version = (2, 0, 0), Types = new[] { - (typeof(UndertaleRoom.Layer), "Room tile layer") + (typeof(UndertaleRoom.Layer), LocalizationSource.GetString("RefType_RoomTileLayer")) } } } @@ -376,7 +377,7 @@ public static class UndertaleResourceReferenceMap Version = (2023, 2, 0), Types = new[] { - (typeof(UndertaleRoom.ParticleSystemInstance), "Room particle system instances") + (typeof(UndertaleRoom.ParticleSystemInstance), LocalizationSource.GetString("RefType_RoomParticleSystemInstances")) } } } @@ -390,8 +391,8 @@ public static class UndertaleResourceReferenceMap Version = (2023, 2, 0), Types = new[] { - (typeof(UndertaleParticleSystem), "Particle systems"), - (typeof(UndertaleParticleSystemEmitter), "Particle system emitters") + (typeof(UndertaleParticleSystem), LocalizationSource.GetString("RefType_ParticleSystems")), + (typeof(UndertaleParticleSystemEmitter), LocalizationSource.GetString("RefType_ParticleSystemEmitters")) } } } @@ -400,19 +401,19 @@ public static class UndertaleResourceReferenceMap private static readonly Dictionary referenceableTypes = new() { - { typeof(UndertaleSprite), "Sprites" }, - { typeof(UndertaleBackground), "Backgrounds" }, - { typeof(UndertaleEmbeddedTexture), "Embedded textures" }, - { typeof(UndertaleTexturePageItem), "Texture page items" }, - { typeof(UndertaleString), "Strings" }, - { typeof(UndertaleGameObject), "Game objects" }, - { typeof(UndertaleCode), "Code entries" }, - { typeof(UndertaleFunction), "Functions" }, - { typeof(UndertaleVariable), "Variables" }, - { typeof(UndertaleEmbeddedAudio), "Embedded audio" }, - { typeof(UndertaleAudioGroup), "Audio groups" }, - { typeof(UndertaleParticleSystem), "Particle systems" }, - { typeof(UndertaleParticleSystemEmitter), "Particle system emitters" } + { typeof(UndertaleSprite), LocalizationSource.GetString("RefType_Sprites") }, + { typeof(UndertaleBackground), LocalizationSource.GetString("RefType_Backgrounds") }, + { typeof(UndertaleEmbeddedTexture), LocalizationSource.GetString("RefType_EmbeddedTextures") }, + { typeof(UndertaleTexturePageItem), LocalizationSource.GetString("RefType_TexturePageItems") }, + { typeof(UndertaleString), LocalizationSource.GetString("RefType_Strings") }, + { typeof(UndertaleGameObject), LocalizationSource.GetString("RefType_GameObjects") }, + { typeof(UndertaleCode), LocalizationSource.GetString("RefType_CodeEntries") }, + { typeof(UndertaleFunction), LocalizationSource.GetString("RefType_Functions") }, + { typeof(UndertaleVariable), LocalizationSource.GetString("RefType_Variables") }, + { typeof(UndertaleEmbeddedAudio), LocalizationSource.GetString("RefType_EmbeddedAudio") }, + { typeof(UndertaleAudioGroup), LocalizationSource.GetString("RefType_AudioGroups") }, + { typeof(UndertaleParticleSystem), LocalizationSource.GetString("RefType_ParticleSystems") }, + { typeof(UndertaleParticleSystemEmitter), LocalizationSource.GetString("RefType_ParticleSystemEmitters") } }; public static readonly HashSet CodeTypes = new() @@ -462,7 +463,7 @@ public static (Type, string)[] GetTypeMapForVersion(Type type, UndertaleData dat public static Dictionary GetReferenceableTypes((uint, uint, uint) version) { referenceableTypes[typeof(UndertaleBackground)] = version.CompareTo((2, 0, 0)) >= 0 - ? "Tile sets" : "Backgrounds"; + ? LocalizationSource.GetString("RefType_TileSets") : LocalizationSource.GetString("RefType_Backgrounds"); return referenceableTypes; } diff --git a/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMethodsMap.cs b/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMethodsMap.cs index 42e6b2883..1031083b6 100644 --- a/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMethodsMap.cs +++ b/UndertaleModTool/Windows/FindReferencesTypesDialog/UndertaleResourceReferenceMethodsMap.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; @@ -8,6 +8,7 @@ using System.Windows; using UndertaleModLib; using UndertaleModLib.Models; +using UndertaleModTool.Localization; using static UndertaleModLib.Models.UndertaleSequence; namespace UndertaleModTool.Windows @@ -64,7 +65,7 @@ public static class UndertaleResourceReferenceMethodsMap var gameObjects = data.GameObjects.Where(x => x is not null).Where(x => x.Sprite == obj); if (gameObjects.Any()) - return new() { { "Game objects", checkOne ? gameObjects.ToEmptyArray() : gameObjects.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_GameObjects"), checkOne ? gameObjects.ToEmptyArray() : gameObjects.ToArray() } }; else return null; } @@ -99,7 +100,7 @@ IEnumerable GetTiles() var tiles = GetTiles(); if (tiles.Any()) - outDict["Room tiles"] = checkOne ? tiles.ToEmptyArray() : tiles.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomTiles")] = checkOne ? tiles.ToEmptyArray() : tiles.ToArray(); } if (types.Contains(typeof(UndertaleRoom.SpriteInstance))) { @@ -121,7 +122,7 @@ IEnumerable GetSprInstances() var sprInstances = GetSprInstances(); if (sprInstances.Any()) - outDict["Room sprite instances"] = checkOne ? sprInstances.ToEmptyArray() : sprInstances.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomSpriteInstances")] = checkOne ? sprInstances.ToEmptyArray() : sprInstances.ToArray(); } if (types.Contains(typeof(UndertaleRoom.Layer))) { @@ -140,7 +141,7 @@ IEnumerable GetBgLayers() var bgLayers = GetBgLayers(); if (bgLayers.Any()) - outDict["Room background layers"] = checkOne ? bgLayers.ToEmptyArray() : bgLayers.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomBackgroundLayers")] = checkOne ? bgLayers.ToEmptyArray() : bgLayers.ToArray(); } if (outDict.Count == 0) @@ -162,7 +163,7 @@ IEnumerable GetBgLayers() var textGroups = data.TextureGroupInfo.Where(x => x is not null).Where(x => x.Sprites.Any(s => s.Resource == obj) || (x.SpineSprites?.Any(s => s.Resource == obj) == true)); if (textGroups.Any()) - return new() { { "Texture groups", checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_TextureGroups"), checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; else return null; } @@ -181,7 +182,7 @@ IEnumerable GetBgLayers() var partSysEmitters = data.ParticleSystemEmitters.Where(x => x is not null).Where(x => x.Sprite == obj); if (partSysEmitters.Any()) - return new() { { "Particle system emitters", checkOne ? partSysEmitters.ToEmptyArray() : partSysEmitters.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_ParticleSystemEmitters"), checkOne ? partSysEmitters.ToEmptyArray() : partSysEmitters.ToArray() } }; else return null; } @@ -219,7 +220,7 @@ IEnumerable GetBackgrounds() var backgrounds = GetBackgrounds(); if (backgrounds.Any()) - outDict["Room backgrounds"] = checkOne ? backgrounds.ToEmptyArray() : backgrounds.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomBackgrounds")] = checkOne ? backgrounds.ToEmptyArray() : backgrounds.ToArray(); } if (types.Contains(typeof(UndertaleRoom.Tile))) { @@ -235,7 +236,7 @@ IEnumerable GetTiles() var tiles = GetTiles(); if (tiles.Any()) - outDict["Room tiles"] = checkOne ? tiles.ToEmptyArray() : tiles.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomTiles")] = checkOne ? tiles.ToEmptyArray() : tiles.ToArray(); } if (outDict.Count == 0) @@ -270,7 +271,7 @@ IEnumerable GetTileLayers() var tileLayers = GetTileLayers(); if (tileLayers.Any()) - return new() { { "Room tile layers", checkOne ? tileLayers.ToEmptyArray() : tileLayers.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_RoomTileLayers"), checkOne ? tileLayers.ToEmptyArray() : tileLayers.ToArray() } }; else return null; } @@ -288,7 +289,7 @@ IEnumerable GetTileLayers() var textGroups = data.TextureGroupInfo.Where(x => x is not null).Where(x => x.Tilesets.Any(s => s.Resource == obj)); if (textGroups.Any()) - return new() { { "Texture groups", checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_TextureGroups"), checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; else return null; } @@ -309,7 +310,7 @@ IEnumerable GetTileLayers() var pageItems = data.TexturePageItems.Where(x => x is not null).Where(x => x.TexturePage == obj); if (pageItems.Any()) - return new() { { "Texture page items", checkOne ? pageItems.ToEmptyArray() : pageItems.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_TexturePageItems"), checkOne ? pageItems.ToEmptyArray() : pageItems.ToArray() } }; else return null; } @@ -327,7 +328,7 @@ IEnumerable GetTileLayers() var textGroups = data.TextureGroupInfo.Where(x => x is not null).Where(x => x.TexturePages.Any(s => s.Resource == obj)); if (textGroups.Any()) - return new() { { "Texture groups", checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_TextureGroups"), checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; else return null; } @@ -352,21 +353,21 @@ IEnumerable GetTileLayers() { var sprites = data.Sprites.Where(x => x is not null).Where(x => x.Textures.Any(t => t.Texture == obj)); if (sprites.Any()) - outDict["Sprites"] = checkOne ? sprites.ToEmptyArray() : sprites.ToArray(); + outDict[LocalizationSource.GetString("RefType_Sprites")] = checkOne ? sprites.ToEmptyArray() : sprites.ToArray(); } if (types.Contains(typeof(UndertaleBackground))) { var backgrounds = data.Backgrounds.Where(x => x is not null).Where(x => x.Texture == obj); if (backgrounds.Any()) - outDict[data.IsGameMaker2() ? "Tile sets" : "Backgrounds"] = checkOne ? backgrounds.ToEmptyArray() : backgrounds.ToArray(); + outDict[data.IsGameMaker2() ? LocalizationSource.GetString("RefType_TileSets") : LocalizationSource.GetString("RefType_Backgrounds")] = checkOne ? backgrounds.ToEmptyArray() : backgrounds.ToArray(); } if (types.Contains(typeof(UndertaleFont))) { var fonts = data.Fonts.Where(x => x is not null).Where(x => x.Texture == obj); if (fonts.Any()) - outDict["Fonts"] = checkOne ? fonts.ToEmptyArray() : fonts.ToArray(); + outDict[LocalizationSource.GetString("RefType_Fonts")] = checkOne ? fonts.ToEmptyArray() : fonts.ToArray(); } if (outDict.Count == 0) @@ -387,7 +388,7 @@ IEnumerable GetTileLayers() var embImages = data.EmbeddedImages.Where(x => x is not null).Where(x => x.TextureEntry == obj); if (embImages.Any()) - return new() { { "Embedded images", checkOne ? embImages.ToEmptyArray() : embImages.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_EmbeddedImages"), checkOne ? embImages.ToEmptyArray() : embImages.ToArray() } }; else return null; } @@ -412,7 +413,7 @@ IEnumerable GetTileLayers() { var backgrounds = data.Backgrounds.Where(x => x is not null).Where(x => x.Name == obj); if (backgrounds.Any()) - outDict[data.IsGameMaker2() ? "Tile sets" : "Backgrounds"] = checkOne ? backgrounds.ToEmptyArray() : backgrounds.ToArray(); + outDict[data.IsGameMaker2() ? LocalizationSource.GetString("RefType_TileSets") : LocalizationSource.GetString("RefType_Backgrounds")] = checkOne ? backgrounds.ToEmptyArray() : backgrounds.ToArray(); } if (types.Contains(typeof(UndertaleCode))) @@ -428,33 +429,33 @@ IEnumerable GetTileLayers() codeEntries = codeEntries.Concat(stringRefs); if (codeEntries.Any()) - outDict["Code entries (name and contents)"] = checkOne ? codeEntries.ToEmptyArray() : codeEntries.ToArray(); + outDict[LocalizationSource.GetString("RefType_CodeEntriesNameAndContents")] = checkOne ? codeEntries.ToEmptyArray() : codeEntries.ToArray(); } if (types.Contains(typeof(UndertaleFunction))) { var functions = data.Functions.Where(x => x is not null).Where(x => x.Name == obj); if (functions.Any()) - outDict["Functions"] = checkOne ? functions.ToEmptyArray() : functions.ToArray(); + outDict[LocalizationSource.GetString("RefType_Functions")] = checkOne ? functions.ToEmptyArray() : functions.ToArray(); } if (types.Contains(typeof(UndertaleVariable))) { var variables = data.Variables.Where(x => x is not null).Where(x => x.Name == obj); if (variables.Any()) - outDict["Variables"] = checkOne ? variables.ToEmptyArray() : variables.ToArray(); + outDict[LocalizationSource.GetString("RefType_Variables")] = checkOne ? variables.ToEmptyArray() : variables.ToArray(); } if (types.Contains(typeof(UndertaleSound))) { var sounds = data.Sounds.Where(x => x is not null).Where(x => x.Name == obj || x.Type == obj || x.File == obj); if (sounds.Any()) - outDict["Sounds"] = checkOne ? sounds.ToEmptyArray() : sounds.ToArray(); + outDict[LocalizationSource.GetString("RefType_Sounds")] = checkOne ? sounds.ToEmptyArray() : sounds.ToArray(); } if (types.Contains(typeof(UndertaleAudioGroup))) { var audioGroups = data.AudioGroups.Where(x => x is not null).Where(x => x.Name == obj); if (audioGroups.Any()) - outDict["Audio groups"] = checkOne ? audioGroups.ToEmptyArray() : audioGroups.ToArray(); + outDict[LocalizationSource.GetString("RefType_AudioGroups")] = checkOne ? audioGroups.ToEmptyArray() : audioGroups.ToArray(); } if (types.Contains(typeof(UndertaleSprite))) @@ -470,7 +471,7 @@ IEnumerable GetTileLayers() } if (sprites.Any()) - outDict["Sprites"] = checkOne ? sprites.ToEmptyArray() : sprites.ToArray(); + outDict[LocalizationSource.GetString("RefType_Sprites")] = checkOne ? sprites.ToEmptyArray() : sprites.ToArray(); } @@ -478,7 +479,7 @@ IEnumerable GetTileLayers() { var extensions = data.Extensions.Where(x => x is not null).Where(x => x.Name == obj || x.ClassName == obj || x.FolderName == obj); if (extensions.Any()) - outDict["Extensions"] = checkOne ? extensions.ToEmptyArray() : extensions.ToArray(); + outDict[LocalizationSource.GetString("RefType_Extensions")] = checkOne ? extensions.ToEmptyArray() : extensions.ToArray(); } if (types.Contains(typeof(UndertaleExtensionOption))) { @@ -494,7 +495,7 @@ IEnumerable GetExtnOptions() var extnOptions = GetExtnOptions(); if (extnOptions.Any()) - outDict["Extension options"] = checkOne ? extnOptions.ToEmptyArray() : extnOptions.ToArray(); + outDict[LocalizationSource.GetString("RefType_ExtensionOptions")] = checkOne ? extnOptions.ToEmptyArray() : extnOptions.ToArray(); } if (types.Contains(typeof(UndertaleExtensionFile))) { @@ -510,7 +511,7 @@ IEnumerable GetExtnFiles() var extnFiles = GetExtnFiles(); if (extnFiles.Any()) - outDict["Extension files"] = checkOne ? extnFiles.ToEmptyArray() : extnFiles.ToArray(); + outDict[LocalizationSource.GetString("RefType_ExtensionFiles")] = checkOne ? extnFiles.ToEmptyArray() : extnFiles.ToArray(); } if (types.Contains(typeof(UndertaleExtensionFunction))) { @@ -529,21 +530,21 @@ IEnumerable GetExtnFunctions() var extnFunctions = GetExtnFunctions(); if (extnFunctions.Any()) - outDict["Extension functions"] = checkOne ? extnFunctions.ToEmptyArray() : extnFunctions.ToArray(); + outDict[LocalizationSource.GetString("RefType_ExtensionFunctions")] = checkOne ? extnFunctions.ToEmptyArray() : extnFunctions.ToArray(); } if (types.Contains(typeof(UndertaleFont))) { var fonts = data.Fonts.Where(x => x is not null).Where(x => x.Name == obj || x.DisplayName == obj); if (fonts.Any()) - outDict["Fonts"] = checkOne ? fonts.ToEmptyArray() : fonts.ToArray(); + outDict[LocalizationSource.GetString("RefType_Fonts")] = checkOne ? fonts.ToEmptyArray() : fonts.ToArray(); } if (types.Contains(typeof(UndertaleGameObject))) { var gameObjects = data.GameObjects.Where(x => x is not null).Where(x => x.Name == obj); if (gameObjects.Any()) - outDict["Game objects"] = checkOne ? gameObjects.ToEmptyArray() : gameObjects.ToArray(); + outDict[LocalizationSource.GetString("RefType_GameObjects")] = checkOne ? gameObjects.ToEmptyArray() : gameObjects.ToArray(); } if (types.Contains(typeof(UndertaleGeneralInfo))) @@ -551,35 +552,35 @@ IEnumerable GetExtnFunctions() bool genInfoMatches = data.GeneralInfo.Name == obj || data.GeneralInfo.FileName == obj || data.GeneralInfo.Config == obj || data.GeneralInfo.DisplayName == obj; if (genInfoMatches) - outDict["General Info"] = new object[] { new GeneralInfoEditor(data.GeneralInfo, data.Options, data.Language) }; + outDict[LocalizationSource.GetString("RefType_GeneralInfo")] = new object[] { new GeneralInfoEditor(data.GeneralInfo, data.Options, data.Language) }; } if (types.Contains(typeof(UndertaleOptions.Constant))) { bool constantsMatches = data.Options.Constants.Any(x => x.Name == obj || x.Value == obj); if (constantsMatches) - outDict["Game options constants"] = new object[] { new GeneralInfoEditor(data.GeneralInfo, data.Options, data.Language) }; + outDict[LocalizationSource.GetString("RefType_GameOptionsConstants")] = new object[] { new GeneralInfoEditor(data.GeneralInfo, data.Options, data.Language) }; } if (types.Contains(typeof(UndertalePath))) { var paths = data.Paths.Where(x => x is not null).Where(x => x.Name == obj); if (paths.Any()) - outDict["Paths"] = checkOne ? paths.ToEmptyArray() : paths.ToArray(); + outDict[LocalizationSource.GetString("RefType_Paths")] = checkOne ? paths.ToEmptyArray() : paths.ToArray(); } if (types.Contains(typeof(UndertaleRoom))) { var rooms = data.Rooms.Where(x => x is not null).Where(x => x.Name == obj || x.Caption == obj); if (rooms.Any()) - outDict["Rooms"] = checkOne ? rooms.ToEmptyArray() : rooms.ToArray(); + outDict[LocalizationSource.GetString("RefType_Rooms")] = checkOne ? rooms.ToEmptyArray() : rooms.ToArray(); } if (types.Contains(typeof(UndertaleScript))) { var scripts = data.Scripts.Where(x => x is not null).Where(x => x.Name == obj); if (scripts.Any()) - outDict["Scripts"] = checkOne ? scripts.ToEmptyArray() : scripts.ToArray(); + outDict[LocalizationSource.GetString("RefType_Scripts")] = checkOne ? scripts.ToEmptyArray() : scripts.ToArray(); } if (types.Contains(typeof(UndertaleShader))) @@ -589,14 +590,14 @@ IEnumerable GetExtnFunctions() || x.GLSL_ES_Fragment == obj || x.GLSL_Fragment == obj || x.HLSL9_Fragment == obj || x.VertexShaderAttributes.Any(a => a.Name == obj)); if (shaders.Any()) - outDict["Shaders"] = checkOne ? shaders.ToEmptyArray() : shaders.ToArray(); + outDict[LocalizationSource.GetString("RefType_Shaders")] = checkOne ? shaders.ToEmptyArray() : shaders.ToArray(); } if (types.Contains(typeof(UndertaleTimeline))) { var timelines = data.Timelines.Where(x => x is not null).Where(x => x.Name == obj); if (timelines.Any()) - outDict["Timelines"] = checkOne ? timelines.ToEmptyArray() : timelines.ToArray(); + outDict[LocalizationSource.GetString("RefType_Timelines")] = checkOne ? timelines.ToEmptyArray() : timelines.ToArray(); } if (outDict.Count == 0) @@ -619,7 +620,7 @@ IEnumerable GetExtnFunctions() var codeLocals = data.CodeLocals.Where(x => x is not null).Where(x => x.Name == obj || x.Locals.Any(l => l.Name == obj)); if (codeLocals.Any()) - return new() { { "Code locals", checkOne ? codeLocals.ToEmptyArray() : codeLocals.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_CodeLocals"), checkOne ? codeLocals.ToEmptyArray() : codeLocals.ToArray() } }; else return null; } @@ -641,7 +642,7 @@ IEnumerable GetExtnFunctions() || x.Entries.Contains(obj)); if (langsMatches) - return new() { { "Languages", new object[] { new GeneralInfoEditor(data.GeneralInfo, data.Options, data.Language) } } }; + return new() { { LocalizationSource.GetString("RefType_Languages"), new object[] { new GeneralInfoEditor(data.GeneralInfo, data.Options, data.Language) } } }; else return null; } @@ -660,7 +661,7 @@ IEnumerable GetExtnFunctions() { var embImages = data.EmbeddedImages.Where(x => x is not null).Where(x => x.Name == obj); if (embImages.Any()) - outDict["Embedded images"] = checkOne ? embImages.ToEmptyArray() : embImages.ToArray(); + outDict[LocalizationSource.GetString("RefType_EmbeddedImages")] = checkOne ? embImages.ToEmptyArray() : embImages.ToArray(); } if (types.Contains(typeof(UndertaleRoom.Layer))) @@ -675,7 +676,7 @@ IEnumerable GetLayers() var layers = GetLayers(); if (layers.Any()) - outDict["Room layers"] = checkOne ? layers.ToEmptyArray() : layers.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomLayers")] = checkOne ? layers.ToEmptyArray() : layers.ToArray(); } if (types.Contains(typeof(UndertaleRoom.SpriteInstance))) { @@ -697,7 +698,7 @@ IEnumerable GetSprInstances() var sprInstances = GetSprInstances(); if (sprInstances.Any()) - outDict["Room sprite instances"] = checkOne ? sprInstances.ToEmptyArray() : sprInstances.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomSpriteInstances")] = checkOne ? sprInstances.ToEmptyArray() : sprInstances.ToArray(); } if (outDict.Count == 0) @@ -718,7 +719,7 @@ IEnumerable GetSprInstances() var textGroups = data.TextureGroupInfo.Where(x => x is not null).Where(x => x.Name == obj); if (textGroups.Any()) - return new() { { "Texture groups", checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_TextureGroups"), checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } }; else return null; } @@ -737,7 +738,7 @@ IEnumerable GetSprInstances() { var animCurves = data.AnimationCurves.Where(x => x is not null).Where(x => x.Name == obj); if (animCurves.Any()) - outDict["Animation curves"] = checkOne ? animCurves.ToEmptyArray() : animCurves.ToArray(); + outDict[LocalizationSource.GetString("RefType_AnimationCurves")] = checkOne ? animCurves.ToEmptyArray() : animCurves.ToArray(); } if (types.Contains(typeof(UndertaleAnimationCurve.Channel))) { @@ -753,7 +754,7 @@ IEnumerable GetAnimCurveChannels() var animCurveChannels = GetAnimCurveChannels(); if (animCurveChannels.Any()) - outDict["Animation curve channels"] = checkOne ? animCurveChannels.ToEmptyArray() : animCurveChannels.ToArray(); + outDict[LocalizationSource.GetString("RefType_AnimationCurveChannels")] = checkOne ? animCurveChannels.ToEmptyArray() : animCurveChannels.ToArray(); } if (types.Contains(typeof(UndertaleRoom.SequenceInstance))) @@ -776,7 +777,7 @@ IEnumerable GetSeqInstances() var seqInstances = GetSeqInstances(); if (seqInstances.Any()) - outDict["Room sequence instances"] = checkOne ? seqInstances.ToEmptyArray() : seqInstances.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomSequenceInstances")] = checkOne ? seqInstances.ToEmptyArray() : seqInstances.ToArray(); } if (types.Contains(typeof(UndertaleSequence))) @@ -784,7 +785,7 @@ IEnumerable GetSeqInstances() var sequences = data.Sequences.Where(x => x is not null).Where(x => x.Name == obj || x.FunctionIDs.Any(f => f.FunctionName == obj)); if (sequences.Any()) - outDict["Sequences"] = checkOne ? sequences.ToEmptyArray() : sequences.ToArray(); + outDict[LocalizationSource.GetString("RefType_Sequences")] = checkOne ? sequences.ToEmptyArray() : sequences.ToArray(); } // TODO: make these "IEnumerable" @@ -827,9 +828,9 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) } } if (sequenceTracks.Count > 0) - outDict["Sequence tracks"] = checkOne ? sequenceTracks.ToEmptyArray() : sequenceTracks.ToArray(); + outDict[LocalizationSource.GetString("RefType_SequenceTracks")] = checkOne ? sequenceTracks.ToEmptyArray() : sequenceTracks.ToArray(); if (seqStringKeyframes.Count > 0) - outDict["Sequence string keyframes"] = checkOne ? seqStringKeyframes.ToEmptyArray() : seqStringKeyframes.ToArray(); + outDict[LocalizationSource.GetString("RefType_SequenceStringKeyframes")] = checkOne ? seqStringKeyframes.ToEmptyArray() : seqStringKeyframes.ToArray(); if (types.Contains(typeof(BroadcastMessage))) { @@ -848,7 +849,7 @@ IEnumerable GetSeqBroadMessages() var seqBroadMessages = GetSeqBroadMessages(); if (seqBroadMessages.Any()) - outDict["Sequence broadcast messages"] = checkOne ? seqBroadMessages.ToEmptyArray() : seqBroadMessages.ToArray(); + outDict[LocalizationSource.GetString("RefType_SequenceBroadcastMessages")] = checkOne ? seqBroadMessages.ToEmptyArray() : seqBroadMessages.ToArray(); } if (types.Contains(typeof(Moment))) { @@ -867,7 +868,7 @@ IEnumerable GetSequenceMoments() var sequenceMoments = GetSequenceMoments(); if (sequenceMoments.Any()) - outDict["Sequence moments"] = checkOne ? sequenceMoments.ToEmptyArray() : sequenceMoments.ToArray(); + outDict[LocalizationSource.GetString("RefType_SequenceMoments")] = checkOne ? sequenceMoments.ToEmptyArray() : sequenceMoments.ToArray(); } if (outDict.Count == 0) @@ -888,7 +889,7 @@ IEnumerable GetSequenceMoments() var filterEffects = data.FilterEffects.Where(x => x is not null).Where(x => x.Name == obj || x.Value == obj); if (filterEffects.Any()) - return new() { { "Filter effects", checkOne ? filterEffects.ToEmptyArray() : filterEffects.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_FilterEffects"), checkOne ? filterEffects.ToEmptyArray() : filterEffects.ToArray() } }; else return null; } @@ -919,7 +920,7 @@ IEnumerable GetEffectProps() var effectProps = GetEffectProps(); if (effectProps.Any()) - return new() { { "Room effect properties", checkOne ? effectProps.ToEmptyArray() : effectProps.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_RoomEffectProperties"), checkOne ? effectProps.ToEmptyArray() : effectProps.ToArray() } }; else return null; } @@ -965,7 +966,7 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) } } if (textKeyframesList.Count > 0) - return new() { { "Sequence text keyframes", checkOne ? textKeyframesList.ToEmptyArray() : textKeyframesList.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_SequenceTextKeyframes"), checkOne ? textKeyframesList.ToEmptyArray() : textKeyframesList.ToArray() } }; else return null; } @@ -985,14 +986,14 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) { var partSystems = data.ParticleSystems.Where(x => x is not null).Where(x => x.Name == obj); if (partSystems.Any()) - outDict["Particle systems"] = checkOne ? partSystems.ToEmptyArray() : partSystems.ToArray(); + outDict[LocalizationSource.GetString("RefType_ParticleSystems")] = checkOne ? partSystems.ToEmptyArray() : partSystems.ToArray(); } if (types.Contains(typeof(UndertaleParticleSystemEmitter))) { var partSysEmitters = data.ParticleSystemEmitters.Where(x => x is not null).Where(x => x.Name == obj); if (partSysEmitters.Any()) - outDict["Particle system emitters"] = checkOne ? partSysEmitters.ToEmptyArray() : partSysEmitters.ToArray(); + outDict[LocalizationSource.GetString("RefType_ParticleSystemEmitters")] = checkOne ? partSysEmitters.ToEmptyArray() : partSysEmitters.ToArray(); } if (types.Contains(typeof(UndertaleRoom.ParticleSystemInstance))) @@ -1015,7 +1016,7 @@ IEnumerable GetPartSysInstances() var partSysInstances = GetPartSysInstances(); if (partSysInstances.Any()) - outDict["Room particle system instances"] = checkOne ? partSysInstances.ToEmptyArray() : partSysInstances.ToArray(); + outDict[LocalizationSource.GetString("RefType_RoomParticleSystemInstances")] = checkOne ? partSysInstances.ToEmptyArray() : partSysInstances.ToArray(); } if (outDict.Count == 0) @@ -1070,7 +1071,7 @@ IEnumerable GetObjInstances() var objInstances = GetObjInstances(); if (objInstances.Any()) - return new() { { "Room object instance", checkOne ? objInstances.ToEmptyArray() : objInstances.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_RoomObjectInstance"), checkOne ? objInstances.ToEmptyArray() : objInstances.ToArray() } }; else return null; } @@ -1116,7 +1117,7 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) } } if (instKeyframesList.Count > 0) - return new() { { "Sequence object instance keyframes", checkOne ? instKeyframesList.ToEmptyArray() : instKeyframesList.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_SequenceObjectInstanceKeyframes"), checkOne ? instKeyframesList.ToEmptyArray() : instKeyframesList.ToArray() } }; else return null; } @@ -1143,32 +1144,32 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) e => e.Any(se => se.Actions.Any( a => a.CodeId == obj)))); if (gameObjects.Any()) - outDict["Game objects"] = checkOne ? gameObjects.ToEmptyArray() : gameObjects.ToArray(); + outDict[LocalizationSource.GetString("RefType_GameObjects")] = checkOne ? gameObjects.ToEmptyArray() : gameObjects.ToArray(); } if (types.Contains(typeof(UndertaleRoom))) { var rooms = data.Rooms.Where(x => x is not null).Where(x => x.CreationCodeId == obj); if (rooms.Any()) - outDict["Rooms"] = checkOne ? rooms.ToEmptyArray() : rooms.ToArray(); + outDict[LocalizationSource.GetString("RefType_Rooms")] = checkOne ? rooms.ToEmptyArray() : rooms.ToArray(); } if (types.Contains(typeof(UndertaleGlobalInit))) { bool matches = data.GlobalInitScripts?.Any(x => x.Code == obj) == true; if (matches) - outDict["Global init"] = new object[] { new GlobalInitEditor(data.GlobalInitScripts) }; + outDict[LocalizationSource.GetString("RefType_GlobalInit")] = new object[] { new GlobalInitEditor(data.GlobalInitScripts) }; matches = data.GameEndScripts?.Any(x => x.Code == obj) == true; if (matches) - outDict["Game end scripts"] = new object[] { new GameEndEditor(data.GameEndScripts) }; + outDict[LocalizationSource.GetString("RefType_GameEndScripts")] = new object[] { new GameEndEditor(data.GameEndScripts) }; } if (types.Contains(typeof(UndertaleScript))) { var scripts = data.Scripts.Where(x => x is not null).Where(x => x.Code == obj); if (scripts.Any()) - outDict["Scripts"] = checkOne ? scripts.ToEmptyArray() : scripts.ToArray(); + outDict[LocalizationSource.GetString("RefType_Scripts")] = checkOne ? scripts.ToEmptyArray() : scripts.ToArray(); } if (outDict.Count == 0) @@ -1195,7 +1196,7 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) var sounds = data.Sounds.Where(x => x is not null).Where(x => x.AudioFile == obj); if (sounds.Any()) - return new() { { "Sounds", checkOne ? sounds.ToEmptyArray() : sounds.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_Sounds"), checkOne ? sounds.ToEmptyArray() : sounds.ToArray() } }; else return null; } @@ -1219,7 +1220,7 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) var sounds = data.Sounds.Where(x => x is not null).Where(x => x.AudioGroup == obj); if (sounds.Any()) - return new() { { "Sounds", checkOne ? sounds.ToEmptyArray() : sounds.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_Sounds"), checkOne ? sounds.ToEmptyArray() : sounds.ToArray() } }; else return null; } @@ -1248,7 +1249,7 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) else funcRefs = data.Code.Where(x => x is not null).Where(x => x.Instructions.Any(i => i.ValueFunction == obj)); if (funcRefs.Any()) - return new() { { "Code", checkOne ? funcRefs.ToEmptyArray() : funcRefs.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_Code"), checkOne ? funcRefs.ToEmptyArray() : funcRefs.ToArray() } }; else return null; } @@ -1277,7 +1278,7 @@ void ProcessTrack(UndertaleSequence seq, Track track, List trackChain) else variRefs = data.Code.Where(x => x is not null).Where(x => x.Instructions.Any(i => i.ValueVariable == obj)); if (variRefs.Any()) - return new() { { "Code", checkOne ? variRefs.ToEmptyArray() : variRefs.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_Code"), checkOne ? variRefs.ToEmptyArray() : variRefs.ToArray() } }; else return null; } @@ -1325,7 +1326,7 @@ IEnumerable GetTileLayers() var tileLayers = GetTileLayers(); if (tileLayers.Any()) - return new() { { "Room tile layers", checkOne ? tileLayers.ToEmptyArray() : tileLayers.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_RoomTileLayers"), checkOne ? tileLayers.ToEmptyArray() : tileLayers.ToArray() } }; else return null; } @@ -1366,7 +1367,7 @@ IEnumerable GetPartSysInstances() var partSysInstances = GetPartSysInstances(); if (partSysInstances.Any()) - return new() { { "Room particle system instance", checkOne ? partSysInstances.ToEmptyArray() : partSysInstances.ToArray() } }; + return new() { { LocalizationSource.GetString("RefType_RoomParticleSystemInstance"), checkOne ? partSysInstances.ToEmptyArray() : partSysInstances.ToArray() } }; else return null; } @@ -1392,14 +1393,14 @@ IEnumerable GetPartSysInstances() { var partSystems = data.ParticleSystems.Where(x => x is not null).Where(x => x.Emitters.Any(e => e.Resource == obj)); if (partSystems.Any()) - outDict["Particle systems"] = checkOne ? partSystems.ToEmptyArray() : partSystems.ToArray(); + outDict[LocalizationSource.GetString("RefType_ParticleSystems")] = checkOne ? partSystems.ToEmptyArray() : partSystems.ToArray(); } if (types.Contains(typeof(UndertaleParticleSystemEmitter))) { var partSysEmitters = data.ParticleSystemEmitters.Where(x => x is not null).Where(x => x.SpawnOnDeath == obj || x.SpawnOnUpdate == obj); if (partSysEmitters.Any()) - outDict["Particle system emitters"] = checkOne ? partSysEmitters.ToEmptyArray() : partSysEmitters.ToArray(); + outDict[LocalizationSource.GetString("RefType_ParticleSystemEmitters")] = checkOne ? partSysEmitters.ToEmptyArray() : partSysEmitters.ToArray(); } if (outDict.Count == 0) @@ -1510,8 +1511,8 @@ public static async Task>> GetUnreferencedObject mainWindow.IsEnabled = false; try { - mainWindow.InitializeProgressDialog("Searching in progress...", "Please wait..."); - mainWindow.SetProgressBar(null, "Assets", 0, assets.Count); + mainWindow.InitializeProgressDialog(LocalizationSource.GetString("Msg_SearchingInProgress"), LocalizationSource.GetString("Msg_PleaseWait")); + mainWindow.SetProgressBar(null, LocalizationSource.GetString("RefType_Assets"), 0, assets.Count); mainWindow.StartProgressBarUpdater(); List>> dicts = new(); diff --git a/UndertaleModTool/Windows/GMLSettingsWindow.xaml b/UndertaleModTool/Windows/GMLSettingsWindow.xaml index 489862348..66e1d47fc 100644 --- a/UndertaleModTool/Windows/GMLSettingsWindow.xaml +++ b/UndertaleModTool/Windows/GMLSettingsWindow.xaml @@ -1,14 +1,15 @@ - + Title="{loc:Loc GML_Title}" Height="Auto" Width="700" SizeToContent="Height" WindowStartupLocation="CenterOwner"> @@ -19,8 +20,8 @@ - + @@ -33,16 +34,16 @@ - - - + - + @@ -57,11 +58,11 @@ - - - + @@ -70,22 +71,22 @@ - - - - - - @@ -99,7 +100,7 @@ - + @@ -110,23 +111,23 @@ - - - - - - @@ -141,8 +142,8 @@ - + @@ -153,20 +154,20 @@ - - - - - @@ -179,7 +180,7 @@ - Restore to defaults + diff --git a/UndertaleModTool/Windows/GMLSettingsWindow.xaml.cs b/UndertaleModTool/Windows/GMLSettingsWindow.xaml.cs index 40bb6ffd5..b514316e8 100644 --- a/UndertaleModTool/Windows/GMLSettingsWindow.xaml.cs +++ b/UndertaleModTool/Windows/GMLSettingsWindow.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -12,6 +12,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -57,10 +58,10 @@ public object Convert(object value, Type targetType, object parameter, CultureIn } return kind switch { - DecompilerSettings.IndentStyleKind.FourSpaces => "4 spaces", - DecompilerSettings.IndentStyleKind.TwoSpaces => "2 spaces", - DecompilerSettings.IndentStyleKind.Tabs => "Tabs", - _ => throw new Exception("Unknown indent style kind") + DecompilerSettings.IndentStyleKind.FourSpaces => LocalizationSource.GetString("Editor_4Spaces"), + DecompilerSettings.IndentStyleKind.TwoSpaces => LocalizationSource.GetString("Editor_2Spaces"), + DecompilerSettings.IndentStyleKind.Tabs => LocalizationSource.GetString("Editor_Tabs"), + _ => throw new Exception(LocalizationSource.GetString("Msg_UnknownIndentStyle")) }; } diff --git a/UndertaleModTool/Windows/LoaderDialog.xaml.cs b/UndertaleModTool/Windows/LoaderDialog.xaml.cs index 3870c219d..f82ab3656 100644 --- a/UndertaleModTool/Windows/LoaderDialog.xaml.cs +++ b/UndertaleModTool/Windows/LoaderDialog.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; @@ -17,6 +17,7 @@ using System.Windows.Shapes; using System.Windows.Threading; using System.Windows.Shell; +using UndertaleModTool.Localization; namespace UndertaleModTool { @@ -30,7 +31,7 @@ public partial class LoaderDialog : Window public string Message { get; set; } public bool PreventClose { get; set; } - public string StatusText { get; set; } = "Please wait..."; + public string StatusText { get; set; } = LocalizationSource.GetString("Msg_PleaseWait"); public string SavedStatusText { get; set; } public double? Maximum { @@ -143,16 +144,18 @@ public void ReportProgress(string status) { StatusText = status; } - public void ReportProgress(double value) //update without status text changing + public void ReportProgress(double value) { try { - ReportProgress(value + "/" + Maximum + (!String.IsNullOrEmpty(SavedStatusText) ? ": " + SavedStatusText : "")); + var format = !String.IsNullOrEmpty(SavedStatusText) + ? LocalizationSource.GetString("LoaderDialog_ProgressFormatWithStatus") + : LocalizationSource.GetString("LoaderDialog_ProgressFormat"); + ReportProgress(string.Format(format, value, Maximum, SavedStatusText)); UpdateValue(value); } catch { - //Silently fail... } } public void UpdateValue(double value) @@ -167,12 +170,14 @@ public void ReportProgress(string status, double value) { try { - ReportProgress(value + "/" + Maximum + (!String.IsNullOrEmpty(status) ? ": " + status : "")); //update status + var format = !String.IsNullOrEmpty(status) + ? LocalizationSource.GetString("LoaderDialog_ProgressFormatWithStatus") + : LocalizationSource.GetString("LoaderDialog_ProgressFormat"); + ReportProgress(string.Format(format, value, Maximum, status)); UpdateValue(value); } catch { - // Silently fail... } }); } diff --git a/UndertaleModTool/Windows/ProjectAssetsWindow.xaml b/UndertaleModTool/Windows/ProjectAssetsWindow.xaml index 81027d013..9efbf9323 100644 --- a/UndertaleModTool/Windows/ProjectAssetsWindow.xaml +++ b/UndertaleModTool/Windows/ProjectAssetsWindow.xaml @@ -1,20 +1,21 @@ - - - - - - + + + + + @@ -24,12 +25,8 @@ - - +