diff --git a/Ultima/Maps/MapSizes.cs b/Ultima/Maps/MapSizes.cs index e7b4126d..56e1bffa 100644 --- a/Ultima/Maps/MapSizes.cs +++ b/Ultima/Maps/MapSizes.cs @@ -17,6 +17,17 @@ namespace Ultima.Maps { + /// + /// Static helper for localizing map detection messages. + /// + public static class MapSizesLocalization + { + /// + /// Optional localization getter for error messages (set by main application). + /// + public static Func? LocalizationGetter { get; set; } + } + public readonly struct MapSize : IEquatable { public MapSize(int width, int height) @@ -135,9 +146,9 @@ private static bool TryDetect(Func resolve, int fileIndex, out M if (!TryMeasureBlocks(resolve, fileIndex, out long blocks, out string source)) { size = Fallback(fileIndex); - evidence = string.Format(CultureInfo.InvariantCulture, - "no map or statics index found for facet {0}; assuming {1}", fileIndex, size); - + string template = MapSizesLocalization.LocalizationGetter?.Invoke("Ultima.Maps.MapSizes.NoMapOrStaticsIndex") + ?? "no map or statics index found for facet {0}; assuming {1}"; + evidence = string.Format(CultureInfo.InvariantCulture, template, fileIndex, size); return false; } @@ -149,17 +160,16 @@ private static bool TryDetect(Func resolve, int fileIndex, out M } size = candidate; - evidence = string.Format(CultureInfo.InvariantCulture, - "{0} holds {1:N0} blocks, which is {2}", source, blocks, candidate); - + string template1 = MapSizesLocalization.LocalizationGetter?.Invoke("Ultima.Maps.MapSizes.HoldsBlocks") + ?? "{0} holds {1:N0} blocks, which is {2}"; + evidence = string.Format(CultureInfo.InvariantCulture, template1, source, blocks, candidate); return true; } size = Fallback(fileIndex); - evidence = string.Format(CultureInfo.InvariantCulture, - "{0} holds {1:N0} blocks, which matches no known shape for facet {2}; assuming {3}", - source, blocks, fileIndex, size); - + string template2 = MapSizesLocalization.LocalizationGetter?.Invoke("Ultima.Maps.MapSizes.HoldsBlocksNoMatch") + ?? "{0} holds {1:N0} blocks, which matches no known shape for facet {2}; assuming {3}"; + evidence = string.Format(CultureInfo.InvariantCulture, template2, source, blocks, fileIndex, size); return false; } diff --git a/UoFiddler.Controls/Classes/ClientFileSaveCommand.cs b/UoFiddler.Controls/Classes/ClientFileSaveCommand.cs index a87d79ba..6fbad45a 100644 --- a/UoFiddler.Controls/Classes/ClientFileSaveCommand.cs +++ b/UoFiddler.Controls/Classes/ClientFileSaveCommand.cs @@ -91,7 +91,35 @@ public static bool Run(Control owner, FileType type, Action writeMul, st Options.ChangedUltimaClass[dirtyKey] = false; } - FileSavedDialog.Show(owner?.FindForm(), outputDirectory, BuildMessage(result)); + // 尝试通过反射访问 LocalizationService 来获取汉化 + Func getLocalized = null; + try + { + var localizationServiceType = Type.GetType("UoFiddler.Localization.LocalizationService, UoFiddler"); + if (localizationServiceType != null) + { + var getStringMethod = localizationServiceType.GetMethod("GetString", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (getStringMethod != null) + { + getLocalized = key => (string?)getStringMethod.Invoke(null, new object[] { key }); + } + } + } + catch { } + + string title = "Saved"; + if (getLocalized != null) + { + var localizedTitle = getLocalized("Forms.FileSavedDialog.Title"); + if (localizedTitle != null) + title = localizedTitle; + } + + if (getLocalized != null) + FileSavedDialog.Show(owner?.FindForm(), outputDirectory, BuildMessage(result), title, getLocalized); + else + FileSavedDialog.Show(owner?.FindForm(), outputDirectory, BuildMessage(result)); return true; } @@ -133,7 +161,35 @@ private static bool ConfirmPreflight(Control owner, FileType type, ContainerForm sb.AppendLine().AppendLine().Append("Save anyway?"); - return MessageBox.Show(owner?.FindForm(), sb.ToString(), "Save", MessageBoxButtons.YesNo, + // 尝试获取汉化文本 + string confirmTitle = "Save"; + string confirmQuestion = "Save anyway?"; + try + { + var localizationServiceType = Type.GetType("UoFiddler.Localization.LocalizationService, UoFiddler"); + if (localizationServiceType != null) + { + var getStringMethod = localizationServiceType.GetMethod("GetString", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (getStringMethod != null) + { + var localizedTitle = getStringMethod.Invoke(null, new object[] { "Forms.ClientFileSaveCommand.SaveConfirmTitle" }); + if (localizedTitle is string title) + confirmTitle = title; + + var localizedQuestion = getStringMethod.Invoke(null, new object[] { "Forms.ClientFileSaveCommand.SaveAnywayQuestion" }); + if (localizedQuestion is string question) + confirmQuestion = question; + } + } + } + catch { } + + // 替换英文问题为汉化版本 + sb.Length -= "Save anyway?".Length; + sb.Append(confirmQuestion); + + return MessageBox.Show(owner?.FindForm(), sb.ToString(), confirmTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes; } @@ -183,7 +239,25 @@ private static void ShowError(Control owner, FileType type, Exception error) sb.AppendLine(where); } - MessageBox.Show(owner?.FindForm(), sb.ToString(), "Save failed", MessageBoxButtons.OK, + string errorTitle = "Save failed"; + try + { + var localizationServiceType = Type.GetType("UoFiddler.Localization.LocalizationService, UoFiddler"); + if (localizationServiceType != null) + { + var getStringMethod = localizationServiceType.GetMethod("GetString", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (getStringMethod != null) + { + var localizedTitle = getStringMethod.Invoke(null, new object[] { "Forms.ClientFileSaveCommand.SaveFailedTitle" }); + if (localizedTitle is string title) + errorTitle = title; + } + } + } + catch { } + + MessageBox.Show(owner?.FindForm(), sb.ToString(), errorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); } diff --git a/UoFiddler.Controls/Forms/AnimDataExportForm.cs b/UoFiddler.Controls/Forms/AnimDataExportForm.cs index 19eac838..6b610dc4 100644 --- a/UoFiddler.Controls/Forms/AnimDataExportForm.cs +++ b/UoFiddler.Controls/Forms/AnimDataExportForm.cs @@ -8,6 +8,8 @@ namespace UoFiddler.Controls.Forms { public partial class AnimDataExportForm : Form { + private Func? _localizationGetter; + public AnimDataExportForm() { InitializeComponent(); @@ -18,6 +20,30 @@ public AnimDataExportForm() }; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.AnimDataExportForm.Title") ?? "Export AnimData"; + lblAnimationsToExport.Text = _localizationGetter("Forms.AnimDataExportForm.lblAnimationsToExport") ?? "Animations to export:"; + btnExport.Text = _localizationGetter("Forms.AnimDataExportForm.btnExport") ?? "Export"; + + // 更新下拉菜单项 + var items = new List + { + _localizationGetter("Forms.AnimDataExportForm.ExportSelection.All") ?? "All (default-, blue-, and red-colored entries)", + _localizationGetter("Forms.AnimDataExportForm.ExportSelection.IncludeMissing") ?? "Include missing animation tile flag (default- and blue-colored entries)", + _localizationGetter("Forms.AnimDataExportForm.ExportSelection.OnlyValid") ?? "Only valid animations (default-colored entries)" + }; + cboExportSelection.DataSource = items; + } + private void OnClickExport(object sender, EventArgs e) { var type = "json"; @@ -48,11 +74,18 @@ private void OnClickExport(object sender, EventArgs e) var exported = ExportedAnimData.ToFile(dialog.FileName, AnimData, selection); - MessageBox.Show($"Exported {exported.Data.Count} animdata entries to: {dialog.FileName}"); + // 使用汉化的导出成功消息 + string successMessage = _localizationGetter?.Invoke("Forms.AnimDataExportForm.ExportSuccessMessage") ?? $"Exported {exported.Data.Count} animdata entries to: {dialog.FileName}"; + successMessage = string.Format(successMessage, exported.Data.Count, dialog.FileName); + MessageBox.Show(successMessage); } catch (Exception ex) { - MessageBox.Show($"Error exporting animdata: {ex.Message}", "AnimData Export", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + // 使用汉化的导出错误消息 + string errorTitle = _localizationGetter?.Invoke("Forms.AnimDataExportForm.ExportErrorTitle") ?? "AnimData Export"; + string errorMessage = _localizationGetter?.Invoke("Forms.AnimDataExportForm.ExportErrorMessage") ?? $"Error exporting animdata: {ex.Message}"; + errorMessage = string.Format(errorMessage, ex.Message); + MessageBox.Show(errorMessage, errorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } } diff --git a/UoFiddler.Controls/Forms/AnimDataImportForm.cs b/UoFiddler.Controls/Forms/AnimDataImportForm.cs index 3081a939..fdd87786 100644 --- a/UoFiddler.Controls/Forms/AnimDataImportForm.cs +++ b/UoFiddler.Controls/Forms/AnimDataImportForm.cs @@ -9,6 +9,8 @@ namespace UoFiddler.Controls.Forms { public partial class AnimDataImportForm : Form { + private Func? _localizationGetter; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Action OnAfterImport { get; set; } @@ -18,13 +20,40 @@ public AnimDataImportForm() cboConflictAction.SelectedItem = "skip"; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.AnimDataImportForm.Title") ?? "Import AnimData"; + label5.Text = _localizationGetter("Forms.AnimDataImportForm.label5") ?? "Import from:"; + btnImport.Text = _localizationGetter("Forms.AnimDataImportForm.btnImport") ?? "Import"; + label1.Text = _localizationGetter("Forms.AnimDataImportForm.label1") ?? "On conflict:"; + cbErase.Text = _localizationGetter("Forms.AnimDataImportForm.cbErase") ?? "Erase animdata before importing"; + + // 更新下拉菜单项 + var skipText = _localizationGetter("Forms.AnimDataImportForm.ConflictAction.Skip") ?? "skip"; + var overwriteText = _localizationGetter("Forms.AnimDataImportForm.ConflictAction.Overwrite") ?? "overwrite"; + + cboConflictAction.Items.Clear(); + cboConflictAction.Items.Add(skipText); + cboConflictAction.Items.Add(overwriteText); + cboConflictAction.SelectedIndex = 0; + } + private void OnClickImport(object sender, EventArgs e) { try { var fileName = txtImportFileName.Text; var imported = ExportedAnimData.FromFile(fileName); - var overwrite = cboConflictAction.Items[cboConflictAction.SelectedIndex].ToString() == "overwrite"; + // SelectedIndex: 0 = skip, 1 = overwrite + var overwrite = cboConflictAction.SelectedIndex == 1; // Create a new "working copy" AnimData to update, in case there's an exception thrown while processing. // Shallow clone is okay, as UpdateAnimdata does not modify existing entries' members. @@ -44,11 +73,18 @@ private void OnClickImport(object sender, EventArgs e) // Swallow any error from the OnAfterImport callback } - MessageBox.Show($"Imported {importCount} animdata entries from: {fileName}\n\nDo not forget to save your changes!", "AnimData Import"); + MessageBox.Show( + _localizationGetter?.Invoke("Forms.AnimDataImportForm.ImportSuccessMessage") ?? + $"Imported {importCount} animdata entries from: {fileName}\n\nDo not forget to save your changes!", + _localizationGetter?.Invoke("Forms.AnimDataImportForm.ImportSuccessTitle") ?? "AnimData Import"); } catch (Exception ex) { - MessageBox.Show($"Error importing animdata: {ex.Message}", "AnimData Import", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + var errorTitle = _localizationGetter?.Invoke("Forms.AnimDataImportForm.ImportErrorTitle") ?? "AnimData Import"; + var errorMessageFormat = _localizationGetter?.Invoke("Forms.AnimDataImportForm.ImportErrorMessage") ?? "Error importing animdata: {0}"; + var errorMessage = string.Format(errorMessageFormat, ex.Message); + + MessageBox.Show(errorMessage, errorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } diff --git a/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs b/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs index b0a1642f..24187b16 100644 --- a/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs +++ b/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs @@ -1089,7 +1089,7 @@ private void InitializeComponent() // SecondAnimColorComboBox // SecondAnimColorComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - SecondAnimColorComboBox.Items.AddRange(new object[] { "Original", "Green", "Magenta", "Cyan", "Red", "Custom…" }); + SecondAnimColorComboBox.Items.AddRange(new object[] { "原始", "绿色", "洋红色", "青色", "红色", "自定义…" }); SecondAnimColorComboBox.Location = new System.Drawing.Point(9, 98); SecondAnimColorComboBox.Name = "SecondAnimColorComboBox"; SecondAnimColorComboBox.Size = new System.Drawing.Size(172, 23); @@ -1211,7 +1211,7 @@ private void InitializeComponent() // AnimationSpeedTrackBar // AnimationSpeedTrackBar.AutoSize = false; - AnimationSpeedTrackBar.Location = new System.Drawing.Point(48, 22); + AnimationSpeedTrackBar.Location = new System.Drawing.Point(48, 35); AnimationSpeedTrackBar.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); AnimationSpeedTrackBar.Maximum = 14; AnimationSpeedTrackBar.Name = "AnimationSpeedTrackBar"; diff --git a/UoFiddler.Controls/Forms/AnimationEditForm.cs b/UoFiddler.Controls/Forms/AnimationEditForm.cs index 89006bfc..020347ca 100644 --- a/UoFiddler.Controls/Forms/AnimationEditForm.cs +++ b/UoFiddler.Controls/Forms/AnimationEditForm.cs @@ -38,6 +38,8 @@ public partial class AnimationEditForm : Form private float _zoomFactor = 1.0f; + private Func? _localizationGetter; + // Second-animation overlay state private bool _secondAnimActivated; private int _secondAnimId; @@ -114,8 +116,109 @@ public AnimationEditForm() _loaded = false; } + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Tab pages + FramePage.Text = _localizationGetter("Forms.AnimationEditForm.Tabs.Frame") ?? "Frame"; + AnimationEditPage.Text = _localizationGetter("Forms.AnimationEditForm.Tabs.PreviewEdit") ?? "Preview/Edit"; + GalleryPage.Text = _localizationGetter("Forms.AnimationEditForm.Tabs.Gallery") ?? "Gallery"; + + // TreeView context menu + addToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.Replace") ?? "Replace"; + removeToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.Remove") ?? "Remove"; + extractImagesToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ExtractImages") ?? "Extract Images.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ExtractAsFormats.Bmp") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ExtractAsFormats.Tiff") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ExtractAsFormats.Jpg") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ExtractAsFormats.Png") ?? "As Png"; + importToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.Import") ?? "Import.."; + fromvdToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ImportFromVD") ?? "From .vd"; + exportToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.Export") ?? "Export.."; + tovdToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.TreeViewContextMenu.ExportToVD") ?? "To .vd"; + + // Palette context menu + exportToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.Export") ?? "Export.."; + bmpToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.ExportBmp") ?? "Bmp"; + tiffToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.ExportTiff") ?? "Tiff"; + textToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.ExportText") ?? "Text"; + importToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.Import") ?? "Import.."; + fromTxtToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.ImportFromTxt") ?? "From Txt"; + fromGifToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.ImportFromGif") ?? "From Gif"; + fromImageToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.PaletteContextMenu.ImportFromImage") ?? "From Image"; + + // Frames list view context menu + allDirectionsAddToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.AllDirectionsAdd") ?? "All directions add"; + allDirectionsAddWithCanvasToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.AllDirectionsAddWithCanvas") ?? "All directions add with Canvas"; + addDirectionsAddWithCanvasUniqueImageToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.AllDirectionsAddWithCanvasCv5GifStyle") ?? "All directions add with Canvas ( CV5 Gif Style )"; + allDirectionsAddWithCanvasKRframeEditorColorCorrectorToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.AllDirectionsAddWithCanvasKRColorCorrector") ?? "All directions add with Canvas ( KRframeViewer Color Corrector)"; + addToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.Add") ?? "Add"; + addWithCanvasToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.AddWithCanvas") ?? "Add with Canvas"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.Replace") ?? "Replace"; + removeToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationEditForm.FramesListViewContextMenu.Remove") ?? "Remove"; + + // Frame page controls + groupBox1.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ExtraBackground") ?? "Extra background"; + BackgroundRedLabel.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.BackgroundRed") ?? "R"; + BackgroundGreenLabel.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.BackgroundGreen") ?? "G"; + BackgroundBlueLabel.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.BackgroundBlue") ?? "B"; + groupBox2.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.PaletteConverter") ?? "Palette converter"; + SetPalleteButton.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.SetPallete") ?? "Set"; + rbRGB.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorModes.RGB") ?? "RGB"; + rbRBG.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorModes.RBG") ?? "RBG"; + rbGRB.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorModes.GRB") ?? "GRB"; + rbGBR.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorModes.GBR") ?? "GBR"; + rbBGR.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorModes.BGR") ?? "BGR"; + rbBRG.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorModes.BRG") ?? "BRG"; + groupBox3.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorBalance") ?? "Color balance"; + ColorRedLabel.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorRed") ?? "R"; + ColorGreenLabel.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorGreen") ?? "G"; + ColorBlueLabel.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ColorBlue") ?? "B"; + ApplyButton.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.ApplyButton") ?? "Apply"; + LockColorControlsCheckBox.Text = _localizationGetter("Forms.AnimationEditForm.FramePage.LockColorControls") ?? "Lock"; + + // Animation Edit page controls + ZoomLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.Zoom") ?? "Zoom:"; + DrawFullRectangleToolStripButton.ToolTipText = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.DrawFullRectangle") ?? "Draw Full Rectangle"; + DrawEmptyRectangleToolStripButton.ToolTipText = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.DrawEmptyRectangle") ?? "Draw Empty Rectangle"; + DrawReferencialPointToolStripButton.ToolTipText = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.DrawReferencialPoint") ?? "Draw Referencial Point"; + SecondAnimGroupBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.SecondAnimOverlay") ?? "Second Animation Overlay"; + SecondAnimCheckBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.ShowOverlay") ?? "Show overlay"; + SecondAnimIdLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.SecondAnimFileId") ?? "ID"; + SecondAnimOpacityLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.SecondAnimOpacity") ?? "Opacity"; + SecondAnimInFrontCheckBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.OnTop") ?? "On top"; + SecondAnimBoxCheckBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.Box") ?? "Box"; + FramesGroupBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.AnimationFrames") ?? "Animation Frames"; + SpeedGroupBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.AnimationSpeed") ?? "Animation Speed"; + LocationCenterGroupBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.LocationCenter") ?? "Location Center"; + CenterXLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.CenterX") ?? "X"; + CenterYLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.CenterY") ?? "Y"; + SameCenterButton.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.SameCenter") ?? "Same Center"; + ReferencialPointGroupBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.ReferencialPoint") ?? "Referencial Point"; + RefXLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.RefX") ?? "X"; + RefYLabel.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.RefY") ?? "Y"; + ToolStripLockButton.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.LockButton") ?? "Lock"; + CoordinatesGroupBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.Coordinates") ?? "Coordinates"; + SetCoordinatesButton.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.SetCoordinates") ?? "Set"; + SaveCoordinatesCheckBox.Text = _localizationGetter("Forms.AnimationEditForm.AnimationEditPage.SaveCoordinates") ?? "Save"; + + // Status bar items + SaveToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.StatusBar.SaveMenuItem") ?? "Save"; + ShowOnlyValidToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.StatusBar.ShowOnlyValidMenuItem") ?? "Show Only Valid"; + ExportAllToVDToolStripMenuItem.Text = _localizationGetter("Forms.AnimationEditForm.StatusBar.ExportAllToVD") ?? "Export All Valid To VD"; + MiscToolStripButton.Text = _localizationGetter("Forms.AnimationEditForm.StatusBar.MiscButton") ?? "Misc"; + } + // Indexed by MobType enum: Monster=0, Sea=1, Animal=2, Human=3, Equipment=4. // Equipment composites onto a humanoid and shares the human action set. + private readonly string[][] _animNames = { new[] // Monster (22) @@ -1313,7 +1416,7 @@ private void OnClickSave(object sender, EventArgs e) AnimationEdit.Save(_fileType, Options.OutputPath); Options.ChangedUltimaClass["Animations"] = false; - MessageBox.Show($"AnimationFile saved to {Options.OutputPath}", "Saved", MessageBoxButtons.OK, + MessageBox.Show($"动画文件已保存到 {Options.OutputPath}", "已保存", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } @@ -2806,8 +2909,8 @@ private void OnClickExportAllToVD(object sender, EventArgs e) AnimationEdit.ExportToVD(_fileType, index, fileName); } - MessageBox.Show($"All Animations saved to {dialog.SelectedPath}", - "Export", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); + MessageBox.Show($"所有动画已保存到 {dialog.SelectedPath}", + "全部导出到VD", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } } diff --git a/UoFiddler.Controls/Forms/AnimationListNewEntriesForm.cs b/UoFiddler.Controls/Forms/AnimationListNewEntriesForm.cs index 3668631f..cfe5cac6 100644 --- a/UoFiddler.Controls/Forms/AnimationListNewEntriesForm.cs +++ b/UoFiddler.Controls/Forms/AnimationListNewEntriesForm.cs @@ -26,6 +26,7 @@ public partial class AnimationListNewEntriesForm : Form private readonly string[][] _actionNames; private readonly Func _isAlreadyDefinedCallback; private readonly Action _addGraphicAction; + private readonly Func _localizationGetter; private int _currentSelect; private int _facing = 1; @@ -35,8 +36,10 @@ public partial class AnimationListNewEntriesForm : Form private AnimationFrame[] _animation; public AnimationListNewEntriesForm(Func isAlreadyDefinedCallback, - Action addGraphicAction, string[][] actionNames) + Action addGraphicAction, string[][] actionNames, Func localizationGetter = null) { + _localizationGetter = localizationGetter; + InitializeComponent(); Icon = Options.GetFiddlerIcon(); @@ -190,6 +193,31 @@ private void OnLoad(object sender, EventArgs e) } tvAnimationList.EndUpdate(); + + // 应用汉化 + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) + return; + + Text = _localizationGetter("Forms.AnimationListControl.NewEntriesForm.Title") ?? "Animationlist New Entries"; + toolStripButton1.Text = _localizationGetter("Forms.AnimationListControl.NewEntriesForm.AddButton") ?? "Add"; + animateToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.NewEntriesForm.AnimateMenuitem") ?? "Animate"; + + // 更新下拉框选项 + string[] actionTypes = new[] + { + _localizationGetter("Forms.AnimationListControl.NewEntriesForm.ActionTypes.Monster") ?? "Monster", + _localizationGetter("Forms.AnimationListControl.NewEntriesForm.ActionTypes.SeaMonster") ?? "Sea Monster", + _localizationGetter("Forms.AnimationListControl.NewEntriesForm.ActionTypes.Animal") ?? "Animal", + _localizationGetter("Forms.AnimationListControl.NewEntriesForm.ActionTypes.HumanEquipment") ?? "Human/Equipment" + }; + + ComboBoxActionType.Items.Clear(); + ComboBoxActionType.Items.AddRange(actionTypes); } private void ProcessBodyConverterTable(IEnumerable table) diff --git a/UoFiddler.Controls/Forms/FileSavedDialog.cs b/UoFiddler.Controls/Forms/FileSavedDialog.cs index aebcb92d..52711bf1 100644 --- a/UoFiddler.Controls/Forms/FileSavedDialog.cs +++ b/UoFiddler.Controls/Forms/FileSavedDialog.cs @@ -13,6 +13,7 @@ namespace UoFiddler.Controls.Forms public sealed partial class FileSavedDialog : Form { private readonly string _filePath; + private Func? _localizationGetter; public FileSavedDialog(string filePath, string message = null, string title = null) { @@ -32,6 +33,37 @@ public FileSavedDialog(string filePath, string message = null, string title = nu pathLabel.Text = _filePath; } + /// + /// Sets the localization getter and applies localized text. + /// + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Applies localized text to UI elements. + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Localize title + var title = _localizationGetter("Forms.FileSavedDialog.Title"); + if (title != null) + Text = title; + + // Localize status label if not already set to custom message + var saveSuccess = _localizationGetter("Forms.FileSavedDialog.SaveSuccess"); + if (saveSuccess != null && statusLabel.Text == "File saved successfully.") + statusLabel.Text = saveSuccess; + + // Localize buttons + buttonOpenFolder.Text = _localizationGetter("Forms.FileSavedDialog.OpenFolder") ?? "Open Folder"; + buttonOk.Text = _localizationGetter("Forms.FileSavedDialog.OK") ?? "OK"; + } + private void OnOpenFolderClick(object sender, EventArgs e) { try @@ -74,6 +106,23 @@ public static void Show(IWin32Window owner, string filePath, string message = nu } } + /// + /// Shows the file saved dialog with localization support. + /// + /// The owner form. + /// The path to the saved file. + /// Optional custom success message. + /// Optional custom dialog title. + /// Localization getter function. + public static void Show(IWin32Window owner, string filePath, string message, string title, Func getLocalized) + { + using (var dialog = new FileSavedDialog(filePath, message, title)) + { + dialog.SetLocalization(getLocalized); + dialog.ShowDialog(owner); + } + } + /// /// Shows the file saved dialog without an owner. /// diff --git a/UoFiddler.Controls/Forms/HueEditForm.cs b/UoFiddler.Controls/Forms/HueEditForm.cs index f3aebe5b..b8ff00ab 100644 --- a/UoFiddler.Controls/Forms/HueEditForm.cs +++ b/UoFiddler.Controls/Forms/HueEditForm.cs @@ -25,6 +25,7 @@ public partial class HueEditForm : Form private int _selected; private int _secondSel; private Bitmap _preview; + private Func? _localizationGetter; private int Selected { @@ -70,6 +71,45 @@ public HueEditForm(int index) pictureBoxPreview.Image = new Bitmap(pictureBoxPreview.Width, pictureBoxPreview.Height); } + /// + /// 设置本地化函数 + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 窗口标题 - 动态生成,保持索引信息 + var titleFormat = _localizationGetter("Forms.HueEditForm.Title") ?? "HueEdit"; + int hueIndex = int.Parse(Text.Split('/')[0].Replace("HueEdit ", "").Trim()); + Text = $"{titleFormat} {hueIndex} / 0x{hueIndex:X}"; + + // 按钮 + ColorPickerButton.Text = _localizationGetter("Forms.HueEditForm.ColorPickerButton") ?? "Color Picker"; + SaveButton.Text = _localizationGetter("Forms.HueEditForm.SaveButton") ?? "Save"; + Spread.Text = _localizationGetter("Forms.HueEditForm.Spread") ?? "Linear Gradient"; + ExpGradientButton.Text = _localizationGetter("Forms.HueEditForm.ExpGradientButton") ?? "Exp Gradient"; + InverseButton.Text = _localizationGetter("Forms.HueEditForm.InverseButton") ?? "Inverse"; + ModifyRangeButton.Text = _localizationGetter("Forms.HueEditForm.ModifyRangeButton") ?? "Modify Range"; + SetColorButton.Text = _localizationGetter("Forms.HueEditForm.SetColorButton") ?? "Set color"; + + // 标签 + RLabel.Text = _localizationGetter("Forms.HueEditForm.RLabel") ?? "R:"; + GLabel.Text = _localizationGetter("Forms.HueEditForm.GLabel") ?? "G:"; + BLabel.Text = _localizationGetter("Forms.HueEditForm.BLabel") ?? "B:"; + + // 菜单项 + itemToolStripMenuItem.Text = _localizationGetter("Forms.HueEditForm.itemToolStripMenuItem") ?? "Item"; + animationToolStripMenuItem.Text = _localizationGetter("Forms.HueEditForm.animationToolStripMenuItem") ?? "Animation"; + gumpToolStripMenuItem.Text = _localizationGetter("Forms.HueEditForm.gumpToolStripMenuItem") ?? "Gump"; + hueOnlyGreyToolStripMenuItem.Text = _localizationGetter("Forms.HueEditForm.hueOnlyGreyToolStripMenuItem") ?? "Hue only Grey"; + } + private int GetIndex(int x) { return (int)(x * 1.0 / (pictureBox.Width * 1.0 / _colors.Length * 1.0)); diff --git a/UoFiddler.Controls/Forms/HuePopUpForm.cs b/UoFiddler.Controls/Forms/HuePopUpForm.cs index 323c04aa..132d56e4 100644 --- a/UoFiddler.Controls/Forms/HuePopUpForm.cs +++ b/UoFiddler.Controls/Forms/HuePopUpForm.cs @@ -18,6 +18,7 @@ namespace UoFiddler.Controls.Forms public partial class HuePopUpForm : Form { private readonly Action _changeHueAction; + private Func? _localizationGetter; public HuePopUpForm(Action changeHueAction, int hue) { @@ -41,6 +42,25 @@ public HuePopUpForm(Action changeHueAction, int hue) } } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.HuePopUpForm.Title") ?? "Hue Picker"; + toolStripButton1.Text = _localizationGetter("Forms.HuePopUpForm.OkButton") ?? "OK"; + toolStripButton2.Text = _localizationGetter("Forms.HuePopUpForm.ClearButton") ?? "Clear"; + HueOnlyGray.Text = _localizationGetter("Forms.HuePopUpForm.HueOnlyGrayCheckBox") ?? "Hue Only Gray (+0x8000)"; + + // 为内部 HuesControl 调用汉化 + control.SetLocalization(_localizationGetter); + } + private void Click_OK(object sender, EventArgs e) { int selected = control.Selected; diff --git a/UoFiddler.Controls/Forms/MapClearStaticsForm.cs b/UoFiddler.Controls/Forms/MapClearStaticsForm.cs index 6eebb53d..748ea5d7 100644 --- a/UoFiddler.Controls/Forms/MapClearStaticsForm.cs +++ b/UoFiddler.Controls/Forms/MapClearStaticsForm.cs @@ -19,6 +19,7 @@ public partial class MapClearStaticsForm : Form { private readonly Action _refreshMap; private readonly Ultima.Map _map; + private Func? _localizationGetter; public MapClearStaticsForm(Action refreshMap, Ultima.Map map) { @@ -35,6 +36,24 @@ public MapClearStaticsForm(Action refreshMap, Ultima.Map map) numericUpDownY2.Maximum = map.Height; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapClearStaticsForm.Title") ?? "Clear Statics"; + label2.Text = _localizationGetter("Forms.MapClearStaticsForm.X1Label") ?? "X1"; + label3.Text = _localizationGetter("Forms.MapClearStaticsForm.Y1Label") ?? "Y1"; + label4.Text = _localizationGetter("Forms.MapClearStaticsForm.X2Label") ?? "X2"; + label5.Text = _localizationGetter("Forms.MapClearStaticsForm.Y2Label") ?? "Y2"; + button1.Text = _localizationGetter("Forms.MapClearStaticsForm.ClearButton") ?? "Clear"; + } + private void OnClickClear(object sender, EventArgs e) { int blockX1 = (int)numericUpDownX1.Value; @@ -78,7 +97,9 @@ private void OnClickClear(object sender, EventArgs e) _map.ResetCache(); - MessageBox.Show("Done", "Clear Static", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); + string title = _localizationGetter?.Invoke("Forms.MapClearStaticsForm.DoneTitle") ?? "Clear Statics"; + string message = _localizationGetter?.Invoke("Forms.MapClearStaticsForm.DoneMessage") ?? "Done"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); _refreshMap(); } diff --git a/UoFiddler.Controls/Forms/MapDefragStaticsForm.Designer.cs b/UoFiddler.Controls/Forms/MapDefragStaticsForm.Designer.cs index dc1b8b0b..55de86a5 100644 --- a/UoFiddler.Controls/Forms/MapDefragStaticsForm.Designer.cs +++ b/UoFiddler.Controls/Forms/MapDefragStaticsForm.Designer.cs @@ -159,9 +159,9 @@ private void InitializeComponent() // // labelCeiling // - this.labelCeiling.Location = new System.Drawing.Point(244, 25); + this.labelCeiling.Location = new System.Drawing.Point(240, 25); this.labelCeiling.Name = "labelCeiling"; - this.labelCeiling.Size = new System.Drawing.Size(50, 17); + this.labelCeiling.Size = new System.Drawing.Size(56, 17); this.labelCeiling.TabIndex = 1; this.labelCeiling.Text = "ceiling:"; // diff --git a/UoFiddler.Controls/Forms/MapDefragStaticsForm.cs b/UoFiddler.Controls/Forms/MapDefragStaticsForm.cs index 1223bdcf..5e254045 100644 --- a/UoFiddler.Controls/Forms/MapDefragStaticsForm.cs +++ b/UoFiddler.Controls/Forms/MapDefragStaticsForm.cs @@ -32,6 +32,10 @@ public sealed partial class MapDefragStaticsForm : Form private const string DefaultCollapseIds = "0x1797-0x179C"; private readonly Map _map; + private Func? _localizationGetter; + + private string[] _idCeilingOptions; + private string[] _outOfBlockOptions; private CancellationTokenSource _cancellation; private bool _lastRunUsedFilters; @@ -45,10 +49,14 @@ public MapDefragStaticsForm(Map map, string outputPath) _map = map ?? throw new ArgumentNullException(nameof(map)); - comboBoxIdCeiling.Items.AddRange(new object[] { "tiledata", "art", "legacy 0x3FFF" }); + // 保存原始英文值用于业务逻辑 + _idCeilingOptions = new[] { "tiledata", "art", "legacy 0x3FFF" }; + _outOfBlockOptions = new[] { "Drop", "Mask to 0-7", "Keep" }; + + comboBoxIdCeiling.Items.AddRange(_idCeilingOptions); comboBoxIdCeiling.SelectedIndex = 0; - comboBoxOutOfBlock.Items.AddRange(new object[] { "Drop", "Mask to 0-7", "Keep" }); + comboBoxOutOfBlock.Items.AddRange(_outOfBlockOptions); comboBoxOutOfBlock.SelectedIndex = 0; checkBoxDropInvalidIds.Checked = true; @@ -66,6 +74,70 @@ public MapDefragStaticsForm(Map map, string outputPath) ActiveControl = buttonAnalyze; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapDefragStaticsForm.Title") ?? "Defrag Statics"; + groupBoxSource.Text = _localizationGetter("Forms.MapDefragStaticsForm.Source") ?? "Source"; + checkBoxAllowTruncation.Text = _localizationGetter("Forms.MapDefragStaticsForm.AllowTruncation") ?? "Discard the blocks past the configured map size"; + groupBoxFilters.Text = _localizationGetter("Forms.MapDefragStaticsForm.Filters") ?? "Filters"; + checkBoxDropInvalidIds.Text = _localizationGetter("Forms.MapDefragStaticsForm.DropInvalidIds") ?? "Drop statics with an unknown item id"; + labelCeiling.Text = _localizationGetter("Forms.MapDefragStaticsForm.Ceiling") ?? "ceiling:"; + labelOutOfBlock.Text = _localizationGetter("Forms.MapDefragStaticsForm.OutOfBlock") ?? "Out-of-block x/y offsets:"; + checkBoxDropInvalidZ.Text = _localizationGetter("Forms.MapDefragStaticsForm.DropInvalidZ") ?? "Drop statics at z = -128"; + checkBoxNormalizeHue.Text = _localizationGetter("Forms.MapDefragStaticsForm.NormalizeHue") ?? "Normalize negative hues to 0"; + checkBoxBelowTerrain.Text = _localizationGetter("Forms.MapDefragStaticsForm.BelowTerrain") ?? "Drop statics buried under the land tile (never drawn)"; + checkBoxRemoveDuplicates.Text = _localizationGetter("Forms.MapDefragStaticsForm.RemoveDuplicates") ?? "Remove duplicates (same id, x, y and z)"; + checkBoxDuplicatesHue.Text = _localizationGetter("Forms.MapDefragStaticsForm.DuplicatesHue") ?? "compare hue too (legacy)"; + checkBoxCollapseStacks.Text = _localizationGetter("Forms.MapDefragStaticsForm.CollapseStacks") ?? "Collapse stacked statics sharing a cell down to one"; + labelCollapseFlags.Text = _localizationGetter("Forms.MapDefragStaticsForm.CollapseFlags") ?? "flags:"; + checkBoxCollapseWet.Text = _localizationGetter("Forms.MapDefragStaticsForm.CollapseWet") ?? "Wet"; + checkBoxCollapseSurface.Text = _localizationGetter("Forms.MapDefragStaticsForm.CollapseSurface") ?? "Surface"; + checkBoxCollapseIgnoreZ.Text = _localizationGetter("Forms.MapDefragStaticsForm.CollapseIgnoreZ") ?? "ignore z"; + labelCollapseIds.Text = _localizationGetter("Forms.MapDefragStaticsForm.CollapseIds") ?? "item ids:"; + checkBoxSortTiles.Text = _localizationGetter("Forms.MapDefragStaticsForm.SortTiles") ?? "Sort the statics within each block"; + groupBoxOutput.Text = _localizationGetter("Forms.MapDefragStaticsForm.Output") ?? "Output folder"; + buttonBrowse.Text = _localizationGetter("Forms.MapDefragStaticsForm.BrowseButton") ?? "Browse..."; + buttonAnalyze.Text = _localizationGetter("Forms.MapDefragStaticsForm.AnalyzeButton") ?? "Analyze"; + buttonDefrag.Text = _localizationGetter("Forms.MapDefragStaticsForm.DefragButton") ?? "Defrag"; + buttonCancel.Text = _localizationGetter("Forms.MapDefragStaticsForm.CancelButton") ?? "Cancel"; + buttonClose.Text = _localizationGetter("Forms.MapDefragStaticsForm.CloseButton") ?? "Close"; + + // 汉化下拉菜单项 - 保存选中的原始值 + string selectedIdCeilingValue = comboBoxIdCeiling.SelectedIndex >= 0 ? _idCeilingOptions[comboBoxIdCeiling.SelectedIndex] : _idCeilingOptions[0]; + string selectedOutOfBlockValue = comboBoxOutOfBlock.SelectedIndex >= 0 ? _outOfBlockOptions[comboBoxOutOfBlock.SelectedIndex] : _outOfBlockOptions[0]; + + comboBoxIdCeiling.Items.Clear(); + foreach (string option in _idCeilingOptions) + { + string displayText = _localizationGetter($"Forms.MapDefragStaticsForm.IdCeilingOptions.{option}") ?? option; + comboBoxIdCeiling.Items.Add(displayText); + } + // 恢复选中项(通过原始值查找索引) + int idCeilingIndex = System.Array.IndexOf(_idCeilingOptions, selectedIdCeilingValue); + comboBoxIdCeiling.SelectedIndex = Math.Max(0, idCeilingIndex); + + comboBoxOutOfBlock.Items.Clear(); + foreach (string option in _outOfBlockOptions) + { + string displayText = _localizationGetter($"Forms.MapDefragStaticsForm.OutOfBlockOptions.{option}") ?? option; + comboBoxOutOfBlock.Items.Add(displayText); + } + // 恢复选中项(通过原始值查找索引) + int outOfBlockIndex = System.Array.IndexOf(_outOfBlockOptions, selectedOutOfBlockValue); + comboBoxOutOfBlock.SelectedIndex = Math.Max(0, outOfBlockIndex); + + // 重新生成源描述(包含动态消息) + DescribeSource(); + } + /// /// Shows what is about to be read and how it lines up with the configured map size. The /// engine performs the authoritative check; this is here so the mismatch is visible before @@ -78,11 +150,17 @@ private void DescribeSource() var sb = new StringBuilder(); - sb.AppendLine($"Facet {_map.FileIndex} map size {_map.Width} x {_map.Height} blocks {_map.Width >> 3} x {_map.Height >> 3}"); + string facetLabel = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.Facet") ?? "Facet"; + string mapSizeLabel = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.MapSize") ?? "map size"; + string blocksLabel = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.Blocks") ?? "blocks"; + + sb.AppendLine($"{facetLabel} {_map.FileIndex} {mapSizeLabel} {_map.Width} x {_map.Height} {blocksLabel} {_map.Width >> 3} x {_map.Height >> 3}"); if (indexPath == null || staticsPath == null) { - sb.AppendLine($"staidx{_map.FileIndex}.mul or statics{_map.FileIndex}.mul was not found in the loaded client."); + string notFoundMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.FilesNotFound") + ?? "staidx{0}.mul or statics{0}.mul was not found in the loaded client."; + sb.AppendLine(string.Format(notFoundMsg, _map.FileIndex)); textBoxSource.Text = sb.ToString(); buttonAnalyze.Enabled = false; @@ -99,7 +177,9 @@ private void DescribeSource() if (File.Exists(Path.Combine(Path.GetDirectoryName(indexPath) ?? string.Empty, $"staidx{_map.FileIndex}x.mul"))) { - sb.AppendLine($"staidx{_map.FileIndex}x.mul is present - the client prefers those override files over this pair."); + string overrideMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.OverridePresent") + ?? "staidx{0}x.mul is present - the client prefers those override files over this pair."; + sb.AppendLine(string.Format(overrideMsg, _map.FileIndex)); } textBoxSource.Text = sb.ToString(); @@ -110,10 +190,9 @@ private void DescribeSource() if (indexBlocks > configuredBlocks) { labelGeometry.ForeColor = Options.DarkMode ? Color.OrangeRed : Color.Red; - labelGeometry.Text = string.Format(CultureInfo.InvariantCulture, - "staidx holds {0:N0} blocks but the configured map size covers only {1:N0}." + Environment.NewLine + - "The statics in the surplus blocks would be discarded.", - indexBlocks, configuredBlocks); + string surplusMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.SurplusBlocks") + ?? "staidx holds {0:N0} blocks but the configured map size covers only {1:N0}. The statics in the surplus blocks would be discarded."; + labelGeometry.Text = string.Format(CultureInfo.InvariantCulture, surplusMsg, indexBlocks, configuredBlocks); _truncationAvailable = true; checkBoxAllowTruncation.Enabled = true; @@ -121,16 +200,16 @@ private void DescribeSource() else if (indexBlocks < configuredBlocks) { labelGeometry.ForeColor = SystemColors.ControlText; - labelGeometry.Text = string.Format(CultureInfo.InvariantCulture, - "staidx holds {0:N0} blocks, the configured map size covers {1:N0}." + Environment.NewLine + - "The blocks past the end of the index will be written empty.", - indexBlocks, configuredBlocks); + string emptyBlocksMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.EmptyBlocks") + ?? "staidx holds {0:N0} blocks, the configured map size covers {1:N0}. The blocks past the end of the index will be written empty."; + labelGeometry.Text = string.Format(CultureInfo.InvariantCulture, emptyBlocksMsg, indexBlocks, configuredBlocks); } else { labelGeometry.ForeColor = SystemColors.ControlText; - labelGeometry.Text = string.Format(CultureInfo.InvariantCulture, - "staidx holds {0:N0} blocks, matching the configured map size.", indexBlocks); + string matchingMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.Messages.MatchingBlocks") + ?? "staidx holds {0:N0} blocks, matching the configured map size."; + labelGeometry.Text = string.Format(CultureInfo.InvariantCulture, matchingMsg, indexBlocks); } } @@ -301,7 +380,7 @@ private void Start(bool dryRun) } catch (Exception ex) { - MessageBox.Show(this, ex.Message, "Defrag Statics", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -316,7 +395,9 @@ private void Start(bool dryRun) progressBar.Style = ProgressBarStyle.Continuous; progressBar.Value = 0; - labelStatus.Text = dryRun ? "Analyzing..." : "Defragging..."; + string analyzingMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.StatusAnalyzing") ?? "Analyzing..."; + string defraggingMsg = _localizationGetter?.Invoke("Forms.MapDefragStaticsForm.StatusDefragging") ?? "Defragging..."; + labelStatus.Text = dryRun ? analyzingMsg : defraggingMsg; worker.RunWorkerAsync(options); } @@ -375,7 +456,7 @@ private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) labelStatus.Text = "Failed."; progressBar.Value = 0; - MessageBox.Show(this, e.Error.Message, "Defrag Statics", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, e.Error.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -388,6 +469,11 @@ private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) using (var form = new MapDefragStaticsResultForm(result, _lastRunUsedFilters)) { + var getter = _localizationGetter; + if (getter != null) + { + form.SetLocalization(getter); + } form.ShowDialog(this); } } diff --git a/UoFiddler.Controls/Forms/MapDefragStaticsResultForm.cs b/UoFiddler.Controls/Forms/MapDefragStaticsResultForm.cs index 3f00fae7..15951a3d 100644 --- a/UoFiddler.Controls/Forms/MapDefragStaticsResultForm.cs +++ b/UoFiddler.Controls/Forms/MapDefragStaticsResultForm.cs @@ -22,6 +22,7 @@ public sealed partial class MapDefragStaticsResultForm : Form { private readonly StaticsDefragResult _result; private readonly bool _filtered; + private Func? _localizationGetter; public MapDefragStaticsResultForm(StaticsDefragResult result, bool filtered) { @@ -39,6 +40,24 @@ public MapDefragStaticsResultForm(StaticsDefragResult result, bool filtered) buttonOpenFolder.Enabled = !result.DryRun; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapDefragStaticsResultForm.Title") ?? "Defrag Statics - Result"; + buttonVerify.Text = _localizationGetter("Forms.MapDefragStaticsResultForm.VerifyButton") ?? "Verify output"; + buttonSave.Text = _localizationGetter("Forms.MapDefragStaticsResultForm.SaveButton") ?? "Save report..."; + buttonCopy.Text = _localizationGetter("Forms.MapDefragStaticsResultForm.CopyButton") ?? "Copy"; + buttonOpenFolder.Text = _localizationGetter("Forms.MapDefragStaticsResultForm.OpenFolderButton") ?? "Open output folder"; + buttonClose.Text = _localizationGetter("Forms.MapDefragStaticsResultForm.CloseButton") ?? "Close"; + } + /// /// Reads the output back and compares it against the file it was made from. With no filters /// the two must hold exactly the same statics; with filters the output may only be missing diff --git a/UoFiddler.Controls/Forms/MapDetailsForm.cs b/UoFiddler.Controls/Forms/MapDetailsForm.cs index ae323bbd..8af894cb 100644 --- a/UoFiddler.Controls/Forms/MapDetailsForm.cs +++ b/UoFiddler.Controls/Forms/MapDetailsForm.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Drawing; using System.Windows.Forms; using Ultima; @@ -18,6 +19,10 @@ namespace UoFiddler.Controls.Forms { public partial class MapDetailsForm : Form { + private Func? _localizationGetter; + private Map _currentMap; + private Point _point; + public MapDetailsForm(Map currentMap, Point point) { InitializeComponent(); @@ -25,17 +30,50 @@ public MapDetailsForm(Map currentMap, Point point) Icon = Options.GetFiddlerIcon(); TopMost = true; - Tile currentTile = currentMap.Tiles.GetLandTile(point.X, point.Y); - richTextBox.AppendText($"X: {point.X} Y: {point.Y}\n\n"); - richTextBox.AppendText("LandTile:\n"); - richTextBox.AppendText($"{TileData.LandTable[currentTile.Id].Name}: 0x{currentTile.Id:X} Altitude: {currentTile.Z}\n\n"); - HuedTile[] staticsAtPoint = currentMap.Tiles.GetStaticTiles(point.X, point.Y); - richTextBox.AppendText("Statics:\n"); + _currentMap = currentMap; + _point = point; + + // 先生成英文内容 + GenerateContent(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapDetailsForm.Title") ?? "MapDetails"; + + // 重新生成内容以应用汉化 + GenerateContent(); + } + + private void GenerateContent() + { + richTextBox.Clear(); + + string coordLabel = _localizationGetter?.Invoke("Forms.MapDetailsForm.Labels.Coordinate") ?? "X"; + string landTileLabel = _localizationGetter?.Invoke("Forms.MapDetailsForm.Labels.LandTile") ?? "LandTile"; + string altitudeLabel = _localizationGetter?.Invoke("Forms.MapDetailsForm.Labels.Altitude") ?? "Altitude"; + string staticsLabel = _localizationGetter?.Invoke("Forms.MapDetailsForm.Labels.Statics") ?? "Statics"; + string hueLabel = _localizationGetter?.Invoke("Forms.MapDetailsForm.Labels.Hue") ?? "Hue"; + + Tile currentTile = _currentMap.Tiles.GetLandTile(_point.X, _point.Y); + richTextBox.AppendText($"{coordLabel}: {_point.X} Y: {_point.Y}\n\n"); + richTextBox.AppendText($"{landTileLabel}:\n"); + richTextBox.AppendText($"{TileData.LandTable[currentTile.Id].Name}: 0x{currentTile.Id:X} {altitudeLabel}: {currentTile.Z}\n\n"); + HuedTile[] staticsAtPoint = _currentMap.Tiles.GetStaticTiles(_point.X, _point.Y); + richTextBox.AppendText($"{staticsLabel}:\n"); foreach (HuedTile @static in staticsAtPoint) { ushort id = @static.Id; - richTextBox.AppendText($"{TileData.ItemTable[id].Name}: 0x{id:X} Hue: {@static.Hue} Altitude: {@static.Z}\n"); + richTextBox.AppendText($"{TileData.ItemTable[id].Name}: 0x{id:X} {hueLabel}: {@static.Hue} {altitudeLabel}: {@static.Z}\n"); } } } -} \ No newline at end of file +} diff --git a/UoFiddler.Controls/Forms/MapDiffApplyResultForm.cs b/UoFiddler.Controls/Forms/MapDiffApplyResultForm.cs index 3aee339b..6f24909a 100644 --- a/UoFiddler.Controls/Forms/MapDiffApplyResultForm.cs +++ b/UoFiddler.Controls/Forms/MapDiffApplyResultForm.cs @@ -25,6 +25,7 @@ namespace UoFiddler.Controls.Forms public sealed partial class MapDiffApplyResultForm : Form { private readonly MapDiffApplyResult _result; + private Func _localizationGetter; public MapDiffApplyResultForm(MapDiffApplyResult result) { @@ -40,6 +41,24 @@ public MapDiffApplyResultForm(MapDiffApplyResult result) buttonOpenFolder.Enabled = result.OutputMapPath != null || result.OutputIndexPath != null; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + Text = _localizationGetter("Forms.MapDiffApplyResultForm.Title") ?? "Diff to Map Copy - Result"; + buttonVerify.Text = _localizationGetter("Forms.MapDiffApplyResultForm.buttonVerify") ?? "Verify output"; + buttonSave.Text = _localizationGetter("Forms.MapDiffApplyResultForm.buttonSave") ?? "Save report..."; + buttonCopy.Text = _localizationGetter("Forms.MapDiffApplyResultForm.buttonCopy") ?? "Copy"; + buttonOpenFolder.Text = _localizationGetter("Forms.MapDiffApplyResultForm.buttonOpenFolder") ?? "Open output folder"; + buttonClose.Text = _localizationGetter("Forms.MapDiffApplyResultForm.buttonClose") ?? "Close"; + } + /// /// Reads the written map back and checks every block against what it should hold: the patch /// where the diff covers a block inside the region, the unpatched map everywhere else. diff --git a/UoFiddler.Controls/Forms/MapDiffInsertForm.cs b/UoFiddler.Controls/Forms/MapDiffInsertForm.cs index 65b60a83..d55e6880 100644 --- a/UoFiddler.Controls/Forms/MapDiffInsertForm.cs +++ b/UoFiddler.Controls/Forms/MapDiffInsertForm.cs @@ -32,6 +32,8 @@ public partial class MapDiffInsertForm : Form private CancellationTokenSource _cancellation; + private Func _localizationGetter; + /// Guards the round trip between a drag on the panel and the spinners it writes to. private bool _syncingPreview; @@ -82,6 +84,77 @@ public MapDiffInsertForm(Map currentMap) ActiveControl = buttonCopy; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + Text = _localizationGetter("Forms.MapDiffInsertForm.Title") ?? "Diff to Map Copy"; + groupBoxWhat.Text = _localizationGetter("Forms.MapDiffInsertForm.groupBoxWhat") ?? "Insert"; + groupBoxFrom.Text = _localizationGetter("Forms.MapDiffInsertForm.groupBoxFrom") ?? "Region, in map tiles"; + groupBoxPreview.Text = _localizationGetter("Forms.MapDiffInsertForm.groupBoxPreview") ?? "What will be inserted - drag to choose the region"; + + checkBoxMap.Text = _localizationGetter("Forms.MapDiffInsertForm.checkBoxMap") ?? "Map"; + checkBoxStatics.Text = _localizationGetter("Forms.MapDiffInsertForm.checkBoxStatics") ?? "Statics"; + RemoveDupl.Text = _localizationGetter("Forms.MapDiffInsertForm.RemoveDupl") ?? "remove duplicates"; + checkBoxDuplicatesHue.Text = _localizationGetter("Forms.MapDiffInsertForm.checkBoxDuplicatesHue") ?? "comparing hue too (legacy)"; + checkBoxPreviewStatics.Text = _localizationGetter("Forms.MapDiffInsertForm.checkBoxPreviewStatics") ?? "Show statics"; + checkBoxPreviewPatched.Text = _localizationGetter("Forms.MapDiffInsertForm.checkBoxPreviewPatched") ?? "Mark the blocks the diff covers"; + + labelMapFormat.Text = _localizationGetter("Forms.MapDiffInsertForm.labelMapFormat") ?? "written as:"; + label1.Text = _localizationGetter("Forms.MapDiffInsertForm.label1") ?? "X1"; + label2.Text = _localizationGetter("Forms.MapDiffInsertForm.label2") ?? "Y1"; + label3.Text = _localizationGetter("Forms.MapDiffInsertForm.label3") ?? "X2"; + label4.Text = _localizationGetter("Forms.MapDiffInsertForm.label4") ?? "Y2"; + + buttonCopy.Text = _localizationGetter("Forms.MapDiffInsertForm.buttonCopy") ?? "Insert"; + buttonCancel.Text = _localizationGetter("Forms.MapDiffInsertForm.buttonCancel") ?? "Cancel"; + buttonClose.Text = _localizationGetter("Forms.MapDiffInsertForm.buttonClose") ?? "Close"; + + // 应用下拉框汉化 + ApplyComboBoxLocalization(); + + // 刷新预览区域的汉化消息 + UpdatePreview(); + } + + private string GetLocalizedMessage(string key, string defaultValue) + { + return _localizationGetter?.Invoke($"Forms.MapDiffInsertForm.Messages.{key}") ?? defaultValue; + } + + private string GetLocalizedComboBoxFormat(string key, string defaultValue) + { + return _localizationGetter?.Invoke($"Forms.MapDiffInsertForm.ComboBoxFormats.{key}") ?? defaultValue; + } + + private void ApplyComboBoxLocalization() + { + if (_localizationGetter == null || comboBoxMapFormat.Items.Count == 0) return; + + bool uop = _workingMap.Tiles.IsUOPFormat; + + // 第一项:与此客户端相同格式 + string firstItemText = uop + ? GetLocalizedComboBoxFormat("SameFormatUOP", "the same format as this client (.uop)") + : GetLocalizedComboBoxFormat("SameFormatMUL", "the same format as this client (.mul)"); + comboBoxMapFormat.Items[0] = firstItemText; + + // 其他项保持原样(包含地图索引),但 LegacyMUL 文本可以汉化 + // 第三项:map{index}LegacyMUL.uop + if (comboBoxMapFormat.Items.Count >= 3) + { + int index = _workingMap.FileIndex; + string legacyUopFormat = GetLocalizedComboBoxFormat("LegacyMUL", "LegacyMUL"); + comboBoxMapFormat.Items[2] = $"map{index}{legacyUopFormat}.uop"; + } + } + private void OnOptionChanged(object sender, EventArgs e) { comboBoxMapFormat.Enabled = checkBoxMap.Checked; @@ -125,15 +198,25 @@ private void UpdatePreview() var sb = new StringBuilder(); - sb.AppendLine($"region {region}"); + // 获取区域标签和块标签的汉化文本 + string regionLabel = GetLocalizedMessage("RegionLabel", "region"); + string blockFormat = GetLocalizedMessage("BlocksLabel", "块 {0},{1} - {2},{3},{4} x {5}"); + string diffDataFormat = GetLocalizedMessage("DiffDataLoaded", "差异数据已加载: {0:N0} 陆地块,{1:N0} 静态块"); + + // 构建输出,第一行使用汉化的块格式而不是 region.ToString() + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, + blockFormat, + region.BlockX1, region.BlockY1, region.BlockX2, region.BlockY2, + region.BlockWidth, region.BlockHeight)); sb.AppendLine(string.Format(CultureInfo.InvariantCulture, - "diff data loaded: {0:N0} land blocks, {1:N0} static blocks", + diffDataFormat, patch.LandBlocksCount, patch.StaticBlocksCount)); if (region.TileX1 != x1 || region.TileY1 != y1 || region.TileX2 != x2 || region.TileY2 != y2) { + string requestWideningFormat = GetLocalizedMessage("RequestWidened", "the request {0},{1} - {2},{3} was widened to whole 8-tile blocks"); sb.AppendLine(string.Format(CultureInfo.InvariantCulture, - "the request {0},{1} - {2},{3} was widened to whole 8-tile blocks", x1, y1, x2, y2)); + requestWideningFormat, x1, y1, x2, y2)); } textBoxPreview.Text = sb.ToString(); @@ -251,7 +334,7 @@ private void OnClickCopy(object sender, EventArgs e) SetRunning(true); progressBar1.Value = 0; - labelStatus.Text = "Inserting..."; + labelStatus.Text = GetLocalizedMessage("Inserting", "Inserting..."); worker.RunWorkerAsync(options); } @@ -290,7 +373,7 @@ private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) if (e.Error is OperationCanceledException) { progressBar1.Value = 0; - labelStatus.Text = "Cancelled. Nothing was written."; + labelStatus.Text = GetLocalizedMessage("Cancelled", "Cancelled. Nothing was written."); return; } @@ -298,7 +381,7 @@ private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) if (e.Error != null) { progressBar1.Value = 0; - labelStatus.Text = "Failed."; + labelStatus.Text = GetLocalizedMessage("Failed", "Failed."); ShowError("Diff to Map Copy", e.Error); @@ -308,10 +391,13 @@ private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) var result = (MapDiffApplyResult)e.Result; progressBar1.Value = 100; - labelStatus.Text = "Done."; + labelStatus.Text = GetLocalizedMessage("Done", "Done."); using (var form = new MapDiffApplyResultForm(result)) { + if (_localizationGetter != null) + form.SetLocalization(_localizationGetter); + form.ShowDialog(this); } } @@ -354,7 +440,7 @@ private void ShowError(string title, Exception error) private void OnClickCancel(object sender, EventArgs e) { _cancellation?.Cancel(); - labelStatus.Text = "Cancelling..."; + labelStatus.Text = GetLocalizedMessage("Cancelling", "Cancelling..."); } private void OnClickClose(object sender, EventArgs e) diff --git a/UoFiddler.Controls/Forms/MapMarkerForm.cs b/UoFiddler.Controls/Forms/MapMarkerForm.cs index 9defb5c8..d2e624eb 100644 --- a/UoFiddler.Controls/Forms/MapMarkerForm.cs +++ b/UoFiddler.Controls/Forms/MapMarkerForm.cs @@ -20,6 +20,7 @@ public partial class MapMarkerForm : Form { private readonly Action _addOverlayAction; private static Color _lastColor = Color.FromArgb(180, Color.Yellow); + private Func? _localizationGetter; public MapMarkerForm(Action addOverlayAction, int x, int y, int map) { @@ -37,6 +38,22 @@ public MapMarkerForm(Action addOverlayAction, int pictureBox1.BackColor = _lastColor; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapMarkerForm.Title") ?? "Add MapMarker"; + label1.Text = _localizationGetter("Forms.MapMarkerForm.TextLabel") ?? "Text"; + button1.Text = _localizationGetter("Forms.MapMarkerForm.ChooseColorButton") ?? "Choose Color"; + button2.Text = _localizationGetter("Forms.MapMarkerForm.SaveButton") ?? "Save"; + } + private void OnClickColor(object sender, EventArgs e) { if (colorDialog1.ShowDialog() != DialogResult.Cancel) diff --git a/UoFiddler.Controls/Forms/MapMeltStaticsForm.cs b/UoFiddler.Controls/Forms/MapMeltStaticsForm.cs index 69f265f8..52f3ff39 100644 --- a/UoFiddler.Controls/Forms/MapMeltStaticsForm.cs +++ b/UoFiddler.Controls/Forms/MapMeltStaticsForm.cs @@ -21,6 +21,7 @@ public partial class MapMeltStaticsForm : Form { private readonly Action _refreshMap; private readonly Map _map; + private Func? _localizationGetter; public MapMeltStaticsForm(Action refreshMap, Map map) { @@ -37,6 +38,24 @@ public MapMeltStaticsForm(Action refreshMap, Map map) numericUpDownY2.Maximum = map.Height; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapMeltStaticsForm.Title") ?? "Melt Statics"; + label2.Text = _localizationGetter("Forms.MapMeltStaticsForm.X1Label") ?? "X1"; + label3.Text = _localizationGetter("Forms.MapMeltStaticsForm.Y1Label") ?? "Y1"; + label4.Text = _localizationGetter("Forms.MapMeltStaticsForm.X2Label") ?? "X2"; + label5.Text = _localizationGetter("Forms.MapMeltStaticsForm.Y2Label") ?? "Y2"; + button1.Text = _localizationGetter("Forms.MapMeltStaticsForm.MeltButton") ?? "Melt"; + } + private void OnClickMelt(object sender, EventArgs e) { SaveFileDialog dialog = new SaveFileDialog @@ -122,7 +141,9 @@ private void OnClickMelt(object sender, EventArgs e) _map.ResetCache(); - MessageBox.Show("Done", "Melt Static", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); + string title = _localizationGetter?.Invoke("Forms.MapMeltStaticsForm.DoneTitle") ?? "Melt Static"; + string message = _localizationGetter?.Invoke("Forms.MapMeltStaticsForm.DoneMessage") ?? "Done"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); _refreshMap(); } diff --git a/UoFiddler.Controls/Forms/MapRegionCopyResultForm.cs b/UoFiddler.Controls/Forms/MapRegionCopyResultForm.cs index 2ec9e964..b7ad1e42 100644 --- a/UoFiddler.Controls/Forms/MapRegionCopyResultForm.cs +++ b/UoFiddler.Controls/Forms/MapRegionCopyResultForm.cs @@ -24,6 +24,7 @@ namespace UoFiddler.Controls.Forms public sealed partial class MapRegionCopyResultForm : Form { private readonly MapRegionCopyResult _result; + private Func? _localizationGetter; public MapRegionCopyResultForm(MapRegionCopyResult result) { @@ -39,6 +40,24 @@ public MapRegionCopyResultForm(MapRegionCopyResult result) buttonOpenFolder.Enabled = result.OutputMapPath != null || result.OutputIndexPath != null; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapRegionCopyResultForm.Title") ?? "Map and Statics Copy - Result"; + buttonVerify.Text = _localizationGetter("Forms.MapRegionCopyResultForm.VerifyButton") ?? "Verify output"; + buttonSave.Text = _localizationGetter("Forms.MapRegionCopyResultForm.SaveButton") ?? "Save report..."; + buttonCopy.Text = _localizationGetter("Forms.MapRegionCopyResultForm.CopyButton") ?? "Copy"; + buttonOpenFolder.Text = _localizationGetter("Forms.MapRegionCopyResultForm.OpenFolderButton") ?? "Open output folder"; + buttonClose.Text = _localizationGetter("Forms.MapRegionCopyResultForm.CloseButton") ?? "Close"; + } + /// /// Reads the written map back and checks it block by block: inside the pasted rectangle it /// has to match the source, everywhere else the map it was made from. diff --git a/UoFiddler.Controls/Forms/MapReplaceForm.Designer.cs b/UoFiddler.Controls/Forms/MapReplaceForm.Designer.cs index 6383be9e..40006905 100644 --- a/UoFiddler.Controls/Forms/MapReplaceForm.Designer.cs +++ b/UoFiddler.Controls/Forms/MapReplaceForm.Designer.cs @@ -504,24 +504,24 @@ private void InitializeComponent() // progressBar1 // this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.progressBar1.Location = new System.Drawing.Point(12, 884); + this.progressBar1.Location = new System.Drawing.Point(12, 844); this.progressBar1.Name = "progressBar1"; - this.progressBar1.Size = new System.Drawing.Size(1440, 18); + this.progressBar1.Size = new System.Drawing.Size(1432, 18); this.progressBar1.TabIndex = 5; // // labelStatus // this.labelStatus.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.labelStatus.AutoEllipsis = true; - this.labelStatus.Location = new System.Drawing.Point(12, 907); + this.labelStatus.Location = new System.Drawing.Point(12, 867); this.labelStatus.Name = "labelStatus"; - this.labelStatus.Size = new System.Drawing.Size(1440, 17); + this.labelStatus.Size = new System.Drawing.Size(1432, 17); this.labelStatus.TabIndex = 6; // // buttonCopy // this.buttonCopy.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.buttonCopy.Location = new System.Drawing.Point(1204, 932); + this.buttonCopy.Location = new System.Drawing.Point(1196, 892); this.buttonCopy.Name = "buttonCopy"; this.buttonCopy.Size = new System.Drawing.Size(80, 28); this.buttonCopy.TabIndex = 7; @@ -533,7 +533,7 @@ private void InitializeComponent() // this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonCancel.Enabled = false; - this.buttonCancel.Location = new System.Drawing.Point(1292, 932); + this.buttonCancel.Location = new System.Drawing.Point(1284, 892); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(72, 28); this.buttonCancel.TabIndex = 8; @@ -545,7 +545,7 @@ private void InitializeComponent() // this.buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.buttonClose.Location = new System.Drawing.Point(1380, 932); + this.buttonClose.Location = new System.Drawing.Point(1372, 892); this.buttonClose.Name = "buttonClose"; this.buttonClose.Size = new System.Drawing.Size(80, 28); this.buttonClose.TabIndex = 9; @@ -564,7 +564,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.buttonClose; - this.ClientSize = new System.Drawing.Size(1464, 972); + this.ClientSize = new System.Drawing.Size(1464, 1012); this.Controls.Add(this.groupBoxSource); this.Controls.Add(this.groupBoxWhat); this.Controls.Add(this.groupBoxFrom); diff --git a/UoFiddler.Controls/Forms/MapReplaceForm.cs b/UoFiddler.Controls/Forms/MapReplaceForm.cs index 4c6c7674..a5f13425 100644 --- a/UoFiddler.Controls/Forms/MapReplaceForm.cs +++ b/UoFiddler.Controls/Forms/MapReplaceForm.cs @@ -31,6 +31,8 @@ namespace UoFiddler.Controls.Forms public partial class MapReplaceForm : Form { private readonly Map _workingMap; + private Func? _localizationGetter; + private bool _initialized = false; private CancellationTokenSource _cancellation; private MapSize _detectedSize; @@ -58,6 +60,82 @@ public partial class MapReplaceForm : Form private ZSurveyRequest _zPending; private CancellationTokenSource _zCancellation; + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + CompleteInitialization(); // 完成初始化,确保动态消息被汉化 + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapReplaceForm.Title") ?? $"Map and Statics Copy - into map {_workingMap.FileIndex}"; + groupBoxSource.Text = _localizationGetter("Forms.MapReplaceForm.GroupBoxSource") ?? "Copy from"; + labelFolder.Text = _localizationGetter("Forms.MapReplaceForm.LabelFolder") ?? "Folder:"; + buttonBrowse.Text = _localizationGetter("Forms.MapReplaceForm.ButtonBrowse") ?? "Browse..."; + labelMap.Text = _localizationGetter("Forms.MapReplaceForm.LabelMap") ?? "Map:"; + groupBoxWhat.Text = _localizationGetter("Forms.MapReplaceForm.GroupBoxWhat") ?? "Copy"; + checkBoxMap.Text = _localizationGetter("Forms.MapReplaceForm.CheckBoxMap") ?? "Map"; + labelMapFormat.Text = _localizationGetter("Forms.MapReplaceForm.LabelMapFormat") ?? "written as:"; + checkBoxStatics.Text = _localizationGetter("Forms.MapReplaceForm.CheckBoxStatics") ?? "Statics"; + RemoveDupl.Text = _localizationGetter("Forms.MapReplaceForm.RemoveDupl") ?? "remove duplicates"; + checkBoxDuplicatesHue.Text = _localizationGetter("Forms.MapReplaceForm.CheckBoxDuplicatesHue") ?? "comparing hue too (legacy)"; + groupBoxFrom.Text = _localizationGetter("Forms.MapReplaceForm.GroupBoxFrom") ?? "From region, in source map tiles"; + label1.Text = _localizationGetter("Forms.MapReplaceForm.Label1") ?? "X1"; + label2.Text = _localizationGetter("Forms.MapReplaceForm.Label2") ?? "Y1"; + label3.Text = _localizationGetter("Forms.MapReplaceForm.Label3") ?? "X2"; + label4.Text = _localizationGetter("Forms.MapReplaceForm.Label4") ?? "Y2"; + groupBoxTo.Text = _localizationGetter("Forms.MapReplaceForm.GroupBoxTo") ?? "To position, in this map's tiles"; + label6.Text = _localizationGetter("Forms.MapReplaceForm.Label6") ?? "X"; + label7.Text = _localizationGetter("Forms.MapReplaceForm.Label7") ?? "Y"; + labelZAdjust.Text = _localizationGetter("Forms.MapReplaceForm.LabelZAdjust") ?? "Z adjust"; + checkBoxZClamp.Text = _localizationGetter("Forms.MapReplaceForm.CheckBoxZClamp") ?? "hold what passes the limit"; + groupBoxPreview.Text = _localizationGetter("Forms.MapReplaceForm.GroupBoxPreview") ?? "What will be copied"; + labelSourcePreview.Text = _localizationGetter("Forms.MapReplaceForm.LabelSourcePreview") ?? "From - drag to choose the region"; + labelTargetPreview.Text = _localizationGetter("Forms.MapReplaceForm.LabelTargetPreview") ?? "To - drag to place it"; + checkBoxPreviewStatics.Text = _localizationGetter("Forms.MapReplaceForm.CheckBoxPreviewStatics") ?? "Show statics"; + checkBoxPreviewOverlay.Text = _localizationGetter("Forms.MapReplaceForm.CheckBoxPreviewOverlay") ?? "Show the piece in place"; + buttonCopy.Text = _localizationGetter("Forms.MapReplaceForm.ButtonCopy") ?? "Copy"; + buttonCancel.Text = _localizationGetter("Forms.MapReplaceForm.ButtonCancel") ?? "Cancel"; + buttonClose.Text = _localizationGetter("Forms.MapReplaceForm.ButtonClose") ?? "Close"; + + // 汉化 ComboBox 项(类型 2 - 动态列表项汉化) + ApplyComboBoxLocalization(); + + // 刷新预览消息,应用动态文本汉化(类型 8 - 运行时动态文本) + UpdatePreview(); + } + + private void ApplyComboBoxLocalization() + { + if (_localizationGetter == null) return; + + bool uop = _workingMap.Tiles.IsUOPFormat; + + // 清空并重新添加翻译后的项 + comboBoxMapFormat.Items.Clear(); + + // 第一项:与客户端相同格式 + string sameFormatKey = uop ? "SameFormatUOP" : "SameFormatMUL"; + string sameFormatText = _localizationGetter($"Forms.MapReplaceForm.ComboBoxFormats.{sameFormatKey}") + ?? (uop ? "the same format as this client (.uop)" : "the same format as this client (.mul)"); + comboBoxMapFormat.Items.Add(sameFormatText); + + // 第二项:map{0}.mul + string mapMulTemplate = _localizationGetter("Forms.MapReplaceForm.ComboBoxFormats.MapMUL") + ?? "map{0}.mul"; + comboBoxMapFormat.Items.Add(string.Format(mapMulTemplate, _workingMap.FileIndex)); + + // 第三项:map{0}LegacyMUL.uop + string legacyTemplate = _localizationGetter("Forms.MapReplaceForm.ComboBoxFormats.LegacyMUL") + ?? "map{0}LegacyMUL.uop"; + comboBoxMapFormat.Items.Add(string.Format(legacyTemplate, _workingMap.FileIndex)); + + comboBoxMapFormat.SelectedIndex = ClientFileSaveFormats.DefaultIndex(Options.SaveFormat); + } + public MapReplaceForm(Map currentMap) { InitializeComponent(); @@ -115,11 +193,24 @@ public MapReplaceForm(Map currentMap) _zWorker.DoWork += OnZWorkerDoWork; _zWorker.RunWorkerCompleted += OnZWorkerCompleted; - OnSourceMapChanged(this, EventArgs.Empty); - OnOptionChanged(this, EventArgs.Empty); + // 不在这里初始化,等待 SetLocalization 被调用 + // OnSourceMapChanged(this, EventArgs.Empty); + // OnOptionChanged(this, EventArgs.Empty); ActiveControl = buttonBrowse; } + + /// + /// 完成初始化 - 在 SetLocalization 之后调用 + /// + private void CompleteInitialization() + { + if (_initialized) return; + _initialized = true; + + OnSourceMapChanged(this, EventArgs.Empty); + OnOptionChanged(this, EventArgs.Empty); + } private SupportedMap SelectedMap => comboBoxMapID.SelectedItem as SupportedMap; @@ -173,7 +264,8 @@ private void OnClickBrowse(object sender, EventArgs e) { using (var dialog = new FolderBrowserDialog { - Description = "Select the folder holding the map files to copy from", + Description = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.SelectFolderDialog") + ?? "Select the folder holding the map files to copy from", ShowNewFolderButton = false, SelectedPath = Directory.Exists(textBoxFolder.Text) ? textBoxFolder.Text : string.Empty }) @@ -248,19 +340,41 @@ private void Detect() RebuildSourceMap(map); - labelDetected.Text = _detectionKnown ? $"folder holds {_detectedSize}" : "size not recognised"; + string sizeNotRecognised = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.SizeNotRecognised") ?? "size not recognised"; + string folderHoldsTemplate = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.FolderHolds") ?? "folder holds {0}"; + + labelDetected.Text = _detectionKnown ? string.Format(folderHoldsTemplate, _detectedSize) : sizeNotRecognised; if (_detectionKnown && _detectedSize.Width == map.Width && _detectedSize.Height == map.Height) { labelSizeWarning.ForeColor = SystemColors.ControlText; - labelSizeWarning.Text = _detectionEvidence; + // 将 evidence 中的英文消息汉化 + string localizedEvidence = _detectionEvidence; + if (_localizationGetter != null && _detectionEvidence != null) + { + string template = _localizationGetter("Forms.MapReplaceForm.PreviewMessages.NoMapOrStaticsIndex") + ?? "no map or statics index found for facet {0}; assuming {1}"; + // 尝试汉化 evidence 中的模板消息 + localizedEvidence = _detectionEvidence.Replace( + "no map or statics index found for facet", + template.Split(';')[0]); + } + labelSizeWarning.Text = localizedEvidence; } else { labelSizeWarning.ForeColor = Options.DarkMode ? Color.OrangeRed : Color.Red; - labelSizeWarning.Text = _detectionKnown - ? $"This folder holds {_detectedSize} for map {map.Id}, not the {map.Width}x{map.Height} selected.{Environment.NewLine}Pick the entry that matches." - : _detectionEvidence; + if (_detectionKnown) + { + string template = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.SizeCheckMismatch") + ?? "This folder holds {0} for map {1}, not the {2} selected.{3}Pick the entry that matches."; + labelSizeWarning.Text = string.Format(template, _detectedSize, map.Id, + $"{map.Width}x{map.Height}", Environment.NewLine); + } + else + { + labelSizeWarning.Text = _detectionEvidence; + } } UpdatePreview(); @@ -326,7 +440,7 @@ private void UpdatePreviewRectangles() if (map == null) { - textBoxPreview.Text = "Choose a folder and a map."; + textBoxPreview.Text = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.ChooseFolderAndMap") ?? "Choose a folder and a map."; return; } @@ -356,17 +470,24 @@ private void UpdatePreviewRectangles() var sb = new StringBuilder(); // The panels carry the shape now, so this is only the numbers. - sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "from {0},{1} - {2},{3} {4} x {5} blocks", + string sourceFormat = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.SourceFormat") + ?? "from {0},{1} - {2},{3} {4} x {5} blocks"; + string destinationFormat = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.DestinationFormat") + ?? "to {0},{1} - {2},{3}"; + string widenedFormat = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.WidenedFormat") + ?? "{0},{1} - {2},{3} widened to whole blocks"; + + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, sourceFormat, source.TileX1, source.TileY1, source.TileX2, source.TileY2, source.BlockWidth, source.BlockHeight)); - sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "to {0},{1} - {2},{3}", + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, destinationFormat, destination.TileX1, destination.TileY1, destination.TileX2, destination.TileY2)); bool snapped = source.TileX1 != x1 || source.TileY1 != y1 || source.TileX2 != x2 || source.TileY2 != y2; if (snapped) { - sb.AppendLine(string.Format(CultureInfo.InvariantCulture, - "{0},{1} - {2},{3} widened to whole blocks", x1, y1, x2, y2)); + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, widenedFormat, + x1, y1, x2, y2)); } textBoxPreview.Text = sb.ToString(); @@ -406,7 +527,7 @@ private void RebuildSourceMap(SupportedMap map) previewSource.Map = _sourceMap; previewSource.MapSize = map == null ? default : new MapSize(map.Width, map.Height); - previewSource.Message = _sourceMap == null ? "Choose a folder to copy from" : null; + previewSource.Message = _sourceMap == null ? (_localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.ChooseFolderToCopyFrom") ?? "Choose a folder to copy from") : null; previewTarget.OverlayMap = checkBoxPreviewOverlay.Checked ? _sourceMap : null; } @@ -519,7 +640,8 @@ private void UpdateZ() _zPending = request; labelZRange.ForeColor = SystemColors.ControlText; - labelZRange.Text = "reading the heights in this region..."; + labelZRange.Text = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.ReadingHeights") + ?? "reading the heights in this region..."; _zDebounce.Stop(); _zDebounce.Start(); @@ -623,13 +745,19 @@ private void ShowZ() // Kept short: this sits on one line beside the spinners, and an ellipsis in the middle // of the warning is worse than no warning at all. - sb.Append(CultureInfo.InvariantCulture, - $"region z land {Span(_zSurvey.Land, 0)}, statics {Span(_zSurvey.Statics, 0)}"); + string regionTemplate = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.RegionZ") + ?? "region z land {0}, statics {1}"; + + sb.Append(string.Format(CultureInfo.InvariantCulture, + regionTemplate, Span(_zSurvey.Land, 0), Span(_zSurvey.Statics, 0))); if (adjust != 0) { - sb.Append(CultureInfo.InvariantCulture, - $" -> land {Span(_zSurvey.Land, adjust)}, statics {Span(_zSurvey.Statics, adjust)}"); + string adjustTemplate = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.RegionZAdjusted") + ?? " -> land {0}, statics {1}"; + + sb.Append(string.Format(CultureInfo.InvariantCulture, + adjustTemplate, Span(_zSurvey.Land, adjust), Span(_zSurvey.Statics, adjust))); } long past = _zSurvey.OutOfRange(adjust); @@ -642,18 +770,24 @@ private void ShowZ() return; } - sb.Append(CultureInfo.InvariantCulture, - $" - {past:N0} past the limit, {(checkBoxZClamp.Checked ? "held there" : "refused")}"); + string limitTemplate = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.RegionZOutOfRange") + ?? " - {0:N0} past the limit, {1}"; + string limitAction = checkBoxZClamp.Checked + ? (_localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.HeldThere") ?? "held there") + : (_localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.Refused") ?? "refused"); + + sb.Append(string.Format(CultureInfo.InvariantCulture, + limitTemplate, past, limitAction)); labelZRange.ForeColor = Options.DarkMode ? Color.OrangeRed : Color.Red; labelZRange.Text = sb.ToString(); } - private static string Span(ZHistogram z, int adjust) + private string Span(ZHistogram z, int adjust) { if (!z.HasTiles) { - return "none"; + return _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.StaticsNone") ?? "none"; } return string.Format(CultureInfo.InvariantCulture, "{0}..{1}", @@ -682,30 +816,34 @@ private void OnClickCopy(object sender, EventArgs e) if (!Directory.Exists(textBoxFolder.Text)) { - Fail("Choose the folder holding the map files to copy from."); - + string msg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.ChooseFolderForCopy") + ?? "Choose the folder holding the map files to copy from."; + Fail(msg); return; } if (!checkBoxMap.Checked && !checkBoxStatics.Checked) { - Fail("Nothing is selected to copy."); - + string msg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.NothingSelected") + ?? "Nothing is selected to copy."; + Fail(msg); return; } if (!_detectionKnown) { - Fail($"The size of map {map.Id} in that folder could not be worked out.{Environment.NewLine}{Environment.NewLine}{_detectionEvidence}"); + string msg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.SizeCouldNotBeDetermined") + ?? $"The size of map {map.Id} in that folder could not be worked out.{Environment.NewLine}{Environment.NewLine}{_detectionEvidence}"; + Fail(string.Format(msg, map.Id, Environment.NewLine, _detectionEvidence)); return; } if (_detectedSize.Width != map.Width || _detectedSize.Height != map.Height) { - Fail($"That folder holds {_detectedSize} for map {map.Id}, but {map} is selected." + - $"{Environment.NewLine}{Environment.NewLine}{_detectionEvidence}" + - $"{Environment.NewLine}{Environment.NewLine}Pick the entry that matches, or the wrong blocks will be read."); + string template = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.SizeMismatchError") + ?? $"That folder holds {{0}} for map {{1}}, but {{2}} is selected.{{3}}{{3}}{{4}}{{3}}{{3}}Pick the entry that matches, or the wrong blocks will be read."; + Fail(string.Format(template, _detectedSize, map.Id, map, Environment.NewLine, _detectionEvidence)); return; } @@ -719,7 +857,9 @@ private void OnClickCopy(object sender, EventArgs e) SetRunning(true); progressBar1.Value = 0; - labelStatus.Text = "Copying..."; + string copyingMsg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.Copying") + ?? "Copying..."; + labelStatus.Text = copyingMsg; worker.RunWorkerAsync(options); } @@ -787,8 +927,16 @@ private void OnProgress(MapCopyProgress progress) } progressBar1.Value = Math.Min(100, Math.Max(0, (int)(progress.BlocksDone * 100L / progress.BlocksTotal))); + + string stageText = progress.Stage switch + { + "Copying land" => _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.CopyingLand") ?? "Copying land", + "Copying statics" => _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.CopyingStatics") ?? "Copying statics", + _ => progress.Stage + }; + labelStatus.Text = string.Format(CultureInfo.InvariantCulture, "{0}: {1:N0} of {2:N0} blocks", - progress.Stage, progress.BlocksDone, progress.BlocksTotal); + stageText, progress.BlocksDone, progress.BlocksTotal); } private void OnWorkerDoWork(object sender, DoWorkEventArgs e) @@ -803,27 +951,47 @@ private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) if (e.Error is OperationCanceledException) { progressBar1.Value = 0; - labelStatus.Text = "Cancelled. Nothing was written."; - + string cancelledMsg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.Cancelled") + ?? "Cancelled. Nothing was written."; + labelStatus.Text = cancelledMsg; return; } if (e.Error != null) { progressBar1.Value = 0; - labelStatus.Text = "Failed."; + string failedMsg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.Failed") + ?? "Failed."; + labelStatus.Text = failedMsg; ShowError("Map and Statics Copy", e.Error); - return; } var result = (MapRegionCopyResult)e.Result; progressBar1.Value = 100; - labelStatus.Text = "Done."; + string doneMsg = _localizationGetter?.Invoke("Forms.MapReplaceForm.PreviewMessages.Done") + ?? "Done."; + labelStatus.Text = doneMsg; using (var form = new MapRegionCopyResultForm(result)) { + // 通过反射尝试获取 LocalizationService 的 GetString 方法 + try + { + var localizationServiceType = Type.GetType("UoFiddler.Localization.LocalizationService, UoFiddler"); + if (localizationServiceType != null) + { + var getStringMethod = localizationServiceType.GetMethod("GetString", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (getStringMethod != null) + { + form.SetLocalization(key => (string?)getStringMethod.Invoke(null, new object[] { key })); + } + } + } + catch { } + form.ShowDialog(this); } } @@ -887,7 +1055,9 @@ private void SetRunning(bool running) private void Fail(string message) { - MessageBox.Show(this, message, "Map and Statics Copy", MessageBoxButtons.OK, MessageBoxIcon.Error); + string title = _localizationGetter?.Invoke("Forms.MapReplaceForm.WindowTitle") + ?? "Map and Statics Copy"; + MessageBox.Show(this, message, title, MessageBoxButtons.OK, MessageBoxIcon.Error); } protected override void OnFormClosing(FormClosingEventArgs e) diff --git a/UoFiddler.Controls/Forms/MapReplaceTilesForm.cs b/UoFiddler.Controls/Forms/MapReplaceTilesForm.cs index b9209a7f..1f30fe87 100644 --- a/UoFiddler.Controls/Forms/MapReplaceTilesForm.cs +++ b/UoFiddler.Controls/Forms/MapReplaceTilesForm.cs @@ -23,6 +23,7 @@ public partial class MapReplaceTilesForm : Form { private readonly Map _map; private List _toReplace; + private Func? _localizationGetter; public MapReplaceTilesForm(Map map) { @@ -31,6 +32,21 @@ public MapReplaceTilesForm(Map map) _map = map; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.MapReplaceTilesForm.Title") ?? "Map - replace tiles"; + label5.Text = _localizationGetter("Forms.MapReplaceTilesForm.ReplaceFromLabel") ?? "Replace From"; + button2.Text = _localizationGetter("Forms.MapReplaceTilesForm.ReplaceButton") ?? "Replace"; + } + private void OnReplace(object sender, EventArgs e) { using (new WaitCursorScope(this)) diff --git a/UoFiddler.Controls/Forms/MultisHelpForm.Designer.cs b/UoFiddler.Controls/Forms/MultisHelpForm.Designer.cs index 7401afd0..85e61ee5 100644 --- a/UoFiddler.Controls/Forms/MultisHelpForm.Designer.cs +++ b/UoFiddler.Controls/Forms/MultisHelpForm.Designer.cs @@ -51,12 +51,12 @@ private void InitializeComponent() // // _columnKey // - _columnKey.Text = "Shortcut / Control"; + _columnKey.Text = "快捷方式 / 控制"; _columnKey.Width = 180; // // _columnAction // - _columnAction.Text = "Action"; + _columnAction.Text = "操作"; _columnAction.Width = 420; // // _btnClose @@ -67,7 +67,7 @@ private void InitializeComponent() _btnClose.Name = "_btnClose"; _btnClose.Size = new System.Drawing.Size(75, 27); _btnClose.TabIndex = 1; - _btnClose.Text = "Close"; + _btnClose.Text = "关闭"; _btnClose.UseVisualStyleBackColor = true; // // MultisHelpForm @@ -85,7 +85,7 @@ private void InitializeComponent() Padding = new System.Windows.Forms.Padding(8); ShowInTaskbar = false; StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - Text = "Multis — Keyboard Shortcuts & Controls"; + Text = "多重组件 — 键盘快捷方式和控制"; ResumeLayout(false); } diff --git a/UoFiddler.Controls/Forms/MultisHelpForm.cs b/UoFiddler.Controls/Forms/MultisHelpForm.cs index e41f2a2c..2d5a3c49 100644 --- a/UoFiddler.Controls/Forms/MultisHelpForm.cs +++ b/UoFiddler.Controls/Forms/MultisHelpForm.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Drawing; using System.Windows.Forms; using UoFiddler.Controls.Classes; @@ -17,27 +18,74 @@ namespace UoFiddler.Controls.Forms { public partial class MultisHelpForm : Form { + private Func? _localizationGetter; + public MultisHelpForm() { InitializeComponent(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) + { + // 如果没有汉化,使用默认英文 + PopulateShortcuts(); + return; + } + + // 应用窗口标题 + Text = _localizationGetter("Forms.MultisControl.HelpForm.Title") ?? "Multis — Keyboard Shortcuts & Controls"; + + // 应用列标题 + _columnKey.Text = _localizationGetter("Forms.MultisControl.HelpForm.ColumnShortcut") ?? "Shortcut / Control"; + _columnAction.Text = _localizationGetter("Forms.MultisControl.HelpForm.ColumnAction") ?? "Action"; + + // 应用按钮文本 + _btnClose.Text = _localizationGetter("Forms.MultisControl.HelpForm.ButtonClose") ?? "Close"; + + // 清除并重新填充列表 + _listView.Items.Clear(); PopulateShortcuts(); } private void PopulateShortcuts() { - AddHeader("Preview"); - Add("Fit preview to window", "Toggle button on toolbar — scales to fit or shows at 100% with scrollbars"); - - AddHeader("Zoom (100% mode only)"); - Add("Ctrl + Mouse Wheel", "Zoom In / Out"); - Add("Shift + = (Plus key)", "Zoom In"); - Add("- (Minus key)", "Zoom Out"); - Add("Numpad +", "Zoom In"); - Add("Numpad -", "Zoom Out"); - Add("Ctrl + 0", "Reset zoom to 100%"); - - AddHeader("Panning (100% mode only)"); - Add("Left-click drag", "Pan the view"); + // Preview 部分 + AddHeader(GetLocalizedString("Forms.MultisControl.HelpForm.HeaderPreview", "Preview")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.PreviewFit", "Fit preview to window"), + GetLocalizedString("Forms.MultisControl.HelpForm.PreviewFitDesc", "Toggle button on toolbar — scales to fit or shows at 100% with scrollbars")); + + // Zoom 部分 + AddHeader(GetLocalizedString("Forms.MultisControl.HelpForm.HeaderZoom", "Zoom (100% mode only)")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.ZoomCtrlWheel", "Ctrl + Mouse Wheel"), + GetLocalizedString("Forms.MultisControl.HelpForm.ZoomCtrlWheelDesc", "Zoom In / Out")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.ZoomShiftPlus", "Shift + = (Plus key)"), + GetLocalizedString("Forms.MultisControl.HelpForm.ZoomShiftPlusDesc", "Zoom In")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.ZoomMinus", "- (Minus key)"), + GetLocalizedString("Forms.MultisControl.HelpForm.ZoomMinusDesc", "Zoom Out")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.ZoomNumpadPlus", "Numpad +"), + GetLocalizedString("Forms.MultisControl.HelpForm.ZoomNumpadPlusDesc", "Zoom In")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.ZoomNumpadMinus", "Numpad -"), + GetLocalizedString("Forms.MultisControl.HelpForm.ZoomNumpadMinusDesc", "Zoom Out")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.ZoomCtrl0", "Ctrl + 0"), + GetLocalizedString("Forms.MultisControl.HelpForm.ZoomCtrl0Desc", "Reset zoom to 100%")); + + // Panning 部分 + AddHeader(GetLocalizedString("Forms.MultisControl.HelpForm.HeaderPanning", "Panning (100% mode only)")); + Add(GetLocalizedString("Forms.MultisControl.HelpForm.PanningLeftDrag", "Left-click drag"), + GetLocalizedString("Forms.MultisControl.HelpForm.PanningLeftDragDesc", "Pan the view")); + } + + private string GetLocalizedString(string key, string fallback) + { + return _localizationGetter?.Invoke(key) ?? fallback; } private void AddHeader(string text) diff --git a/UoFiddler.Controls/Forms/TileDataFilterForm.cs b/UoFiddler.Controls/Forms/TileDataFilterForm.cs index 830d2c78..278d9209 100644 --- a/UoFiddler.Controls/Forms/TileDataFilterForm.cs +++ b/UoFiddler.Controls/Forms/TileDataFilterForm.cs @@ -10,6 +10,7 @@ ***************************************************************************/ using System; +using System.Collections.Generic; using System.Windows.Forms; using Ultima; using UoFiddler.Controls.Classes; @@ -20,6 +21,7 @@ public partial class TileDataFilterForm : Form { private readonly Action _applyItemFilterAction; private readonly Action _applyLandFilterAction; + private Func? _localizationGetter; public TileDataFilterForm(Action applyItemFilterAction, Action applyLandFilterAction) { @@ -32,6 +34,125 @@ public TileDataFilterForm(Action applyItemFilterAction, Action + /// Sets the localization getter and applies localized text. + /// + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Applies localized text to UI elements. + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Localize title + var title = _localizationGetter("Forms.TileDataFilterForm.Title"); + if (title != null) + Text = title; + + // Localize tab pages - look for TabPage controls named tabPageItems and tabPageLand + foreach (TabPage page in tabcontrol.TabPages) + { + if (page.Name == "tabPageItems") + { + var itemsTab = _localizationGetter("Forms.TileDataFilterForm.ItemsTab"); + if (itemsTab != null) + page.Text = itemsTab; + } + else if (page.Name == "tabPageLand") + { + var landTab = _localizationGetter("Forms.TileDataFilterForm.LandTab"); + if (landTab != null) + page.Text = landTab; + } + } + + // Map textbox names to their corresponding label names and JSON keys + // The label names are defined in Designer.cs (e.g., label1, label2, etc.) + var textboxToLabelName = new Dictionary + { + { "textBoxName", ("label1", "Forms.TileDataFilterForm.Labels.Name") }, + { "textBoxAnim", ("label2", "Forms.TileDataFilterForm.Labels.Anim") }, + { "textBoxWeight", ("label3", "Forms.TileDataFilterForm.Labels.Weight") }, + { "textBoxQuality", ("label4", "Forms.TileDataFilterForm.Labels.Quality") }, + { "textBoxQuantity", ("label5", "Forms.TileDataFilterForm.Labels.Quantity") }, + { "textBoxHue", ("label8", "Forms.TileDataFilterForm.Labels.Hue") }, + { "textBoxStackOff", ("label7", "Forms.TileDataFilterForm.Labels.StackOff") }, + { "textBoxValue", ("label6", "Forms.TileDataFilterForm.Labels.Value") }, + { "textBoxHeigth", ("label11", "Forms.TileDataFilterForm.Labels.Height") }, // label11.Text = "Heigth" (typo in Designer) + { "textBoxUnk1", ("label10", "Forms.TileDataFilterForm.Labels.MiscData") }, + { "textBoxUnk2", ("label9", "Forms.TileDataFilterForm.Labels.Unk2") }, + { "textBoxUnk3", ("label12", "Forms.TileDataFilterForm.Labels.Unk3") }, + { "textBoxNameLand", ("label23", "Forms.TileDataFilterForm.Labels.NameLand") }, + { "textBoxTexID", ("label24", "Forms.TileDataFilterForm.Labels.TexID") } + }; + + // Create a map of label names to controls for quick lookup + var labelMap = new Dictionary(); + foreach (Control control in GetAllControls(this)) + { + if (control is Label && !string.IsNullOrEmpty(control.Name)) + { + labelMap[control.Name] = control; + } + } + + // Now apply localizations to each label based on its mapped textbox + foreach (var textbox in textboxToLabelName) + { + if (labelMap.TryGetValue(textbox.Value.labelName, out Control? labelControl)) + { + if (labelControl is Label label) + { + var localized = _localizationGetter(textbox.Value.jsonKey); + if (localized != null) + label.Text = localized; + } + } + } + + // Localize buttons + foreach (Control control in GetAllControls(this)) + { + if (control is Button button) + { + if (button.Text == "Apply Filter" || button.Text == "应用过滤器") + { + var applyButton = _localizationGetter("Forms.TileDataFilterForm.ApplyButton"); + if (applyButton != null) + button.Text = applyButton; + } + else if (button.Text == "Reset Filter" || button.Text == "重置过滤器") + { + var resetButton = _localizationGetter("Forms.TileDataFilterForm.ResetButton"); + if (resetButton != null) + button.Text = resetButton; + } + } + } + + // Re-initialize flag checkboxes with localized names + InitFlagCheckBoxes(); + } + + /// + /// Helper method to recursively get all controls. + /// + private IEnumerable GetAllControls(Control container) + { + foreach (Control control in container.Controls) + { + yield return control; + foreach (Control child in GetAllControls(control)) + yield return child; + } + } + private void InitFlagCheckBoxes() { string[] enumNames = Enum.GetNames(typeof(TileFlag)); @@ -42,7 +163,10 @@ private void InitFlagCheckBoxes() checkedListBox1.Items.Clear(); for (int i = 1; i < maxLength; ++i) { - checkedListBox1.Items.Add(enumNames[i], false); + string flagName = enumNames[i]; + // 获取汉化文本,如果没有就用原始英文 + string displayName = _localizationGetter?.Invoke($"Forms.TileDataControl.TileFlags.{flagName}") ?? flagName; + checkedListBox1.Items.Add(displayName, false); } checkedListBox1.EndUpdate(); @@ -51,7 +175,10 @@ private void InitFlagCheckBoxes() checkedListBox2.Items.Clear(); for (int i = 1; i < maxLength; ++i) { - checkedListBox2.Items.Add(enumNames[i], false); + string flagName = enumNames[i]; + // 获取汉化文本,如果没有就用原始英文 + string displayName = _localizationGetter?.Invoke($"Forms.TileDataControl.TileFlags.{flagName}") ?? flagName; + checkedListBox2.Items.Add(displayName, false); } checkedListBox2.EndUpdate(); } diff --git a/UoFiddler.Controls/Forms/TileDataHelpForm.cs b/UoFiddler.Controls/Forms/TileDataHelpForm.cs index dc4bbf60..1f3ab14a 100644 --- a/UoFiddler.Controls/Forms/TileDataHelpForm.cs +++ b/UoFiddler.Controls/Forms/TileDataHelpForm.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Drawing; using System.Text; using System.Windows.Forms; @@ -18,155 +19,183 @@ namespace UoFiddler.Controls.Forms { public partial class TileDataHelpForm : Form { + private Func? _localizationGetter; + public TileDataHelpForm() { InitializeComponent(); PopulateFields(); } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.TileDataHelpForm.Title") ?? "TileData Help"; + + // Localize column header + var fieldHeader = _localizationGetter("Forms.TileDataHelpForm.FieldColumnHeader") ?? "Field"; + _columnField.Text = fieldHeader; + + // Localize Close button + var closeButton = _localizationGetter("Forms.TileDataHelpForm.CloseButton") ?? "Close"; + _btnClose.Text = closeButton; + + // Clear and re-populate with localized content + _listView.Items.Clear(); + PopulateFields(); + } + private void PopulateFields() { - AddHeader("Editing many entries"); - Add("Multi-select", - "Ctrl+click and Shift+click select several entries at once. The right hand pane keeps showing the entry " - + "you picked last, and 'Save Changes' writes to every selected entry.", + // Helper function to get localized text + Func GetLocalized = (string key) => + { + return _localizationGetter?.Invoke(key) ?? null; + }; + + // Editing many entries section + AddHeader(GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.Title") ?? "Editing many entries"); + Add(GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.MultiSelect.Title") ?? "Multi-select", + GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.MultiSelect.Description") + ?? "Ctrl+click and Shift+click select several entries at once. The right hand pane keeps showing the entry you picked last, and 'Save Changes' writes to every selected entry.", "multi"); - Add("Empty boxes", - "With more than one entry selected, a box the entries disagree on comes up empty, and an empty box is " - + "left alone when you save - the entries keep their own values. Fill it in to give them all the same " - + "value. Because empty means 'leave alone', a name cannot be cleared across a multi-selection; select " - + "the entry on its own to do that.", + Add(GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.EmptyBoxes.Title") ?? "Empty boxes", + GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.EmptyBoxes.Description") + ?? "With more than one entry selected, a box the entries disagree on comes up empty, and an empty box is left alone when you save - the entries keep their own values. Fill it in to give them all the same value. Because empty means 'leave alone', a name cannot be cleared across a multi-selection; select the entry on its own to do that.", "multi"); - Add("Greyed flags", - "A flag that is set on some of the selected entries but not all shows greyed, and a greyed flag is left " - + "alone when you save. Clicking it cycles leave alone -> set on all -> clear on all. A flag they all " - + "already agree on just toggles.", + Add(GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.GreyedFlags.Title") ?? "Greyed flags", + GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.GreyedFlags.Description") + ?? "A flag that is set on some of the selected entries but not all shows greyed, and a greyed flag is left alone when you save. Clicking it cycles leave alone -> set on all -> clear on all. A flag they all already agree on just toggles.", "multi"); - Add("Copy / Paste special", - "Right-click an entry and choose 'Copy tile data' to remember it, then select any number of entries and " - + "choose 'Paste special...' to pick which of the copied fields and flags to write onto them.", + Add(GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.CopyPasteSpecial.Title") ?? "Copy / Paste special", + GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.CopyPasteSpecial.Description") + ?? "Right-click an entry and choose 'Copy tile data' to remember it, then select any number of entries and choose 'Paste special...' to pick which of the copied fields and flags to write onto them.", "multi"); - Add("Undo", - "Misc -> 'Undo last bulk apply' puts back the values the entries had before the last apply. Only the " - + "most recent apply is kept, and it is forgotten when tiledata is reloaded.", + Add(GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.Undo.Title") ?? "Undo", + GetLocalized("Forms.TileDataHelpForm.EditingManyEntries.Undo.Description") + ?? "Misc -> 'Undo last bulk apply' puts back the values the entries had before the last apply. Only the most recent apply is kept, and it is forgotten when tiledata is reloaded.", "multi"); - AddHeader("Items"); - Add("Name", "This field is for the name of the item, which can be a maximum of 20 characters.", "items"); - Add("Animation", "This field is for the animation ID associated with the item.", "items"); - Add("Weight", "This field is for the weight of the item.", "items"); - Add("Layer", new StringBuilder() - .AppendLine("This field is for the layer of the item:") - .AppendLine("") - .AppendLine("1 One handed weapon") - .AppendLine("2 Two handed weapon, shield, or misc.") - .AppendLine("3 Shoes") - .AppendLine("4 Pants") - .AppendLine("5 Shirt") - .AppendLine("6 Helm / Line") - .AppendLine("7 Gloves") - .AppendLine("8 Ring") - .AppendLine("9 Talisman") - .AppendLine("10 Neck") - .AppendLine("11 Hair") - .AppendLine("12 Waist (half apron)") - .AppendLine("13 Torso (inner) (chest armor)") - .AppendLine("14 Bracelet") - .AppendLine("15 Unused (but backpackers for backpackers go to 21)") - .AppendLine("16 Facial Hair") - .AppendLine("17 Torso (middle) (surcoat, tunic, full apron, sash)") - .AppendLine("18 Earrings") - .AppendLine("19 Arms") - .AppendLine("20 Back (cloak)") - .AppendLine("21 Backpack") - .AppendLine("22 Torso (outer) (robe)") - .AppendLine("23 Legs (outer) (skirt / kilt)") - .AppendLine("24 Legs (inner) (leg armor)") - .AppendLine("25 Mount (horse, ostard, etc)") - .AppendLine("26 NPC Buy Restock container") - .AppendLine("27 NPC Buy no restock container") - .Append("28 NPC Sell container") - .ToString(), "items"); - Add("Quantity", "This field is for the quantity of the item.", "items"); - Add("Value", "This field is for the value of the item.", "items"); - Add("Stacking Offset", new StringBuilder() - .AppendLine("StackOff refers to the stacking offset in pixels when multiple items are stacked.") - .Append("A higher StackOff value means the items will appear further apart from each other within the stack.") - .ToString(), "items"); - Add("Hue", "This field is for the hue (color) of the item.", "items"); - Add("Unknown 2", "This field is for the second unknown value.", "items"); - Add("Misc Data", "Old UO Demo weapon template definition", "items"); - Add("Height", "This field is for the height of the item.", "items"); - Add("Unknown 3", "This field is for the third unknown value.", "items"); - - AddHeader("Land Tiles"); - Add("Name", "This field is for the name of the land tile, which can be a maximum of 20 characters.", "land"); - Add("Texture ID", "This field is for the texture ID associated with the land tile.", "land"); - - AddHeader("Flags"); - Add("Background", "Not yet documented.", "flags"); - Add("Weapon", "Not yet documented.", "flags"); - Add("Transparent", "Not yet documented.", "flags"); - Add("Translucent", "The tile is rendered with partial alpha-transparency.", "flags"); - Add("Wall", "The tile is a wall.", "flags"); - Add("Damaging", "The tile can cause damage when moved over.", "flags"); - Add("Impassable", "The tile may not be moved over or through.", "flags"); - Add("Wet", "Not yet documented.", "flags"); - Add("Unknown1", "Unknown.", "flags"); - Add("Surface", "The tile is a surface. It may be moved over, but not through.", "flags"); - Add("Bridge", "The tile is a stair, ramp, or ladder.", "flags"); - Add("Generic", "The tile is stackable.", "flags"); - Add("Window", "The tile is a window. Like NoShoot, tiles with this flag block line of sight.", "flags"); - Add("NoShoot", "The tile blocks line of sight.", "flags"); - Add("ArticleA", "For single-amount tiles, the string \"a \" should be prepended to the tile name.", "flags"); - Add("ArticleAn", "For single-amount tiles, the string \"an \" should be prepended to the tile name.", "flags"); - Add("ArticleThe", "Probably article \"The\" prepended to the tile name.", "flags"); - Add("Foliage", "The tile becomes translucent when walked behind. Boat masts also have this flag.", "flags"); - Add("PartialHue", "Only gray pixels will be hued.", "flags"); - Add("NoHouse", "NoHouse or Unknown. Needs further research.", "flags"); - Add("Map", "The tile is a map in the cartography sense. Unknown usage.", "flags"); - Add("Container", "The tile is a container.", "flags"); - Add("Wearable", "The tile may be equipped.", "flags"); - Add("LightSource", "The tile gives off light.", "flags"); - Add("Animation", "The tile is animated.", "flags"); - Add("HoverOver", "Gargoyles can fly over, or NoDiagonal.", "flags"); - Add("NoDiagonal", "NoDiagonal (Unknown3).", "flags"); - Add("Armor", "Not yet documented.", "flags"); - Add("Roof", "The tile is a slanted roof.", "flags"); - Add("Door", "The tile is a door. Tiles with this flag can be moved through by ghosts and GMs.", "flags"); - Add("StairBack", "Not yet documented.", "flags"); - Add("StairRight", "Not yet documented.", "flags"); - Add("AlphaBlend", "Blend Alphas, tile blending.", "flags"); - Add("UseNewArt", "Uses new art style? Something related to the nodraw tile?", "flags"); - Add("ArtUsed", "Has art being used?", "flags"); - Add("Unused8", "Unused or unknown yet.", "flags"); - Add("NoShadow", "Disallow shadow on this tile, light source? lava?", "flags"); - Add("PixelBleed", "Let pixels bleed in to other tiles? Is this Disabling Texture Clamp?", "flags"); - Add("PlayAnimOnce", "Play tile animation once.", "flags"); - Add("MultiMovable", "Movable multi? Cool ships and vehicles etc? Something related to the masts.", "flags"); - Add("Unused10", "Unused or unknown yet.", "flags"); - Add("Unused11", "Unused or unknown yet.", "flags"); - Add("Unused12", "Unused or unknown yet.", "flags"); - Add("Unused13", "Unused or unknown yet.", "flags"); - Add("Unused14", "Unused or unknown yet.", "flags"); - Add("Unused15", "Unused or unknown yet.", "flags"); - Add("Unused16", "Unused or unknown yet.", "flags"); - Add("Unused17", "Unused or unknown yet.", "flags"); - Add("Unused18", "Unused or unknown yet.", "flags"); - Add("Unused19", "Unused or unknown yet.", "flags"); - Add("Unused20", "Unused or unknown yet.", "flags"); - Add("Unused21", "Unused or unknown yet.", "flags"); - Add("Unused22", "Unused or unknown yet.", "flags"); - Add("Unused23", "Unused or unknown yet.", "flags"); - Add("Unused24", "Unused or unknown yet.", "flags"); - Add("Unused25", "Unused or unknown yet.", "flags"); - Add("Unused26", "Unused or unknown yet.", "flags"); - Add("Unused27", "Unused or unknown yet.", "flags"); - Add("Unused28", "Unused or unknown yet.", "flags"); - Add("Unused29", "Unused or unknown yet.", "flags"); - Add("Unused30", "Unused or unknown yet.", "flags"); - Add("Unused31", "Unused or unknown yet.", "flags"); - Add("Unused32", "Unused or unknown yet.", "flags"); + // Items section + AddHeader(GetLocalized("Forms.TileDataHelpForm.Items.Title") ?? "Items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Name.Title") ?? "Name", + GetLocalized("Forms.TileDataHelpForm.Items.Name.Description") + ?? "This field is for the name of the item, which can be a maximum of 20 characters.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Animation.Title") ?? "Animation", + GetLocalized("Forms.TileDataHelpForm.Items.Animation.Description") + ?? "This field is for the animation ID associated with the item.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Weight.Title") ?? "Weight", + GetLocalized("Forms.TileDataHelpForm.Items.Weight.Description") + ?? "This field is for the weight of the item.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Layer.Title") ?? "Layer", + GetLocalized("Forms.TileDataHelpForm.Items.Layer.Description") + ?? "This field is for the layer of the item:\n\n1 One handed weapon\n2 Two handed weapon, shield, or misc.\n3 Shoes\n4 Pants\n5 Shirt\n6 Helm / Line\n7 Gloves\n8 Ring\n9 Talisman\n10 Neck\n11 Hair\n12 Waist (half apron)\n13 Torso (inner) (chest armor)\n14 Bracelet\n15 Unused (but backpackers for backpackers go to 21)\n16 Facial Hair\n17 Torso (middle) (surcoat, tunic, full apron, sash)\n18 Earrings\n19 Arms\n20 Back (cloak)\n21 Backpack\n22 Torso (outer) (robe)\n23 Legs (outer) (skirt / kilt)\n24 Legs (inner) (leg armor)\n25 Mount (horse, ostard, etc)\n26 NPC Buy Restock container\n27 NPC Buy no restock container\n28 NPC Sell container", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Quantity.Title") ?? "Quantity", + GetLocalized("Forms.TileDataHelpForm.Items.Quantity.Description") + ?? "This field is for the quantity of the item.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Value.Title") ?? "Value", + GetLocalized("Forms.TileDataHelpForm.Items.Value.Description") + ?? "This field is for the value of the item.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.StackingOffset.Title") ?? "Stacking Offset", + GetLocalized("Forms.TileDataHelpForm.Items.StackingOffset.Description") + ?? "StackOff refers to the stacking offset in pixels when multiple items are stacked.\nA higher StackOff value means the items will appear further apart from each other within the stack.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Hue.Title") ?? "Hue", + GetLocalized("Forms.TileDataHelpForm.Items.Hue.Description") + ?? "This field is for the hue (color) of the item.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Unknown2.Title") ?? "Unknown 2", + GetLocalized("Forms.TileDataHelpForm.Items.Unknown2.Description") + ?? "This field is for the second unknown value.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.MiscData.Title") ?? "Misc Data", + GetLocalized("Forms.TileDataHelpForm.Items.MiscData.Description") + ?? "Old UO Demo weapon template definition", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Height.Title") ?? "Height", + GetLocalized("Forms.TileDataHelpForm.Items.Height.Description") + ?? "This field is for the height of the item.", "items"); + Add(GetLocalized("Forms.TileDataHelpForm.Items.Unknown3.Title") ?? "Unknown 3", + GetLocalized("Forms.TileDataHelpForm.Items.Unknown3.Description") + ?? "This field is for the third unknown value.", "items"); + + // Land Tiles section + AddHeader(GetLocalized("Forms.TileDataHelpForm.LandTiles.Title") ?? "Land Tiles"); + Add(GetLocalized("Forms.TileDataHelpForm.LandTiles.Name.Title") ?? "Name", + GetLocalized("Forms.TileDataHelpForm.LandTiles.Name.Description") + ?? "This field is for the name of the land tile, which can be a maximum of 20 characters.", "land"); + Add(GetLocalized("Forms.TileDataHelpForm.LandTiles.TextureID.Title") ?? "Texture ID", + GetLocalized("Forms.TileDataHelpForm.LandTiles.TextureID.Description") + ?? "This field is for the texture ID associated with the land tile.", "land"); + + // Flags section + AddHeader(GetLocalized("Forms.TileDataHelpForm.Flags.Title") ?? "Flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Background.Title") ?? "Background", GetLocalized("Forms.TileDataHelpForm.Flags.Background.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Weapon.Title") ?? "Weapon", GetLocalized("Forms.TileDataHelpForm.Flags.Weapon.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Transparent.Title") ?? "Transparent", GetLocalized("Forms.TileDataHelpForm.Flags.Transparent.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Translucent.Title") ?? "Translucent", GetLocalized("Forms.TileDataHelpForm.Flags.Translucent.Description") ?? "The tile is rendered with partial alpha-transparency.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Wall.Title") ?? "Wall", GetLocalized("Forms.TileDataHelpForm.Flags.Wall.Description") ?? "The tile is a wall.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Damaging.Title") ?? "Damaging", GetLocalized("Forms.TileDataHelpForm.Flags.Damaging.Description") ?? "The tile can cause damage when moved over.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Impassable.Title") ?? "Impassable", GetLocalized("Forms.TileDataHelpForm.Flags.Impassable.Description") ?? "The tile may not be moved over or through.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Wet.Title") ?? "Wet", GetLocalized("Forms.TileDataHelpForm.Flags.Wet.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unknown1.Title") ?? "Unknown1", GetLocalized("Forms.TileDataHelpForm.Flags.Unknown1.Description") ?? "Unknown.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Surface.Title") ?? "Surface", GetLocalized("Forms.TileDataHelpForm.Flags.Surface.Description") ?? "The tile is a surface. It may be moved over, but not through.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Bridge.Title") ?? "Bridge", GetLocalized("Forms.TileDataHelpForm.Flags.Bridge.Description") ?? "The tile is a stair, ramp, or ladder.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Generic.Title") ?? "Generic", GetLocalized("Forms.TileDataHelpForm.Flags.Generic.Description") ?? "The tile is stackable.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Window.Title") ?? "Window", GetLocalized("Forms.TileDataHelpForm.Flags.Window.Description") ?? "The tile is a window. Like NoShoot, tiles with this flag block line of sight.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.NoShoot.Title") ?? "NoShoot", GetLocalized("Forms.TileDataHelpForm.Flags.NoShoot.Description") ?? "The tile blocks line of sight.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.ArticleA.Title") ?? "ArticleA", GetLocalized("Forms.TileDataHelpForm.Flags.ArticleA.Description") ?? "For single-amount tiles, the string \"a \" should be prepended to the tile name.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.ArticleAn.Title") ?? "ArticleAn", GetLocalized("Forms.TileDataHelpForm.Flags.ArticleAn.Description") ?? "For single-amount tiles, the string \"an \" should be prepended to the tile name.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.ArticleThe.Title") ?? "ArticleThe", GetLocalized("Forms.TileDataHelpForm.Flags.ArticleThe.Description") ?? "Probably article \"The\" prepended to the tile name.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Foliage.Title") ?? "Foliage", GetLocalized("Forms.TileDataHelpForm.Flags.Foliage.Description") ?? "The tile becomes translucent when walked behind. Boat masts also have this flag.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.PartialHue.Title") ?? "PartialHue", GetLocalized("Forms.TileDataHelpForm.Flags.PartialHue.Description") ?? "Only gray pixels will be hued.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.NoHouse.Title") ?? "NoHouse", GetLocalized("Forms.TileDataHelpForm.Flags.NoHouse.Description") ?? "NoHouse or Unknown. Needs further research.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Map.Title") ?? "Map", GetLocalized("Forms.TileDataHelpForm.Flags.Map.Description") ?? "The tile is a map in the cartography sense. Unknown usage.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Container.Title") ?? "Container", GetLocalized("Forms.TileDataHelpForm.Flags.Container.Description") ?? "The tile is a container.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Wearable.Title") ?? "Wearable", GetLocalized("Forms.TileDataHelpForm.Flags.Wearable.Description") ?? "The tile may be equipped.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.LightSource.Title") ?? "LightSource", GetLocalized("Forms.TileDataHelpForm.Flags.LightSource.Description") ?? "The tile gives off light.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Animation.Title") ?? "Animation", GetLocalized("Forms.TileDataHelpForm.Flags.Animation.Description") ?? "The tile is animated.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.HoverOver.Title") ?? "HoverOver", GetLocalized("Forms.TileDataHelpForm.Flags.HoverOver.Description") ?? "Gargoyles can fly over, or NoDiagonal.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.NoDiagonal.Title") ?? "NoDiagonal", GetLocalized("Forms.TileDataHelpForm.Flags.NoDiagonal.Description") ?? "NoDiagonal (Unknown3).", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Armor.Title") ?? "Armor", GetLocalized("Forms.TileDataHelpForm.Flags.Armor.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Roof.Title") ?? "Roof", GetLocalized("Forms.TileDataHelpForm.Flags.Roof.Description") ?? "The tile is a slanted roof.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Door.Title") ?? "Door", GetLocalized("Forms.TileDataHelpForm.Flags.Door.Description") ?? "The tile is a door. Tiles with this flag can be moved through by ghosts and GMs.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.StairBack.Title") ?? "StairBack", GetLocalized("Forms.TileDataHelpForm.Flags.StairBack.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.StairRight.Title") ?? "StairRight", GetLocalized("Forms.TileDataHelpForm.Flags.StairRight.Description") ?? "Not yet documented.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.AlphaBlend.Title") ?? "AlphaBlend", GetLocalized("Forms.TileDataHelpForm.Flags.AlphaBlend.Description") ?? "Blend Alphas, tile blending.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.UseNewArt.Title") ?? "UseNewArt", GetLocalized("Forms.TileDataHelpForm.Flags.UseNewArt.Description") ?? "Uses new art style? Something related to the nodraw tile?", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.ArtUsed.Title") ?? "ArtUsed", GetLocalized("Forms.TileDataHelpForm.Flags.ArtUsed.Description") ?? "Has art being used?", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused8.Title") ?? "Unused8", GetLocalized("Forms.TileDataHelpForm.Flags.Unused8.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.NoShadow.Title") ?? "NoShadow", GetLocalized("Forms.TileDataHelpForm.Flags.NoShadow.Description") ?? "Disallow shadow on this tile, light source? lava?", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.PixelBleed.Title") ?? "PixelBleed", GetLocalized("Forms.TileDataHelpForm.Flags.PixelBleed.Description") ?? "Let pixels bleed in to other tiles? Is this Disabling Texture Clamp?", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.PlayAnimOnce.Title") ?? "PlayAnimOnce", GetLocalized("Forms.TileDataHelpForm.Flags.PlayAnimOnce.Description") ?? "Play tile animation once.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.MultiMovable.Title") ?? "MultiMovable", GetLocalized("Forms.TileDataHelpForm.Flags.MultiMovable.Description") ?? "Movable multi? Cool ships and vehicles etc? Something related to the masts.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused10.Title") ?? "Unused10", GetLocalized("Forms.TileDataHelpForm.Flags.Unused10.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused11.Title") ?? "Unused11", GetLocalized("Forms.TileDataHelpForm.Flags.Unused11.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused12.Title") ?? "Unused12", GetLocalized("Forms.TileDataHelpForm.Flags.Unused12.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused13.Title") ?? "Unused13", GetLocalized("Forms.TileDataHelpForm.Flags.Unused13.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused14.Title") ?? "Unused14", GetLocalized("Forms.TileDataHelpForm.Flags.Unused14.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused15.Title") ?? "Unused15", GetLocalized("Forms.TileDataHelpForm.Flags.Unused15.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused16.Title") ?? "Unused16", GetLocalized("Forms.TileDataHelpForm.Flags.Unused16.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused17.Title") ?? "Unused17", GetLocalized("Forms.TileDataHelpForm.Flags.Unused17.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused18.Title") ?? "Unused18", GetLocalized("Forms.TileDataHelpForm.Flags.Unused18.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused19.Title") ?? "Unused19", GetLocalized("Forms.TileDataHelpForm.Flags.Unused19.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused20.Title") ?? "Unused20", GetLocalized("Forms.TileDataHelpForm.Flags.Unused20.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused21.Title") ?? "Unused21", GetLocalized("Forms.TileDataHelpForm.Flags.Unused21.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused22.Title") ?? "Unused22", GetLocalized("Forms.TileDataHelpForm.Flags.Unused22.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused23.Title") ?? "Unused23", GetLocalized("Forms.TileDataHelpForm.Flags.Unused23.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused24.Title") ?? "Unused24", GetLocalized("Forms.TileDataHelpForm.Flags.Unused24.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused25.Title") ?? "Unused25", GetLocalized("Forms.TileDataHelpForm.Flags.Unused25.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused26.Title") ?? "Unused26", GetLocalized("Forms.TileDataHelpForm.Flags.Unused26.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused27.Title") ?? "Unused27", GetLocalized("Forms.TileDataHelpForm.Flags.Unused27.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused28.Title") ?? "Unused28", GetLocalized("Forms.TileDataHelpForm.Flags.Unused28.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused29.Title") ?? "Unused29", GetLocalized("Forms.TileDataHelpForm.Flags.Unused29.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused30.Title") ?? "Unused30", GetLocalized("Forms.TileDataHelpForm.Flags.Unused30.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused31.Title") ?? "Unused31", GetLocalized("Forms.TileDataHelpForm.Flags.Unused31.Description") ?? "Unused or unknown yet.", "flags"); + Add(GetLocalized("Forms.TileDataHelpForm.Flags.Unused32.Title") ?? "Unused32", GetLocalized("Forms.TileDataHelpForm.Flags.Unused32.Description") ?? "Unused or unknown yet.", "flags"); if (_listView.Items.Count > 0) { diff --git a/UoFiddler.Controls/Forms/TileDataSyncPreviewForm.cs b/UoFiddler.Controls/Forms/TileDataSyncPreviewForm.cs index 0bc681fe..edfedc97 100644 --- a/UoFiddler.Controls/Forms/TileDataSyncPreviewForm.cs +++ b/UoFiddler.Controls/Forms/TileDataSyncPreviewForm.cs @@ -27,6 +27,7 @@ private sealed class Row private readonly List _rows = new(); private readonly Dictionary _rowByItem = new(); + private Func? _localizationGetter; public IReadOnlyList AcceptedChanges { @@ -80,6 +81,42 @@ public TileDataSyncPreviewForm(IReadOnlyList changes) UpdateSummary(); } + /// + /// Set localization for TileDataSyncPreviewForm + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Apply localization to all UI elements + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 窗口标题 + this.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.Title") ?? "Sync from TileData — Preview"; + + // 表格列头 + actionColumn.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.ColumnAction") ?? "Action"; + numberColumn.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.ColumnNumber") ?? "Number"; + oldTextColumn.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.ColumnCurrentText") ?? "Current text"; + newTextColumn.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.ColumnNewText") ?? "New text"; + + // 按钮 + checkAllButton.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.CheckAllButton") ?? "Check all"; + uncheckAllButton.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.UncheckAllButton") ?? "Uncheck all"; + uncheckRemovesButton.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.UncheckRemovesButton") ?? "Uncheck removes"; + applyButton.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.ApplyButton") ?? "Apply"; + cancelButton.Text = _localizationGetter("Forms.TileDataSyncPreviewForm.CancelButton") ?? "Cancel"; + + // 重新更新 Summary 标签以应用汉化的模板 + UpdateSummary(); + } + protected override void OnShown(EventArgs e) { base.OnShown(e); @@ -165,7 +202,10 @@ private void UpdateSummary() } } - summaryLabel.Text = $"Will apply: Add {selAdd}/{totalAdd} Update {selUpdate}/{totalUpdate} Remove {selRemove}/{totalRemove}"; + // 使用汉化的 Summary 模板 + string template = _localizationGetter?.Invoke("Forms.TileDataSyncPreviewForm.WillApplyTemplate") + ?? "Will apply: Add {0}/{1} Update {2}/{3} Remove {4}/{5}"; + summaryLabel.Text = string.Format(template, selAdd, totalAdd, selUpdate, totalUpdate, selRemove, totalRemove); } private void SetCheckedForAll(bool value) diff --git a/UoFiddler.Controls/Helpers/RichTextBoxExtensions.cs b/UoFiddler.Controls/Helpers/RichTextBoxExtensions.cs index 136aa835..c944b5fc 100644 --- a/UoFiddler.Controls/Helpers/RichTextBoxExtensions.cs +++ b/UoFiddler.Controls/Helpers/RichTextBoxExtensions.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Windows.Forms; namespace UoFiddler.Controls.Helpers @@ -16,6 +17,11 @@ namespace UoFiddler.Controls.Helpers public static class RichTextBoxExtensions { public static void AddBasicContextMenu(this RichTextBox richTextBox) + { + AddBasicContextMenu(richTextBox, null); + } + + public static void AddBasicContextMenu(this RichTextBox richTextBox, Func? getLocalized) { if (richTextBox.ContextMenuStrip != null) { @@ -24,13 +30,15 @@ public static void AddBasicContextMenu(this RichTextBox richTextBox) ContextMenuStrip menuStrip = new ContextMenuStrip { ShowImageMargin = false }; - ToolStripMenuItem menuItemCopy = new ToolStripMenuItem("Copy"); + string copyText = getLocalized?.Invoke("Forms.ItemsControl.DetailTextBoxContextMenu.Copy") ?? "Copy"; + ToolStripMenuItem menuItemCopy = new ToolStripMenuItem(copyText); menuItemCopy.Click += (sender, e) => richTextBox.Copy(); menuStrip.Items.Add(menuItemCopy); menuStrip.Items.Add(new ToolStripSeparator()); - ToolStripMenuItem menuItemSelectAll = new ToolStripMenuItem("Select All"); + string selectAllText = getLocalized?.Invoke("Forms.ItemsControl.DetailTextBoxContextMenu.SelectAll") ?? "Select All"; + ToolStripMenuItem menuItemSelectAll = new ToolStripMenuItem(selectAllText); menuItemSelectAll.Click += (sender, e) => richTextBox.SelectAll(); menuStrip.Items.Add(menuItemSelectAll); diff --git a/UoFiddler.Controls/Plugin/PluginBase.cs b/UoFiddler.Controls/Plugin/PluginBase.cs index d653ed01..098bc93c 100644 --- a/UoFiddler.Controls/Plugin/PluginBase.cs +++ b/UoFiddler.Controls/Plugin/PluginBase.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Windows.Forms; using Microsoft.Extensions.Logging; using UoFiddler.Controls.Classes; @@ -26,6 +27,11 @@ public abstract class PluginBase public abstract string Author { get; } public abstract string Version { get; } + /// + /// 静态属性:汉化字符串获取器,由主程序在启动时设置 + /// + public static Func LocalizationGetter { get; set; } + /// /// Per-plugin logger, categorized by the concrete plugin type. Resolved on demand /// since plugins are instantiated via Activator and cannot take constructor parameters. diff --git a/UoFiddler.Controls/UserControls/AnimDataControl.cs b/UoFiddler.Controls/UserControls/AnimDataControl.cs index 7e0ad8ce..956ee2dc 100644 --- a/UoFiddler.Controls/UserControls/AnimDataControl.cs +++ b/UoFiddler.Controls/UserControls/AnimDataControl.cs @@ -45,6 +45,10 @@ public AnimDataControl() private HuePopUpForm _showForm; private int _customHue = 0; private bool _hueOnlyGray = false; + private Func? _localizationGetter; + private string _baseGraphicLabelPrefix = "Base Graphic: "; + private string _graphicLabelPrefix = "Graphic: "; + private string _hueLabelPrefix = "Hue: "; [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] private int CurrFrame @@ -56,7 +60,7 @@ private int CurrFrame { var newGraphic = _currentSelect + value; - toolStripStatusGraphic.Text = $"Graphic: {newGraphic} (0x{newGraphic:X})"; + toolStripStatusGraphic.Text = $"{_graphicLabelPrefix}{newGraphic} (0x{newGraphic:X})"; MainPictureBox.FrameIndex = value; _currentFrame = value; } @@ -88,8 +92,8 @@ private int CurrentSelect } treeViewFrames.EndUpdate(); - toolStripStatusBaseGraphic.Text = $"Base Graphic: {value} (0x{value:X})"; - toolStripStatusGraphic.Text = $"Graphic: {value} (0x{value:X})"; + toolStripStatusBaseGraphic.Text = $"{_baseGraphicLabelPrefix}{value} (0x{value:X})"; + toolStripStatusGraphic.Text = $"{_graphicLabelPrefix}{value} (0x{value:X})"; _currentSelect = value; @@ -104,7 +108,7 @@ private void MainPictureBox_FrameChanged(object sender, EventArgs e) { var newGraphic = _currentSelect + MainPictureBox.FrameIndex; - toolStripStatusGraphic.Text = $"Graphic: {newGraphic} (0x{newGraphic:X})"; + toolStripStatusGraphic.Text = $"{_graphicLabelPrefix}{newGraphic} (0x{newGraphic:X})"; } private void SetPicture() @@ -302,6 +306,7 @@ private void OnClickExport(object sender, EventArgs e) { TopMost = true }; + _exportForm.SetLocalization(_localizationGetter); _exportForm.Show(); } @@ -318,6 +323,7 @@ private void OnClickImport(object sender, EventArgs e) TopMost = true, OnAfterImport = Reload }; + _importForm.SetLocalization(_localizationGetter); _importForm.Show(); } @@ -577,7 +583,11 @@ private void OnClickSave(object sender, EventArgs e) Options.ChangedUltimaClass["Animdata"] = false; } - FileSavedDialog.Show(FindForm(), Options.OutputPath, "File saved successfully."); + // 获取汉化的保存成功消息 + string message = _localizationGetter?.Invoke("Forms.FileSavedDialog.SaveSuccess") ?? "File saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Save Success"; + + FileSavedDialog.Show(FindForm(), Options.OutputPath, message, title, _localizationGetter ?? (k => null)); } private void OnClickRemoveAnim(object sender, EventArgs e) @@ -688,6 +698,7 @@ private void OnClick_Hue(object sender, EventArgs e) ? new HuePopUpForm(ChangeHue, 1) : new HuePopUpForm(ChangeHue, _customHue - 1); + _showForm.SetLocalization(key => _localizationGetter?.Invoke(key)); _showForm.TopMost = true; _showForm.Show(); } @@ -698,7 +709,12 @@ private void OnClick_ExportAsGif(object sender, EventArgs e) { var outputFile = Path.Combine(Options.OutputPath, $"AnimData {Utils.FormatExportId(_currentSelect)}.gif"); MainPictureBox.Frames.ToGif(outputFile, delay: 150, showFrameBounds: MainPictureBox.ShowFrameBounds); - MessageBox.Show($"Saved to {outputFile}"); + + var title = _localizationGetter?.Invoke("Forms.AnimDataControl.ExportGifDialog.Title") ?? "Saved"; + var message = _localizationGetter?.Invoke("Forms.AnimDataControl.ExportGifDialog.Message") ?? "Saved to {0}"; + message = string.Format(message, outputFile); + + MessageBox.Show(message, title); } } @@ -728,6 +744,64 @@ private void SearchByIdToolStripTextBox_KeyUp(object sender, KeyEventArgs e) } } + /// + /// 设置本地化(从 MainForm 调用) + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 应用汉化 + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // GroupBox 标题 + groupBox1.Text = _localizationGetter("Forms.AnimDataControl.PreviewGroupBox") ?? "Preview"; + groupBox2.Text = _localizationGetter("Forms.AnimDataControl.DataGroupBox") ?? "Data"; + groupBox4.Text = _localizationGetter("Forms.AnimDataControl.FramesGroupBox") ?? "Frames"; + + // Label + label1.Text = _localizationGetter("Forms.AnimDataControl.StartDelayLabel") ?? "Start Delay"; + label2.Text = _localizationGetter("Forms.AnimDataControl.FrameDelayLabel") ?? "Frame Delay"; + + // Button + button2.Text = _localizationGetter("Forms.AnimDataControl.AddButton") ?? "Add"; + button5.Text = _localizationGetter("Forms.AnimDataControl.RemoveButton") ?? "Remove"; + button6.Text = _localizationGetter("Forms.AnimDataControl.SaveButton") ?? "Save"; + button7.Text = _localizationGetter("Forms.AnimDataControl.ImportButton") ?? "Import..."; + button8.Text = _localizationGetter("Forms.AnimDataControl.ExportButton") ?? "Export..."; + + // ToolStripMenuItem + addToolStripMenuItem.Text = _localizationGetter("Forms.AnimDataControl.AddMenuItem") ?? "Add"; + removeToolStripMenuItem.Text = _localizationGetter("Forms.AnimDataControl.RemoveMenuItem") ?? "Remove"; + hueToolStripMenuItem.Text = _localizationGetter("Forms.AnimDataControl.HueMenuItem") ?? "Hue"; + animateToolStripMenuItem.Text = _localizationGetter("Forms.AnimDataControl.AnimateMenuItem") ?? "Animate"; + showFrameBoundsToolStripMenuItem.Text = _localizationGetter("Forms.AnimDataControl.ShowFrameBoundsMenuItem") ?? "Show frame bounds"; + exportAsAnimatedGifToolStripMenuItem.Text = _localizationGetter("Forms.AnimDataControl.ExportGifMenuItem") ?? "Export as animated Gif"; + + // ToolStrip + searchByIdToolStripLabel.Text = _localizationGetter("Forms.AnimDataControl.IndexLabel") ?? "Index:"; + toolStripDropDownButton1.Text = _localizationGetter("Forms.AnimDataControl.SettingsButton") ?? "Settings"; + + // CheckBox + checkBoxRelative.Text = _localizationGetter("Forms.AnimDataControl.RelativeCheckBox") ?? "Relative"; + + // 状态栏标签前缀 + _baseGraphicLabelPrefix = _localizationGetter("Forms.AnimDataControl.BaseGraphicLabel") ?? "Base Graphic: "; + _graphicLabelPrefix = _localizationGetter("Forms.AnimDataControl.GraphicLabel") ?? "Graphic: "; + _hueLabelPrefix = _localizationGetter("Forms.AnimDataControl.HueLabel") ?? "Hue: "; + + // 设置初始状态栏显示 + toolStripStatusBaseGraphic.Text = $"{_baseGraphicLabelPrefix}0 (0x0)"; + toolStripStatusGraphic.Text = $"{_graphicLabelPrefix}0 (0x0)"; + toolStripStatusHue.Text = _hueLabelPrefix + "0"; + } + public static bool Select(int graphic) { if (_refMarker == null) diff --git a/UoFiddler.Controls/UserControls/AnimationListControl.cs b/UoFiddler.Controls/UserControls/AnimationListControl.cs index 3cc2df5c..fb235d33 100644 --- a/UoFiddler.Controls/UserControls/AnimationListControl.cs +++ b/UoFiddler.Controls/UserControls/AnimationListControl.cs @@ -37,6 +37,11 @@ public AnimationListControl() listView1.Height += SystemInformation.HorizontalScrollBarHeight; } + private Func? _localizationGetter; + private string _graphicLabelPrefix = "Graphic: "; + private string _baseGraphicLabelPrefix = "Base Graphic: "; + private string _hueLabelPrefix = "Hue: "; + public string[][] GetActionNames { get; } = { // Monster new[] @@ -349,9 +354,9 @@ private void SetPicture() _defHue = hue; } - BaseGraphicLabel.Text = $"BaseGraphic: {body} (0x{body:X})"; - GraphicLabel.Text = $"Graphic: {_currentSelect} (0x{_currentSelect:X})"; - HueLabel.Text = $"Hue: {hue + 1} (0x{hue + 1:X})"; + BaseGraphicLabel.Text = $"{_baseGraphicLabelPrefix}{body} (0x{body:X})"; + GraphicLabel.Text = $"{_graphicLabelPrefix}{_currentSelect} (0x{_currentSelect:X})"; + HueLabel.Text = $"{_hueLabelPrefix}{hue + 1} (0x{hue + 1:X})"; LoadListViewFrames(); } @@ -365,9 +370,9 @@ private void ClearPicture() MainPictureBox.Frames = null; LoadListViewFrames(); - BaseGraphicLabel.Text = "BaseGraphic:"; - GraphicLabel.Text = "Graphic: "; - HueLabel.Text = "Hue:"; + BaseGraphicLabel.Text = _baseGraphicLabelPrefix; + GraphicLabel.Text = _graphicLabelPrefix; + HueLabel.Text = _hueLabelPrefix; } private void TreeViewMobs_AfterSelect(object sender, TreeViewEventArgs e) @@ -597,7 +602,9 @@ private void AddUopActionNodes(TreeNode parent, int body, int actionType) ? GetActionNames[actionType][i] : $"Action{i}"; - parent.Nodes.Add(new TreeNode($"{i} {actionName}") { Tag = i }); + // 使用汉化后的动作名称 + string displayName = GetLocalizedActionName(actionName); + parent.Nodes.Add(new TreeNode($"{i} {displayName}") { Tag = i }); } } @@ -681,7 +688,9 @@ private void PopulateActionNodes(TreeNode bodyNode) continue; } - bodyNode.Nodes.Add(new TreeNode($"{i} {GetActionNames[actionType][i]}") { Tag = i }); + // 使用汉化后的动作名称 + string displayName = GetLocalizedActionName(GetActionNames[actionType][i]); + bodyNode.Nodes.Add(new TreeNode($"{i} {displayName}") { Tag = i }); } } } @@ -858,6 +867,7 @@ private void OnClick_Hue(object sender, EventArgs e) ? new HuePopUpForm(ChangeHue, _defHue + 1) : new HuePopUpForm(ChangeHue, _customHue - 1); + _showForm.SetLocalization(key => _localizationGetter?.Invoke(key)); _showForm.TopMost = true; _showForm.Show(); } @@ -977,6 +987,7 @@ private void OnClickAnimationEdit(object sender, EventArgs e) _animEditFormEntry = new AnimationEditForm(); //animEditEntry.TopMost = true; // TODO: should it be topMost? + _animEditFormEntry.SetLocalization(_localizationGetter); _animEditFormEntry.Show(); } @@ -989,7 +1000,7 @@ private void OnClickFindNewEntries(object sender, EventArgs e) return; } - _animNewEntryForm = new AnimationListNewEntriesForm(IsAlreadyDefined, AddGraphic, GetActionNames) + _animNewEntryForm = new AnimationListNewEntriesForm(IsAlreadyDefined, AddGraphic, GetActionNames, _localizationGetter) { TopMost = true }; @@ -1050,7 +1061,9 @@ private void RewriteXml(object sender, EventArgs e) dom.Save(fileName); } - MessageBox.Show("XML saved", "Rewrite", MessageBoxButtons.OK, MessageBoxIcon.Information, + string message = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.RewriteMessage") ?? "XML saved"; + string title = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.RewriteTitle") ?? "Rewrite"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } @@ -1139,8 +1152,12 @@ private void ExtractImage(ImageFormat imageFormat) newBitmap.Save(fileName, imageFormat); } - MessageBox.Show($"{what} saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information, - MessageBoxDefaultButton.Button1); + // 获取汉化标题和消息 + string title = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportSingleFrameTitle") ?? "Saved"; + string messageTemplate = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportSingleFrameMessage") ?? "{0} saved to {1}"; + string message = string.Format(messageTemplate, what, fileName); + + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } private void OnClickExtractAnimBmp(object sender, EventArgs e) @@ -1190,7 +1207,18 @@ private void ExportAnimationFrames(ImageFormat imageFormat) } } - FileSavedDialog.Show(FindForm(), Options.OutputPath, $"Files with following format {fileName}-X.{fileExtension} saved successfully."); + string message = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportFramesMessage") + ?? $"Files with following format {fileName}-X.{fileExtension} saved successfully."; + + if (_localizationGetter != null) + { + string title = _localizationGetter("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), Options.OutputPath, message, title, _localizationGetter); + } + else + { + FileSavedDialog.Show(FindForm(), Options.OutputPath, message); + } } private void OnClickExportFrameBmp(object sender, EventArgs e) @@ -1227,7 +1255,7 @@ private void ExportSingleFrame(ImageFormat imageFormat) } string fileExtension = Utils.GetFileExtensionFor(imageFormat); - string fileName = Path.Combine(Options.OutputPath, $"{what} {Utils.FormatExportId(_currentSelect)}"); + string fileName = Path.Combine(Options.OutputPath, $"{what} {Utils.FormatExportId(_currentSelect)}-{(int)listView1.SelectedItems[0].Tag}.{fileExtension}"); Bitmap bit = MainPictureBox.Frames[(int)listView1.SelectedItems[0].Tag].Bitmap; using (Bitmap newBitmap = new Bitmap(bit.Width, bit.Height)) @@ -1239,8 +1267,15 @@ private void ExportSingleFrame(ImageFormat imageFormat) newGraph.Save(); } - newBitmap.Save($"{fileName}-{(int)listView1.SelectedItems[0].Tag}.{fileExtension}", imageFormat); + newBitmap.Save(fileName, imageFormat); } + + // 获取汉化标题和消息 + string title = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportSingleFrameTitle") ?? "Saved"; + string messageTemplate = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportSingleFrameMessage") ?? "{0} saved to {1}"; + string message = string.Format(messageTemplate, what, fileName); + + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } private void OnClickExportAllThumbnailsBmp(object sender, EventArgs e) @@ -1329,7 +1364,20 @@ private void ExportAllThumbnails(ImageFormat imageFormat) } } - FileSavedDialog.Show(FindForm(), thumbnailPath, "All thumbnails saved successfully."); + // Localize message and title + string message = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportAllThumbnailsMessage") + ?? "All thumbnails saved successfully."; + string title = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportAllThumbnailsTitle") + ?? "Saved"; + + if (_localizationGetter != null) + { + FileSavedDialog.Show(FindForm(), thumbnailPath, message, title, _localizationGetter); + } + else + { + FileSavedDialog.Show(FindForm(), thumbnailPath, message); + } } private void ExportAnimatedGif(bool looping) @@ -1342,7 +1390,109 @@ private void ExportAnimatedGif(bool looping) var outputFile = Path.Combine(Options.OutputPath, $"{(_displayType == 1 ? "Equipment" : "Mob")} {_currentSelect}.gif"); MainPictureBox.Frames.ToGif(outputFile, looping: looping, delay: 150, showFrameBounds: MainPictureBox.ShowFrameBounds); - FileSavedDialog.Show(FindForm(), outputFile, "InGame Anim saved successfully."); + // Localize message and title + string message = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportAnimatedGifMessage") + ?? "InGame Anim saved successfully."; + string title = _localizationGetter?.Invoke("Forms.AnimationListControl.Messages.ExportAnimatedGifTitle") + ?? "Saved"; + + if (_localizationGetter != null) + { + FileSavedDialog.Show(FindForm(), outputFile, message, title, _localizationGetter); + } + else + { + FileSavedDialog.Show(FindForm(), outputFile, message); + } + } + + /// + /// 设置本地化获取器 + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 应用本地化到所有 UI 元素 + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 搜索标签 + searchToolStripLabel.Text = _localizationGetter("Forms.AnimationListControl.searchToolStripLabel") ?? "Search:"; + + // 右键菜单项 + removeToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.removeToolStripMenuItem") ?? "Remove"; + + // 标签页 + tabPage1.Text = _localizationGetter("Forms.AnimationListControl.tabPage1") ?? "Animation"; + tabPage2.Text = _localizationGetter("Forms.AnimationListControl.tabPage2") ?? "Thumbnail List"; + + // 导出图像菜单 + extractImageToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.extractImageToolStripMenuItem") ?? "Export Image.."; + asBMpToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.asBMpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.asJpgToolStripMenuItem") ?? "As Jpg"; + asPngToolStripMenuItem2.Text = _localizationGetter("Forms.AnimationListControl.asPngToolStripMenuItem2") ?? "As Png"; + + // 导出动画菜单 + extractAnimationToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.extractAnimationToolStripMenuItem") ?? "Export Animation.."; + asBmpToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationListControl.asBmpToolStripMenuItem1") ?? "As Bmp"; + asTiffToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationListControl.asTiffToolStripMenuItem1") ?? "As Tiff"; + asJpgToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationListControl.asJpgToolStripMenuItem1") ?? "As Jpg"; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.AnimationListControl.asPngToolStripMenuItem1") ?? "As Png"; + asAnimatedGifToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.asAnimatedGifToolStripMenuItem") ?? "As animated Gif (looping)"; + asAnimatedGifnoLoopingToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.asAnimatedGifnoLoopingToolStripMenuItem") ?? "As animated Gif (no looping)"; + + // 导出帧菜单 + exportFrameToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.exportFrameToolStripMenuItem") ?? "Export Frame.."; + asBmpToolStripMenuItem2.Text = _localizationGetter("Forms.AnimationListControl.asBmpToolStripMenuItem2") ?? "As Bmp"; + asTiffToolStripMenuItem2.Text = _localizationGetter("Forms.AnimationListControl.asTiffToolStripMenuItem2") ?? "As Tiff"; + asJpgToolStripMenuItem2.Text = _localizationGetter("Forms.AnimationListControl.asJpgToolStripMenuItem2") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.asPngToolStripMenuItem") ?? "As Png"; + + // 导出所有缩略图菜单 + exportAllThumbnailsToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.ContextMenu.exportAllThumbnailsToolStripMenuItem") ?? "Export All Thumbnails.."; + asBmpToolStripMenuItem3.Text = _localizationGetter("Forms.AnimationListControl.ContextMenu.asBmpToolStripMenuItem3") ?? "As Bmp"; + asTiffToolStripMenuItem3.Text = _localizationGetter("Forms.AnimationListControl.ContextMenu.asTiffToolStripMenuItem3") ?? "As Tiff"; + asJpgToolStripMenuItem3.Text = _localizationGetter("Forms.AnimationListControl.ContextMenu.asJpgToolStripMenuItem3") ?? "As Jpg"; + asPngToolStripMenuItem3.Text = _localizationGetter("Forms.AnimationListControl.ContextMenu.asPngToolStripMenuItem3") ?? "As Png"; + + // 帧设置组 + groupBoxSettings.Text = _localizationGetter("Forms.AnimationListControl.groupBoxSettings") ?? "Frames"; + directionLabel.Text = _localizationGetter("Forms.AnimationListControl.directionLabel") ?? "Direction"; + ShowFrameBoundsCheckBox.Text = _localizationGetter("Forms.AnimationListControl.ShowFrameBoundsCheckBox") ?? "Show frame bounds"; + AnimateCheckBox.Text = _localizationGetter("Forms.AnimationListControl.AnimateCheckBox") ?? "Animate"; + + // 设置菜单 + SettingsButton.Text = _localizationGetter("Forms.AnimationListControl.SettingsButton") ?? "Settings"; + sortAlphaToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.sortAlphaToolStripMenuItem") ?? "Sort alphabetical"; + hueToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.hueToolStripMenuItem") ?? "Hue"; + rewriteXmlToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.rewriteXmlToolStripMenuItem") ?? "Rewrite Xml"; + tryToFindNewGraphicsToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.tryToFindNewGraphicsToolStripMenuItem") ?? "Try to find new Graphics"; + animationEditToolStripMenuItem.Text = _localizationGetter("Forms.AnimationListControl.animationEditToolStripMenuItem") ?? "Animation edit"; + + // 状态栏标签 + _graphicLabelPrefix = _localizationGetter("Forms.AnimationListControl.GraphicLabel") ?? "Graphic: "; + _baseGraphicLabelPrefix = _localizationGetter("Forms.AnimationListControl.BaseGraphicLabel") ?? "Base Graphic: "; + _hueLabelPrefix = _localizationGetter("Forms.AnimationListControl.HueLabel") ?? "Hue: "; + + GraphicLabel.Text = _graphicLabelPrefix; + BaseGraphicLabel.Text = _baseGraphicLabelPrefix; + HueLabel.Text = _hueLabelPrefix; + } + + /// + /// 获取动作名称的本地化文本(汉化) + /// + private string GetLocalizedActionName(string actionName) + { + if (_localizationGetter == null) return actionName; + return _localizationGetter($"Forms.AnimationListControl.ActionNames.{actionName}") ?? actionName; } private void OnClickExtractAnimGifLooping(object sender, EventArgs e) diff --git a/UoFiddler.Controls/UserControls/ClilocControl.cs b/UoFiddler.Controls/UserControls/ClilocControl.cs index 794489d5..e2d2ce7f 100644 --- a/UoFiddler.Controls/UserControls/ClilocControl.cs +++ b/UoFiddler.Controls/UserControls/ClilocControl.cs @@ -42,6 +42,78 @@ public ClilocControl() private SortOrder _sortOrder; private int _sortColumn; private bool _loaded; + private Func? _localizationGetter; + + /// + /// Set localization for CliLocControl + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Apply localization to all UI elements + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 工具栏按钮和文本框 + GotoEntry.Text = _localizationGetter("Forms.CliLocControl.GotoEntry") ?? "Enter Number"; + GotoButton.Text = _localizationGetter("Forms.CliLocControl.GotoButton") ?? "Goto"; + FindEntry.Text = _localizationGetter("Forms.CliLocControl.FindEntry") ?? "Enter Text"; + FindButton.Text = _localizationGetter("Forms.CliLocControl.FindButton") ?? "Find"; + RegexToolStripButton.Text = _localizationGetter("Forms.CliLocControl.RegexToolStripButton") ?? "Use regular expression"; + toolStripButton1.Text = _localizationGetter("Forms.CliLocControl.SaveButton") ?? "Save"; + ClilocExportButton.Text = _localizationGetter("Forms.CliLocControl.ExportButton") ?? "Export"; + toolStripDropDownButton1.Text = _localizationGetter("Forms.CliLocControl.ImportButton") ?? "Import"; + + // 右键菜单项 + copyCliLocNumberToolStripMenuItem.Text = _localizationGetter("Forms.CliLocControl.copyCliLocNumberToolStripMenuItem") ?? "Copy CliLoc Number"; + copyCliLocTextToolStripMenuItem.Text = _localizationGetter("Forms.CliLocControl.copyCliLocTextToolStripMenuItem") ?? "Copy CliLoc Text"; + addEntryToolStripMenuItem.Text = _localizationGetter("Forms.CliLocControl.addEntryToolStripMenuItem") ?? "Add Entry"; + deleteEntryToolStripMenuItem.Text = _localizationGetter("Forms.CliLocControl.deleteEntryToolStripMenuItem") ?? "Delete Entry"; + + // 导入菜单项 (CSV 保持英文,不汉化) + // cSVToolStripMenuItem.Text 保持 "CSV" + tileDataToolStripMenuItem.Text = _localizationGetter("Forms.CliLocControl.tileDataToolStripMenuItem") ?? "Sync from TileData"; + + // 语言下拉菜单项 + LangComboBox.Items.Clear(); + LangComboBox.Items.Add(_localizationGetter("Forms.CliLocControl.LangComboBoxEnglish") ?? "English"); + LangComboBox.Items.Add(_localizationGetter("Forms.CliLocControl.LangComboBoxGerman") ?? "German"); + LangComboBox.Items.Add(_localizationGetter("Forms.CliLocControl.LangComboBoxCustom1") ?? "Custom 1"); + LangComboBox.Items.Add(_localizationGetter("Forms.CliLocControl.LangComboBoxCustom2") ?? "Custom 2"); + + // 表格列头 + if (dataGridView1.Columns.Count > 0) + { + dataGridView1.Columns[0].HeaderText = _localizationGetter("Forms.CliLocControl.ColumnNumber") ?? "Number"; + dataGridView1.Columns[1].HeaderText = _localizationGetter("Forms.CliLocControl.ColumnText") ?? "Text"; + if (dataGridView1.Columns.Count > 2) + { + dataGridView1.Columns[2].HeaderText = _localizationGetter("Forms.CliLocControl.ColumnFlag") ?? "Flag"; + } + } + } + + /// + /// Apply column headers localization + /// + private void ApplyColumnHeaders() + { + if (dataGridView1.Columns.Count > 0) + { + dataGridView1.Columns[0].HeaderText = _localizationGetter("Forms.CliLocControl.ColumnNumber") ?? "Number"; + dataGridView1.Columns[1].HeaderText = _localizationGetter("Forms.CliLocControl.ColumnText") ?? "Text"; + if (dataGridView1.Columns.Count > 2) + { + dataGridView1.Columns[2].HeaderText = _localizationGetter("Forms.CliLocControl.ColumnFlag") ?? "Flag"; + } + } + } /// /// Sets Language and loads cliloc @@ -72,7 +144,9 @@ private int Lang if (!string.IsNullOrEmpty(_cliloc?.LoadWarning)) { - MessageBox.Show(this, _cliloc.LoadWarning, "Cliloc parsed with warnings", + string warningTitle = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.ClilocLoadWarningTitle") + ?? "Cliloc parsed with warnings"; + MessageBox.Show(this, _cliloc.LoadWarning, warningTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } @@ -109,6 +183,8 @@ private void OnLoad(object sender, EventArgs e) dataGridView1.DataSource = _source; if (dataGridView1.Columns.Count > 0) { + // 应用列头汉化 + ApplyColumnHeaders(); dataGridView1.Columns[0].HeaderCell.SortGlyphDirection = SortOrder.Ascending; dataGridView1.Columns[0].Width = 60; dataGridView1.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.None; @@ -187,6 +263,8 @@ private void OnLangChange(object sender, EventArgs e) if (dataGridView1.Columns.Count > 0) { + // 应用列头汉化 + ApplyColumnHeaders(); dataGridView1.Columns[0].HeaderCell.SortGlyphDirection = SortOrder.Ascending; dataGridView1.Columns[0].Width = 60; dataGridView1.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.None; @@ -215,9 +293,14 @@ private void GotoNr(object sender, EventArgs e) } } + string gotoNotFoundMessage = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.NumberNotFound") + ?? "Number not found."; + string gotoTitle = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.GotoTitle") + ?? "Goto"; + MessageBox.Show( - "Number not found.", - "Goto", + gotoNotFoundMessage, + gotoTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); @@ -227,7 +310,11 @@ private void FindEntryClick(object sender, EventArgs e) { if (string.IsNullOrEmpty(FindEntry.Text) || FindEntry.Text == _searchTextPlaceholder) { - MessageBox.Show("Please provide search text", "Find Entry", MessageBoxButtons.OK, MessageBoxIcon.Error, + string message = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.PleaseProvideSearchText") + ?? "Please provide search text"; + string title = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.FindEntryTitle") + ?? "Find Entry"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return; @@ -256,7 +343,12 @@ private void FindEntryClick(object sender, EventArgs e) return; } - MessageBox.Show(hasErrors ? "Invalid regular expression." : "Entry not found.", "Find Entry", + string msgContent = hasErrors + ? (_localizationGetter?.Invoke("Forms.CliLocControl.Messages.InvalidRegex") ?? "Invalid regular expression.") + : (_localizationGetter?.Invoke("Forms.CliLocControl.Messages.EntryNotFound") ?? "Entry not found."); + string msgTitle = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.FindEntryTitle") + ?? "Find Entry"; + MessageBox.Show(msgContent, msgTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } @@ -282,7 +374,11 @@ private void FindPreviousEntry() { if (string.IsNullOrEmpty(FindEntry.Text) || FindEntry.Text == _searchTextPlaceholder) { - MessageBox.Show("Please provide search text", "Find Entry", MessageBoxButtons.OK, MessageBoxIcon.Error, + string message = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.PleaseProvideSearchText") + ?? "Please provide search text"; + string title = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.FindEntryTitle") + ?? "Find Entry"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return; @@ -318,7 +414,12 @@ private void FindPreviousEntry() return; } - MessageBox.Show(hasErrors ? "Invalid regular expression." : "Entry not found.", "Find Entry", + string notFoundMessage = hasErrors + ? (_localizationGetter?.Invoke("Forms.CliLocControl.Messages.InvalidRegex") ?? "Invalid regular expression.") + : (_localizationGetter?.Invoke("Forms.CliLocControl.Messages.EntryNotFound") ?? "Entry not found."); + string dialogTitle = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.FindEntryTitle") + ?? "Find Entry"; + MessageBox.Show(notFoundMessage, dialogTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } @@ -349,7 +450,10 @@ private void OnClickSave(object sender, EventArgs e) dataGridView1.Invalidate(); Options.ChangedUltimaClass["CliLoc"] = false; - FileSavedDialog.Show(FindForm(), Options.OutputPath, "CliLoc saved successfully."); + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") + ?? "Saved"; + + FileSavedDialog.Show(FindForm(), Options.OutputPath, null, title, key => _localizationGetter?.Invoke(key)); } private void OnCell_dbClick(object sender, DataGridViewCellEventArgs e) @@ -558,15 +662,21 @@ private void OnClickExportCSV(object sender, EventArgs e) } } - FileSavedDialog.Show(FindForm(), fileName, "CliLoc saved successfully."); + // 使用支持汉化的 Show 重载 + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, "CliLoc saved successfully.", title, key => _localizationGetter?.Invoke(key)); } private void OnClickImportCSV(object sender, EventArgs e) { + // 获取汉化的标题 + string dialogTitle = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.ImportChooseFileTitle") + ?? "Choose csv file to import"; + OpenFileDialog dialog = new OpenFileDialog { Multiselect = false, - Title = "Choose csv file to import", + Title = dialogTitle, CheckFileExists = true, Filter = "csv files (*.csv)|*.csv" }; @@ -640,16 +750,26 @@ private void OnClickImportCSV(object sender, EventArgs e) } } + // 获取汉化的消息框文本 + string doneTitle = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.ImportDoneTitle") + ?? "Import Done"; + if (count > 0) { Options.ChangedUltimaClass["CliLoc"] = true; _source.ResetBindings(false); dataGridView1.Invalidate(); - MessageBox.Show(this, $"{count} entries changed.", "Import Done", MessageBoxButtons.OK, MessageBoxIcon.Information); + + string messageTemplate = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.ImportEntriesChanged") + ?? "{0} entries changed."; + string message = string.Format(messageTemplate, count); + MessageBox.Show(this, message, doneTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); } else { - MessageBox.Show(this, "No entries changed.", "Import Done", MessageBoxButtons.OK, MessageBoxIcon.Information); + string noChangeMessage = _localizationGetter?.Invoke("Forms.CliLocControl.Messages.ImportNoEntriesChanged") + ?? "No entries changed."; + MessageBox.Show(this, noChangeMessage, doneTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); } } } @@ -673,6 +793,7 @@ private void TileDataToolStripMenuItem_Click(object sender, EventArgs e) } preview = new TileDataSyncPreviewForm(changes); + preview.SetLocalization(_localizationGetter); } catch { diff --git a/UoFiddler.Controls/UserControls/DressControl.cs b/UoFiddler.Controls/UserControls/DressControl.cs index d3ae25ec..e6984672 100644 --- a/UoFiddler.Controls/UserControls/DressControl.cs +++ b/UoFiddler.Controls/UserControls/DressControl.cs @@ -44,6 +44,9 @@ public DressControl() private int[] _displayedItems = Array.Empty(); private Color[] _displayedColors = Array.Empty(); + // Localization support + private Func? _localizationGetter; + private int GetSelectedObjType() { return listViewItems.SelectedIndices.Count > 0 @@ -181,6 +184,58 @@ public void SetHue(int index, int color) _hues[index] = color; } + /// + /// Sets the localization getter and applies localized text. + /// + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Applies localized text to UI elements. + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Buttons and Labels + FindNextItemButton.Text = _localizationGetter("Forms.DressControl.FindNextItemButton") ?? "Find next"; + button1.Text = _localizationGetter("Forms.DressControl.ExportReportButton") ?? "Export Report"; + LayerSort.Text = _localizationGetter("Forms.DressControl.LayerSort") ?? "Layer Sort"; + buttonDress.Text = _localizationGetter("Forms.DressControl.DressItemButton") ?? "Dress Item"; + checkBoxfemale.Text = _localizationGetter("Forms.DressControl.FemaleCheckBox") ?? "Female"; + checkBoxHuman.Text = _localizationGetter("Forms.DressControl.HumanRadio") ?? "Human"; + checkBoxElve.Text = _localizationGetter("Forms.DressControl.ElfRadio") ?? "Elf"; + checkBoxGargoyle.Text = _localizationGetter("Forms.DressControl.GargoyleRadio") ?? "Gargoyle"; + groupBox1.Text = _localizationGetter("Forms.DressControl.MountGroupBox") ?? "Mount"; + groupBoxAnimate.Text = _localizationGetter("Forms.DressControl.FacingActionGroupBox") ?? "Facing/Action"; + listViewItemsColumn.Text = _localizationGetter("Forms.DressControl.ItemColumnHeader") ?? "Item"; + + // Context Menu 1 (Wear List) + unDressToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.UnDress") ?? "UnDress"; + huToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.Hue") ?? "Hue"; + hueToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.HueEllipsis") ?? "Hue.."; + unDressAllToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.UnDressAll") ?? "UnDress All"; + + // Context Menu 2 (Picture) + showAnimationToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.ShowAnimation") ?? "Show Animation"; + animateToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.Animate") ?? "Animate"; + extractImageToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.ExportImage") ?? "Export Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.AsBmp") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.AsTiff") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.AsJpg") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.AsPng") ?? "As Png"; + extractAnimationToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.ExportAnimation") ?? "Export Animation.."; + asBmpToolStripMenuItem1.Text = _localizationGetter("Forms.DressControl.AsBmp") ?? "As Bmp"; + asTiffToolStripMenuItem1.Text = _localizationGetter("Forms.DressControl.AsTiff") ?? "As Tiff"; + asJpgToolStripMenuItem1.Text = _localizationGetter("Forms.DressControl.AsJpg") ?? "As Jpg"; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.DressControl.AsPng") ?? "As Png"; + asAnimatedGifToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.AsAnimatedGifLooping") ?? "As animated Gif (looping)"; + asAnimatedGifnoLoopingToolStripMenuItem.Text = _localizationGetter("Forms.DressControl.AsAnimatedGifNoLooping") ?? "As animated Gif (no looping)"; + } + /// /// Reload when loaded /// @@ -798,14 +853,25 @@ private void UpdateSelection(int objType) pictureBoxDress.Invalidate(); TextBox.Clear(); + + // Get localized labels + string objTypeLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.ObjType") ?? "Objtype"; + string layerLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.Layer") ?? "Layer"; + string gumpIdLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.GumpID") ?? "GumpID"; + string hueLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.Hue") ?? "Hue"; + string animationLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.Animation") ?? "Animation"; + string validGumpLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.ValidGump") ?? "ValidGump"; + string validAnimLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.ValidAnim") ?? "ValidAnim"; + string validLayerLabel = _localizationGetter?.Invoke("Forms.DressControl.InfoLabels.ValidLayer") ?? "ValidLayer"; + TextBox.AppendText( - $"Objtype: 0x{objType:X4}\nLayer: 0x{TileData.ItemTable[objType].Quality:X2}\n"); - TextBox.AppendText($"GumpID: 0x{gump:X4} (0x{gumpOrig:X4})\nHue: {hue + 1}\n"); - TextBox.AppendText($"Animation: 0x{ani:X4} (0x{TileData.ItemTable[objType].Animation:X4})\n"); + $"{objTypeLabel}: 0x{objType:X4}\n{layerLabel}: 0x{TileData.ItemTable[objType].Quality:X2}\n"); + TextBox.AppendText($"{gumpIdLabel}: 0x{gump:X4} (0x{gumpOrig:X4})\n{hueLabel}: {hue + 1}\n"); + TextBox.AppendText($"{animationLabel}: 0x{ani:X4} (0x{TileData.ItemTable[objType].Animation:X4})\n"); TextBox.AppendText( - $"ValidGump: {Gumps.IsValidIndex(gump)}\nValidAnim: {Animations.IsActionDefined(ani, 0, 0)}\n"); + $"{validGumpLabel}: {Gumps.IsValidIndex(gump)}\n{validAnimLabel}: {Animations.IsActionDefined(ani, 0, 0)}\n"); TextBox.AppendText( - $"ValidLayer: {Array.IndexOf(_drawOrder, TileData.ItemTable[objType].Quality) != -1}"); + $"{validLayerLabel}: {Array.IndexOf(_drawOrder, TileData.ItemTable[objType].Quality) != -1}"); } private void OnClick_Animate(object sender, EventArgs e) @@ -988,13 +1054,13 @@ private void OnClick_ChangeDisplay(object sender, EventArgs e) { groupBoxAnimate.Visible = false; animateToolStripMenuItem.Visible = false; - showAnimationToolStripMenuItem.Text = "Show Animation"; + showAnimationToolStripMenuItem.Text = _localizationGetter?.Invoke("Forms.DressControl.ShowAnimation") ?? "Show Animation"; } else { groupBoxAnimate.Visible = true; animateToolStripMenuItem.Visible = true; - showAnimationToolStripMenuItem.Text = "Show Paperdoll"; + showAnimationToolStripMenuItem.Text = _localizationGetter?.Invoke("Forms.DressControl.ShowPaperdoll") ?? "Show Paperdoll"; } RefreshDrawing(); @@ -1346,7 +1412,9 @@ private void ExportImage(ImageFormat imageFormat) string fileName = Path.Combine(outputPath, $"Dress PD.{fileExtension}"); DressPic.Image.Save(fileName, imageFormat); - FileSavedDialog.Show(FindForm(), fileName, "Paperdoll saved successfully."); + string message = _localizationGetter?.Invoke("Forms.DressControl.ExportMessages.PaperdollSaved") ?? "Paperdoll saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter?.Invoke(key)); } else { @@ -1360,8 +1428,9 @@ private void ExportImage(ImageFormat imageFormat) DressPic.Image.Save(fileName, imageFormat); } - - FileSavedDialog.Show(FindForm(), fileName, "InGame saved successfully."); + string message = _localizationGetter?.Invoke("Forms.DressControl.ExportMessages.InGameSaved") ?? "InGame saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter?.Invoke(key)); } } @@ -1397,7 +1466,9 @@ private void ExportAnimation(ImageFormat imageFormat) _animation[i].Save(Path.Combine(path, $"{fileName}-{i}.{fileExtension}"), imageFormat); } - FileSavedDialog.Show(FindForm(), path, "InGame Anim saved successfully."); + string message = _localizationGetter?.Invoke("Forms.DressControl.ExportMessages.AnimSaved") ?? "InGame Anim saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), path, message, title, key => _localizationGetter?.Invoke(key)); } private void ExportAnimatedGif(bool looping) @@ -1432,7 +1503,9 @@ private void ExportAnimatedGif(bool looping) stream.WriteByte(0); } - FileSavedDialog.Show(FindForm(), outputFile, "InGame Anim saved successfully."); + string message = _localizationGetter?.Invoke("Forms.DressControl.ExportMessages.AnimSaved") ?? "InGame Anim saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), outputFile, message, title, key => _localizationGetter?.Invoke(key)); } private void OnClickExtractAnimGifLooping(object sender, EventArgs e) { @@ -1706,7 +1779,10 @@ private void OnClickBuildAnimationList(object sender, EventArgs e) tex.WriteLine(" "); } - FileSavedDialog.Show(FindForm(), fileName, "Report saved successfully."); + FileSavedDialog.Show(FindForm(), fileName, + _localizationGetter?.Invoke("Forms.DressControl.ExportMessages.ReportSaved") ?? "Report saved successfully.", + _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved", + key => _localizationGetter?.Invoke(key)); } private void MountTextBoxOnKeyDown(object sender, KeyEventArgs e) diff --git a/UoFiddler.Controls/UserControls/FontsControl.cs b/UoFiddler.Controls/UserControls/FontsControl.cs index 4c75b72a..9e2a9ef2 100644 --- a/UoFiddler.Controls/UserControls/FontsControl.cs +++ b/UoFiddler.Controls/UserControls/FontsControl.cs @@ -38,6 +38,40 @@ public FontsControl() private bool _loaded; private static FontsControl _refMarker; private List _fonts = new List(); + private Func? _localizationGetter; + + /// + /// Set localization for FontsControl + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Apply localization to all UI elements + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 右键菜单项 + writeTextToolStripMenuItem.Text = _localizationGetter("Forms.FontsControl.writeTextToolStripMenuItem") ?? "Write Text"; + setOffsetsToolStripMenuItem.Text = _localizationGetter("Forms.FontsControl.setOffsetsToolStripMenuItem") ?? "Set Offsets"; + extractCharacterToolStripMenuItem.Text = _localizationGetter("Forms.FontsControl.extractCharacterToolStripMenuItem") ?? "Extract Character"; + importCharacterToolStripMenuItem.Text = _localizationGetter("Forms.FontsControl.importCharacterToolStripMenuItem") ?? "Import Character"; + saveToolStripMenuItem.Text = _localizationGetter("Forms.FontsControl.saveToolStripMenuItem") ?? "Save"; + + // 复选框 + LoadUnicodeFontsCheckBox.Text = _localizationGetter("Forms.FontsControl.LoadUnicodeFontsCheckBox") ?? "Load Unicode Fonts"; + + // 状态栏初始文本 + if (string.IsNullOrEmpty(toolStripStatusLabel1.Text) || toolStripStatusLabel1.Text == "") + { + toolStripStatusLabel1.Text = _localizationGetter("Forms.FontsControl.toolStripStatusLabel1") ?? ""; + } + } /// /// Reload when loaded (file changed) @@ -229,7 +263,10 @@ private void OnClickExport(object sender, EventArgs e) bmp.Save(fileName, ImageFormat.Tiff); } - FileSavedDialog.Show(FindForm(), fileName, "Character saved successfully."); + string message = _localizationGetter?.Invoke("Forms.FontsControl.Messages.characterSavedSuccess") + ?? "Character saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter?.Invoke(key)); } private static int AsciiFontOffset => 32; @@ -257,7 +294,11 @@ private void OnClickImport(object sender, EventArgs e) { import.Dispose(); - MessageBox.Show("Image Height or Width exceeds 255", "Import", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + string message = _localizationGetter?.Invoke("Forms.FontsControl.Messages.imageSizeExceeds") + ?? "Image Height or Width exceeds 255"; + string title = _localizationGetter?.Invoke("Forms.FontsControl.Messages.importTitle") + ?? "Import"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return; } @@ -286,14 +327,20 @@ private void OnClickSave(object sender, EventArgs e) { string fileName = UnicodeFonts.Save(path, (int)treeView.SelectedNode.Tag); Options.ChangedUltimaClass["UnicodeFont"] = false; - FileSavedDialog.Show(FindForm(), fileName, "Unicode fonts saved successfully."); + string message = _localizationGetter?.Invoke("Forms.FontsControl.Messages.unicodeFontsSavedSuccess") + ?? "Unicode fonts saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter?.Invoke(key)); } else { string fileName = Path.Combine(path, "fonts.mul"); AsciiText.Save(fileName); Options.ChangedUltimaClass["ASCIIFont"] = false; - FileSavedDialog.Show(FindForm(), fileName, "Fonts saved successfully."); + string message = _localizationGetter?.Invoke("Forms.FontsControl.Messages.fontsSavedSuccess") + ?? "Fonts saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter?.Invoke(key)); } } @@ -423,21 +470,38 @@ private void UpdateStatusStrip() int i = FontsTileView.SelectedIndices[0]; - toolStripStatusLabel1.Text = (int)treeView.SelectedNode.Parent.Tag == 1 - ? string.Format("'{0}' : {1} (0x{1:X}) XOffset: {2} YOffset: {3}", (char)i, i, + if ((int)treeView.SelectedNode.Parent.Tag == 1) + { + // Unicode fonts + toolStripStatusLabel1.Text = string.Format("'{0}' : {1} (0x{1:X}) XOffset: {2} YOffset: {3}", (char)i, i, UnicodeFonts.Fonts[(int)treeView.SelectedNode.Tag].Chars[i].XOffset, - UnicodeFonts.Fonts[(int)treeView.SelectedNode.Tag].Chars[i].YOffset) - : string.Format("'{0}' : {1} (0x{1:X})", (char)(_fonts[i] + AsciiFontOffset), _fonts[i] + AsciiFontOffset); + UnicodeFonts.Fonts[(int)treeView.SelectedNode.Tag].Chars[i].YOffset); + } + else + { + // ASCII fonts - 需要边界检查 + if (i >= 0 && i < _fonts.Count) + { + toolStripStatusLabel1.Text = string.Format("'{0}' : {1} (0x{1:X})", + (char)(_fonts[i] + AsciiFontOffset), _fonts[i] + AsciiFontOffset); + } + else + { + toolStripStatusLabel1.Text = string.Empty; + } + } } private void LoadUnicodeFontsCheckBox_CheckedChanged(object sender, EventArgs e) { string message = LoadUnicodeFontsCheckBox.Checked - ? "Would you like to load all fonts including Unicode?" - : "Load only ASCII fonts?"; + ? (_localizationGetter?.Invoke("Forms.FontsControl.Messages.loadAllFontsConfirm") ?? "Would you like to load all fonts including Unicode?") + : (_localizationGetter?.Invoke("Forms.FontsControl.Messages.loadAsciiOnlyConfirm") ?? "Load only ASCII fonts?"); + + string title = _localizationGetter?.Invoke("Forms.FontsControl.Messages.fontsReloadTitle") ?? "Fonts reload"; DialogResult result = - MessageBox.Show(message, "Fonts reload", + MessageBox.Show(message, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) { diff --git a/UoFiddler.Controls/UserControls/GumpControl.Designer.cs b/UoFiddler.Controls/UserControls/GumpControl.Designer.cs index 37afc50d..0eb78b8c 100644 --- a/UoFiddler.Controls/UserControls/GumpControl.Designer.cs +++ b/UoFiddler.Controls/UserControls/GumpControl.Designer.cs @@ -137,7 +137,7 @@ private void InitializeComponent() listView.HideSelection = false; listView.Location = new System.Drawing.Point(0, 50); listView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - listView.MultiSelect = false; + listView.MultiSelect = true; listView.Name = "listView"; listView.OwnerDraw = true; listView.Size = new System.Drawing.Size(289, 380); diff --git a/UoFiddler.Controls/UserControls/GumpControl.cs b/UoFiddler.Controls/UserControls/GumpControl.cs index 64b76262..08140e04 100644 --- a/UoFiddler.Controls/UserControls/GumpControl.cs +++ b/UoFiddler.Controls/UserControls/GumpControl.cs @@ -28,6 +28,10 @@ namespace UoFiddler.Controls.UserControls { public partial class GumpControl : UserControl { + private Func _localizationGetter; + private string _idLabelPrefix = "ID:"; + private string _sizeLabelPrefix = "Size:"; + public GumpControl() { InitializeComponent(); @@ -46,6 +50,60 @@ public GumpControl() pictureBox.BackColor = Options.PreviewBackgroundColor; } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 上下文菜单项 + showFreeSlotsToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.showFreeSlotsToolStripMenuItem") ?? "Show Free Slots"; + findNextFreeSlotToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.findNextFreeSlotToolStripMenuItem") ?? "Find Next Free Slot"; + changeBackgroundColorToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.changeBackgroundColorToolStripMenuItem") ?? "Change background color"; + extractImageToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.extractImageToolStripMenuItem") ?? "Export Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.asBmpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem1.Text = _localizationGetter("Forms.GumpControl.asJpgToolStripMenuItem1") ?? "As Jpg"; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.GumpControl.asPngToolStripMenuItem1") ?? "As Png"; + jumpToMaleFemale.Text = _localizationGetter("Forms.GumpControl.jumpToMaleFemale") ?? "Jump to Male/Female"; + + copyImageToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.copyImageToolStripMenuItem") ?? "Copy Image"; + pasteImageToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + replaceGumpToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.replaceGumpToolStripMenuItem") ?? "Replace"; + insertToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.insertToolStripMenuItem") ?? "Insert At.."; + toolStripMenuItem1.Text = _localizationGetter("Forms.GumpControl.toolStripMenuItem1") ?? "Insert Starting From"; + removeToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.removeToolStripMenuItem") ?? "Remove"; + saveToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.saveToolStripMenuItem") ?? "Save"; + + // 过滤工具栏标签 + nameTagToolStripLabel.Text = _localizationGetter("Forms.GumpControl.nameTagToolStripLabel") ?? "Name:"; + tagFilterDropDownButton.Text = _localizationGetter("Forms.GumpControl.tagFilterDropDownButton") ?? "Tags"; + + // 顶部工具栏 + IndexToolStripLabel.Text = _localizationGetter("Forms.GumpControl.IndexToolStripLabel") ?? "Index:"; + toolStripDropDownButton1.Text = _localizationGetter("Forms.GumpControl.toolStripDropDownButton1") ?? "Misc"; + generateFromTileDataToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.generateFromTileDataToolStripMenuItem") ?? "Generate Gumps.xml entries from TileData Equipment..."; + exportAllToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.exportAllToolStripMenuItem") ?? "Export All.."; + asBmpToolStripMenuItem1.Text = _localizationGetter("Forms.GumpControl.asBmpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem1.Text = _localizationGetter("Forms.GumpControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.asJpgToolStripMenuItem") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.GumpControl.asPngToolStripMenuItem") ?? "As Png"; + saveToolStripButton.Text = _localizationGetter("Forms.GumpControl.saveToolStripButton") ?? "Save"; + + // 底部工具栏标签 - 保存前缀用于动态更新 + IDLabel.Text = _localizationGetter("Forms.GumpControl.IDLabel") ?? "ID:"; + _idLabelPrefix = IDLabel.Text; + + SizeLabel.Text = _localizationGetter("Forms.GumpControl.SizeLabel") ?? "Size:"; + _sizeLabelPrefix = SizeLabel.Text; + + Preload.Text = _localizationGetter("Forms.GumpControl.Preload") ?? "Preload"; + } + private sealed record GumpEntry(string Name, string[] Tags); private static GumpControl _refMarker; @@ -76,30 +134,30 @@ private sealed record GumpEntry(string Name, string[] Tags); private static readonly string[] _layerTags = { "", // 0x00 - "one-hand", // 0x01 - "two-hand", // 0x02 - "boots", // 0x03 - "pants", // 0x04 - "shirt", // 0x05 - "helmet", // 0x06 - "gloves", // 0x07 - "ring", // 0x08 - "talisman", // 0x09 - "gorget", // 0x0A - "hair", // 0x0B - "waist", // 0x0C - "chest-armor", // 0x0D - "bracelet", // 0x0E - "", // 0x0F - "facial-hair", // 0x10 - "tunic", // 0x11 - "earring", // 0x12 - "sleeves", // 0x13 - "cloak", // 0x14 - "backpack", // 0x15 - "robe", // 0x16 - "skirt", // 0x17 - "leg-armor", // 0x18 + "单手武器", // 0x01 + "双手武器", // 0x02 + "靴子", // 0x03 + "裤子", // 0x04 + "衬衣", // 0x05 + "头盔", // 0x06 + "手套", // 0x07 + "戒指", // 0x08 + "护身符", // 0x09 + "喉甲", // 0x0A + "头发", // 0x0B + "腰部", // 0x0C + "胸甲", // 0x0D + "手镯", // 0x0E + "", // 0x0F + "胡须", // 0x10 + "束腰外衣", // 0x11 + "耳环", // 0x12 + "袖子", // 0x13 + "斗篷", // 0x14 + "背包", // 0x15 + "长袍", // 0x16 + "裙子", // 0x17 + "腿甲", // 0x18 }; /// @@ -119,6 +177,9 @@ private void ConfigureListView() splitContainer1.SplitterMoved += (_, _) => ClampListWidth(); splitContainer1.SizeChanged += (_, _) => ClampListWidth(); ClampListWidth(); + + // Shift+Click 范围选择(不干扰系统的 Ctrl 多选) + listView.MouseClick += ListView_MouseClick; } /// @@ -351,14 +412,16 @@ private void RebuildTagDropdown() return; } - var clearItem = new ToolStripMenuItem("Clear All"); + var clearItem = new ToolStripMenuItem(_localizationGetter?.Invoke("Forms.GumpControl.ClearAllTags") ?? "Clear All"); clearItem.Click += OnClearTagFilters; tagFilterDropDownButton.DropDownItems.Add(clearItem); tagFilterDropDownButton.DropDownItems.Add(new ToolStripSeparator()); foreach (string tag in allTags) { - var item = new ToolStripMenuItem(tag) { CheckOnClick = true }; + // 获取本地化的标签显示文本 + string displayText = GetLocalizedTagText(tag) ?? tag; + var item = new ToolStripMenuItem(displayText) { CheckOnClick = true, Tag = tag }; item.CheckedChanged += OnTagFilterChanged; tagFilterDropDownButton.DropDownItems.Add(item); } @@ -368,6 +431,36 @@ private void RebuildTagDropdown() tagFilterDropDownButton.DropDown.Closing += OnTagDropDownClosing; } + /// + /// 获取标签的本地化文本 + /// + private string GetLocalizedTagText(string tag) + { + if (_localizationGetter == null) return null; + + // 尝试从本地化字典中获取标签翻译 + string translatedTag = _localizationGetter($"Forms.GumpControl.Tags.{tag}"); + return translatedTag; + } + + /// + /// 获取选中的所有 Gump 索引 + /// + private List GetSelectedGumpIndexes() + { + // 在虚拟模式中,使用 SelectedIndices 而不是 SelectedItems + return listView.SelectedIndices.Cast().ToList(); + } + + /// + /// 获取主要选中的 Gump 索引(第一个) + /// + private int? GetPrimarySelectedGumpIndex() + { + // 在虚拟模式中,使用 SelectedIndices 而不是 SelectedItems + return listView.SelectedIndices.Count > 0 ? listView.SelectedIndices[0] : null; + } + private void OnTagDropDownClosing(object sender, ToolStripDropDownClosingEventArgs e) { if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked) @@ -397,7 +490,9 @@ private void OnTagFilterChanged(object sender, EventArgs e) { if (item is ToolStripMenuItem { Checked: true } mi) { - _activeTagFilters.Add(mi.Text); + // 使用 Tag 属性存储原始标签值,而不是显示文本 + string tagValue = mi.Tag as string ?? mi.Text; + _activeTagFilters.Add(tagValue); } } @@ -475,7 +570,8 @@ private void ListView_DrawItem(object sender, DrawListViewItemEventArgs e) Brush fontBrush = Brushes.Gray; - bool isSelected = e.ItemIndex == _selectedPosition; + // 在虚拟模式中,需要检查该项是否在 SelectedIndices 中 + bool isSelected = listView.SelectedIndices.Contains(e.ItemIndex); int i = _ids[e.ItemIndex]; bool hasEntry = _gumpEntries.TryGetValue(i, out GumpEntry entry); @@ -562,8 +658,18 @@ private void ListView_SelectedIndexChanged(object sender, EventArgs e) if (bmp != null) { pictureBox.BackgroundImage = bmp; - IDLabel.Text = $"ID: 0x{i:X} ({i})"; - SizeLabel.Text = $"Size: {bmp.Width},{bmp.Height}"; + + // 支持显示多选信息 - 在虚拟模式中使用 SelectedIndices.Count + if (listView.SelectedIndices.Count > 1) + { + IDLabel.Text = $"{_idLabelPrefix} 0x{i:X} ({i}) 等 {listView.SelectedIndices.Count} 项"; + } + else + { + IDLabel.Text = $"{_idLabelPrefix} 0x{i:X} ({i})"; + } + + SizeLabel.Text = $"{_sizeLabelPrefix} {bmp.Width},{bmp.Height}"; } else { @@ -579,6 +685,50 @@ private void ListView_SelectedIndexChanged(object sender, EventArgs e) JumpToMaleFemaleInvalidate(); } + private void ListView_MouseClick(object sender, MouseEventArgs e) + { + // 只处理 Shift+Click 范围选择 + // 关键:如果按着 Ctrl,立即返回,让系统自动处理 Ctrl 多选 + if ((Control.ModifierKeys & Keys.Control) == Keys.Control) + { + return; // Ctrl+Click 由系统自动处理,我们不干扰 + } + + if ((Control.ModifierKeys & Keys.Shift) != Keys.Shift) + { + return; // 不是 Shift+Click,不处理 + } + + ListViewHitTestInfo hitTest = listView.HitTest(e.Location); + if (hitTest.Item == null) + { + return; + } + + int clickedIndex = hitTest.Item.Index; + + // Shift+Click 范围选择:只有当已有选择时才执行 + if (listView.SelectedIndices.Count > 0) + { + int firstSelected = listView.SelectedIndices[0]; + int startIndex = Math.Min(firstSelected, clickedIndex); + int endIndex = Math.Max(firstSelected, clickedIndex); + + // 清除现有选择并选择范围 + listView.SelectedIndices.Clear(); + for (int i = startIndex; i <= endIndex; i++) + { + if (i >= 0 && i < _ids.Count) + { + listView.SelectedIndices.Add(i); + } + } + + // 手动触发以更新 UI + ListView_SelectedIndexChanged(listView, EventArgs.Empty); + } + } + private void JumpToMaleFemaleInvalidate() { int gumpId = SelectedGumpId; @@ -591,25 +741,30 @@ private void JumpToMaleFemaleInvalidate() { if (gumpId >= 60000) { - jumpToMaleFemale.Text = "Jump to Male"; + jumpToMaleFemale.Text = _localizationGetter?.Invoke("Forms.GumpControl.jumpToMale") ?? "Jump to Male"; jumpToMaleFemale.Enabled = HasGumpId(gumpId - 10000); } else { - jumpToMaleFemale.Text = "Jump to Female"; + jumpToMaleFemale.Text = _localizationGetter?.Invoke("Forms.GumpControl.jumpToFemale") ?? "Jump to Female"; jumpToMaleFemale.Enabled = HasGumpId(gumpId + 10000); } } else { jumpToMaleFemale.Enabled = false; - jumpToMaleFemale.Text = "Jump to Male/Female"; + jumpToMaleFemale.Text = _localizationGetter?.Invoke("Forms.GumpControl.jumpToMaleFemale") ?? "Jump to Male/Female"; } } private void ContextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) { int id = SelectedGumpId; + int selectedCount = listView.SelectedIndices.Count; + + // 在虚拟模式下,导出菜单项应该只在有选中项时启用 + extractImageToolStripMenuItem.Enabled = selectedCount > 0; + copyImageToolStripMenuItem.Enabled = id >= 0 && Gumps.IsValidIndex(id); pasteImageToolStripMenuItem.Enabled = id >= 0 && ImageClipboard.ContainsImage(); } @@ -624,7 +779,8 @@ private void OnClickCopyImage(object sender, EventArgs e) if (!ImageClipboard.TryCopy(Gumps.GetGump(id), out string error)) { - MessageBox.Show(error, "Copy Image", MessageBoxButtons.OK, MessageBoxIcon.Warning); + string title = _localizationGetter?.Invoke("Forms.GumpControl.Messages.CopyImage") ?? "Copy Image"; + MessageBox.Show(error, title, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } @@ -639,7 +795,8 @@ private void OnClickPasteImage(object sender, EventArgs e) using Bitmap pasted = ImageClipboard.TryPaste(out string error); if (pasted == null) { - MessageBox.Show(error, "Paste Image", MessageBoxButtons.OK, MessageBoxIcon.Warning); + string title = _localizationGetter?.Invoke("Forms.GumpControl.Messages.PasteImage") ?? "Paste Image"; + MessageBox.Show(error, title, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } @@ -647,12 +804,11 @@ private void OnClickPasteImage(object sender, EventArgs e) // decoder refuses anything past 0xFFFF on either axis. if (pasted.Width == 0 || pasted.Height == 0 || pasted.Width > 0xFFFF || pasted.Height > 0xFFFF) { - MessageBox.Show( - $"Invalid gump dimensions!\n\n" + - $"Clipboard image: {pasted.Width}x{pasted.Height}\n" + - $"Gumps may be up to 65535x65535 pixels.\n\n" + - "No changes made.", - "Invalid Size", MessageBoxButtons.OK, MessageBoxIcon.Warning); + string template = _localizationGetter?.Invoke("Forms.GumpControl.Messages.InvalidGumpDimensions") + ?? "Invalid gump dimensions!\n\nClipboard image: {0}x{1}\nGumps may be up to 65535x65535 pixels.\n\nNo changes made."; + string message = string.Format(template, pasted.Width, pasted.Height); + string title = _localizationGetter?.Invoke("Forms.GumpControl.Messages.InvalidSize") ?? "Invalid Size"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } @@ -726,7 +882,9 @@ private void OnClickReplace(object sender, EventArgs e) private void OnClickSave(object sender, EventArgs e) { - DialogResult result = MessageBox.Show("Are you sure? Will take a while", "Save", MessageBoxButtons.YesNo, + string message = _localizationGetter?.Invoke("Forms.GumpControl.Messages.SaveConfirm") ?? "Are you sure? Will take a while"; + string title = _localizationGetter?.Invoke("Forms.GumpControl.Messages.Save") ?? "Save"; + DialogResult result = MessageBox.Show(message, title, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) { @@ -745,7 +903,10 @@ private void OnClickRemove(object sender, EventArgs e) return; } - DialogResult result = MessageBox.Show($"Are you sure to remove {i}", "Remove", MessageBoxButtons.YesNo, + string template = _localizationGetter?.Invoke("Forms.GumpControl.Messages.RemoveConfirm") ?? "Are you sure to remove {0}"; + string message = string.Format(template, i); + string title = _localizationGetter?.Invoke("Forms.GumpControl.Messages.Remove") ?? "Remove"; + DialogResult result = MessageBox.Show(message, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) { @@ -866,26 +1027,64 @@ private void OnKeydown_InsertText(object sender, KeyEventArgs e) private void Extract_Image_ClickBmp(object sender, EventArgs e) { - int i = SelectedGumpId; - ExportGumpImage(i, ImageFormat.Bmp); + ExportSelectedGumpImages(ImageFormat.Bmp); } private void Extract_Image_ClickTiff(object sender, EventArgs e) { - int i = SelectedGumpId; - ExportGumpImage(i, ImageFormat.Tiff); + ExportSelectedGumpImages(ImageFormat.Tiff); } private void Extract_Image_ClickJpg(object sender, EventArgs e) { - int i = SelectedGumpId; - ExportGumpImage(i, ImageFormat.Jpeg); + ExportSelectedGumpImages(ImageFormat.Jpeg); } private void Extract_Image_ClickPng(object sender, EventArgs e) { - int i = SelectedGumpId; - ExportGumpImage(i, ImageFormat.Png); + ExportSelectedGumpImages(ImageFormat.Png); + } + + private void ExportSelectedGumpImages(ImageFormat imageFormat) + { + List selectedIndexes = GetSelectedGumpIndexes(); + if (selectedIndexes.Count == 0) + { + return; + } + + if (selectedIndexes.Count == 1) + { + ExportGumpImage(selectedIndexes[0], imageFormat); + return; + } + + string fileExtension = Utils.GetFileExtensionFor(imageFormat); + using (FolderBrowserDialog dialog = new FolderBrowserDialog()) + { + dialog.Description = "选择导出目录"; + dialog.ShowNewFolderButton = true; + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + Cursor.Current = Cursors.WaitCursor; + foreach (int index in selectedIndexes) + { + string fileName = Path.Combine(dialog.SelectedPath, $"Gump 0x{index:X4}.{fileExtension}"); + if (Gumps.IsValidIndex(index)) + { + using (Bitmap bit = new Bitmap(Gumps.GetGump(index))) + { + bit.Save(fileName, imageFormat); + } + } + } + Cursor.Current = Cursors.Default; + + MessageBox.Show($"已成功导出所选的 {selectedIndexes.Count} 个 Gump。", "导出完成", MessageBoxButtons.OK, MessageBoxIcon.Information); + } } private static void ExportGumpImage(int index, ImageFormat imageFormat) diff --git a/UoFiddler.Controls/UserControls/HuesControl.cs b/UoFiddler.Controls/UserControls/HuesControl.cs index d29fc46b..1f1e30e1 100644 --- a/UoFiddler.Controls/UserControls/HuesControl.cs +++ b/UoFiddler.Controls/UserControls/HuesControl.cs @@ -33,6 +33,32 @@ public HuesControl() pictureBox.MouseWheel += OnMouseWheel; } + private Func? _localizationGetter; + + /// + /// 设置本地化函数 + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + saveToolStripMenuItem.Text = _localizationGetter("Forms.HuesControl.saveToolStripMenuItem") ?? "Save"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.HuesControl.replaceToolStripMenuItem") ?? "Replace With.."; + exportToolStripMenuItem.Text = _localizationGetter("Forms.HuesControl.exportToolStripMenuItem") ?? "Export.."; + importToolStripMenuItem.Text = _localizationGetter("Forms.HuesControl.importToolStripMenuItem") ?? "Import.."; + exportAllHueNamesListToolStripMenuItem.Text = _localizationGetter("Forms.HuesControl.exportAllHueNamesListToolStripMenuItem") ?? "Export all hue names list"; + HueIndexToolStripLabel.Text = _localizationGetter("Forms.HuesControl.HueIndexToolStripLabel") ?? "Index:"; + HueNameToolStripLabel.Text = _localizationGetter("Forms.HuesControl.HueNameToolStripLabel") ?? "Name:"; + SearchNameToolStripButton.Text = _localizationGetter("Forms.HuesControl.SearchNameToolStripButton") ?? "Find next"; + IndexOffsetButton.Text = _localizationGetter("Forms.HuesControl.IndexOffsetButton") ?? "Show ingame numbers"; + } + private const int _itemHeight = 20; private int _selected; private bool _loaded; @@ -232,7 +258,9 @@ private void OnMouseDoubleClick(object sender, MouseEventArgs e) if (Selected >= 0) { - new HueEditForm(Selected).Show(); + var form = new HueEditForm(Selected); + form.SetLocalization(key => _localizationGetter?.Invoke(key)); + form.Show(); } } diff --git a/UoFiddler.Controls/UserControls/ItemsControl.cs b/UoFiddler.Controls/UserControls/ItemsControl.cs index 3f9b1cf7..e6ac5f28 100644 --- a/UoFiddler.Controls/UserControls/ItemsControl.cs +++ b/UoFiddler.Controls/UserControls/ItemsControl.cs @@ -46,6 +46,7 @@ public ItemsControl() private bool _showFreeSlots; private int _selectedGraphicId = -1; + private Func? _localizationGetter; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectedGraphicId @@ -423,25 +424,25 @@ private void UpdateDetail(int graphic) } var sb = new StringBuilder(); - sb.AppendLine($"Name: {item.Name}"); - sb.AppendLine($"Graphic: 0x{graphic:X4}"); - sb.AppendLine($"Height/Capacity: {item.Height}"); - sb.AppendLine($"Weight: {item.Weight}"); - sb.AppendLine($"Animation: {item.Animation}"); - sb.AppendLine($"Quality/Layer/Light: {item.Quality}"); - sb.AppendLine($"Quantity: {item.Quantity}"); - sb.AppendLine($"Hue: {item.Hue}"); - sb.AppendLine($"StackingOffset/Unk4: {item.StackingOffset}"); - sb.AppendLine($"Flags: {item.Flags}"); - sb.AppendLine($"Graphic pixel size width, height: {bit?.Width ?? 0} {bit?.Height ?? 0} "); - sb.AppendLine($"Graphic pixel offset xMin, yMin, xMax, yMax: {xMin} {yMin} {xMax} {yMax}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Name") ?? "Name")}: {item.Name}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Graphic") ?? "Graphic")}: 0x{graphic:X4}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.HeightCapacity") ?? "Height/Capacity")}: {item.Height}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Weight") ?? "Weight")}: {item.Weight}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Animation") ?? "Animation")}: {item.Animation}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.QualityLayerLight") ?? "Quality/Layer/Light")}: {item.Quality}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Quantity") ?? "Quantity")}: {item.Quantity}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Hue") ?? "Hue")}: {item.Hue}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.StackingOffsetUnk4") ?? "StackingOffset/Unk4")}: {item.StackingOffset}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.Flags") ?? "Flags")}: {item.Flags}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.GraphicPixelSizeWidthHeight") ?? "Graphic pixel size width, height")}: {bit?.Width ?? 0} {bit?.Height ?? 0} "); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.GraphicPixelOffsetXMinYMinXMaxYMax") ?? "Graphic pixel offset xMin, yMin, xMax, yMax")}: {xMin} {yMin} {xMax} {yMax}"); if ((item.Flags & TileFlag.Animation) != 0) { Animdata.AnimdataEntry info = Animdata.GetAnimData(graphic); if (info != null) { - sb.AppendLine($"Animation FrameCount: {info.FrameCount} Interval: {info.FrameInterval}"); + sb.AppendLine($"{(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.AnimationFrameCount") ?? "Animation FrameCount")}: {info.FrameCount} {(_localizationGetter?.Invoke("Forms.ItemsControl.DetailLabels.AnimationInterval") ?? "Interval")}: {info.FrameInterval}"); } } @@ -855,13 +856,21 @@ private void UpdateToolStripLabels(int graphic) return; } - NameLabel.Text = !Art.IsValidStatic(graphic) ? "Name: FREE" : $"Name: {TileData.ItemTable[graphic].Name}"; - GraphicLabel.Text = $"Graphic: 0x{graphic:X4} ({graphic})"; + // Type 8: 获取汉化的基础标签文本,然后添加动态信息 + string nameLabel = _localizationGetter?.Invoke("Forms.ItemsControl.NameLabel") ?? "Name:"; + string graphicLabel = _localizationGetter?.Invoke("Forms.ItemsControl.GraphicLabel") ?? "Graphic:"; + + NameLabel.Text = !Art.IsValidStatic(graphic) ? $"{nameLabel} FREE" : $"{nameLabel} {TileData.ItemTable[graphic].Name}"; + GraphicLabel.Text = $"{graphicLabel} 0x{graphic:X4} ({graphic})"; } private void OnClickSave(object sender, EventArgs e) { - DialogResult result = MessageBox.Show("Are you sure? Will take a while", "Save", MessageBoxButtons.YesNo, + // Type 6: MessageBox 文本汉化 + string confirmMessage = _localizationGetter?.Invoke("Forms.ItemsControl.Messages.SaveConfirmation") ?? "Are you sure? Will take a while"; + string saveTitle = _localizationGetter?.Invoke("Forms.ItemsControl.Messages.SaveTitle") ?? "Save"; + + DialogResult result = MessageBox.Show(confirmMessage, saveTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) @@ -1324,13 +1333,21 @@ private void TileViewContextMenuStrip_Opening(object sender, CancelEventArgs e) int selectedCount = ItemsTileView.SelectedIndices.Count; copyImageToolStripMenuItem.Enabled = SelectedGraphicId >= 0 && Art.IsValidStatic(SelectedGraphicId); pasteImageToolStripMenuItem.Enabled = selectedCount > 0 && ImageClipboard.ContainsImage(); - pasteImageToolStripMenuItem.Text = selectedCount > 1 ? $"Paste Image into {selectedCount}" : "Paste Image"; - removeToolStripMenuItem.Text = selectedCount > 1 ? $"Remove {selectedCount}" : "Remove"; - extractToolStripMenuItem.Text = selectedCount > 1 ? $"Export {selectedCount} Images..." : "Export Image.."; - replaceToolStripMenuItem.Text = selectedCount > 1 ? $"Replace {selectedCount}..." : "Replace..."; + + // 类型 12:菜单 Opening 事件中使用汉化基础文本构建动态文本 + string basePasteText = _localizationGetter?.Invoke("Forms.ItemsControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + string baseRemoveText = _localizationGetter?.Invoke("Forms.ItemsControl.removeToolStripMenuItem") ?? "Remove"; + string baseExtractText = _localizationGetter?.Invoke("Forms.ItemsControl.extractToolStripMenuItem") ?? "Export Image.."; + string baseReplaceText = _localizationGetter?.Invoke("Forms.ItemsControl.replaceToolStripMenuItem") ?? "Replace..."; + string baseSelectTileDataText = _localizationGetter?.Invoke("Forms.ItemsControl.selectInTileDataTabToolStripMenuItem") ?? "Select in TileData tab"; + + pasteImageToolStripMenuItem.Text = selectedCount > 1 ? $"{basePasteText} {selectedCount}" : basePasteText; + removeToolStripMenuItem.Text = selectedCount > 1 ? $"{baseRemoveText} {selectedCount}" : baseRemoveText; + extractToolStripMenuItem.Text = selectedCount > 1 ? $"{baseExtractText.Replace("...", "")} {selectedCount}..." : baseExtractText; + replaceToolStripMenuItem.Text = selectedCount > 1 ? $"{baseReplaceText.Replace("...", "")} {selectedCount}..." : baseReplaceText; selectInTileDataTabToolStripMenuItem.Text = selectedCount > 1 - ? $"Select {selectedCount} in TileData tab" - : "Select in TileData tab"; + ? $"{baseSelectTileDataText.Replace(" tab", "")} {selectedCount} tab" + : baseSelectTileDataText; if (SelectedGraphicId <= 0) { @@ -1685,5 +1702,74 @@ private void SearchByNameToolStripButton_Click(object sender, EventArgs e) { SearchName(searchByNameToolStripTextBox.Text, true); } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) + return; + + // ToolStrip Items + toolStripLabel1.Text = _localizationGetter("Forms.ItemsControl.toolStripLabel1") ?? "Index:"; + toolStripLabel2.Text = _localizationGetter("Forms.ItemsControl.toolStripLabel2") ?? "Name:"; + searchByNameToolStripButton.Text = _localizationGetter("Forms.ItemsControl.searchByNameToolStripButton") ?? "Find next"; + PreloadItemsToolStripButton.Text = _localizationGetter("Forms.ItemsControl.PreloadItemsToolStripButton") ?? "Preload Items"; + MiscToolStripDropDownButton.Text = _localizationGetter("Forms.ItemsControl.MiscToolStripDropDownButton") ?? "Misc"; + ExportAllToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.ExportAllToolStripMenuItem") ?? "Export all.."; + + // Context Menu Items - Tile View + showFreeSlotsToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.showFreeSlotsToolStripMenuItem") ?? "Show Free Slots"; + findNextFreeSlotToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.findNextFreeSlotToolStripMenuItem") ?? "Find Next Free Slot"; + ChangeBackgroundColorToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.ChangeBackgroundColorToolStripMenuItem") ?? "Change background color"; + extractToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.extractToolStripMenuItem") ?? "Export Image.."; + bmpToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.bmpToolStripMenuItem") ?? "As Bmp"; + tiffToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.tiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem1.Text = _localizationGetter("Forms.ItemsControl.asJpgToolStripMenuItem1") ?? "As Jpg"; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.ItemsControl.asPngToolStripMenuItem1") ?? "As Png"; + selectInTileDataTabToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.selectInTileDataTabToolStripMenuItem") ?? "Select in TileData tab"; + selectInRadarColorTabToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.selectInRadarColorTabToolStripMenuItem") ?? "Select in RadarColor tab"; + selectInGumpsTabMaleToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.selectInGumpsTabMaleToolStripMenuItem") ?? "Select in Gumps (M)"; + selectInGumpsTabFemaleToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.selectInGumpsTabFemaleToolStripMenuItem") ?? "Select in Gumps (F)"; + copyImageToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.copyImageToolStripMenuItem") ?? "Copy Image"; + pasteImageToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.replaceToolStripMenuItem") ?? "Replace..."; + replaceStartingFromToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.replaceStartingFromToolStripMenuItem") ?? "Replace starting from.."; + replaceFromFolderToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.replaceFromFolderToolStripMenuItem") ?? "Replace from Folder..."; + insertAtToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.insertAtToolStripMenuItem") ?? "Insert At.."; + removeToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.removeToolStripMenuItem") ?? "Remove"; + saveToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.saveToolStripMenuItem") ?? "Save"; + saveToolStripButton.Text = _localizationGetter("Forms.ItemsControl.saveToolStripMenuItem") ?? "Save"; + saveToolStripButton.ToolTipText = _localizationGetter("Forms.ItemsControl.saveToolStripMenuItem") ?? "Save"; + + // Context Menu Items - Detail Picture + changeBackgroundColorToolStripMenuItemDetail.Text = _localizationGetter("Forms.ItemsControl.changeBackgroundColorToolStripMenuItemDetail") ?? "Change background color"; + copyImageToolStripMenuItemDetail.Text = _localizationGetter("Forms.ItemsControl.copyImageToolStripMenuItemDetail") ?? "Copy Image"; + pasteImageToolStripMenuItemDetail.Text = _localizationGetter("Forms.ItemsControl.pasteImageToolStripMenuItemDetail") ?? "Paste Image"; + + // Export Formats + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.asBmpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.asJpgToolStripMenuItem") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.ItemsControl.asPngToolStripMenuItem") ?? "As Png"; + + // Status Bar + NameLabel.Text = _localizationGetter("Forms.ItemsControl.NameLabel") ?? "Name:"; + GraphicLabel.Text = _localizationGetter("Forms.ItemsControl.GraphicLabel") ?? "Graphic:"; + + // Re-apply DetailTextBox context menu localization + DetailTextBox.ContextMenuStrip = null; + DetailTextBox.AddBasicContextMenu(_localizationGetter); + + // Refresh detail if needed + if (_selectedGraphicId >= 0) + { + UpdateDetail(_selectedGraphicId); + } + } } } diff --git a/UoFiddler.Controls/UserControls/LandTilesControl.cs b/UoFiddler.Controls/UserControls/LandTilesControl.cs index db81f395..a42566d9 100644 --- a/UoFiddler.Controls/UserControls/LandTilesControl.cs +++ b/UoFiddler.Controls/UserControls/LandTilesControl.cs @@ -53,6 +53,7 @@ public LandTilesControl() private int _selectedGraphicId = -1; private readonly List _tileList = new List(); private bool _showFreeSlots; + private Func? _localizationGetter; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectedGraphicId @@ -227,9 +228,14 @@ private void UpdateToolStripLabels(int graphic) return; } - NameLabel.Text = $"Name: {TileData.LandTable[graphic].Name}"; - GraphicLabel.Text = string.Format("ID: 0x{0:X4} ({0})", graphic); - FlagsLabel.Text = $"Flags: {TileData.LandTable[graphic].Flags}"; + // Type 8: 获取汉化的基础标签文本,然后添加动态信息 + string nameLabel = _localizationGetter?.Invoke("Forms.LandTilesControl.NameLabel") ?? "Name:"; + string graphicLabel = _localizationGetter?.Invoke("Forms.LandTilesControl.GraphicLabel") ?? "ID:"; + string flagsLabel = _localizationGetter?.Invoke("Forms.LandTilesControl.FlagsLabel") ?? "Flags:"; + + NameLabel.Text = $"{nameLabel} {TileData.LandTable[graphic].Name}"; + GraphicLabel.Text = $"{graphicLabel} 0x{graphic:X4} ({graphic})"; + FlagsLabel.Text = $"{flagsLabel} {TileData.LandTable[graphic].Flags}"; } private void OnTileDataChangeEvent(object sender, int id) @@ -353,12 +359,15 @@ private void OnClickRemove(object sender, EventArgs e) return; } + // Type 6: MessageBox 确认对话框汉化 string prompt = ids.Count == 1 - ? $"Are you sure to remove {ids[0]}" - : $"Are you sure to remove {ids.Count} land tiles?"; + ? _localizationGetter?.Invoke($"Forms.LandTilesControl.Messages.RemoveSingleConfirm")?.Replace("{0}", ids[0].ToString()) ?? $"Are you sure to remove {ids[0]}" + : _localizationGetter?.Invoke($"Forms.LandTilesControl.Messages.RemoveMultipleConfirm")?.Replace("{0}", ids.Count.ToString()) ?? $"Are you sure to remove {ids.Count} land tiles?"; + + string removeTitle = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.RemoveTitle") ?? "Save"; DialogResult result = - MessageBox.Show(prompt, "Save", + MessageBox.Show(prompt, removeTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result != DialogResult.Yes) { @@ -438,9 +447,14 @@ private void OnClickPasteImage(object sender, EventArgs e) if (ids.Count > 1) { + // Type 6: 粘贴确认对话框汉化 + string pasteConfirm = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.PasteMultipleConfirm")?.Replace("{0}", ids.Count.ToString()) + ?? $"Paste this image into {ids.Count} selected land tiles?"; + string pasteTitle = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.PasteImageTitle") ?? "Paste Image"; + DialogResult confirm = MessageBox.Show( - $"Paste this image into {ids.Count} selected land tiles?", - "Paste Image", MessageBoxButtons.YesNo, MessageBoxIcon.Question, + pasteConfirm, + pasteTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (confirm != DialogResult.Yes) @@ -718,8 +732,12 @@ private void OnKeyDownInsert(object sender, KeyEventArgs e) private void OnClickSave(object sender, EventArgs e) { + // Type 6: Save 确认对话框汉化 + string saveConfirm = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.SaveConfirmation") ?? "Are you sure? Will take a while"; + string saveTitle = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.SaveTitle") ?? "Save"; + DialogResult result = - MessageBox.Show("Are you sure? Will take a while", "Save", + MessageBox.Show(saveConfirm, saveTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) { @@ -785,10 +803,12 @@ private void ExportMultipleLandTileImages(List ids, ImageFormat imageFormat } } - FileSavedDialog.Show(FindForm(), Options.OutputPath, $"{ids.Count} land tiles saved successfully."); + string message = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.AllLandTilesSavedSuccessfully") + ?? $"{ids.Count} land tiles saved successfully."; + FileSavedDialog.Show(FindForm(), Options.OutputPath, message, null, _localizationGetter); } - private static void ExportLandTileImage(int index, ImageFormat imageFormat) + private void ExportLandTileImage(int index, ImageFormat imageFormat) { if (!Art.IsValidLand(index)) { @@ -803,7 +823,9 @@ private static void ExportLandTileImage(int index, ImageFormat imageFormat) bit.Save(fileName, imageFormat); } - MessageBox.Show($"Landtile saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information, + string message = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.LandtileSavedTo") ?? "Landtile saved to"; + string title = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.Saved") ?? "Saved"; + MessageBox.Show($"{message} {fileName}", title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } @@ -849,13 +871,21 @@ private void LandTilesContextMenuStrip_Opening(object sender, System.ComponentMo int selectedCount = LandTilesTileView.SelectedIndices.Count; copyImageToolStripMenuItem.Enabled = _selectedGraphicId >= 0 && Art.IsValidLand(_selectedGraphicId); pasteImageToolStripMenuItem.Enabled = selectedCount > 0 && ImageClipboard.ContainsImage(); - pasteImageToolStripMenuItem.Text = selectedCount > 1 ? $"Paste Image into {selectedCount}" : "Paste Image"; - removeToolStripMenuItem.Text = selectedCount > 1 ? $"Remove {selectedCount}" : "Remove"; - exportImageToolStripMenuItem.Text = selectedCount > 1 ? $"Export {selectedCount} Images..." : "Export Image.."; - replaceToolStripMenuItem.Text = selectedCount > 1 ? $"Replace {selectedCount}" : "Replace"; + + // ✅ Type 12 汉化:使用汉化基础文本构建动态文本 + string basePasteText = _localizationGetter?.Invoke("Forms.LandTilesControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + string baseRemoveText = _localizationGetter?.Invoke("Forms.LandTilesControl.removeToolStripMenuItem") ?? "Remove"; + string baseExportText = _localizationGetter?.Invoke("Forms.LandTilesControl.exportImageToolStripMenuItem") ?? "Export Image.."; + string baseReplaceText = _localizationGetter?.Invoke("Forms.LandTilesControl.replaceToolStripMenuItem") ?? "Replace"; + string baseSelectTileDataText = _localizationGetter?.Invoke("Forms.LandTilesControl.selectInTileDataTabToolStripMenuItem") ?? "Select in TileData tab"; + + pasteImageToolStripMenuItem.Text = selectedCount > 1 ? $"{basePasteText} {selectedCount}" : basePasteText; + removeToolStripMenuItem.Text = selectedCount > 1 ? $"{baseRemoveText} {selectedCount}" : baseRemoveText; + exportImageToolStripMenuItem.Text = selectedCount > 1 ? $"{baseExportText.Replace("...", "")} {selectedCount}..." : baseExportText; + replaceToolStripMenuItem.Text = selectedCount > 1 ? $"{baseReplaceText} {selectedCount}" : baseReplaceText; selectInTileDataTabToolStripMenuItem.Text = selectedCount > 1 - ? $"Select {selectedCount} in TileData tab" - : "Select in TileData tab"; + ? $"{baseSelectTileDataText.Replace(" tab", "")} {selectedCount} tab" + : baseSelectTileDataText; bool hasTexture = _selectedGraphicId >= 0 && TileData.LandTable[_selectedGraphicId].TextureId != 0 @@ -889,7 +919,7 @@ private void ExportAllLandTiles(ImageFormat imageFormat) using (FolderBrowserDialog dialog = new FolderBrowserDialog()) { - dialog.Description = "Select directory"; + dialog.Description = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.SelectDirectoryDescription") ?? "Select directory"; dialog.ShowNewFolderButton = true; if (dialog.ShowDialog() != DialogResult.OK) { @@ -919,7 +949,7 @@ private void ExportAllLandTiles(ImageFormat imageFormat) } } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All land tiles saved successfully."); + FileSavedDialog.Show(FindForm(), dialog.SelectedPath, _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.AllLandTilesSavedSuccessfully") ?? "All land tiles saved successfully.", null, _localizationGetter); } } @@ -1081,7 +1111,7 @@ private static bool IsIndexValid(int index) private void OnClickReplaceFromFolder(object sender, EventArgs e) { using FolderBrowserDialog dialog = new FolderBrowserDialog(); - dialog.Description = "Select folder containing images to replace"; + dialog.Description = _localizationGetter?.Invoke("Forms.LandTilesControl.Messages.SelectFolderForReplaceDescription") ?? "Select folder containing images to replace"; if (dialog.ShowDialog() != DialogResult.OK) { @@ -1392,5 +1422,59 @@ private void SearchByNameToolStripButton_Click(object sender, EventArgs e) { SearchName(searchByNameToolStripTextBox.Text, true); } + + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Menu Items + showFreeSlotsToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.showFreeSlotsToolStripMenuItem") ?? "Show Free Slots"; + findNextFreeSlotToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.findNextFreeSlotToolStripMenuItem") ?? "Find Next Free Slot"; + changeBackgroundColorToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.changeBackgroundColorToolStripMenuItem") ?? "Change background color"; + exportImageToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.exportImageToolStripMenuItem") ?? "Export Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.asBmpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem1.Text = _localizationGetter("Forms.LandTilesControl.asJpgToolStripMenuItem1") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.asPngToolStripMenuItem") ?? "As Png"; + selectInTileDataTabToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.selectInTileDataTabToolStripMenuItem") ?? "Select in TileData tab"; + selectInRadarColorTabToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.selectInRadarColorTabToolStripMenuItem") ?? "Select in RadarColor tab"; + selectInTexturesTabToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.selectInTexturesTabToolStripMenuItem") ?? "Select in Textures tab"; + copyImageToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.copyImageToolStripMenuItem") ?? "Copy Image"; + pasteImageToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.replaceToolStripMenuItem") ?? "Replace"; + replaceStartingFromToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.replaceStartingFromToolStripMenuItem") ?? "Replace starting from.."; + replaceFromFolderToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.replaceFromFolderToolStripMenuItem") ?? "Replace from Folder..."; + insertAtToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.insertAtToolStripMenuItem") ?? "Insert At.."; + removeToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.removeToolStripMenuItem") ?? "Remove"; + saveToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.saveToolStripMenuItem") ?? "Save"; + + // Toolbar Labels + IndexToolStripLabel.Text = _localizationGetter("Forms.LandTilesControl.IndexToolStripLabel") ?? "Index:"; + NameToolStripLabel.Text = _localizationGetter("Forms.LandTilesControl.NameToolStripLabel") ?? "Name:"; + searchByNameToolStripButton.Text = _localizationGetter("Forms.LandTilesControl.searchByNameToolStripButton") ?? "Find next"; + + // Misc Menu + MiscToolStripDropDownButton.Text = _localizationGetter("Forms.LandTilesControl.MiscToolStripDropDownButton") ?? "Misc"; + exportAllToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.exportAllToolStripMenuItem") ?? "Export All.."; + ExportAllAsBmp.Text = _localizationGetter("Forms.LandTilesControl.ExportAllAsBmp") ?? "As Bmp"; + ExportAllAsTiff.Text = _localizationGetter("Forms.LandTilesControl.ExportAllAsTiff") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.LandTilesControl.asJpgToolStripMenuItem") ?? "As Jpg"; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.LandTilesControl.asPngToolStripMenuItem1") ?? "As Png"; + + // Save Button + SaveButton.Text = _localizationGetter("Forms.LandTilesControl.SaveButton") ?? "Save"; + SaveButton.ToolTipText = _localizationGetter("Forms.LandTilesControl.SaveButton") ?? "Save"; + + // Status Bar Labels + NameLabel.Text = _localizationGetter("Forms.LandTilesControl.NameLabel") ?? "Name:"; + GraphicLabel.Text = _localizationGetter("Forms.LandTilesControl.GraphicLabel") ?? "Graphic:"; + FlagsLabel.Text = _localizationGetter("Forms.LandTilesControl.FlagsLabel") ?? "Flags:"; + } } } diff --git a/UoFiddler.Controls/UserControls/LightControl.cs b/UoFiddler.Controls/UserControls/LightControl.cs index 3045f734..197a6acd 100644 --- a/UoFiddler.Controls/UserControls/LightControl.cs +++ b/UoFiddler.Controls/UserControls/LightControl.cs @@ -35,6 +35,7 @@ public LightControl() private bool _loaded; private int _landTile = 0x3; private int _lightTile = 0x0B20; + private Func? _localizationGetter; /// /// ReLoads if loaded @@ -239,9 +240,9 @@ private void OnClickReplace(object sender, EventArgs e) using (var dialog = new OpenFileDialog()) { dialog.Multiselect = false; - dialog.Title = "Choose image file to replace"; + dialog.Title = _localizationGetter?.Invoke("Forms.LightControl.Dialogs.ReplaceTitle") ?? "Choose image file to replace"; dialog.CheckFileExists = true; - dialog.Filter = "Image files (*.tif;*.tiff;*.bmp;*.png)|*.tif;*.tiff;*.bmp;*.png"; + dialog.Filter = _localizationGetter?.Invoke("Forms.LightControl.Dialogs.FileFilter") ?? "Image files (*.tif;*.tiff;*.bmp;*.png)|*.tif;*.tiff;*.bmp;*.png"; if (dialog.ShowDialog() != DialogResult.OK) { return; @@ -302,9 +303,10 @@ private void OnKeyDownInsert(object sender, KeyEventArgs e) using (var dialog = new OpenFileDialog()) { dialog.Multiselect = false; - dialog.Title = string.Format("Choose image file to insert at {0} (0x{0:X})", index); + string insertTitleFormat = _localizationGetter?.Invoke("Forms.LightControl.Dialogs.InsertTitle") ?? "Choose image file to insert at {0} (0x{0:X})"; + dialog.Title = string.Format(insertTitleFormat, index); dialog.CheckFileExists = true; - dialog.Filter = "Image files (*.tif;*.tiff;*.bmp;*.png)|*.tif;*.tiff;*.bmp;*.png"; + dialog.Filter = _localizationGetter?.Invoke("Forms.LightControl.Dialogs.FileFilter") ?? "Image files (*.tif;*.tiff;*.bmp;*.png)|*.tif;*.tiff;*.bmp;*.png"; if (dialog.ShowDialog() != DialogResult.OK) { return; @@ -342,7 +344,8 @@ private void OnClickSave(object sender, EventArgs e) { Light.Save(Options.OutputPath); Options.ChangedUltimaClass["Light"] = false; - FileSavedDialog.Show(FindForm(), Options.OutputPath, "Files saved successfully."); + string successMessage = _localizationGetter?.Invoke("Forms.LightControl.Messages.FilesSavedSuccessfully") ?? "Files saved successfully."; + FileSavedDialog.Show(FindForm(), Options.OutputPath, successMessage, null, key => _localizationGetter?.Invoke(key)); } private void OnClickExportBmp(object sender, EventArgs e) @@ -356,7 +359,8 @@ private void OnClickExportBmp(object sender, EventArgs e) string path = Options.OutputPath; string fileName = Path.Combine(path, $"Light {Utils.FormatExportId(i)}.bmp"); Light.GetLight(i).Save(fileName, ImageFormat.Bmp); - FileSavedDialog.Show(FindForm(), fileName, "Light saved successfully."); + string successMessage = _localizationGetter?.Invoke("Forms.LightControl.Messages.LightSavedSuccessfully") ?? "Light saved successfully."; + FileSavedDialog.Show(FindForm(), fileName, successMessage, null, key => _localizationGetter?.Invoke(key)); } private void OnClickExportTiff(object sender, EventArgs e) @@ -370,7 +374,8 @@ private void OnClickExportTiff(object sender, EventArgs e) string path = Options.OutputPath; string fileName = Path.Combine(path, $"Light {Utils.FormatExportId(i)}.tiff"); Ultima.Light.GetLight(i).Save(fileName, ImageFormat.Tiff); - FileSavedDialog.Show(FindForm(), fileName, "Light saved successfully."); + string successMessage = _localizationGetter?.Invoke("Forms.LightControl.Messages.LightSavedSuccessfully") ?? "Light saved successfully."; + FileSavedDialog.Show(FindForm(), fileName, successMessage, null, key => _localizationGetter?.Invoke(key)); } private void OnClickExportJpg(object sender, EventArgs e) @@ -384,7 +389,8 @@ private void OnClickExportJpg(object sender, EventArgs e) string path = Options.OutputPath; string fileName = Path.Combine(path, $"Light {Utils.FormatExportId(i)}.jpg"); Ultima.Light.GetLight(i).Save(fileName, ImageFormat.Jpeg); - FileSavedDialog.Show(FindForm(), fileName, "Light saved successfully."); + string successMessage = _localizationGetter?.Invoke("Forms.LightControl.Messages.LightSavedSuccessfully") ?? "Light saved successfully."; + FileSavedDialog.Show(FindForm(), fileName, successMessage, null, key => _localizationGetter?.Invoke(key)); } private void IgPreviewClicked(object sender, EventArgs e) @@ -472,5 +478,34 @@ private void LightTileKeyDown(object sender, KeyEventArgs e) pictureBoxPreview.Image = GetImage(); } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 菜单项汉化 + exportImageToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.ExportImage") ?? "Export Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.AsBmp") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.AsTiff") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.AsJpg") ?? "As Jpg"; + removeToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.Remove") ?? "Remove"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.Replace") ?? "Replace"; + insertAtToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.InsertAt") ?? "Insert At.."; + saveToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.Save") ?? "Save"; + + // 预览菜单项汉化 + iGPreviewToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.IGPreview") ?? "IG Preview"; + backgroundLandTileToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.BackgroundLandTile") ?? "Background LandTile"; + lightTileToolStripMenuItem.Text = _localizationGetter("Forms.LightControl.LightTile") ?? "LightTile"; + + // 列表列标题汉化 + listViewLightsColumn.Text = _localizationGetter("Forms.LightControl.Light") ?? "Light"; + } } } diff --git a/UoFiddler.Controls/UserControls/MapControl.cs b/UoFiddler.Controls/UserControls/MapControl.cs index 563a5b54..8ca318f0 100644 --- a/UoFiddler.Controls/UserControls/MapControl.cs +++ b/UoFiddler.Controls/UserControls/MapControl.cs @@ -46,6 +46,113 @@ public MapControl() AddAltitudeIntensityMenuItems(); } + private Func? _localizationGetter; + + // 状态栏标签前缀缓存(用于汉化) + private string _coordsPrefix = "Coords: "; + private string _clientLocPrefix = "ClientLoc: "; + private string _zoomPrefix = "Zoom: "; + + /// + /// Set localization for MapControl + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// Get the localization getter for child forms + /// + internal Func? GetLocalizationGetter() + { + return _localizationGetter; + } + + /// + /// Apply localization to all UI elements + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 上下文菜单项 + zoomToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ZoomPlus") ?? "+Zoom"; + zoomToolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.ZoomMinus") ?? "-Zoom"; + getMapInfoToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.GetMapInfo") ?? "GetMapInfo"; + insertMarkerToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.InsertMarker") ?? "Insert Marker"; + gotoToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.Goto") ?? "Goto..."; + sendClientToPosToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.SendClientToPos") ?? "Send Client To Pos"; + + // 地图名称 + feluccaToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.Felucca") ?? "Felucca"; + trammelToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.Trammel") ?? "Trammel"; + ilshenarToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.Ilshenar") ?? "Ilshenar"; + malasToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.Malas") ?? "Malas"; + tokunoToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.Tokuno") ?? "Tokuno"; + terMurToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.TerMur") ?? "TerMur"; + + // 导出地图 + extractMapToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ExtractMap") ?? "Extract Map.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AsBmp") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AsTiff") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AsJpg") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AsPng") ?? "As Png"; + + // 标记菜单 + gotoToolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.GotoMarker") ?? "Goto"; + removeToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.RemoveMarker") ?? "Remove"; + switchVisibleToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.SwitchVisibility") ?? "Switch Visibility"; + + // 工具栏按钮 + toolStripDropDownButton1.Text = _localizationGetter("Forms.MapControl.View") ?? "View"; + showStaticsToolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.ShowStatics") ?? "Show Statics"; + showCenterCrossToolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.ShowCenterCross") ?? "Show Center Cross"; + showMarkersToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ShowMarkers") ?? "Show Markers"; + showClientCrossToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ShowClientCross") ?? "Show Client Cross"; + + // 高度模式 + altitudeModeNormalToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AltitudeModeNormal") ?? "Normal"; + altitudeModeNormalWithAltitudeToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AltitudeModeNormalWithAltitude") ?? "Normal + Altitude"; + altitudeModeAltitudeToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.AltitudeModeAltitude") ?? "Altitude Map"; + + // 客户端交互 + toolStripDropDownButton2.Text = _localizationGetter("Forms.MapControl.ClientInteract") ?? "Client Interact"; + showClientLocToolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.ShowClientLoc") ?? "Show Client Loc"; + gotoClientLocToolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.GotoClientLoc") ?? "Goto Client Loc"; + sendClientToCenterToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.SendClientToCenter") ?? "Send Client to Center"; + + // 预加载 + PreloadMap.Text = _localizationGetter("Forms.MapControl.PreloadMap") ?? "Preload Map"; + + // 杂项菜单 + toolStripDropDownButton3.Text = _localizationGetter("Forms.MapControl.Misc") ?? "Misc"; + defragStaticsToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.DefragStatics") ?? "Defrag Statics..."; + importStaticsToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.FreezeStatics") ?? "Freeze Statics.. (in Memory)"; + meltStaticsToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.MeltStatics") ?? "Melt Statics.. (in Memory)"; + clearStaticsinMemoryToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ClearStatics") ?? "Clear Statics..(in Memory)"; + reportStaticsUnderMapToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ReportStaticsUnderMap") ?? "Report Statics below Map (possible invisible)"; + toolStripMenuItem1.Text = _localizationGetter("Forms.MapControl.ReportInvalidMapIDs") ?? "Report Invalid Map IDs"; + rewriteMapToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.RewriteMap") ?? "Rewrite Map"; + copyToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.MapAndStaticsCopy") ?? "Map and Statics Copy..."; + insertDiffDataToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.DiffToMapCopy") ?? "Diff to Map Copy..."; + replaceTilesToolStripMenuItem.Text = _localizationGetter("Forms.MapControl.ReplaceTiles") ?? "Replace Tiles.."; + + // 状态栏标签前缀汉化 + _coordsPrefix = _localizationGetter("Forms.MapControl.CoordsLabel") ?? "Coords: "; + _clientLocPrefix = _localizationGetter("Forms.MapControl.ClientLocLabel") ?? "ClientLoc: "; + _zoomPrefix = _localizationGetter("Forms.MapControl.ZoomLabel") ?? "Zoom: "; + + // 初始化状态栏标签显示(使用默认值,等待数据更新) + CoordsLabel.Text = $"{_coordsPrefix}0,0"; + ClientLocLabel.Text = $"{_clientLocPrefix}0,0"; + ZoomLabel.Text = $"{_zoomPrefix}1"; + + // 高度模式菜单项汉化(动态生成的菜单项) + ApplyAltitudeMenuLocalization(); + } + private void AddAltitudeIntensityMenuItems() { // Create preset menu items at the top @@ -79,7 +186,59 @@ private void AddAltitudeIntensityMenuItems() altitudeModeNormalWithAltitudeToolStripMenuItem.DropDownItems.Add(subtleItem); altitudeModeNormalWithAltitudeToolStripMenuItem.DropDownItems.Add(normalItem); altitudeModeNormalWithAltitudeToolStripMenuItem.DropDownItems.Add(strongItem); - } + } + + /// + /// Apply localization to altitude mode menu items + /// 处理动态菜单中的下拉项汉化(在 ApplyLocalization() 中调用) + /// + private void ApplyAltitudeMenuLocalization() + { + if (_localizationGetter == null || altitudeModeNormalWithAltitudeToolStripMenuItem.DropDownItems.Count == 0) + return; + + // 遍历菜单项并应用汉化 + foreach (var item in altitudeModeNormalWithAltitudeToolStripMenuItem.DropDownItems) + { + if (!(item is ToolStripMenuItem menuItem)) + continue; + + string originalText = menuItem.Text.Trim(); + + // 预设选项 + if (originalText == "Sharp (High Contrast)") + { + menuItem.Text = _localizationGetter("Forms.MapControl.AltitudePreset.Sharp") + ?? "Sharp (High Contrast)"; + } + else if (originalText == "Normal (More Contrast)") + { + menuItem.Text = _localizationGetter("Forms.MapControl.AltitudePreset.Normal") + ?? "Normal (More Contrast)"; + } + else if (originalText == "Soft (Subtle)") + { + menuItem.Text = _localizationGetter("Forms.MapControl.AltitudePreset.Soft") + ?? "Soft (Subtle)"; + } + // 强度选项(保留项目符号) + else if (originalText.Contains("Subtle Intensity")) + { + menuItem.Text = " • " + (_localizationGetter("Forms.MapControl.AltitudeIntensity.Subtle") + ?? "Subtle Intensity"); + } + else if (originalText.Contains("Normal Intensity")) + { + menuItem.Text = " • " + (_localizationGetter("Forms.MapControl.AltitudeIntensity.Normal") + ?? "Normal Intensity"); + } + else if (originalText.Contains("Strong Intensity")) + { + menuItem.Text = " • " + (_localizationGetter("Forms.MapControl.AltitudeIntensity.Strong") + ?? "Strong Intensity"); + } + } + } private static MapControl _refMarker; public static double Zoom = 1; @@ -155,7 +314,7 @@ private void OnLoad(object sender, EventArgs e) tokunoToolStripMenuItem.Checked = false; PreloadMap.Visible = true; ChangeMapNames(); - ZoomLabel.Text = $"Zoom: {Zoom}"; + ZoomLabel.Text = $"{_zoomPrefix}{Zoom}"; SetScrollBarValues(); Refresh(); pictureBox.Invalidate(); @@ -497,7 +656,7 @@ private void OnMouseMove(object sender, MouseEventArgs e) int xDelta = Math.Min(CurrentMap.Width, (int)(e.X / Zoom) + Round(hScrollBar.Value)); int yDelta = Math.Min(CurrentMap.Height, (int)(e.Y / Zoom) + Round(vScrollBar.Value)); - CoordsLabel.Text = $"Coords: {xDelta},{yDelta}"; + CoordsLabel.Text = $"{_coordsPrefix}{xDelta},{yDelta}"; if (!_moving) { @@ -604,7 +763,7 @@ private void OnClick_GotoClientLoc(object sender, EventArgs e) hScrollBar.Value = (int)Math.Max(0, x - (pictureBox.Right / Zoom / 2)); vScrollBar.Value = (int)Math.Max(0, y - (pictureBox.Bottom / Zoom / 2)); pictureBox.Invalidate(); - ClientLocLabel.Text = $"ClientLoc: {x},{y},{z},{Options.MapNames[mapClient]}"; + ClientLocLabel.Text = $"{_clientLocPrefix}{x},{y},{z},{Options.MapNames[mapClient]}"; } private void SwitchMap(int mapId) @@ -668,13 +827,19 @@ private void SyncClientTimer(object sender, EventArgs e) } } - ClientLocLabel.Text = $"ClientLoc: {x},{y},{z},{mapName}"; + ClientLocLabel.Text = $"{_clientLocPrefix}{x},{y},{z},{mapName}"; pictureBox.Invalidate(); } private void GetMapInfo(object sender, EventArgs e) { - new MapDetailsForm(CurrentMap, _currentPoint).Show(); + var form = new MapDetailsForm(CurrentMap, _currentPoint); + var getter = GetLocalizationGetter(); + if (getter != null) + { + form.SetLocalization(getter); + } + form.Show(); } private void OnOpenContext(object sender, CancelEventArgs e) // Save for GetMapInfo @@ -749,7 +914,7 @@ private void DoZoom() _renderingZoom = true; ChangeScrollBar(); - ZoomLabel.Text = $"Zoom: {Zoom}"; + ZoomLabel.Text = $"{_zoomPrefix}{Zoom}"; int x = Math.Max(0, _currentPoint.X - ((int)(pictureBox.ClientSize.Width / Zoom) / 2)); int y = Math.Max(0, _currentPoint.Y - ((int)(pictureBox.ClientSize.Height / Zoom) / 2)); x = Math.Min(x, hScrollBar.Maximum); @@ -1051,6 +1216,11 @@ private void OnClickInsertMarker(object sender, EventArgs e) { TopMost = true }; + var getter = GetLocalizationGetter(); + if (getter != null) + { + _mapMarkerForm.SetLocalization(getter); + } _mapMarkerForm.Show(); } @@ -1306,6 +1476,11 @@ private void OnClickDefragStatics(object sender, EventArgs e) { using (var form = new MapDefragStaticsForm(CurrentMap, Options.OutputPath)) { + var getter = GetLocalizationGetter(); + if (getter != null) + { + form.SetLocalization(getter); + } form.ShowDialog(FindForm()); } } @@ -1333,7 +1508,11 @@ private void OnClickRewriteMap(object sender, EventArgs e) Map.RewriteMap(Options.OutputPath, _currentMapId, CurrentMap.Width, CurrentMap.Height); } - FileSavedDialog.Show(FindForm(), Options.OutputPath, "Files saved successfully."); + var getter = GetLocalizationGetter(); + if (getter != null) + FileSavedDialog.Show(FindForm(), Options.OutputPath, "File saved successfully.", null, getter); + else + FileSavedDialog.Show(FindForm(), Options.OutputPath, "File saved successfully."); } private void OnClickReportInvisStatics(object sender, EventArgs e) @@ -1342,7 +1521,11 @@ private void OnClickReportInvisStatics(object sender, EventArgs e) { CurrentMap.ReportInvisibleStatics(Options.OutputPath); } - FileSavedDialog.Show(FindForm(), Options.OutputPath, "Report saved successfully."); + var getter = GetLocalizationGetter(); + if (getter != null) + FileSavedDialog.Show(FindForm(), Options.OutputPath, "Report saved successfully.", null, getter); + else + FileSavedDialog.Show(FindForm(), Options.OutputPath, "Report saved successfully."); } private void OnClickReportInvalidMapIDs(object sender, EventArgs e) @@ -1351,7 +1534,11 @@ private void OnClickReportInvalidMapIDs(object sender, EventArgs e) { CurrentMap.ReportInvalidMapIDs(Options.OutputPath); } - FileSavedDialog.Show(FindForm(), Options.OutputPath, "Report saved successfully."); + var getter = GetLocalizationGetter(); + if (getter != null) + FileSavedDialog.Show(FindForm(), Options.OutputPath, "Report saved successfully.", null, getter); + else + FileSavedDialog.Show(FindForm(), Options.OutputPath, "Report saved successfully."); } private MapReplaceForm _showForm; @@ -1367,6 +1554,11 @@ private void OnClickCopy(object sender, EventArgs e) { TopMost = true }; + + var getter = GetLocalizationGetter(); + if (getter != null) + _showForm.SetLocalization(getter); + _showForm.Show(); } @@ -1383,6 +1575,11 @@ private void OnClickInsertDiffData(object sender, EventArgs e) { TopMost = true }; + + var getter = GetLocalizationGetter(); + if (getter != null) + _showFormMapDiff.SetLocalization(getter); + _showFormMapDiff.Show(); } @@ -1515,6 +1712,11 @@ private void OnClickMeltStatics(object sender, EventArgs e) { TopMost = true }; + var getter = GetLocalizationGetter(); + if (getter != null) + { + _showMeltStaticsForm.SetLocalization(getter); + } _showMeltStaticsForm.Show(); } @@ -1531,6 +1733,11 @@ private void OnClickClearStatics(object sender, EventArgs e) { TopMost = true }; + var getter = GetLocalizationGetter(); + if (getter != null) + { + _showClearStaticsForm.SetLocalization(getter); + } _showClearStaticsForm.Show(); } @@ -1547,6 +1754,11 @@ private void OnClickReplaceTiles(object sender, EventArgs e) { TopMost = true }; + var getter = GetLocalizationGetter(); + if (getter != null) + { + _showMapReplaceTilesForm.SetLocalization(getter); + } _showMapReplaceTilesForm.Show(); } diff --git a/UoFiddler.Controls/UserControls/MultiMapControl.cs b/UoFiddler.Controls/UserControls/MultiMapControl.cs index cbcf3b27..331f08b5 100644 --- a/UoFiddler.Controls/UserControls/MultiMapControl.cs +++ b/UoFiddler.Controls/UserControls/MultiMapControl.cs @@ -41,6 +41,7 @@ public MultiMapControl() private Point _movingPoint; private bool _loaded; + private Func? _localizationGetter; /// /// ReLoads if loaded @@ -195,6 +196,41 @@ private void OnFilePathChangeEvent() Reload(); } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 工具栏按钮 + toolStripDropDownButton1.Text = _localizationGetter("Forms.MultiMapControl.loadButton") ?? "Load.."; + toolStripDropDownButton2.Text = _localizationGetter("Forms.MultiMapControl.generateButton") ?? "Generate.."; + toolStripDropDownButton3.Text = _localizationGetter("Forms.MultiMapControl.exportButton") ?? "Export.."; + + // 加载菜单项 + multiMapToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.multiMap") ?? "MultiMap"; + facet00ToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facet00") ?? "Facet00"; + facet01ToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facet01") ?? "Facet01"; + facet02ToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facet02") ?? "Facet02"; + facet03ToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facet03") ?? "Facet03"; + facet04ToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facet04") ?? "Facet04"; + facet05ToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facet05") ?? "Facet05"; + + // 生成菜单项 + multiMapFromImageToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.multiMapFromImage") ?? "MultiMap from Image"; + facetFromImageToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.facetFromImage") ?? "Facet from Image"; + + // 导出菜单项 + asBmpToolStripMenuItem1.Text = _localizationGetter("Forms.MultiMapControl.Menus.asBmp") ?? "As Bmp.."; + asTiffToolStripMenuItem1.Text = _localizationGetter("Forms.MultiMapControl.Menus.asTiff") ?? "As Tiff.."; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.MultiMapControl.Menus.asJpg") ?? "As Jpg.."; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.MultiMapControl.Menus.asPng") ?? "As Png.."; + } + private void OnClickExportBmp(object sender, EventArgs e) { ExportMultiMapImage(ImageFormat.Bmp); @@ -222,7 +258,12 @@ private void ExportMultiMapImage(ImageFormat imageFormat) pictureBox.Image.Save(fileName, imageFormat); - FileSavedDialog.Show(FindForm(), fileName, $"{CheckedToString()} saved successfully."); + string successMessage = _localizationGetter?.Invoke("Forms.MultiMapControl.Messages.multiMapSavedSuccess") + ?? $"{CheckedToString()} saved successfully."; + if (_localizationGetter != null) + FileSavedDialog.Show(FindForm(), fileName, successMessage, null, _localizationGetter); + else + FileSavedDialog.Show(FindForm(), fileName, successMessage); } private string CheckedToString() @@ -300,7 +341,8 @@ private void OnClickGenerateRLE(object sender, EventArgs e) { using (OpenFileDialog dialog = new OpenFileDialog()) { - dialog.Title = "Select Image to convert"; + dialog.Title = _localizationGetter?.Invoke("Forms.MultiMapControl.Dialogs.selectImageTitle") + ?? "Select Image to convert"; if (dialog.ShowDialog() != DialogResult.OK) { return; @@ -314,7 +356,10 @@ private void OnClickGenerateRLE(object sender, EventArgs e) if (image.Height != 2048 || image.Width != 2560) { - MessageBox.Show("Invalid image height or width", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + string errorTitle = _localizationGetter?.Invoke("Forms.MultiMapControl.Dialogs.errorTitle") ?? "Error"; + string errorMsg = _localizationGetter?.Invoke("Forms.MultiMapControl.Messages.invalidImageSize") + ?? "Invalid image height or width"; + MessageBox.Show(errorMsg, errorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return; } @@ -326,12 +371,20 @@ private void OnClickGenerateRLE(object sender, EventArgs e) Ultima.MultiMap.SaveMultiMap(image, bin); } - FileSavedDialog.Show(FindForm(), fileName, "MultiMap saved successfully."); + string successMsg = _localizationGetter?.Invoke("Forms.MultiMapControl.Messages.multiMapSavedSuccess") + ?? "MultiMap saved successfully."; + if (_localizationGetter != null) + FileSavedDialog.Show(FindForm(), fileName, successMsg, null, _localizationGetter); + else + FileSavedDialog.Show(FindForm(), fileName, successMsg); } } catch (FileNotFoundException) { - MessageBox.Show("No image found", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, + string errorTitle = _localizationGetter?.Invoke("Forms.MultiMapControl.Dialogs.errorTitle") ?? "Error"; + string errorMsg = _localizationGetter?.Invoke("Forms.MultiMapControl.Messages.imageNotFound") + ?? "No image found"; + MessageBox.Show(errorMsg, errorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } @@ -341,7 +394,8 @@ private void OnClickGenerateFacetFromImage(object sender, EventArgs e) { using (var dialog = new OpenFileDialog()) { - dialog.Title = "Select Image to convert"; + dialog.Title = _localizationGetter?.Invoke("Forms.MultiMapControl.Dialogs.selectImageTitle") + ?? "Select Image to convert"; if (dialog.ShowDialog() != DialogResult.OK) { return; @@ -356,12 +410,20 @@ private void OnClickGenerateFacetFromImage(object sender, EventArgs e) string fileName = Path.Combine(path, "facet.mul"); Ultima.MultiMap.SaveFacetImage(fileName, image); - FileSavedDialog.Show(FindForm(), fileName, "Facet saved successfully."); + string successMsg = _localizationGetter?.Invoke("Forms.MultiMapControl.Messages.facetSavedSuccess") + ?? "Facet saved successfully."; + if (_localizationGetter != null) + FileSavedDialog.Show(FindForm(), fileName, successMsg, null, _localizationGetter); + else + FileSavedDialog.Show(FindForm(), fileName, successMsg); } } catch (FileNotFoundException) { - MessageBox.Show("No image found", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, + string errorTitle = _localizationGetter?.Invoke("Forms.MultiMapControl.Dialogs.errorTitle") ?? "Error"; + string errorMsg = _localizationGetter?.Invoke("Forms.MultiMapControl.Messages.imageNotFound") + ?? "No image found"; + MessageBox.Show(errorMsg, errorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } diff --git a/UoFiddler.Controls/UserControls/MultisControl.cs b/UoFiddler.Controls/UserControls/MultisControl.cs index 8ca277cc..39ed87a4 100644 --- a/UoFiddler.Controls/UserControls/MultisControl.cs +++ b/UoFiddler.Controls/UserControls/MultisControl.cs @@ -49,6 +49,7 @@ public MultisControl() private bool _loaded; private bool _showFreeSlots; private readonly MultisControl _refMarker; + private Func? _localizationGetter; // Virtual ListView backing: row index → multi id. _mulIds includes both // present and (when _showFreeSlots is on) empty slots; emptiness is @@ -359,15 +360,17 @@ private void AfterSelect_Multi(object sender, EventArgs e) if (multi == MultiComponentList.Empty) { HeightChangeMulti.Maximum = 0; - toolTip.SetToolTip(HeightChangeMulti, "MaxHeight: 0"); - SetMulStatus("Size: 0,0 MaxHeight: 0 MultiRegion: 0,0,0,0"); + string maxHeightLabel = GetMaxHeightLabel(); + toolTip.SetToolTip(HeightChangeMulti, $"{maxHeightLabel}: 0"); + SetMulStatus(FormatMultiStatus(multi)); } else { HeightChangeMulti.Maximum = multi.MaxHeight; + string maxHeightLabel = GetMaxHeightLabel(); toolTip.SetToolTip(HeightChangeMulti, - $"MaxHeight: {HeightChangeMulti.Maximum - HeightChangeMulti.Value}"); - SetMulStatus($"Size: {multi.Width},{multi.Height} MaxHeight: {multi.MaxHeight} MultiRegion: {multi.Min.X},{multi.Min.Y},{multi.Max.X},{multi.Max.Y} Surface: {multi.Surface}"); + $"{maxHeightLabel}: {HeightChangeMulti.Maximum - HeightChangeMulti.Value}"); + SetMulStatus(FormatMultiStatus(multi)); } ChangeComponentList(multi); RefreshMulBitmap(); @@ -535,7 +538,7 @@ private void ExtractMultiImage(ImageFormat imageFormat, Color backgroundColor) string floorSuffix = HeightChangeMulti.Value > 0 ? $"_Z{HeightChangeMulti.Value:000}" : string.Empty; string fileName = Path.Combine(Options.OutputPath, $"Multi {Utils.FormatExportId(GetSelectedMulId())}{floorSuffix}.{fileExtension}"); SaveImage(_mulBitmap, fileName, imageFormat, backgroundColor); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private static void SaveImage(Image sourceImage, string fileName, ImageFormat imageFormat, Color backgroundColor) @@ -575,7 +578,7 @@ private void OnExportTextFile(object sender, EventArgs e) string fileName = Path.Combine(path, $"Multi {Utils.FormatExportId(id)}.txt"); multi.ExportToTextFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnExportWscFile(object sender, EventArgs e) @@ -596,7 +599,7 @@ private void OnExportWscFile(object sender, EventArgs e) string fileName = Path.Combine(path, $"Multi {Utils.FormatExportId(id)}.wsc"); multi.ExportToWscFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnExportUOAFile(object sender, EventArgs e) @@ -617,7 +620,7 @@ private void OnExportUOAFile(object sender, EventArgs e) string fileName = Path.Combine(path, $"Multi {Utils.FormatExportId(id)}.uoa"); multi.ExportToUOAFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnClickSave(object sender, EventArgs e) @@ -738,7 +741,7 @@ private void ExportAllMultis(ImageFormat imageFormat, Color backgroundColor) } } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); } } @@ -771,7 +774,7 @@ private void OnClick_SaveAllText(object sender, EventArgs e) multi.ExportToTextFile(fileName); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); } } @@ -804,7 +807,7 @@ private void OnClick_SaveAllUOA(object sender, EventArgs e) multi.ExportToUOAFile(fileName); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); } } @@ -837,7 +840,7 @@ private void OnClick_SaveAllWSC(object sender, EventArgs e) multi.ExportToWscFile(fileName); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); } } @@ -870,7 +873,7 @@ private void OnClick_SaveAllCSV(object sender, EventArgs e) multi.ExportToCsvFile(fileName); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); } } @@ -903,7 +906,7 @@ private void OnClick_SaveAllUox3(object sender, EventArgs e) multi.ExportToUox3File(fileName); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); } } @@ -924,7 +927,7 @@ private void OnExportCsvFile(object sender, EventArgs e) string path = Options.OutputPath; string fileName = Path.Combine(path, $"{id:D4}.csv"); multi.ExportToCsvFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnExportUox3File(object sender, EventArgs e) @@ -944,7 +947,7 @@ private void OnExportUox3File(object sender, EventArgs e) string path = Options.OutputPath; string fileName = Path.Combine(path, $"Multi {Utils.FormatExportId(id)}.uox3"); multi.ExportToUox3File(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void ChangeBackgroundColorToolStripMenuItem_Click(object sender, EventArgs e) @@ -998,14 +1001,16 @@ private void AfterSelect_UopMulti(object sender, EventArgs e) if (multi == MultiComponentList.Empty) { HeightChangeUop.Maximum = 0; - toolTip.SetToolTip(HeightChangeUop, "MaxHeight: 0"); - SetUopStatus("Size: 0,0 MaxHeight: 0 MultiRegion: 0,0,0,0"); + string maxHeightLabel = GetMaxHeightLabel(); + toolTip.SetToolTip(HeightChangeUop, $"{maxHeightLabel}: 0"); + SetUopStatus(FormatMultiStatus(multi)); } else { HeightChangeUop.Maximum = multi.MaxHeight; - toolTip.SetToolTip(HeightChangeUop, $"MaxHeight: {HeightChangeUop.Maximum - HeightChangeUop.Value}"); - SetUopStatus($"Size: {multi.Width},{multi.Height} MaxHeight: {multi.MaxHeight} MultiRegion: {multi.Min.X},{multi.Min.Y},{multi.Max.X},{multi.Max.Y} Surface: {multi.Surface}"); + string maxHeightLabel = GetMaxHeightLabel(); + toolTip.SetToolTip(HeightChangeUop, $"{maxHeightLabel}: {HeightChangeUop.Maximum - HeightChangeUop.Value}"); + SetUopStatus(FormatMultiStatus(multi)); } ChangeUopComponentList(multi); @@ -1236,12 +1241,47 @@ private void UpdateZoomStatus() SetUopStatus(_uopStatusBase); } + /// + /// 格式化多重组件状态文本(应用汉化) + /// + private string FormatMultiStatus(MultiComponentList multi) + { + if (multi == MultiComponentList.Empty) + { + return _localizationGetter?.Invoke("Forms.MultisControl.Status.Empty") + ?? "Size: 0,0 MaxHeight: 0 MultiRegion: 0,0,0,0"; + } + + string sizeLabel = _localizationGetter?.Invoke("Forms.MultisControl.Status.Size") ?? "Size"; + string maxHeightLabel = _localizationGetter?.Invoke("Forms.MultisControl.Status.MaxHeight") ?? "MaxHeight"; + string regionLabel = _localizationGetter?.Invoke("Forms.MultisControl.Status.MultiRegion") ?? "MultiRegion"; + string surfaceLabel = _localizationGetter?.Invoke("Forms.MultisControl.Status.Surface") ?? "Surface"; + + return $"{sizeLabel}: {multi.Width},{multi.Height} {maxHeightLabel}: {multi.MaxHeight} {regionLabel}: {multi.Min.X},{multi.Min.Y},{multi.Max.X},{multi.Max.Y} {surfaceLabel}: {multi.Surface}"; + } + + /// + /// 获取本地化的 MaxHeight 标签文本 + /// + private string GetMaxHeightLabel() + { + return _localizationGetter?.Invoke("Forms.MultisControl.Status.MaxHeight") ?? "MaxHeight"; + } + private void SetMulStatus(string baseText) { _mulStatusBase = baseText; StatusMultiText.Text = _previewFitMode ? baseText - : $"{baseText} Zoom: {_zoomLevel * 100:F0}%"; + : $"{baseText} {GetZoomLabel()}: {_zoomLevel * 100:F0}%"; + } + + /// + /// 获取本地化的 Zoom 标签文本 + /// + private string GetZoomLabel() + { + return _localizationGetter?.Invoke("Forms.MultisControl.Status.Zoom") ?? "Zoom"; } private void SetUopStatus(string baseText) @@ -1249,7 +1289,7 @@ private void SetUopStatus(string baseText) _uopStatusBase = baseText; StatusUopText.Text = _previewFitMode ? baseText - : $"{baseText} Zoom: {_zoomLevel * 100:F0}%"; + : $"{baseText} {GetZoomLabel()}: {_zoomLevel * 100:F0}%"; } protected override void OnHandleCreated(EventArgs e) @@ -1298,6 +1338,11 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) private void OnClickHelp(object sender, EventArgs e) { using var form = new MultisHelpForm(); + // 为帮助窗口提供汉化 + if (_localizationGetter != null) + { + form.SetLocalization(_localizationGetter); + } form.ShowDialog(this); } @@ -1325,7 +1370,7 @@ private void ExtractUopMultiImage(ImageFormat imageFormat, Color backgroundColor int id = GetSelectedUopId(); string fileName = Path.Combine(Options.OutputPath, $"UopMulti {Utils.FormatExportId(id)}{floorSuffix}.{fileExtension}"); SaveImage(_uopBitmap, fileName, imageFormat, backgroundColor); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnUopExportTextFile(object sender, EventArgs e) @@ -1342,7 +1387,7 @@ private void OnUopExportTextFile(object sender, EventArgs e) } string fileName = Path.Combine(Options.OutputPath, $"UopMulti {Utils.FormatExportId(id)}.txt"); multi.ExportToTextFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnUopExportUOAFile(object sender, EventArgs e) @@ -1359,7 +1404,7 @@ private void OnUopExportUOAFile(object sender, EventArgs e) } string fileName = Path.Combine(Options.OutputPath, $"UopMulti {Utils.FormatExportId(id)}.uoa"); multi.ExportToUOAFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnUopExportWscFile(object sender, EventArgs e) @@ -1376,7 +1421,7 @@ private void OnUopExportWscFile(object sender, EventArgs e) } string fileName = Path.Combine(Options.OutputPath, $"UopMulti {Utils.FormatExportId(id)}.wsc"); multi.ExportToWscFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnUopExportCsvFile(object sender, EventArgs e) @@ -1393,7 +1438,7 @@ private void OnUopExportCsvFile(object sender, EventArgs e) } string fileName = Path.Combine(Options.OutputPath, $"{id:D4}_uop.csv"); multi.ExportToCsvFile(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Multi saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.MultiSavedMessage") ?? "Multi saved successfully."); } private void OnUopClick_SaveAllBmp(object sender, EventArgs e) => @@ -1440,7 +1485,7 @@ private void ExportAllUopMultis(ImageFormat imageFormat, Color backgroundColor) } } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All UOP Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllUopMultisSavedMessage") ?? "All UOP Multis saved successfully."); } private void OnUopClick_SaveAllText(object sender, EventArgs e) @@ -1468,7 +1513,7 @@ private void OnUopClick_SaveAllText(object sender, EventArgs e) multi.ExportToTextFile(Path.Combine(dialog.SelectedPath, $"UopMulti {Utils.FormatExportId(index)}.txt")); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All UOP Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllUopMultisSavedMessage") ?? "All UOP Multis saved successfully."); } private void OnUopClick_SaveAllUOA(object sender, EventArgs e) @@ -1496,7 +1541,7 @@ private void OnUopClick_SaveAllUOA(object sender, EventArgs e) multi.ExportToUOAFile(Path.Combine(dialog.SelectedPath, $"UopMulti {Utils.FormatExportId(index)}.uoa")); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All UOP Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllUopMultisSavedMessage") ?? "All UOP Multis saved successfully."); } private void OnUopClick_SaveAllWSC(object sender, EventArgs e) @@ -1524,7 +1569,7 @@ private void OnUopClick_SaveAllWSC(object sender, EventArgs e) multi.ExportToWscFile(Path.Combine(dialog.SelectedPath, $"UopMulti {Utils.FormatExportId(index)}.wsc")); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All UOP Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllUopMultisSavedMessage") ?? "All UOP Multis saved successfully."); } private void OnUopClick_SaveAllCSV(object sender, EventArgs e) @@ -1552,7 +1597,7 @@ private void OnUopClick_SaveAllCSV(object sender, EventArgs e) multi.ExportToCsvFile(Path.Combine(dialog.SelectedPath, $"{index:D4}_uop.csv")); } - FileSavedDialog.Show(FindForm(), dialog.SelectedPath, "All UOP Multis saved successfully."); + ShowFileSavedDialog(dialog.SelectedPath, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllUopMultisSavedMessage") ?? "All UOP Multis saved successfully."); } private void OnClick_SaveAllToXML(object sender, EventArgs e) @@ -1623,7 +1668,120 @@ private void OnClick_SaveAllToXML(object sender, EventArgs e) groupWriter.WriteEndDocument(); } - FileSavedDialog.Show(FindForm(), fileName, "All Multis saved successfully."); + ShowFileSavedDialog(fileName, _localizationGetter?.Invoke("Forms.FileSavedDialog.AllMultisSavedMessage") ?? "All Multis saved successfully."); + } + + /// + /// 设置本地化函数 + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 显示文件保存成功对话框,支持汉化 + /// + private void ShowFileSavedDialog(string filePath, string message) + { + if (_localizationGetter != null) + FileSavedDialog.Show(FindForm(), filePath, message, null, _localizationGetter); + else + FileSavedDialog.Show(FindForm(), filePath, message); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // MUL 标签页菜单项 + toolStripMenuItem4.Text = _localizationGetter("Forms.MultisControl.toolStripMenuItem4") ?? "Show Free Slots"; + importToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.importToolStripMenuItem") ?? "Import.."; + exportToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.exportToolStripMenuItem") ?? "Export.."; + removeToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.removeToolStripMenuItem") ?? "Remove"; + saveToolStripMenuItem1.Text = _localizationGetter("Forms.MultisControl.saveToolStripMenuItem1") ?? "Save"; + + toTextfileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toTextfileToolStripMenuItem") ?? "Export to Text File"; + toUOAToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toUOAToolStripMenuItem") ?? "Export to UOA"; + toWscToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toWscToolStripMenuItem") ?? "Export to WSC"; + toCsvToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toCsvToolStripMenuItem") ?? "Export to CSV"; + toUOX3ToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toUOX3ToolStripMenuItem") ?? "Export to UOX3"; + + // 工具栏菜单项 + toolStripDropDownButton1.Text = _localizationGetter("Forms.MultisControl.toolStripDropDownButton1") ?? "Misc"; + exportAllImagesToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.exportAllImagesToolStripMenuItem") ?? "Export all Images"; + aToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.aToolStripMenuItem") ?? "Export as BMP"; + asTiffToolStripMenuItem1.Text = _localizationGetter("Forms.MultisControl.asTiffToolStripMenuItem1") ?? "Export as TIFF"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.asJpgToolStripMenuItem") ?? "Export as JPG"; + asPngToolStripMenuItem1.Text = _localizationGetter("Forms.MultisControl.asPngToolStripMenuItem1") ?? "Export as PNG"; + + ChangeBackgroundColorToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.ChangeBackgroundColorToolStripMenuItem") ?? "Change Background Color"; + UseTransparencyForPNGToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.UseTransparencyForPNGToolStripMenuItem") ?? "Use Transparency for PNG"; + + exportAllPartsToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.exportAllPartsToolStripMenuItem") ?? "Export all Parts"; + toTextFileToolStripMenuItem1.Text = _localizationGetter("Forms.MultisControl.toTextFileToolStripMenuItem1") ?? "Export to Text File"; + toUOAFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toUOAFileToolStripMenuItem") ?? "Export to UOA File"; + toWSCFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toWSCFileToolStripMenuItem") ?? "Export to WSC File"; + toCSVFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toCSVFileToolStripMenuItem") ?? "Export to CSV File"; + toUOX3FileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toUOX3FileToolStripMenuItem") ?? "Export to UOX3 File"; + toXMLFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.toXMLFileToolStripMenuItem") ?? "Export to XML File (CentrED+)"; + + fitModeToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.fitModeToolStripMenuItem") ?? "Fit Mode"; + helpToolStripButton.Text = _localizationGetter("Forms.MultisControl.helpToolStripButton") ?? "Help"; + + // 标签页 + listViewMultiColumn.Text = _localizationGetter("Forms.MultisControl.listViewMultiColumn") ?? "Multi"; + tabPage5.Text = _localizationGetter("Forms.MultisControl.tabPage5") ?? "Graphics"; + tabPage6.Text = _localizationGetter("Forms.MultisControl.tabPage6") ?? "Components"; + + // MUL 预览菜单 + extractImageToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.extractImageToolStripMenuItem") ?? "Extract Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.asBmpToolStripMenuItem") ?? "Export as BMP"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.asTiffToolStripMenuItem") ?? "Export as TIFF"; + asJpgToolStripMenuItem1.Text = _localizationGetter("Forms.MultisControl.asJpgToolStripMenuItem1") ?? "Export as JPG"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.asPngToolStripMenuItem") ?? "Export as PNG"; + + // UOP 标签页菜单项 + listViewUopColumn.Text = _localizationGetter("Forms.MultisControl.listViewUopColumn") ?? "UOP"; + tabPageMul.Text = _localizationGetter("Forms.MultisControl.tabPageMul") ?? "MUL"; + tabPageUop.Text = _localizationGetter("Forms.MultisControl.tabPageUop") ?? "UOP"; + tabPageUopPreview.Text = _localizationGetter("Forms.MultisControl.tabPageUopPreview") ?? "Preview"; + tabPageUopComponents.Text = _localizationGetter("Forms.MultisControl.tabPageUopComponents") ?? "Components"; + + uopExportToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopExportToolStripMenuItem") ?? "Export"; + uopToUOAToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToUOAToolStripMenuItem") ?? "Export to UOA"; + uopToWscToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToWscToolStripMenuItem") ?? "Export to WSC"; + uopToCsvToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToCsvToolStripMenuItem") ?? "Export to CSV"; + + toolStripDropDownUop.Text = _localizationGetter("Forms.MultisControl.toolStripDropDownUop") ?? "Misc"; + uopExportAllImagesToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopExportAllImagesToolStripMenuItem") ?? "Export all Images"; + uopAsBmpToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsBmpToolStripMenuItem") ?? "Export as BMP"; + uopAsTiffToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsTiffToolStripMenuItem") ?? "Export as TIFF"; + uopAsJpgToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsJpgToolStripMenuItem") ?? "Export as JPG"; + uopAsPngToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsPngToolStripMenuItem") ?? "Export as PNG"; + + uopExportAllPartsToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopExportAllPartsToolStripMenuItem") ?? "Export all Parts"; + uopToTextfileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToTextfileToolStripMenuItem") ?? "Export to Text File"; + uopToUOAFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToUOAFileToolStripMenuItem") ?? "Export to UOA File"; + uopToWSCFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToWSCFileToolStripMenuItem") ?? "Export to WSC File"; + uopToCSVFileToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopToCSVFileToolStripMenuItem") ?? "Export to CSV File"; + + uopFitModeToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopFitModeToolStripMenuItem") ?? "Fit Mode"; + uopHelpToolStripButton.Text = _localizationGetter("Forms.MultisControl.uopHelpToolStripButton") ?? "Help"; + + uopExtractImageToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopExtractImageToolStripMenuItem") ?? "Extract Image.."; + uopAsBmpPicToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsBmpPicToolStripMenuItem") ?? "Export as BMP"; + uopAsTiffPicToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsTiffPicToolStripMenuItem") ?? "Export as TIFF"; + uopAsJpgPicToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsJpgPicToolStripMenuItem") ?? "Export as JPG"; + uopAsPngPicToolStripMenuItem.Text = _localizationGetter("Forms.MultisControl.uopAsPngPicToolStripMenuItem") ?? "Export as PNG"; + } + + private void UpdateStatusDisplay() + { + // 重新显示状态文本以应用新的汉化 + SetMulStatus(_mulStatusBase); + SetUopStatus(_uopStatusBase); } private sealed class MouseWheelFilter : IMessageFilter @@ -1676,3 +1834,5 @@ private static bool IsOver(Control c, Point screenPt) } } } + + diff --git a/UoFiddler.Controls/UserControls/RadarColorControl.Designer.cs b/UoFiddler.Controls/UserControls/RadarColorControl.Designer.cs index e046dfbe..cc8a3423 100644 --- a/UoFiddler.Controls/UserControls/RadarColorControl.Designer.cs +++ b/UoFiddler.Controls/UserControls/RadarColorControl.Designer.cs @@ -549,7 +549,7 @@ private void InitializeComponent() // // numericUpDownShortCol // - numericUpDownShortCol.Location = new System.Drawing.Point(50, 22); + numericUpDownShortCol.Location = new System.Drawing.Point(100, 22); numericUpDownShortCol.Margin = new System.Windows.Forms.Padding(4); numericUpDownShortCol.Maximum = new decimal(new int[] { 32767, 0, 0, 0 }); numericUpDownShortCol.Name = "numericUpDownShortCol"; diff --git a/UoFiddler.Controls/UserControls/RadarColorControl.cs b/UoFiddler.Controls/UserControls/RadarColorControl.cs index 7ba310b0..9a7d8b78 100644 --- a/UoFiddler.Controls/UserControls/RadarColorControl.cs +++ b/UoFiddler.Controls/UserControls/RadarColorControl.cs @@ -71,6 +71,9 @@ public RadarColorControl() private ushort _currentColor; private static RadarColorControl _refMarker; private bool _updating; + + // 汉化字段 + private Func? _localizationGetter; private readonly Dictionary _originalItemColors = []; private readonly Dictionary _originalLandColors = []; private Timer _debounceTimer; @@ -488,6 +491,110 @@ private void Reload() } } + // 汉化支持 + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // GroupBox 标题 + groupColor.Text = _localizationGetter("Forms.RadarColorControl.groupColor") ?? "Color preview"; + groupColorValue.Text = _localizationGetter("Forms.RadarColorControl.groupColorValue") ?? "Color value"; + groupSingleTile.Text = _localizationGetter("Forms.RadarColorControl.groupSingleTile") ?? "Single tile"; + groupBatch.Text = _localizationGetter("Forms.RadarColorControl.groupBatch") ?? "Batch"; + groupFile.Text = _localizationGetter("Forms.RadarColorControl.groupFile") ?? "File"; + + // TabPage 标题 + tabPage3.Text = _localizationGetter("Forms.RadarColorControl.tabPageItems") ?? "Items"; + tabPage4.Text = _localizationGetter("Forms.RadarColorControl.tabPageLand") ?? "Land Tiles"; + + // 标签文本 + labelHex.Text = _localizationGetter("Forms.RadarColorControl.labelHex") ?? "Hex:"; + labelRgb.Text = _localizationGetter("Forms.RadarColorControl.labelRgb") ?? "RGB:"; + label1.Text = _localizationGetter("Forms.RadarColorControl.labelItemsProgress") ?? "Items:"; + label2.Text = _localizationGetter("Forms.RadarColorControl.labelLandProgress") ?? "Land:"; + + // 按钮文本 + buttonMean.Text = _localizationGetter("Forms.RadarColorControl.buttonMean") ?? "Average Color"; + buttonRevert.Text = _localizationGetter("Forms.RadarColorControl.buttonRevert") ?? "Revert"; + buttonSaveColor.Text = _localizationGetter("Forms.RadarColorControl.buttonSaveColor") ?? "Save Color"; + buttonSaveFile.Text = _localizationGetter("Forms.RadarColorControl.buttonSaveFile") ?? "Save File"; + buttonRevertAll.Text = _localizationGetter("Forms.RadarColorControl.buttonRevertAll") ?? "Revert All"; + buttonExport.Text = _localizationGetter("Forms.RadarColorControl.buttonExport") ?? "Export.."; + buttonImport.Text = _localizationGetter("Forms.RadarColorControl.buttonImport") ?? "Import.."; + buttonAverageAll.Text = _localizationGetter("Forms.RadarColorControl.buttonAverageAll") ?? "Auto-fill empty entries"; + buttonSelectAllItems.Text = _localizationGetter("Forms.RadarColorControl.buttonSelectAllItems") ?? "Select All"; + buttonSelectNoneItems.Text = _localizationGetter("Forms.RadarColorControl.buttonSelectNoneItems") ?? "Select None"; + buttonSelectAllLand.Text = _localizationGetter("Forms.RadarColorControl.buttonSelectAllLand") ?? "Select All"; + buttonSelectNoneLand.Text = _localizationGetter("Forms.RadarColorControl.buttonSelectNoneLand") ?? "Select None"; + + // 注意:以下三个按钮的文本会在 Selection/Range 模式切换时动态改变 + // 初始化时先设置为 Selection 模式,但会立即被 ApplyBatchButtonLocalization() 覆盖 + buttonCurrentToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonCurrentToRangeAverage_selection") ?? "Current tile to selection average"; + buttonRangeToIndividualAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToIndividualAverage_selection") ?? "Selected tiles to individual average"; + buttonRangeToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToRangeAverage_selection") ?? "Selected tiles to selection average"; + + // 单选框文本 + radioUseSelection.Text = _localizationGetter("Forms.RadarColorControl.radioUseSelection") ?? "Selection / Checked tiles"; + radioUseRange.Text = _localizationGetter("Forms.RadarColorControl.radioUseRange") ?? "Range:"; + + // 动态按钮文本(初始化为 Selection 模式) + bool useSelection = radioUseSelection.Checked; + ApplyBatchButtonLocalization(useSelection); + + // 菜单项文本 + selectInItemsTabToolStripMenuItem.Text = _localizationGetter("Forms.RadarColorControl.selectInItemsTabToolStripMenuItem") ?? "Select in Items tab"; + selectInTiledataTabToolStripMenuItem.Text = _localizationGetter("Forms.RadarColorControl.selectInTiledataTabToolStripMenuItem") ?? "Select in Tiledata tab"; + setAsRangeFromToolStripMenuItem.Text = _localizationGetter("Forms.RadarColorControl.setAsRangeFromToolStripMenuItem") ?? "Set as Range \"from\""; + setAsRangeToToolStripMenuItem.Text = _localizationGetter("Forms.RadarColorControl.setAsRangeToToolStripMenuItem") ?? "Set as Range \"to\""; + toolStripMenuItem1.Text = _localizationGetter("Forms.RadarColorControl.toolStripMenuItem1") ?? "Select in Landtiles tab"; + toolStripMenuItem2.Text = _localizationGetter("Forms.RadarColorControl.toolStripMenuItem2") ?? "Select in Tiledata tab"; + setAsRangeFromToolStripMenuItem1.Text = _localizationGetter("Forms.RadarColorControl.setAsRangeFromToolStripMenuItem1") ?? "Set as Range \"from\""; + setAsRangeToToolStripMenuItem1.Text = _localizationGetter("Forms.RadarColorControl.setAsRangeToToolStripMenuItem1") ?? "Set as Range \"to\""; + changeBackgroundColorToolStripMenuItem.Text = _localizationGetter("Forms.RadarColorControl.changeBackgroundColorToolStripMenuItem") ?? "Change background color"; + + // 文本框占位符 + textBoxMeanFrom.PlaceholderText = _localizationGetter("Forms.RadarColorControl.textBoxMeanFromPlaceholder") ?? "from"; + textBoxMeanTo.PlaceholderText = _localizationGetter("Forms.RadarColorControl.textBoxMeanToPlaceholder") ?? "to"; + + // Filter 文本框占位符 + textFilterItems.PlaceholderText = _localizationGetter("Forms.RadarColorControl.textFilterItems") ?? "Filter"; + textFilterLand.PlaceholderText = _localizationGetter("Forms.RadarColorControl.textFilterLand") ?? "Filter"; + + // Tooltip + toolTip.SetToolTip(buttonStrategyHelp, + _localizationGetter("Forms.RadarColorControl.toolTipStrategyHelp") ?? "About these averaging strategies"); + } + + private void ApplyBatchButtonLocalization(bool useSelection) + { + if (_localizationGetter == null) return; + + if (useSelection) + { + buttonCurrentToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonCurrentToRangeAverage_selection") + ?? "Current tile to selection average"; + buttonRangeToIndividualAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToIndividualAverage_selection") + ?? "Selected tiles to individual average"; + buttonRangeToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToRangeAverage_selection") + ?? "Selected tiles to selection average"; + } + else + { + buttonCurrentToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonCurrentToRangeAverage_range") + ?? "Current tile to range average"; + buttonRangeToIndividualAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToIndividualAverage_range") + ?? "Range tiles to individual average"; + buttonRangeToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToRangeAverage_range") + ?? "Range tiles to range average"; + } + } + public void OnLoad(object sender, EventArgs e) { if (IsAncestorSiteInDesignMode || FormsDesignerHelper.IsInDesignMode()) @@ -539,7 +646,7 @@ private void PopulateMeanStrategyCombo() comboMeanStrategy.BeginUpdate(); foreach (RadarAveragingStrategy s in RadarColorAveraging.All) { - comboMeanStrategy.Items.Add(new MeanStrategyItem(s)); + comboMeanStrategy.Items.Add(new MeanStrategyItem(s, _localizationGetter)); } // Select the persisted/runtime strategy. for (int i = 0; i < comboMeanStrategy.Items.Count; ++i) @@ -560,8 +667,29 @@ private void PopulateMeanStrategyCombo() private sealed class MeanStrategyItem { public RadarAveragingStrategy Strategy { get; } - public MeanStrategyItem(RadarAveragingStrategy s) { Strategy = s; } - public override string ToString() => RadarColorAveraging.DisplayName(Strategy); + private readonly Func? _localizationGetter; + + public MeanStrategyItem(RadarAveragingStrategy s, Func? localizationGetter = null) + { + Strategy = s; + _localizationGetter = localizationGetter; + } + + public override string ToString() + { + // DEBUG: 打印枚举名称用于调试 + string enumName = Strategy.ToString(); + + if (_localizationGetter != null) + { + string localizedName = _localizationGetter($"Forms.RadarColorControl.AveragingStrategies.{enumName}"); + if (!string.IsNullOrEmpty(localizedName)) + { + return localizedName; + } + } + return RadarColorAveraging.DisplayName(Strategy); + } } private RadarAveragingStrategy CurrentStrategy => @@ -577,7 +705,7 @@ private void OnSelectedMeanStrategyChanged(object sender, EventArgs e) private void OnClickStrategyHelp(object sender, EventArgs e) { - using var dlg = new StrategyHelpForm(); + using var dlg = new StrategyHelpForm(_localizationGetter); dlg.ShowDialog(FindForm()); } @@ -586,9 +714,13 @@ private void OnClickStrategyHelp(object sender, EventArgs e) // to the code that defines the strategies. private sealed class StrategyHelpForm : Form { - public StrategyHelpForm() + private readonly Func? _localizationGetter; + + public StrategyHelpForm(Func? localizationGetter = null) { - Text = "Radar color — averaging strategies"; + _localizationGetter = localizationGetter; + + Text = _localizationGetter?.Invoke("Forms.StrategyHelpForm.Title") ?? "Radar color — averaging strategies"; FormBorderStyle = FormBorderStyle.Sizable; StartPosition = FormStartPosition.CenterParent; MinimumSize = new Size(560, 400); @@ -605,12 +737,12 @@ public StrategyHelpForm() WordWrap = true, Dock = DockStyle.Fill, Font = new Font(FontFamily.GenericSansSerif, 9f), - Text = HelpText, + Text = GetHelpText(), TabStop = false, }; var ok = new Button { - Text = "Close", + Text = _localizationGetter?.Invoke("Forms.StrategyHelpForm.CloseButton") ?? "Close", DialogResult = DialogResult.OK, Dock = DockStyle.Bottom, Height = 32, @@ -640,6 +772,19 @@ protected override void OnShown(EventArgs e) } } + private string GetHelpText() + { + if (_localizationGetter != null) + { + var helpText = _localizationGetter("Forms.StrategyHelpForm.HelpText"); + if (!string.IsNullOrEmpty(helpText)) + { + return helpText; + } + } + return HelpText; + } + private const string HelpText = "Radar color averaging strategies\r\n" + "================================\r\n\r\n" + @@ -860,7 +1005,10 @@ private void OnClickSaveFile(object sender, EventArgs e) Options.ChangedUltimaClass["RadarCol"] = false; - FileSavedDialog.Show(FindForm(), fileName, "RadarCol saved successfully."); + string message = _localizationGetter("Forms.RadarColorControl.Messages.RadarColSaved") + ?? "RadarCol saved successfully."; + string title = _localizationGetter("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter(key)); } private void SaveColor() @@ -900,11 +1048,12 @@ private void SaveColor(int index, ushort color, bool isItemTile) private void OnClickRevertAll(object sender, EventArgs e) { - DialogResult result = MessageBox.Show( - "Do you want to revert all changes to items and land tiles?", - "Revert All", - MessageBoxButtons.YesNo - ); + string message = _localizationGetter("Forms.RadarColorControl.Messages.ConfirmRevertAll") + ?? "Do you want to revert all changes to items and land tiles?"; + string title = _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.RevertAll") + ?? "Revert All"; + + DialogResult result = MessageBox.Show(message, title, MessageBoxButtons.YesNo); if (result != DialogResult.Yes) { @@ -1076,7 +1225,11 @@ private IEnumerable GetValidSequence() if (!Utils.ConvertStringToInt(textBoxMeanFrom.Text, out int from, 0, maxIndex) || !Utils.ConvertStringToInt(textBoxMeanTo.Text, out int to, 0, maxIndex)) { - MessageBox.Show($"Invalid parameters. Expected [to, from] between [0, {maxIndex} (0x{maxIndex:X4})]", "Error", MessageBoxButtons.OK); + string message = _localizationGetter("Forms.RadarColorControl.Messages.InvalidParametersRange") + ?? $"Invalid parameters. Expected [to, from] between [0, {maxIndex} (0x{maxIndex:X4})]"; + string title = _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.Error") + ?? "Error"; + MessageBox.Show(string.Format(message, maxIndex), title, MessageBoxButtons.OK); return null; } @@ -1092,7 +1245,11 @@ private IEnumerable GetValidSequence() var sequence = isItem ? _selectedItems : _selectedLand; if (sequence.Count == 0) { - MessageBox.Show("Invalid parameters. No tiles selected/checked.", "Error", MessageBoxButtons.OK); + string message = _localizationGetter("Forms.RadarColorControl.Messages.InvalidParametersNoSelection") + ?? "Invalid parameters. No tiles selected/checked."; + string title = _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.Error") + ?? "Error"; + MessageBox.Show(message, title, MessageBoxButtons.OK); return null; } return sequence; @@ -1228,7 +1385,11 @@ private void OnClickSelectItemsTab(object sender, EventArgs e) var found = ItemsControl.SearchGraphic(index); if (!found) { - MessageBox.Show("You need to load Items tab first.", "Information"); + string message = _localizationGetter("Forms.RadarColorControl.Messages.ItemsTabNotLoaded") + ?? "You need to load Items tab first."; + string title = _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.Information") + ?? "Information"; + MessageBox.Show(message, title); } } @@ -1254,7 +1415,11 @@ private void OnClickSelectLandTilesTab(object sender, EventArgs e) var found = LandTilesControl.SearchGraphic(index); if (!found) { - MessageBox.Show("You need to load LandTiles tab first.", "Information"); + string message = _localizationGetter("Forms.RadarColorControl.Messages.LandTilesTabNotLoaded") + ?? "You need to load LandTiles tab first."; + string title = _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.Information") + ?? "Information"; + MessageBox.Show(message, title); } } @@ -1309,16 +1474,20 @@ private void OnClickExport(object sender, EventArgs e) string fileName = Path.Combine(path, "RadarColor.csv"); RadarCol.ExportToCSV(fileName); - FileSavedDialog.Show(FindForm(), fileName, "RadarColor saved successfully."); + string message = _localizationGetter("Forms.RadarColorControl.Messages.RadarColorSaved") + ?? "RadarColor saved successfully."; + string title = _localizationGetter("Forms.FileSavedDialog.Title") ?? "Saved"; + FileSavedDialog.Show(FindForm(), fileName, message, title, key => _localizationGetter(key)); } private void OnClickMeanColorAll(object sender, EventArgs e) { - DialogResult result = MessageBox.Show( - "Do you want to calculate and set new radar color values for all items and land tiles entries where current color is black or missing?", - "Average All", - MessageBoxButtons.YesNo - ); + string message = _localizationGetter("Forms.RadarColorControl.Messages.ConfirmAutoFillEmpty") + ?? "Do you want to calculate and set new radar color values for all items and land tiles entries where current color is black or missing?"; + string title = _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.AverageAll") + ?? "Average All"; + + DialogResult result = MessageBox.Show(message, title, MessageBoxButtons.YesNo); if (result != DialogResult.Yes) { @@ -1384,7 +1553,10 @@ private void OnClickMeanColorAll(object sender, EventArgs e) } } - MessageBox.Show("Done!", "Average All"); + MessageBox.Show( + _localizationGetter("Forms.RadarColorControl.Messages.OperationDone") ?? "Done!", + _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.AverageAll") ?? "Average All" + ); progressBar1.Value = 0; progressBar2.Value = 0; @@ -1515,18 +1687,44 @@ private void OnCheckedChangeUseSelection(object sender, EventArgs e) { textBoxMeanFrom.Enabled = false; textBoxMeanTo.Enabled = false; - buttonRangeToRangeAverage.Text = "Selected tiles to selection average"; - buttonRangeToIndividualAverage.Text = "Selected tiles to individual average"; - buttonCurrentToRangeAverage.Text = "Current tile to selection average"; + + if (_localizationGetter != null) + { + buttonRangeToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToRangeAverage_selection") + ?? "Selected tiles to selection average"; + buttonRangeToIndividualAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToIndividualAverage_selection") + ?? "Selected tiles to individual average"; + buttonCurrentToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonCurrentToRangeAverage_selection") + ?? "Current tile to selection average"; + } + else + { + buttonRangeToRangeAverage.Text = "Selected tiles to selection average"; + buttonRangeToIndividualAverage.Text = "Selected tiles to individual average"; + buttonCurrentToRangeAverage.Text = "Current tile to selection average"; + } } private void OnCheckedChangeUseRange(object sender, EventArgs e) { textBoxMeanFrom.Enabled = true; textBoxMeanTo.Enabled = true; - buttonRangeToRangeAverage.Text = "Range tiles to range average"; - buttonRangeToIndividualAverage.Text = "Range tiles to individual average"; - buttonCurrentToRangeAverage.Text = "Current tile to range average"; + + if (_localizationGetter != null) + { + buttonRangeToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToRangeAverage_range") + ?? "Range tiles to range average"; + buttonRangeToIndividualAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonRangeToIndividualAverage_range") + ?? "Range tiles to individual average"; + buttonCurrentToRangeAverage.Text = _localizationGetter("Forms.RadarColorControl.DynamicButtonTexts.buttonCurrentToRangeAverage_range") + ?? "Current tile to range average"; + } + else + { + buttonRangeToRangeAverage.Text = "Range tiles to range average"; + buttonRangeToIndividualAverage.Text = "Range tiles to individual average"; + buttonCurrentToRangeAverage.Text = "Current tile to range average"; + } } private void OnClickSelectAllLand(object sender, EventArgs e) @@ -1659,7 +1857,8 @@ private void OnClickAlgorithmBenchmark(object sender, EventArgs e) $" item : {item.Exact[bestIdx]}/{item.Counted[bestIdx]} " + $"({(item.Counted[bestIdx] == 0 ? 0 : 100.0 * item.Exact[bestIdx] / item.Counted[bestIdx]):F2}%)\n\n" + $"Full report: {outPath}"; - MessageBox.Show(FindForm(), summary, "Algorithm benchmark"); + MessageBox.Show(FindForm(), summary, + _localizationGetter("Forms.RadarColorControl.MessageBoxTitles.AlgorithmBenchmark") ?? "Algorithm benchmark"); } private sealed class BenchStats diff --git a/UoFiddler.Controls/UserControls/RadarColorControl.resx b/UoFiddler.Controls/UserControls/RadarColorControl.resx index f7497ab1..699e61c6 100644 --- a/UoFiddler.Controls/UserControls/RadarColorControl.resx +++ b/UoFiddler.Controls/UserControls/RadarColorControl.resx @@ -129,9 +129,6 @@ 17, 17 - - 17, 17 - 316, 17 diff --git a/UoFiddler.Controls/UserControls/SkillGroupControl.cs b/UoFiddler.Controls/UserControls/SkillGroupControl.cs index aa593beb..40faf30c 100644 --- a/UoFiddler.Controls/UserControls/SkillGroupControl.cs +++ b/UoFiddler.Controls/UserControls/SkillGroupControl.cs @@ -31,6 +31,31 @@ public SkillGroupControl() private bool _loaded; private TreeNode _sourceNode; + private Func? _localizationGetter; + + /// + /// 设置本地化(从 MainForm 调用) + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 应用菜单项汉化 + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 菜单项汉化 + addToolStripMenuItem.Text = _localizationGetter("Forms.SkillGroupControl.AddButton") ?? "Add"; + removeToolStripMenuItem.Text = _localizationGetter("Forms.SkillGroupControl.RemoveButton") ?? "Remove"; + renameToolStripMenuItem.Text = _localizationGetter("Forms.SkillGroupControl.RenameButton") ?? "Rename"; + toolStripMenuItem1.Text = _localizationGetter("Forms.SkillGroupControl.SkillIDLabel") ?? "SkillID"; + saveToolStripMenuItem.Text = _localizationGetter("Forms.SkillGroupControl.SaveButton") ?? "Save"; + } /// /// ReLoads if loaded @@ -145,7 +170,9 @@ private void OnClickSave(object sender, EventArgs e) SkillGroups.Save(Options.OutputPath); Options.ChangedUltimaClass["SkillGrp"] = false; - FileSavedDialog.Show(FindForm(), Options.OutputPath, "SkillGrp saved successfully."); + string message = _localizationGetter?.Invoke("Forms.SkillGroupControl.FileSavedMessage") ?? "SkillGrp saved successfully."; + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "File Saved"; + FileSavedDialog.Show(FindForm(), Options.OutputPath, message, title, key => _localizationGetter?.Invoke(key)); } private void OnItemDrag(object sender, ItemDragEventArgs e) @@ -254,10 +281,14 @@ private void AfterLabelEdit(object sender, NodeLabelEditEventArgs e) return; } + string editFormTitle = _localizationGetter?.Invoke("Forms.SkillGroupControl.EditFormTitle") ?? "SkillGroup Edit"; + string invalidNameReserved = _localizationGetter?.Invoke("Forms.SkillGroupControl.InvalidNameReserved") ?? "Invalid name. Name is reserved."; + string invalidNameEmpty = _localizationGetter?.Invoke("Forms.SkillGroupControl.InvalidNameEmpty") ?? "Invalid name. Name cannot be empty."; + if (string.Equals("Misc", e.Label)) { e.CancelEdit = true; - MessageBox.Show("Invalid name. Name is reserved.", "SkillGroup Edit"); + MessageBox.Show(invalidNameReserved, editFormTitle); e.Node.BeginEdit(); } else if (e.Label.Length > 0) @@ -267,7 +298,7 @@ private void AfterLabelEdit(object sender, NodeLabelEditEventArgs e) else { e.CancelEdit = true; - MessageBox.Show("Invalid name. Name cannot be empty.", "SkillGroup Edit"); + MessageBox.Show(invalidNameEmpty, editFormTitle); e.Node.BeginEdit(); } treeView1.LabelEdit = false; diff --git a/UoFiddler.Controls/UserControls/SkillsControl.cs b/UoFiddler.Controls/UserControls/SkillsControl.cs index 697803f4..096432ec 100644 --- a/UoFiddler.Controls/UserControls/SkillsControl.cs +++ b/UoFiddler.Controls/UserControls/SkillsControl.cs @@ -29,6 +29,7 @@ public SkillsControl() private bool _loaded; private static BindingSource _source; + private Func? _localizationGetter; /// /// ReLoads if loaded @@ -69,6 +70,9 @@ private void OnLoad(object sender, EventArgs e) dataGridView1.Columns[2].FillWeight = 54.86799F; dataGridView1.Columns[2].ReadOnly = false; dataGridView1.Columns[3].Visible = false; // extraFlag + + // 应用列头汉化 + ApplyColumnHeaderLocalization(); } if (!_loaded) @@ -98,7 +102,9 @@ private void OnClickSave(object sender, EventArgs e) Skills.Save(path); Options.ChangedUltimaClass["Skills"] = false; - FileSavedDialog.Show(FindForm(), Options.OutputPath, "Skills saved successfully."); + string message = _localizationGetter?.Invoke("Forms.SkillsControl.FileSavedMessage") ?? "Skills saved successfully."; + string title = _localizationGetter?.Invoke("Forms.SkillsControl.FileSavedTitle") ?? "Save"; + FileSavedDialog.Show(FindForm(), Options.OutputPath, message, title); } private void OnClickAdd(object sender, EventArgs e) @@ -128,5 +134,43 @@ private void OnClickDelete(object sender, EventArgs e) _source.RemoveCurrent(); dataGridView1.Invalidate(); } + + /// + /// 设置本地化(从 MainForm 调用) + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 应用列标题汉化(在数据绑定后调用) + /// + private void ApplyColumnHeaderLocalization() + { + // 设置表格列标题 + if (dataGridView1.Columns.Count > 0) + { + dataGridView1.Columns[0].HeaderText = _localizationGetter?.Invoke("Forms.SkillsControl.ColumnId") ?? "ID"; + if (dataGridView1.Columns.Count > 1) + dataGridView1.Columns[1].HeaderText = _localizationGetter?.Invoke("Forms.SkillsControl.ColumnIsAction") ?? "is Action"; + if (dataGridView1.Columns.Count > 2) + dataGridView1.Columns[2].HeaderText = _localizationGetter?.Invoke("Forms.SkillsControl.ColumnName") ?? "Name"; + } + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 菜单项汉化 + saveToolStripMenuItem.Text = _localizationGetter("Forms.SkillsControl.SaveButton") ?? "Save"; + addToolStripMenuItem.Text = _localizationGetter("Forms.SkillsControl.AddButton") ?? "Add"; + deleteToolStripMenuItem.Text = _localizationGetter("Forms.SkillsControl.DeleteButton") ?? "Delete"; + + // 表格列标题 + ApplyColumnHeaderLocalization(); + } } } diff --git a/UoFiddler.Controls/UserControls/SoundsControl.cs b/UoFiddler.Controls/UserControls/SoundsControl.cs index 47555dc5..5ad0c935 100644 --- a/UoFiddler.Controls/UserControls/SoundsControl.cs +++ b/UoFiddler.Controls/UserControls/SoundsControl.cs @@ -42,6 +42,9 @@ public partial class SoundsControl : UserControl // across all list items, recreated each reload (the control Font may change). private Font _underlineFont; + // Localization support + private Func? _localizationGetter; + public SoundsControl() { InitializeComponent(); @@ -264,6 +267,66 @@ private void PlaySound(int id) } } + /// + /// 设置本地化的字符串获取委托 + /// + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 应用本地化翻译到所有 UI 控件 + /// + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Type 1: 菜单项文本汉化 + nameSortToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.nameSortToolStripMenuItem") ?? "Name Sort"; + showFreeSlotsToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.showFreeSlotsToolStripMenuItem") ?? "Show free slots"; + nextFreeSlotToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.nextFreeSlotToolStripMenuItem") ?? "Find next free slot"; + playSoundToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.playSoundToolStripMenuItem") ?? "Play"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.replaceToolStripMenuItem") ?? "Insert/Replace"; + extractSoundToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.extractSoundToolStripMenuItem") ?? "Extract"; + removeSoundToolStripMenuItem.Text = _localizationGetter("Forms.SoundsControl.removeSoundToolStripMenuItem") ?? "Remove"; + itemSave.Text = _localizationGetter("Forms.SoundsControl.itemSave") ?? "Save"; + + // Type 1: GroupBox 标题 + groupBox1.Text = _localizationGetter("Forms.SoundsControl.groupBox1") ?? "Generic"; + SelectedSoundGroup.Text = _localizationGetter("Forms.SoundsControl.SelectedSoundGroup") ?? "Current Sound"; + groupBox2.Text = _localizationGetter("Forms.SoundsControl.groupBox2") ?? "Search"; + groupBox3.Text = _localizationGetter("Forms.SoundsControl.groupBox3") ?? "Insert/Replace"; + + // Type 1: 按钮文本 + PlaySoundButton.Text = _localizationGetter("Forms.SoundsControl.PlaySoundButton") ?? "Play"; + StopSoundButton.Text = _localizationGetter("Forms.SoundsControl.StopSoundButton") ?? "Stop"; + ExtractSoundButton.Text = _localizationGetter("Forms.SoundsControl.ExtractSoundButton") ?? "Extract"; + RemoveSoundButton.Text = _localizationGetter("Forms.SoundsControl.RemoveSoundButton") ?? "Remove"; + SaveFileButton.Text = _localizationGetter("Forms.SoundsControl.SaveFileButton") ?? "Save"; + exportAllSoundsButton.Text = _localizationGetter("Forms.SoundsControl.exportAllSoundsButton") ?? "Export all sounds"; + ExportSoundListCsvButton.Text = _localizationGetter("Forms.SoundsControl.ExportSoundListCsvButton") ?? "Export sound list (.csv)"; + WavChooseInsertButton.Text = _localizationGetter("Forms.SoundsControl.WavChooseInsertButton") ?? "..."; + AddInsertReplaceButton.Text = _localizationGetter("Forms.SoundsControl.AddInsertReplaceButton") ?? "Add"; + GoPrevResultButton.Text = _localizationGetter("Forms.SoundsControl.GoPrevResultButton") ?? "< Prev"; + GoNextResultButton.Text = _localizationGetter("Forms.SoundsControl.GoNextResultButton") ?? "Next >"; + SearchByIdButton.Text = _localizationGetter("Forms.SoundsControl.SearchByIdButton") ?? "Search"; + SearchByNameButton.Text = _localizationGetter("Forms.SoundsControl.SearchByNameButton") ?? "Search"; + + // Type 1: 标签和复选框 + label1.Text = _localizationGetter("Forms.SoundsControl.label1") ?? "ID:"; + label3.Text = _localizationGetter("Forms.SoundsControl.label3") ?? "WAV File:"; + SortByNameCheckbox.Text = _localizationGetter("Forms.SoundsControl.SortByNameCheckbox") ?? "Sort tree by name"; + includeSoundIdCheckBox.Text = _localizationGetter("Forms.SoundsControl.includeSoundIdCheckBox") ?? "Export with sound id in file name"; + + // Type 1: 列表列头 + listViewColumn.Text = _localizationGetter("Forms.SoundsControl.listViewColumn") ?? "Sound"; + + // Type 1: 工具栏文本 + stopButton.Text = _localizationGetter("Forms.SoundsControl.stopButton") ?? "Stop"; + } + private void AfterSelect(object sender, EventArgs e) { // Mirror the old TreeView BeforeSelect behaviour: stop playback @@ -281,13 +344,16 @@ private void AfterSelect(object sender, EventArgs e) extractSoundToolStripMenuItem.Enabled = false; removeSoundToolStripMenuItem.Enabled = false; replaceToolStripMenuItem.Enabled = false; - replaceToolStripMenuItem.Text = "Insert/Replace"; + // Type 12 初始值设置(在 ApplyLocalization 中已设置,此处保持为保障) + replaceToolStripMenuItem.Text = _localizationGetter?.Invoke("Forms.SoundsControl.replaceToolStripMenuItem") ?? "Insert/Replace"; } if (selected != null) { double length = Sounds.GetSoundLength((int)selected.Tag); - seconds.Text = length > 0 ? $"{length:f}s" : "Empty Slot"; + // Type 8:动态文本 - 空插槽提示 + string emptySlotText = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.emptySlot") ?? "Empty Slot"; + seconds.Text = length > 0 ? $"{length:f}s" : emptySlotText; } bool isValidSound = selected != null && Sounds.IsValidSound((int)selected.Tag, out _, out _); @@ -297,13 +363,22 @@ private void AfterSelect(object sender, EventArgs e) removeSoundToolStripMenuItem.Enabled = isValidSound; replaceToolStripMenuItem.Enabled = true; - replaceToolStripMenuItem.Text = isValidSound ? "Replace" : "Insert"; + // Type 8:动态文本 - Replace/Insert 菜单项文本切换 + if (isValidSound) + { + replaceToolStripMenuItem.Text = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.replace") ?? "Replace"; + } + else + { + replaceToolStripMenuItem.Text = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.insert") ?? "Insert"; + } SelectedSoundGroup.Visible = selected != null; if (selected != null) { - SelectedSoundGroup.Text = $"Current Sound: {selected.Text} - Duration: {seconds.Text}"; + // Type 8:动态文本 - GroupBox 标题,包含选中音效信息 + SelectedSoundGroup.Text = $"{_localizationGetter?.Invoke("Forms.SoundsControl.Messages.currentSoundFormat") ?? "Current Sound"}: {selected.Text} - {_localizationGetter?.Invoke("Forms.SoundsControl.Messages.duration") ?? "Duration"}: {seconds.Text}"; IdInsertTextbox.Text = $"0x{(int)selected.Tag + _soundIdOffset:X}"; } } @@ -436,7 +511,12 @@ private void OnClickExtract(object sender, EventArgs e) } } - MessageBox.Show($"Sound saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information, + // Type 6:消息框文本汉化 - 提取成功提示 + string message = string.Format( + _localizationGetter?.Invoke("Forms.SoundsControl.Messages.soundSavedTo") ?? "Sound saved to {0}", + fileName); + string title = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.saved") ?? "Saved"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } @@ -486,7 +566,15 @@ private void OnClickExportSoundListCsv(object sender, EventArgs e) Sounds.SaveSoundListToCsv(fileName, _soundIdOffset); - FileSavedDialog.Show(FindForm(), fileName, "SoundList saved successfully."); + // Type 6:消息框文本汉化 - 导出音效列表完成 + string message = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.soundListSavedSuccessfully") ?? "SoundList saved successfully."; + var dialog = new FileSavedDialog(fileName, message); + if (_localizationGetter != null) + { + dialog.SetLocalization(_localizationGetter); + } + dialog.ShowDialog(FindForm()); + dialog.Dispose(); } public bool SearchId(int id) @@ -524,7 +612,7 @@ private void OnClickReplace(object sender, EventArgs e) using (OpenFileDialog dialog = new OpenFileDialog()) { dialog.Multiselect = false; - dialog.Title = "Choose wave file"; + dialog.Title = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.chooseWaveFile") ?? "Choose wave file"; dialog.CheckFileExists = true; dialog.Filter = "wav file (*.wav)|*.wav"; if (dialog.ShowDialog() == DialogResult.OK) @@ -542,6 +630,15 @@ private void OnClickReplace(object sender, EventArgs e) file = _wavChosen; } + // ✅ 修复:检查 file 是否为空或文件是否存在 + if (string.IsNullOrEmpty(file) || !File.Exists(file)) + { + string errorMsg = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.invalidFilename") ?? "Invalid Filename"; + string titleMsg = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.addReplaceTitle") ?? "Add/Replace"; + MessageBox.Show(errorMsg, titleMsg, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + return; // ✅ 修复:缺少的 return 语句 + } + if (listView.SelectedItems.Count == 0) { return; @@ -550,11 +647,6 @@ private void OnClickReplace(object sender, EventArgs e) int id = (int)listView.SelectedItems[0].Tag; string name = Path.GetFileName(file); - if (!File.Exists(file)) - { - MessageBox.Show("Invalid Filename", "Add/Replace", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); - } - if (name.Length > 32) { name = name.Substring(0, 32); @@ -562,8 +654,10 @@ private void OnClickReplace(object sender, EventArgs e) if (Sounds.IsValidSound(id, out _, out _)) { - DialogResult result = MessageBox.Show($"Are you sure to replace {listView.SelectedItems[0].Text}?", - "Replace", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); + string confirmMsg = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.confirmReplace") ?? $"Are you sure to replace {listView.SelectedItems[0].Text}?"; + confirmMsg = string.Format(confirmMsg, listView.SelectedItems[0].Text); + string titleMsg = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.replaceTitle") ?? "Replace"; + DialogResult result = MessageBox.Show(confirmMsg, titleMsg, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (result != DialogResult.Yes) { @@ -577,7 +671,9 @@ private void OnClickReplace(object sender, EventArgs e) } catch (WaveFormatException waveFormatException) { - MessageBox.Show("Unexpected WAV format:\n" + waveFormatException.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + string errorMsg = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.unexpectedWavFormat") ?? "Unexpected WAV format:"; + string titleMsg = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.errorTitle") ?? "Error"; + MessageBox.Show(errorMsg + "\n" + waveFormatException.Message, titleMsg, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -854,7 +950,10 @@ private void ExportAllSounds() } } - MessageBox.Show("Extract all sounds complete.", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information, + // Type 6:消息框文本汉化 - 导出所有完成提示 + string message = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.extractAllComplete") ?? "Extract all sounds complete."; + string title = _localizationGetter?.Invoke("Forms.SoundsControl.Messages.saved") ?? "Saved"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1); } } diff --git a/UoFiddler.Controls/UserControls/SpeechControl.cs b/UoFiddler.Controls/UserControls/SpeechControl.cs index f25b67bf..e986072b 100644 --- a/UoFiddler.Controls/UserControls/SpeechControl.cs +++ b/UoFiddler.Controls/UserControls/SpeechControl.cs @@ -29,6 +29,7 @@ public partial class SpeechControl : UserControl private SortOrder _sortOrder; private int _sortColumn; private bool _loaded; + private Func? _localizationGetter; public SpeechControl() { @@ -72,6 +73,8 @@ private void OnLoad(object sender, EventArgs e) if (dataGridView1.Columns.Count > 0) { dataGridView1.Columns[0].Width = 60; + // 应用列标题(初始时用英文或汉化值) + ApplyColumnHeaderLocalization(); } dataGridView1.Invalidate(); @@ -126,6 +129,12 @@ private void OnHeaderClick(object sender, DataGridViewCellMouseEventArgs e) private void OnCellValueChanged(object sender, DataGridViewCellEventArgs e) { + // 跳过列标题改变事件(e.RowIndex == -1) + if (e.RowIndex < 0 || e.RowIndex >= SpeechList.Entries.Count) + { + return; + } + SpeechList.Entries[e.RowIndex].KeyWord ??= string.Empty; Options.ChangedUltimaClass["Speech"] = true; @@ -148,7 +157,9 @@ private void FindId(int index) } } - MessageBox.Show("ID not found.", "Goto", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + string message = _localizationGetter?.Invoke("Forms.SpeechControl.Messages.IDNotFound") ?? "ID not found."; + string title = _localizationGetter?.Invoke("Forms.SpeechControl.Messages.IDNotFoundTitle") ?? "Goto"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } private void FindKeyWord(int index) @@ -168,7 +179,9 @@ private void FindKeyWord(int index) return; } - MessageBox.Show("KeyWord not found.", "Entry", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); + string message = _localizationGetter?.Invoke("Forms.SpeechControl.Messages.KeyWordNotFound") ?? "KeyWord not found."; + string title = _localizationGetter?.Invoke("Forms.SpeechControl.Messages.KeyWordNotFoundTitle") ?? "Entry"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } private void OnClickFindID(object sender, EventArgs e) @@ -317,7 +330,8 @@ private void OnClickSave(object sender, EventArgs e) Options.ChangedUltimaClass["Speech"] = false; - FileSavedDialog.Show(FindForm(), fileName, "Speech saved successfully."); + string message = _localizationGetter?.Invoke("Forms.SpeechControl.Messages.SpeechSavedSuccessfully") ?? "Speech saved successfully."; + FileSavedDialog.Show(FindForm(), fileName, message, null, key => _localizationGetter?.Invoke(key)); } private void OnAddEntry(object sender, EventArgs e) @@ -352,7 +366,8 @@ private void OnClickExport(object sender, EventArgs e) SpeechList.ExportToCsv(fileName); - FileSavedDialog.Show(FindForm(), fileName, "Speech saved successfully."); + string message = _localizationGetter?.Invoke("Forms.SpeechControl.Messages.SpeechSavedSuccessfully") ?? "Speech saved successfully."; + FileSavedDialog.Show(FindForm(), fileName, message, null, key => _localizationGetter?.Invoke(key)); } private void OnClickImport(object sender, EventArgs e) @@ -360,9 +375,9 @@ private void OnClickImport(object sender, EventArgs e) OpenFileDialog dialog = new OpenFileDialog { Multiselect = false, - Title = "Choose csv file to import", + Title = _localizationGetter?.Invoke("Forms.SpeechControl.Dialogs.ImportTitle") ?? "Choose csv file to import", CheckFileExists = true, - Filter = "csv files (*.csv)|*.csv" + Filter = _localizationGetter?.Invoke("Forms.SpeechControl.Dialogs.CSVFilter") ?? "csv files (*.csv)|*.csv" }; if (dialog.ShowDialog() == DialogResult.OK) @@ -378,6 +393,62 @@ private void OnClickImport(object sender, EventArgs e) dialog.Dispose(); } + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + /// + /// 应用列标题汉化(在数据绑定后调用,支持初始化和汉化两种场景) + /// + private void ApplyColumnHeaderLocalization() + { + // 设置表格列标题 + // 如果 _localizationGetter 为 null,会使用 ?? 后面的英文默认值 + if (dataGridView1.Columns.Count > 0) + { + dataGridView1.Columns[0].HeaderText = _localizationGetter?.Invoke("Forms.SpeechControl.ColumnId") ?? "Id"; + if (dataGridView1.Columns.Count > 1) + dataGridView1.Columns[1].HeaderText = _localizationGetter?.Invoke("Forms.SpeechControl.ColumnKeyWord") ?? "KeyWord"; + } + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 占位符 + string idPlaceholder = _localizationGetter("Forms.SpeechControl.IDEntryPlaceholder") ?? _idEntryPlaceholder; + string keywordPlaceholder = _localizationGetter("Forms.SpeechControl.KeyWordEntryPlaceholder") ?? _keywordEntryPlaceholder; + + if (IDEntry.Text == _idEntryPlaceholder) + IDEntry.Text = idPlaceholder; + if (KeyWordEntry.Text == _keywordEntryPlaceholder) + KeyWordEntry.Text = keywordPlaceholder; + + // 工具栏按钮 + IDButton.Text = _localizationGetter("Forms.SpeechControl.IDButton") ?? "Find"; + IDNextButton.Text = _localizationGetter("Forms.SpeechControl.IDNextButton") ?? "Find Next"; + KeyWordButton.Text = _localizationGetter("Forms.SpeechControl.KeyWordButton") ?? "Find"; + KeyWordNextButton.Text = _localizationGetter("Forms.SpeechControl.KeyWordNextButton") ?? "Find Next"; + + // 保存按钮 + toolStripButton1.Text = _localizationGetter("Forms.SpeechControl.SaveButton") ?? "Save"; + + // 杂项菜单及其下拉项 + toolStripDropDownButton1.Text = _localizationGetter("Forms.SpeechControl.MiscMenu") ?? "Misc"; + toolStripMenuItem1.Text = _localizationGetter("Forms.SpeechControl.ImportFromCSV") ?? "Import from CSV"; + toolStripMenuItem2.Text = _localizationGetter("Forms.SpeechControl.ExportToCSV") ?? "Export to CSV"; + + // 右键菜单 + addEntryToolStripMenuItem.Text = _localizationGetter("Forms.SpeechControl.AddEntryMenu") ?? "Add Entry"; + deleteEntryToolStripMenuItem.Text = _localizationGetter("Forms.SpeechControl.DeleteEntryMenu") ?? "Delete Entry"; + + // 表格列标题 + ApplyColumnHeaderLocalization(); + } + private void IDEntry_Enter(object sender, EventArgs e) { if (IDEntry.Text == _idEntryPlaceholder) diff --git a/UoFiddler.Controls/UserControls/TexturesControl.cs b/UoFiddler.Controls/UserControls/TexturesControl.cs index c02ba781..2840adbd 100644 --- a/UoFiddler.Controls/UserControls/TexturesControl.cs +++ b/UoFiddler.Controls/UserControls/TexturesControl.cs @@ -40,6 +40,9 @@ public TexturesControl() private bool _showFreeSlots; private bool _loaded; private int _selectedTextureId = -1; + + // 汉化支持 + private Func? _localizationGetter; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectedTextureId @@ -609,7 +612,13 @@ private void OnClickSave(object sender, EventArgs e) Options.ChangedUltimaClass["Texture"] = false; - FileSavedDialog.Show(FindForm(), Options.OutputPath, "Files saved successfully."); + // Create and localize FileSavedDialog (Type 11) + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Files saved successfully."; + string message = _localizationGetter?.Invoke("Forms.FileSavedDialog.SaveSuccess") ?? "Files saved successfully."; + + var dialog = new FileSavedDialog(Options.OutputPath, message, title); + dialog.SetLocalization(_localizationGetter); + dialog.ShowDialog(FindForm()); } private void OnClickExportBmp(object sender, EventArgs e) @@ -668,7 +677,14 @@ private void ExportMultipleTextureImages(List ids, ImageFormat imageFormat) } } - FileSavedDialog.Show(FindForm(), Options.OutputPath, $"{ids.Count} textures saved successfully."); + // Create and localize FileSavedDialog (Type 11) + string title = _localizationGetter?.Invoke("Forms.FileSavedDialog.Title") ?? "Files saved successfully."; + string message = _localizationGetter?.Invoke("Forms.FileSavedDialog.SaveSuccess") + ?? $"{ids.Count} textures saved successfully."; + + var dialog = new FileSavedDialog(Options.OutputPath, message, title); + dialog.SetLocalization(_localizationGetter); + dialog.ShowDialog(FindForm()); } private static void ExportTextureImage(int index, ImageFormat imageFormat) @@ -1049,10 +1065,17 @@ private void contextMenuStrip_Opening(object sender, System.ComponentModel.Cance int selectedCount = TextureTileView.SelectedIndices.Count; copyImageToolStripMenuItem.Enabled = _selectedTextureId >= 0 && Textures.TestTexture(_selectedTextureId); pasteImageToolStripMenuItem.Enabled = selectedCount > 0 && ImageClipboard.ContainsImage(); - pasteImageToolStripMenuItem.Text = selectedCount > 1 ? $"Paste Image into {selectedCount}" : "Paste Image"; - removeToolStripMenuItem.Text = selectedCount > 1 ? $"Remove {selectedCount}" : "Remove"; - exportImageToolStripMenuItem.Text = selectedCount > 1 ? $"Export {selectedCount} Images..." : "Export Image.."; - replaceToolStripMenuItem.Text = selectedCount > 1 ? $"Replace {selectedCount}" : "Replace"; + + // ✅ Type 12 汉化:使用汉化基础文本构建动态文本 + string basePasteText = _localizationGetter?.Invoke("Forms.TexturesControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + string baseRemoveText = _localizationGetter?.Invoke("Forms.TexturesControl.removeToolStripMenuItem") ?? "Remove"; + string baseExportText = _localizationGetter?.Invoke("Forms.TexturesControl.exportImageToolStripMenuItem") ?? "Export Image.."; + string baseReplaceText = _localizationGetter?.Invoke("Forms.TexturesControl.replaceToolStripMenuItem") ?? "Replace"; + + pasteImageToolStripMenuItem.Text = selectedCount > 1 ? $"{basePasteText} {selectedCount}" : basePasteText; + removeToolStripMenuItem.Text = selectedCount > 1 ? $"{baseRemoveText} {selectedCount}" : baseRemoveText; + exportImageToolStripMenuItem.Text = selectedCount > 1 ? $"{baseExportText.Replace("...", "")} {selectedCount}..." : baseExportText; + replaceToolStripMenuItem.Text = selectedCount > 1 ? $"{baseReplaceText} {selectedCount}" : baseReplaceText; bool hasLandTile = _selectedTextureId >= 0 && _selectedTextureId < 0x4000 @@ -1083,5 +1106,50 @@ private void SearchByIdToolStripTextBox_KeyUp(object sender, KeyEventArgs e) TextureTileView.FocusIndex = -1; SelectedTextureId = indexValue; } + + // 汉化支持方法 + public void SetLocalization(Func? getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // Menu Items + showFreeSlotsToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.showFreeSlotsToolStripMenuItem") ?? "Show Free Slots"; + findNextFreeSlotToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.findNextFreeSlotToolStripMenuItem") ?? "Find Next Free Slot"; + exportImageToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.exportImageToolStripMenuItem") ?? "Export Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.asBmpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.asJpgToolStripMenuItem") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.asPngToolStripMenuItem") ?? "As Png"; + selectInLandTilesTabToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.selectInLandTilesTabToolStripMenuItem") ?? "Select in Land Tiles tab"; + copyImageToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.copyImageToolStripMenuItem") ?? "Copy Image"; + pasteImageToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.pasteImageToolStripMenuItem") ?? "Paste Image"; + replaceToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.replaceToolStripMenuItem") ?? "Replace"; + replaceStartingFromToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.replaceStartingFromToolStripMenuItem") ?? "Replace starting from.."; + insertAtToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.insertAtToolStripMenuItem") ?? "Insert At.."; + removeToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.removeToolStripMenuItem") ?? "Remove"; + saveToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.saveToolStripMenuItem") ?? "Save"; + + // Toolbar Items + SaveButton.Text = _localizationGetter("Forms.TexturesControl.SaveButton") ?? "Save"; + SaveButton.ToolTipText = _localizationGetter("Forms.TexturesControl.SaveButton") ?? "Save"; + IndexToolStripLabel.Text = _localizationGetter("Forms.TexturesControl.IndexToolStripLabel") ?? "Index:"; + MiscToolStripDropDownButton.Text = _localizationGetter("Forms.TexturesControl.MiscToolStripDropDownButton") ?? "Misc"; + + // Misc Menu + exportAllToolStripMenuItem.Text = _localizationGetter("Forms.TexturesControl.exportAllToolStripMenuItem") ?? "Export All.."; + ExportAllAsBmp.Text = _localizationGetter("Forms.TexturesControl.ExportAllAsBmp") ?? "As Bmp"; + ExportAllAsTiff.Text = _localizationGetter("Forms.TexturesControl.ExportAllAsTiff") ?? "As Tiff"; + ExportAllAsJpeg.Text = _localizationGetter("Forms.TexturesControl.ExportAllAsJpeg") ?? "As Jpg"; + ExportAllAsPng.Text = _localizationGetter("Forms.TexturesControl.ExportAllAsPng") ?? "As Png"; + + // Status Bar + GraphicLabel.Text = _localizationGetter("Forms.TexturesControl.GraphicLabel") ?? "Graphic:"; + } } } diff --git a/UoFiddler.Controls/UserControls/VerdataControl.cs b/UoFiddler.Controls/UserControls/VerdataControl.cs index 65d3f7c3..697588ec 100644 --- a/UoFiddler.Controls/UserControls/VerdataControl.cs +++ b/UoFiddler.Controls/UserControls/VerdataControl.cs @@ -33,9 +33,44 @@ public VerdataControl() private int _currentAnimBody = -1; private bool _suppressAnimEvents; private Entry5D[] _customPatches; + private Func? _localizationGetter; private Entry5D[] ActivePatches => _customPatches ?? Verdata.Patches; + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 工具栏按钮 + buttonReload.Text = _localizationGetter("Forms.VerdataControl.buttonReload") ?? "Reload"; + buttonLoadFile.Text = _localizationGetter("Forms.VerdataControl.buttonLoadFile") ?? "Load File…"; + + // 标签 + labelType.Text = _localizationGetter("Forms.VerdataControl.labelType") ?? "Patch Type:"; + labelCount.Text = _localizationGetter("Forms.VerdataControl.labelCount") ?? "Patches:"; + labelActions.Text = _localizationGetter("Forms.VerdataControl.labelActions") ?? "Actions:"; + labelDir.Text = _localizationGetter("Forms.VerdataControl.labelDir") ?? "Direction:"; + + // 标签页 + if (tabControlMulti.TabCount > 0) + { + tabControlMulti.TabPages[0].Text = _localizationGetter("Forms.VerdataControl.tabPageMultiPreview") ?? "Preview"; + } + if (tabControlMulti.TabCount > 1) + { + tabControlMulti.TabPages[1].Text = _localizationGetter("Forms.VerdataControl.tabPageMultiComponents") ?? "Components"; + } + + // 菜单项 + changeBackgroundColorToolStripMenuItem.Text = _localizationGetter("Forms.VerdataControl.changeBackgroundColor") ?? "Change background color"; + } + // Action names by body type — mirrors AnimationListControl.GetActionNames // [0] Monster (body 0–199): 22 actions // [1] Animal (body 200–399): 13 actions @@ -145,7 +180,8 @@ private void Populate() if (patches.Length == 0) { - labelCount.Text = "No patches found"; + string noPatchesText = _localizationGetter?.Invoke("Forms.VerdataControl.noPatchesFound") ?? "No patches found"; + labelCount.Text = noPatchesText; listBoxType.EndUpdate(); return; } @@ -196,7 +232,8 @@ private void OnTypeSelected(object sender, EventArgs e) listBoxPatches.Items.Add(new PatchItem(patch)); } - labelCount.Text = $"Patches: {group.Patches.Count}"; + string patchesLabel = _localizationGetter?.Invoke("Forms.VerdataControl.labelCount") ?? "Patches:"; + labelCount.Text = $"{patchesLabel} {group.Patches.Count}"; listBoxPatches.EndUpdate(); ClearPreview(); @@ -346,7 +383,9 @@ private void OnClickPlayStop(object sender, EventArgs e) } animatedPictureBox.Animate = !animatedPictureBox.Animate; - buttonPlayStop.Text = animatedPictureBox.Animate ? "Stop" : "Play"; + string stopText = _localizationGetter?.Invoke("Forms.VerdataControl.buttonStop") ?? "Stop"; + string playText = _localizationGetter?.Invoke("Forms.VerdataControl.buttonPlay") ?? "Play"; + buttonPlayStop.Text = animatedPictureBox.Animate ? stopText : playText; } private void OnActionSelected(object sender, EventArgs e) @@ -634,17 +673,24 @@ private void ShowNoPreview(int fileId, string message) private void ClearPreview() { - labelFile.Text = "File:"; - labelIndex.Text = "Index:"; - labelLookup.Text = "Lookup:"; - labelLength.Text = "Length:"; - labelExtra.Text = "Extra:"; + string fileLabel = _localizationGetter?.Invoke("Forms.VerdataControl.labelFile") ?? "File:"; + string indexLabel = _localizationGetter?.Invoke("Forms.VerdataControl.labelIndex") ?? "Index:"; + string lookupLabel = _localizationGetter?.Invoke("Forms.VerdataControl.labelLookup") ?? "Lookup:"; + string lengthLabel = _localizationGetter?.Invoke("Forms.VerdataControl.labelLength") ?? "Length:"; + string extraLabel = _localizationGetter?.Invoke("Forms.VerdataControl.labelExtra") ?? "Extra:"; + string playText = _localizationGetter?.Invoke("Forms.VerdataControl.buttonPlay") ?? "Play"; + + labelFile.Text = fileLabel; + labelIndex.Text = indexLabel; + labelLookup.Text = lookupLabel; + labelLength.Text = lengthLabel; + labelExtra.Text = extraLabel; labelDecoded.Text = string.Empty; _currentAnimBody = -1; animatedPictureBox.Animate = false; animatedPictureBox.Frames = null; panelAnimation.Visible = false; - buttonPlayStop.Text = "Play"; + buttonPlayStop.Text = playText; panelMulti.Visible = false; pictureBoxMulti.Image = null; richTextBoxMultiComponents.Text = string.Empty; @@ -656,9 +702,10 @@ private void ClearPreview() private void OnClickLoadFile(object sender, EventArgs e) { + string dialogTitle = _localizationGetter?.Invoke("Forms.VerdataControl.openVerdataTitle") ?? "Open verdata.mul"; using var dialog = new OpenFileDialog { - Title = "Open verdata.mul", + Title = dialogTitle, Filter = "verdata.mul|verdata.mul|MUL files (*.mul)|*.mul|All files (*.*)|*.*", FilterIndex = 1 }; diff --git a/UoFiddler.Plugin.Compare/ComparePluginBase.cs b/UoFiddler.Plugin.Compare/ComparePluginBase.cs index dbb4f494..39b81442 100644 --- a/UoFiddler.Plugin.Compare/ComparePluginBase.cs +++ b/UoFiddler.Plugin.Compare/ComparePluginBase.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Windows.Forms; using Ultima; using UoFiddler.Controls.Plugin; @@ -64,15 +65,20 @@ public override void Unload() public override void ModifyTabPages(TabControl tabControl) { + // 获取汉化函数 + var localizer = LocalizationGetter; + TabPage page = new TabPage { Tag = tabControl.TabCount + 1, Text = "Compare Items" }; - CompareItemControl compArt = new CompareItemControl + CompareItemControl compArt = new CompareItemControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compArt.SetLocalization(localizer); + } + compArt.Dock = DockStyle.Fill; page.Controls.Add(compArt); tabControl.TabPages.Add(page); @@ -81,10 +87,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare Land" }; - CompareLandControl compLandControl = new CompareLandControl + CompareLandControl compLandControl = new CompareLandControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compLandControl.SetLocalization(localizer); + } + compLandControl.Dock = DockStyle.Fill; page2.Controls.Add(compLandControl); tabControl.TabPages.Add(page2); @@ -93,10 +101,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare CliLocs" }; - CompareCliLocControl compCli = new CompareCliLocControl + CompareCliLocControl compCli = new CompareCliLocControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compCli.SetLocalization(localizer); + } + compCli.Dock = DockStyle.Fill; page3.Controls.Add(compCli); tabControl.TabPages.Add(page3); @@ -105,10 +115,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare Hues" }; - CompareHuesControl compH = new CompareHuesControl + CompareHuesControl compH = new CompareHuesControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compH.SetLocalization(localizer); + } + compH.Dock = DockStyle.Fill; page4.Controls.Add(compH); tabControl.TabPages.Add(page4); @@ -117,10 +129,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare Gumps" }; - CompareGumpControl compG = new CompareGumpControl + CompareGumpControl compG = new CompareGumpControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compG.SetLocalization(localizer); + } + compG.Dock = DockStyle.Fill; page5.Controls.Add(compG); tabControl.TabPages.Add(page5); @@ -129,10 +143,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare Map" }; - CompareMapControl compM = new CompareMapControl + CompareMapControl compM = new CompareMapControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compM.SetLocalization(localizer); + } + compM.Dock = DockStyle.Fill; page6.Controls.Add(compM); tabControl.TabPages.Add(page6); @@ -141,10 +157,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare Texture" }; - CompareTextureControl compTextureControl = new CompareTextureControl + CompareTextureControl compTextureControl = new CompareTextureControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compTextureControl.SetLocalization(localizer); + } + compTextureControl.Dock = DockStyle.Fill; page7.Controls.Add(compTextureControl); tabControl.TabPages.Add(page7); @@ -153,10 +171,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare AnimData" }; - CompareAnimDataControl compAnimData = new CompareAnimDataControl + CompareAnimDataControl compAnimData = new CompareAnimDataControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compAnimData.SetLocalization(localizer); + } + compAnimData.Dock = DockStyle.Fill; page8.Controls.Add(compAnimData); tabControl.TabPages.Add(page8); @@ -165,10 +185,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare RadarCol" }; - CompareRadarColControl compRadarCol = new CompareRadarColControl + CompareRadarColControl compRadarCol = new CompareRadarColControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compRadarCol.SetLocalization(localizer); + } + compRadarCol.Dock = DockStyle.Fill; page9.Controls.Add(compRadarCol); tabControl.TabPages.Add(page9); @@ -177,10 +199,12 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, Text = "Compare TileData" }; - CompareTileDataControl compTileDataControl = new CompareTileDataControl + CompareTileDataControl compTileDataControl = new CompareTileDataControl(); + if (localizer != null) { - Dock = DockStyle.Fill - }; + compTileDataControl.SetLocalization(localizer); + } + compTileDataControl.Dock = DockStyle.Fill; page10.Controls.Add(compTileDataControl); tabControl.TabPages.Add(page10); } diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareAnimDataControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareAnimDataControl.cs index f9a542a4..1b66a4af 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareAnimDataControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareAnimDataControl.cs @@ -12,9 +12,62 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareAnimDataControl : UserControl { + private Func _localizationGetter; + public CompareAnimDataControl() { InitializeComponent(); + + // 在 Load 事件中应用汉化 + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 汉化菜单项 + copyEntryToolStripMenuItem.Text = _localizationGetter("Forms.CompareAnimDataControl.copyEntry") ?? "Copy Entry to left"; + + // 汉化按钮 + buttonLoadSecond.Text = _localizationGetter("Forms.CompareAnimDataControl.buttonLoadSecond") ?? "Load"; + buttonBrowse.Text = _localizationGetter("Forms.CompareAnimDataControl.buttonBrowse") ?? "..."; + buttonCopySelected.Text = _localizationGetter("Forms.CompareAnimDataControl.buttonCopySelected") ?? "Copy Selected"; + buttonCopyAllDiff.Text = _localizationGetter("Forms.CompareAnimDataControl.buttonCopyAllDiff") ?? "Copy All Diff"; + buttonCopyAddedOnly.Text = _localizationGetter("Forms.CompareAnimDataControl.buttonCopyAddedOnly") ?? "Copy Added Only"; + + // 汉化复选框 + checkBoxShowDiff.Text = _localizationGetter("Forms.CompareAnimDataControl.checkBoxShowDiff") ?? "Show only Differences"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareAnimDataControl.chkMultiSelect") ?? "Multi-Select"; + + // 汉化GroupBox + groupBoxOrg.Text = _localizationGetter("Forms.CompareAnimDataControl.groupBoxOrg") ?? "Left (Original)"; + groupBoxSec.Text = _localizationGetter("Forms.CompareAnimDataControl.groupBoxSec") ?? "Right (Second)"; + groupBoxLegend.Text = _localizationGetter("Forms.CompareAnimDataControl.groupBoxLegend") ?? "Legend"; + + // 汉化标签 - Left (Original) + labelOrgFrameCountCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelOrgFrameCountCaption") ?? "Frame Count:"; + labelOrgFrameIntervalCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelOrgFrameIntervalCaption") ?? "Frame Interval:"; + labelOrgFrameStartCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelOrgFrameStartCaption") ?? "Frame Start:"; + labelOrgFrameDataCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelOrgFrameDataCaption") ?? "Frame Data:"; + + // 汉化标签 - Right (Second) + labelSecFrameCountCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelSecFrameCountCaption") ?? "Frame Count:"; + labelSecFrameIntervalCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelSecFrameIntervalCaption") ?? "Frame Interval:"; + labelSecFrameStartCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelSecFrameStartCaption") ?? "Frame Start:"; + labelSecFrameDataCaption.Text = _localizationGetter("Forms.CompareAnimDataControl.labelSecFrameDataCaption") ?? "Frame Data:"; + + // 汉化图例标签 + legendLabelOnlyInOrg.Text = _localizationGetter("Forms.CompareAnimDataControl.legendLabelOnlyInOrg") ?? "Only in original"; + legendLabelOnlyInSecond.Text = _localizationGetter("Forms.CompareAnimDataControl.legendLabelOnlyInSecond") ?? "Only in second"; + legendLabelDifferent.Text = _localizationGetter("Forms.CompareAnimDataControl.legendLabelDifferent") ?? "Different values"; + legendLabelIdentical.Text = _localizationGetter("Forms.CompareAnimDataControl.legendLabelIdentical") ?? "Identical"; } private readonly Dictionary _compare = new Dictionary(); @@ -37,7 +90,7 @@ private void OnLoad(object sender, EventArgs e) int count = tileViewSec.SelectedIndices.Count; copyEntryToolStripMenuItem.Text = tileViewSec.ShowCheckBoxes && count > 1 ? $"Copy {count} Entries to left" - : "Copy Entry to left"; + : (_localizationGetter?.Invoke("Forms.CompareAnimDataControl.copyEntry") ?? "Copy Entry to left"); }; ControlEvents.FilePathChangeEvent += OnFilePathChangeEvent; diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareCliLocControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareCliLocControl.cs index d2b74fbe..beda2e6e 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareCliLocControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareCliLocControl.cs @@ -22,12 +22,35 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareCliLocControl : UserControl { + private Func _localizationGetter; + public CompareCliLocControl() { InitializeComponent(); _source = new BindingSource(); _sortOrder = SortOrder.Ascending; _sortColumn = 0; + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + button1.Text = _localizationGetter("Forms.CompareCliLocControl.button1") ?? "Load"; + button2.Text = _localizationGetter("Forms.CompareCliLocControl.button2") ?? "Load"; + button3.Text = _localizationGetter("Forms.CompareCliLocControl.button3") ?? "..."; + button4.Text = _localizationGetter("Forms.CompareCliLocControl.button4") ?? "..."; + button5.Text = _localizationGetter("Forms.CompareCliLocControl.button5") ?? "Find Next Diff"; + checkBox1.Text = _localizationGetter("Forms.CompareCliLocControl.checkBox1") ?? "Show Only Differences"; + labelDiff1.Text = _localizationGetter("Forms.CompareCliLocControl.labelDiff1") ?? "File 1:"; + labelDiff2.Text = _localizationGetter("Forms.CompareCliLocControl.labelDiff2") ?? "File 2:"; } private static StringList _cliloc1; diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareGumpControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareGumpControl.cs index a66a33f5..94f66d47 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareGumpControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareGumpControl.cs @@ -27,9 +27,30 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareGumpControl : UserControl { + private Func _localizationGetter; + public CompareGumpControl() { InitializeComponent(); + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + checkBox1.Text = _localizationGetter("Forms.CompareGumpControl.checkBox1") ?? "Show only Differences"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareGumpControl.chkMultiSelect") ?? "Multi-Select"; + button1.Text = _localizationGetter("Forms.CompareGumpControl.button1") ?? "Load"; + button2.Text = _localizationGetter("Forms.CompareGumpControl.button2") ?? "Load"; + extractAsToolStripMenuItem.Text = _localizationGetter("Forms.CompareGumpControl.extractAs") ?? "Extract As.."; + copyGump2To1ToolStripMenuItem.Text = _localizationGetter("Forms.CompareGumpControl.copyGump2To1") ?? "Copy Gump to left"; } private readonly Dictionary _compare = new Dictionary(); diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareHuesControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareHuesControl.cs index 5654372c..56860739 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareHuesControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareHuesControl.cs @@ -22,6 +22,8 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareHuesControl : UserControl { + private Func _localizationGetter; + public CompareHuesControl() { InitializeComponent(); @@ -34,6 +36,23 @@ public CompareHuesControl() pictureBox2.MouseWheel += OnMouseWheel; _hue2Loaded = false; + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + button1.Text = _localizationGetter("Forms.CompareHuesControl.button1") ?? "Load"; + button2.Text = _localizationGetter("Forms.CompareHuesControl.button2") ?? "..."; + chkMultiSelect.Text = _localizationGetter("Forms.CompareHuesControl.chkMultiSelect") ?? "Multi-Select"; + applyHue1ToHue2ToolStripMenuItem.Text = _localizationGetter("Forms.CompareHuesControl.applyHue1ToHue2") ?? "Apply Hue to left"; } private const int _itemHeight = 20; diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareItemControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareItemControl.cs index b0bad495..27d46842 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareItemControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareItemControl.cs @@ -27,11 +27,38 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareItemControl : UserControl { + private Func _localizationGetter; + public CompareItemControl() { InitializeComponent(); } + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 汉化菜单项 + extractAsToolStripMenuItem.Text = _localizationGetter("Forms.CompareItemControl.extractAs") ?? "Export Image.."; + tiffToolStripMenuItem.Text = _localizationGetter("Forms.CompareItemControl.asBmp") ?? "As Bmp"; + bmpToolStripMenuItem.Text = _localizationGetter("Forms.CompareItemControl.asTiff") ?? "As Tiff"; + jpgToolStripMenuItem.Text = _localizationGetter("Forms.CompareItemControl.asJpg") ?? "As Jpg"; + pngToolStripMenuItem.Text = _localizationGetter("Forms.CompareItemControl.asPng") ?? "As Png"; + copyItem2To1ToolStripMenuItem.Text = _localizationGetter("Forms.CompareItemControl.copyItem2To1") ?? "Copy Item to left"; + + // 汉化按钮和复选框 + btnCopyAllDiff.Text = _localizationGetter("Forms.CompareItemControl.copyAllDiff") ?? "Copy All Diff"; + button1.Text = _localizationGetter("Forms.CompareItemControl.loadSecond") ?? "Load Second"; + checkBox1.Text = _localizationGetter("Forms.CompareItemControl.showOnlyDifferences") ?? "Show only Differences"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareItemControl.multiSelect") ?? "Multi-Select"; + } + private readonly Dictionary _compare = new Dictionary(); private readonly ImageConverter _ic = new ImageConverter(); private readonly SHA256 _sha256 = SHA256.Create(); diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareLandControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareLandControl.cs index abc4d277..bea47768 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareLandControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareLandControl.cs @@ -27,11 +27,35 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareLandControl : UserControl { + private Func _localizationGetter; + public CompareLandControl() { InitializeComponent(); } + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + exportImageToolStripMenuItem.Text = _localizationGetter("Forms.CompareLandControl.exportImageToolStripMenuItem") ?? "Export Image.."; + asBmpToolStripMenuItem.Text = _localizationGetter("Forms.CompareLandControl.asBmpToolStripMenuItem") ?? "As Bmp"; + asTiffToolStripMenuItem.Text = _localizationGetter("Forms.CompareLandControl.asTiffToolStripMenuItem") ?? "As Tiff"; + asJpgToolStripMenuItem.Text = _localizationGetter("Forms.CompareLandControl.asJpgToolStripMenuItem") ?? "As Jpg"; + asPngToolStripMenuItem.Text = _localizationGetter("Forms.CompareLandControl.asPngToolStripMenuItem") ?? "As Png"; + copyLandTile2To1ToolStripMenuItem.Text = _localizationGetter("Forms.CompareLandControl.copyLandTile2To1ToolStripMenuItem") ?? "Copy Land to left"; + btnCopyAllDiff.Text = _localizationGetter("Forms.CompareLandControl.btnCopyAllDiff") ?? "Copy All Diff"; + button1.Text = _localizationGetter("Forms.CompareLandControl.button1") ?? "Load"; + checkBox1.Text = _localizationGetter("Forms.CompareLandControl.checkBox1") ?? "Show only Differences"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareLandControl.chkMultiSelect") ?? "Multi-Select"; + } + private readonly Dictionary _compare = new Dictionary(); private readonly SHA256 _sha256 = SHA256.Create(); private readonly ImageConverter _ic = new ImageConverter(); diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareMapControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareMapControl.cs index 476567be..f10a71df 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareMapControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareMapControl.cs @@ -25,11 +25,50 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareMapControl : UserControl { + private Func _localizationGetter; + private string _coordsPrefix = "Coords: "; + private string _zoomPrefix = "Zoom: "; + public CompareMapControl() { InitializeComponent(); SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); pictureBox.MouseWheel += OnMouseWheel; + + // 在 Load 事件中应用汉化 + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 保存前缀用于动态更新 + _coordsPrefix = _localizationGetter("Forms.CompareMapControl.coordsPrefix") ?? "Coords: "; + _zoomPrefix = _localizationGetter("Forms.CompareMapControl.zoomPrefix") ?? "Zoom: "; + + toolStripButton2.Text = _localizationGetter("Forms.CompareMapControl.button1") ?? "Load"; + toolStripButton1.Text = _localizationGetter("Forms.CompareMapControl.button2") ?? "..."; + + // 汉化上下文菜单项 + zoomToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.zoomPlus") ?? "Zoom+"; + zoomToolStripMenuItem1.Text = _localizationGetter("Forms.CompareMapControl.zoomMinus") ?? "Zoom-"; + showDifferencesToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.showDifferencesToolStripMenuItem") ?? "Show Differences"; + showMap1ToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.showMap1ToolStripMenuItem") ?? "Show Map1"; + showMap2ToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.showMap2ToolStripMenuItem") ?? "Show Map2"; + feluccaToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.feluccaToolStripMenuItem") ?? "Felucca"; + trammelToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.trammelToolStripMenuItem") ?? "Trammel"; + ilshenarToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.ilshenarToolStripMenuItem") ?? "Ilshenar"; + malasToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.malasToolStripMenuItem") ?? "Malas"; + tokunoToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.tokunoToolStripMenuItem") ?? "Tokuno"; + terMurToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.terMurToolStripMenuItem") ?? "TerMur"; + markDiffToolStripMenuItem.Text = _localizationGetter("Forms.CompareMapControl.markDiffToolStripMenuItem") ?? "Mark Diff"; } private bool _loaded; @@ -71,7 +110,7 @@ private void OnLoad(object sender, EventArgs e) showMap2ToolStripMenuItem.Checked = false; SetScrollBarValues(); ChangeMapNames(); - ZoomLabel.Text = $"Zoom: {_zoom}"; + ZoomLabel.Text = $"{_zoomPrefix}{_zoom}"; Options.LoadedUltimaClass["Map"] = true; Options.LoadedUltimaClass["RadarColor"] = true; @@ -161,7 +200,7 @@ private void OnMouseMove(object sender, MouseEventArgs e) int xDelta = Math.Min(_originalMap.Width, (int)(e.X / _zoom) + Round(hScrollBar.Value)); int yDelta = Math.Min(_originalMap.Height, (int)(e.Y / _zoom) + Round(vScrollBar.Value)); - CoordsLabel.Text = $"Coords: {xDelta},{yDelta}"; + CoordsLabel.Text = $"{_coordsPrefix}{xDelta},{yDelta}"; string diff = string.Empty; @@ -589,7 +628,7 @@ private void DoZoom() { ChangeScrollBar(); - ZoomLabel.Text = $"Zoom: {_zoom}"; + ZoomLabel.Text = $"{_zoomPrefix}{_zoom}"; int x = Math.Max(0, _currentPoint.X - ((int)(pictureBox.ClientSize.Width / _zoom) / 2)); int y = Math.Max(0, _currentPoint.Y - ((int)(pictureBox.ClientSize.Height / _zoom) / 2)); diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareRadarColControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareRadarColControl.cs index 4a0738c8..cc0b2e07 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareRadarColControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareRadarColControl.cs @@ -13,9 +13,53 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareRadarColControl : UserControl { + private Func _localizationGetter; + public CompareRadarColControl() { InitializeComponent(); + + // 在 Load 事件中应用汉化 + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 汉化菜单项 + copyEntry2To1ToolStripMenuItem.Text = _localizationGetter("Forms.CompareRadarColControl.copyEntry2To1") ?? "Copy Entry to left"; + + // 汉化按钮 + buttonLoadSecond.Text = _localizationGetter("Forms.CompareRadarColControl.buttonLoadSecond") ?? "Load"; + buttonBrowse.Text = _localizationGetter("Forms.CompareRadarColControl.buttonBrowse") ?? "..."; + buttonCopySelected.Text = _localizationGetter("Forms.CompareRadarColControl.buttonCopySelected") ?? "Copy Selected"; + buttonCopyAllDiff.Text = _localizationGetter("Forms.CompareRadarColControl.buttonCopyAllDiff") ?? "Copy All Diff"; + + // 汉化复选框 + checkBoxShowDiff.Text = _localizationGetter("Forms.CompareRadarColControl.checkBoxShowDiff") ?? "Show only Differences"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareRadarColControl.chkMultiSelect") ?? "Multi-Select"; + + // 汉化GroupBox + groupBoxOrg.Text = _localizationGetter("Forms.CompareRadarColControl.groupBoxOrg") ?? "Left (Original)"; + groupBoxSec.Text = _localizationGetter("Forms.CompareRadarColControl.groupBoxSec") ?? "Right (Second)"; + groupBoxLegend.Text = _localizationGetter("Forms.CompareRadarColControl.groupBoxLegend") ?? "Legend"; + + // 汉化标签 - Left (Original) + labelOrgColorCaption.Text = _localizationGetter("Forms.CompareRadarColControl.labelOrgColorCaption") ?? "Color:"; + + // 汉化标签 - Right (Second) + labelSecColorCaption.Text = _localizationGetter("Forms.CompareRadarColControl.labelSecColorCaption") ?? "Color:"; + + // 汉化图例标签 + legendLabelDifferent.Text = _localizationGetter("Forms.CompareRadarColControl.legendLabelDifferent") ?? "Different values"; + legendLabelIdentical.Text = _localizationGetter("Forms.CompareRadarColControl.legendLabelIdentical") ?? "Identical"; } private readonly Dictionary _compare = new Dictionary(); @@ -48,7 +92,7 @@ private void OnLoad(object sender, EventArgs e) int count = ActiveSecView.SelectedIndices.Count; copyEntry2To1ToolStripMenuItem.Text = ActiveSecView.ShowCheckBoxes && count > 1 ? $"Copy {count} Entries to left" - : "Copy Entry to left"; + : (_localizationGetter?.Invoke("Forms.CompareRadarColControl.copyEntry2To1") ?? "Copy Entry to left"); }; ControlEvents.FilePathChangeEvent += OnFilePathChangeEvent; diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareTextureControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareTextureControl.cs index fe33ee5d..521c193a 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareTextureControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareTextureControl.cs @@ -27,9 +27,36 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareTextureControl : UserControl { + private Func _localizationGetter; + public CompareTextureControl() { InitializeComponent(); + + // 在 Load 事件中应用汉化 + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + checkBox1.Text = _localizationGetter("Forms.CompareTextureControl.checkBox1") ?? "Show only Differences"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareTextureControl.chkMultiSelect") ?? "Multi-Select"; + button1.Text = _localizationGetter("Forms.CompareTextureControl.button1") ?? "Load"; + button2.Text = _localizationGetter("Forms.CompareTextureControl.button2") ?? "..."; + exportImageToolStripMenuItem.Text = _localizationGetter("Forms.CompareTextureControl.exportImageToolStripMenuItem") ?? "Export Image.."; + copyLandTile2To1ToolStripMenuItem.Text = _localizationGetter("Forms.CompareTextureControl.copyLandTile2To1ToolStripMenuItem") ?? "Copy Texture to left"; + + // 汉化按钮 + FromLeftToRight.Text = _localizationGetter("Forms.CompareTextureControl.fromLeftToRight") ?? "Copy All Diff"; + CopyAddOnly.Text = _localizationGetter("Forms.CompareTextureControl.copyAddOnly") ?? "Copy Added Only"; } private readonly Dictionary _compare = new Dictionary(); @@ -59,7 +86,7 @@ private void OnLoad(object sender, EventArgs e) int count = tileViewSec.SelectedIndices.Count; copyLandTile2To1ToolStripMenuItem.Text = tileViewSec.ShowCheckBoxes && count > 1 ? $"Copy {count} Textures to left" - : "Copy Texture to left"; + : (_localizationGetter?.Invoke("Forms.CompareTextureControl.copyLandTile2To1ToolStripMenuItem") ?? "Copy Texture to left"); }; ControlEvents.FilePathChangeEvent += OnFilePathChangeEvent; diff --git a/UoFiddler.Plugin.Compare/UserControls/CompareTileDataControl.cs b/UoFiddler.Plugin.Compare/UserControls/CompareTileDataControl.cs index 0586d9bf..fff22a78 100644 --- a/UoFiddler.Plugin.Compare/UserControls/CompareTileDataControl.cs +++ b/UoFiddler.Plugin.Compare/UserControls/CompareTileDataControl.cs @@ -13,9 +13,88 @@ namespace UoFiddler.Plugin.Compare.UserControls { public partial class CompareTileDataControl : UserControl { + private Func _localizationGetter; + public CompareTileDataControl() { InitializeComponent(); + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 汉化分组框 + gbLandFields.Text = _localizationGetter("Forms.CompareTileDataControl.gbLandFields") ?? "Land Fields"; + gbItemFields.Text = _localizationGetter("Forms.CompareTileDataControl.gbItemFields") ?? "Item Fields"; + gbIgnoreFlags.Text = _localizationGetter("Forms.CompareTileDataControl.gbIgnoreFlags") ?? "Ignore Flags (checked = excluded from compare)"; + + // 汉化标签 + labelDir.Text = _localizationGetter("Forms.CompareTileDataControl.labelDir") ?? "Directory:"; + + // 汉化按钮 + btnBrowse.Text = _localizationGetter("Forms.CompareTileDataControl.btnBrowse") ?? "Browse..."; + btnLoad.Text = _localizationGetter("Forms.CompareTileDataControl.btnLoad") ?? "Load"; + btnToggleRules.Text = _localizationGetter("Forms.CompareTileDataControl.btnToggleRulesClose") ?? "Rules ▲"; + btnResetRules.Text = _localizationGetter("Forms.CompareTileDataControl.btnResetRules") ?? "Reset to Defaults"; + btnApplyRules.Text = _localizationGetter("Forms.CompareTileDataControl.btnApplyRules") ?? "Apply"; + + // 汉化复选框 - Land Fields + chkLandName.Text = _localizationGetter("Forms.CompareTileDataControl.chkLandName") ?? "Name"; + chkLandTexId.Text = _localizationGetter("Forms.CompareTileDataControl.chkLandTexId") ?? "TextureId"; + chkLandFlags.Text = _localizationGetter("Forms.CompareTileDataControl.chkLandFlags") ?? "Flags"; + + // 汉化复选框 - Item Fields + chkItemName.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemName") ?? "Name"; + chkItemAnim.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemAnim") ?? "Anim"; + chkItemWeight.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemWeight") ?? "Weight"; + chkItemQuality.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemQuality") ?? "Quality"; + chkItemQty.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemQty") ?? "Quantity"; + chkItemHue.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemHue") ?? "Hue"; + chkItemStack.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemStack") ?? "StackOffset"; + chkItemValue.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemValue") ?? "Value"; + chkItemHeight.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemHeight") ?? "Height"; + chkItemMisc.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemMisc") ?? "MiscData"; + chkItemUnk2.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemUnk2") ?? "Unk2"; + chkItemUnk3.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemUnk3") ?? "Unk3"; + chkItemFlags.Text = _localizationGetter("Forms.CompareTileDataControl.chkItemFlags") ?? "Flags"; + + // 汉化其他复选框 + chkShowDiff.Text = _localizationGetter("Forms.CompareTileDataControl.chkShowDiff") ?? "Show Differences Only"; + chkMultiSelect.Text = _localizationGetter("Forms.CompareTileDataControl.chkMultiSelect") ?? "Multi-Select"; + + // 汉化复制按钮 + btnCopyLandSelected.Text = _localizationGetter("Forms.CompareTileDataControl.btnCopyLandSelected") ?? "Copy Selected"; + btnCopyLandAllDiff.Text = _localizationGetter("Forms.CompareTileDataControl.btnCopyLandAllDiff") ?? "Copy All Different"; + btnCopyItemSelected.Text = _localizationGetter("Forms.CompareTileDataControl.btnCopyItemSelected") ?? "Copy Selected"; + btnCopyItemAllDiff.Text = _localizationGetter("Forms.CompareTileDataControl.btnCopyItemAllDiff") ?? "Copy All Different"; + + // 汉化标签页 + tabLand.Text = _localizationGetter("Forms.CompareTileDataControl.tabLand") ?? "Land Tiles"; + tabItem.Text = _localizationGetter("Forms.CompareTileDataControl.tabItem") ?? "Static Tiles"; + + // 汉化详细信息标签 + lblLandName.Text = _localizationGetter("Forms.CompareTileDataControl.labelLandName") ?? "Name"; + lblLandTexId.Text = _localizationGetter("Forms.CompareTileDataControl.labelLandTexId") ?? "Texture ID"; + lblItemName.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemName") ?? "Name"; + lblItemAnim.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemAnim") ?? "Animation"; + lblItemWeight.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemWeight") ?? "Weight"; + lblItemQuality.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemQuality") ?? "Quality"; + lblItemQty.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemQty") ?? "Quantity"; + lblItemHue.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemHue") ?? "Hue"; + lblItemStack.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemStack") ?? "StackOffset"; + lblItemValue.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemValue") ?? "Value"; + lblItemHeight.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemHeight") ?? "Height"; + lblItemMisc.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemMisc") ?? "MiscData"; + lblItemUnk2.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemUnk2") ?? "Unk2"; + lblItemUnk3.Text = _localizationGetter("Forms.CompareTileDataControl.labelItemUnk3") ?? "Unk3"; } // ── State ──────────────────────────────────────────────────────────────── @@ -180,26 +259,26 @@ private List GetCopyTargets(TileViewControl secView) private void SetupDetailPanels() { // Land detail — 2 rows (Name, TextureId); Flags shown per-flag below - SetupDetailRow(tlpLandDetail, 0, lblLandName, "Name", txtLandOrgName, txtLandSecName); - SetupDetailRow(tlpLandDetail, 1, lblLandTexId, "Texture ID", txtLandOrgTexId, txtLandSecTexId); + SetupDetailRow(tlpLandDetail, 0, lblLandName, "labelLandName", txtLandOrgName, txtLandSecName, "Name"); + SetupDetailRow(tlpLandDetail, 1, lblLandTexId, "labelLandTexId", txtLandOrgTexId, txtLandSecTexId, "Texture ID"); SetupFlagsPanel(panelLandDetail, out _landFlagOrgChecks, out _landFlagSecChecks, out _landFlagLabels, out _landFlagTlp); // Push tlpLandDetail to the back so it gets the highest z-order and docks topmost panelLandDetail.Controls.SetChildIndex(tlpLandDetail, panelLandDetail.Controls.Count - 1); // Item detail — 12 rows (no Flags row); Flags shown per-flag below - SetupDetailRow(tlpItemDetail, 0, lblItemName, "Name", txtItemOrgName, txtItemSecName); - SetupDetailRow(tlpItemDetail, 1, lblItemAnim, "Animation", txtItemOrgAnim, txtItemSecAnim); - SetupDetailRow(tlpItemDetail, 2, lblItemWeight, "Weight", txtItemOrgWeight, txtItemSecWeight); - SetupDetailRow(tlpItemDetail, 3, lblItemQuality, "Quality", txtItemOrgQuality, txtItemSecQuality); - SetupDetailRow(tlpItemDetail, 4, lblItemQty, "Quantity", txtItemOrgQty, txtItemSecQty); - SetupDetailRow(tlpItemDetail, 5, lblItemHue, "Hue", txtItemOrgHue, txtItemSecHue); - SetupDetailRow(tlpItemDetail, 6, lblItemStack, "StackOffset", txtItemOrgStack, txtItemSecStack); - SetupDetailRow(tlpItemDetail, 7, lblItemValue, "Value", txtItemOrgValue, txtItemSecValue); - SetupDetailRow(tlpItemDetail, 8, lblItemHeight, "Height", txtItemOrgHeight, txtItemSecHeight); - SetupDetailRow(tlpItemDetail, 9, lblItemMisc, "MiscData", txtItemOrgMisc, txtItemSecMisc); - SetupDetailRow(tlpItemDetail, 10, lblItemUnk2, "Unk2", txtItemOrgUnk2, txtItemSecUnk2); - SetupDetailRow(tlpItemDetail, 11, lblItemUnk3, "Unk3", txtItemOrgUnk3, txtItemSecUnk3); + SetupDetailRow(tlpItemDetail, 0, lblItemName, "labelItemName", txtItemOrgName, txtItemSecName, "Name"); + SetupDetailRow(tlpItemDetail, 1, lblItemAnim, "labelItemAnim", txtItemOrgAnim, txtItemSecAnim, "Animation"); + SetupDetailRow(tlpItemDetail, 2, lblItemWeight, "labelItemWeight", txtItemOrgWeight, txtItemSecWeight, "Weight"); + SetupDetailRow(tlpItemDetail, 3, lblItemQuality, "labelItemQuality", txtItemOrgQuality, txtItemSecQuality, "Quality"); + SetupDetailRow(tlpItemDetail, 4, lblItemQty, "labelItemQty", txtItemOrgQty, txtItemSecQty, "Quantity"); + SetupDetailRow(tlpItemDetail, 5, lblItemHue, "labelItemHue", txtItemOrgHue, txtItemSecHue, "Hue"); + SetupDetailRow(tlpItemDetail, 6, lblItemStack, "labelItemStack", txtItemOrgStack, txtItemSecStack, "StackOffset"); + SetupDetailRow(tlpItemDetail, 7, lblItemValue, "labelItemValue", txtItemOrgValue, txtItemSecValue, "Value"); + SetupDetailRow(tlpItemDetail, 8, lblItemHeight, "labelItemHeight", txtItemOrgHeight, txtItemSecHeight, "Height"); + SetupDetailRow(tlpItemDetail, 9, lblItemMisc, "labelItemMisc", txtItemOrgMisc, txtItemSecMisc, "MiscData"); + SetupDetailRow(tlpItemDetail, 10, lblItemUnk2, "labelItemUnk2", txtItemOrgUnk2, txtItemSecUnk2, "Unk2"); + SetupDetailRow(tlpItemDetail, 11, lblItemUnk3, "labelItemUnk3", txtItemOrgUnk3, txtItemSecUnk3, "Unk3"); SetupFlagsPanel(panelItemDetail, out _itemFlagOrgChecks, out _itemFlagSecChecks, out _itemFlagLabels, out _itemFlagTlp); // Push tlpItemDetail to the back so it gets the highest z-order and docks topmost @@ -220,9 +299,9 @@ private void SetupFlagsPanel( // Header row var header = new Panel { Dock = DockStyle.Top, Height = 22, BackColor = SystemColors.ControlLight }; - var hLbl = new Label { Text = "Flag", Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft, Padding = new Padding(4, 0, 0, 0) }; - var hOrg = new Label { Text = "Org", Width = 44, Dock = DockStyle.Right, TextAlign = ContentAlignment.MiddleCenter }; - var hSec = new Label { Text = "Sec", Width = 44, Dock = DockStyle.Right, TextAlign = ContentAlignment.MiddleCenter }; + var hLbl = new Label { Text = _localizationGetter?.Invoke("Forms.CompareTileDataControl.flagHeaderFlag") ?? "Flag", Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft, Padding = new Padding(4, 0, 0, 0) }; + var hOrg = new Label { Text = _localizationGetter?.Invoke("Forms.CompareTileDataControl.flagHeaderOrg") ?? "Org", Width = 44, Dock = DockStyle.Right, TextAlign = ContentAlignment.MiddleCenter }; + var hSec = new Label { Text = _localizationGetter?.Invoke("Forms.CompareTileDataControl.flagHeaderSec") ?? "Sec", Width = 44, Dock = DockStyle.Right, TextAlign = ContentAlignment.MiddleCenter }; header.Controls.Add(hLbl); // Highest index docks first, so hSec (added last) takes the rightmost slot above secChk (col 2), // and hOrg (added second) ends up to its left above orgChk (col 1). @@ -345,12 +424,13 @@ private void UpdateFlagRows( } } - private static void SetupDetailRow( + private void SetupDetailRow( System.Windows.Forms.TableLayoutPanel tlp, int row, - System.Windows.Forms.Label lbl, string labelText, - System.Windows.Forms.TextBox orgBox, System.Windows.Forms.TextBox secBox) + System.Windows.Forms.Label lbl, string labelKey, + System.Windows.Forms.TextBox orgBox, System.Windows.Forms.TextBox secBox, + string defaultText) { - lbl.Text = labelText; + lbl.Text = _localizationGetter?.Invoke($"Forms.CompareTileDataControl.{labelKey}") ?? defaultText; lbl.Dock = System.Windows.Forms.DockStyle.Fill; lbl.TextAlign = System.Drawing.ContentAlignment.MiddleRight; @@ -1415,9 +1495,18 @@ private void BuildRulesPanel() clbFlags.Items.Clear(); foreach (var (name, _) in MeaningfulFlags) { - clbFlags.Items.Add(name, isChecked: false); // unchecked = not ignored + string localizedName = GetLocalizedFlagName(name); + clbFlags.Items.Add(localizedName, isChecked: false); // unchecked = not ignored } + } + private string GetLocalizedFlagName(string englishName) + { + if (_localizationGetter == null) + return englishName; + + string key = $"Forms.CompareTileDataControl.flag{englishName}"; + return _localizationGetter(key) ?? englishName; } private void WireFieldCheckbox(CheckBox chk, Func getter, Action setter) @@ -1487,7 +1576,17 @@ private void OnClickResetRules(object sender, EventArgs e) private void OnClickToggleRules(object sender, EventArgs e) { panelRules.Visible = !panelRules.Visible; - btnToggleRules.Text = panelRules.Visible ? "Rules ▼" : "Rules ▲"; + + if (_localizationGetter != null) + { + btnToggleRules.Text = panelRules.Visible + ? _localizationGetter("Forms.CompareTileDataControl.btnToggleRulesOpen") ?? "Rules ▼" + : _localizationGetter("Forms.CompareTileDataControl.btnToggleRulesClose") ?? "Rules ▲"; + } + else + { + btnToggleRules.Text = panelRules.Visible ? "Rules ▼" : "Rules ▲"; + } } } } diff --git a/UoFiddler.Plugin.ExamplePlugin/ExamplePluginBase.cs b/UoFiddler.Plugin.ExamplePlugin/ExamplePluginBase.cs index 5ecd39b7..34daba96 100644 --- a/UoFiddler.Plugin.ExamplePlugin/ExamplePluginBase.cs +++ b/UoFiddler.Plugin.ExamplePlugin/ExamplePluginBase.cs @@ -26,6 +26,7 @@ namespace UoFiddler.Plugin.ExamplePlugin public class ExamplePluginBase : PluginBase { private const string _itemDescFileName = "itemdesc.cfg"; + private static Func _localizationGetter; public ExamplePluginBase() { @@ -61,6 +62,12 @@ public override void Initialize() { // make something useful _ = Files.RootDir; + + // 保存 LocalizationGetter 供后续事件使用 + if (PluginBase.LocalizationGetter != null) + { + _localizationGetter = PluginBase.LocalizationGetter; + } } public override void Unload() @@ -74,7 +81,16 @@ public override void ModifyTabPages(TabControl tabControl) Tag = tabControl.TabCount + 1, // at end used for undock/dock feature to define the order Text = "PluginTest" }; - page.Controls.Add(new ExampleControl()); + + ExampleControl control = new ExampleControl(); + + // 应用汉化 + if (PluginBase.LocalizationGetter != null) + { + control.SetLocalization(PluginBase.LocalizationGetter); + } + + page.Controls.Add(control); tabControl.TabPages.Add(page); } @@ -90,7 +106,15 @@ public override void ModifyPluginToolStrip(ToolStripDropDownButton toolStrip) private static void ItemClick(object sender, EventArgs e) { - new ExampleForm().Show(); + ExampleForm form = new ExampleForm(); + + // 应用汉化 + if (PluginBase.LocalizationGetter != null) + { + form.SetLocalization(PluginBase.LocalizationGetter); + } + + form.Show(); } private void EventsModifyItemsControlContextMenuEvent(ContextMenuStrip strip) @@ -99,7 +123,7 @@ private void EventsModifyItemsControlContextMenuEvent(ContextMenuStrip strip) ToolStripMenuItem exportItemDescItem = new ToolStripMenuItem { - Text = "Export selected to itemdesc.cfg" + Text = _localizationGetter?.Invoke("Forms.PluginTestForm.contextmenu_export_selected") ?? "Export selected to itemdesc.cfg" }; exportItemDescItem.Click += ExportToItemDescClicked; strip.Items.Add(exportItemDescItem); @@ -108,7 +132,7 @@ private void EventsModifyItemsControlContextMenuEvent(ContextMenuStrip strip) ToolStripMenuItem exportOffsetItem = new ToolStripMenuItem { - Text = "Export all items to offset.cfg" + Text = _localizationGetter?.Invoke("Forms.PluginTestForm.contextmenu_export_all") ?? "Export all items to offset.cfg" }; exportOffsetItem.Click += ExportToOffsetClicked; strip.Items.Add(exportOffsetItem); @@ -121,13 +145,20 @@ private void ExportToOffsetClicked(object sender, EventArgs e) string fileName = Path.Combine(Options.OutputPath, "offset.cfg"); - string inputMessage = "Do you want to export all items to offset.cfg?\r\n" + string inputMessage = _localizationGetter?.Invoke("Forms.PluginTestForm.messagebox_export_confirm") + ?? ("Do you want to export all items to offset.cfg?\r\n" + "It may take some time (around 10-20 seconds).\r\n\r\n" + "Export will replace existing file located at: " + fileName - + "\r\n\r\nContinue?\r\n"; + + "\r\n\r\nContinue?\r\n"); + + // 如果有本地化文本,需要填入文件名 + if (inputMessage.Contains("{path}")) + { + inputMessage = inputMessage.Replace("{path}", fileName); + } - if (MessageBox.Show(inputMessage, "Export all items to offset.cfg?", MessageBoxButtons.YesNo) == DialogResult.No) + if (MessageBox.Show(inputMessage, _localizationGetter?.Invoke("Forms.PluginTestForm.messagebox_export_title") ?? "Export all items to offset.cfg?", MessageBoxButtons.YesNo) == DialogResult.No) { return; } @@ -153,7 +184,7 @@ private void ExportToOffsetClicked(object sender, EventArgs e) File.WriteAllText(fileName, sb.ToString()); - MessageBox.Show("Done!"); + MessageBox.Show(_localizationGetter?.Invoke("Forms.PluginTestForm.messagebox_export_done") ?? "Done!"); } private void ExportToItemDescClicked(object sender, EventArgs e) diff --git a/UoFiddler.Plugin.ExamplePlugin/Forms/ExampleForm.cs b/UoFiddler.Plugin.ExamplePlugin/Forms/ExampleForm.cs index 7115c020..692db529 100644 --- a/UoFiddler.Plugin.ExamplePlugin/Forms/ExampleForm.cs +++ b/UoFiddler.Plugin.ExamplePlugin/Forms/ExampleForm.cs @@ -9,6 +9,7 @@ * ***************************************************************************/ +using System; using System.Windows.Forms; namespace UoFiddler.Plugin.ExamplePlugin.Forms @@ -18,6 +19,27 @@ public partial class ExampleForm : Form public ExampleForm() { InitializeComponent(); + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + + // 如果窗体已显示,立即应用汉化 + if (this.Visible) + { + ApplyLocalization(); + } + } + + private Func _localizationGetter; + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.PluginTestForm.Title") ?? "Example"; } } } diff --git a/UoFiddler.Plugin.ExamplePlugin/UserControls/ExampleControl.cs b/UoFiddler.Plugin.ExamplePlugin/UserControls/ExampleControl.cs index d48d53fb..c7b7d176 100644 --- a/UoFiddler.Plugin.ExamplePlugin/UserControls/ExampleControl.cs +++ b/UoFiddler.Plugin.ExamplePlugin/UserControls/ExampleControl.cs @@ -20,6 +20,27 @@ public partial class ExampleControl : UserControl public ExampleControl() { InitializeComponent(); + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + + // 如果控件已显示,立即应用汉化 + if (this.Visible) + { + ApplyLocalization(); + } + } + + private Func _localizationGetter; + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + button1.Text = _localizationGetter("Forms.PluginTestForm.SayHello") ?? "Say Hello"; } private void OnClickSayHello(object sender, EventArgs e) @@ -30,7 +51,9 @@ private void OnClickSayHello(object sender, EventArgs e) } else { - MessageBox.Show("UO client is not running so I will say hello here. Hi!"); + string message = _localizationGetter?.Invoke("Forms.PluginTestForm.messagebox_hello") + ?? "UO client is not running so I will say hello here. Hi!"; + MessageBox.Show(message); } } } diff --git a/UoFiddler.Plugin.MassImport/Forms/MassImportForm.cs b/UoFiddler.Plugin.MassImport/Forms/MassImportForm.cs index 4176accf..01588383 100644 --- a/UoFiddler.Plugin.MassImport/Forms/MassImportForm.cs +++ b/UoFiddler.Plugin.MassImport/Forms/MassImportForm.cs @@ -30,6 +30,47 @@ public MassImportForm() Icon = Options.GetFiddlerIcon(); _importList = new List(); + + // 在窗体加载完成后应用汉化 + this.Shown += (s, e) => + { + if (_localizationGetter != null) + { + ApplyLocalization(); + } + }; + } + + private Func _localizationGetter; + + /// + /// 设置汉化回调 + /// + /// 返回汉化字符串的委托,参数为键值 + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + // 如果窗体已显示,立即应用汉化 + if (this.Visible) + { + ApplyLocalization(); + } + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) + { + return; + } + + this.Text = _localizationGetter("Forms.MassImportForm.Title") ?? "批量导入"; + groupBox1.Text = _localizationGetter("Forms.MassImportForm.groupBox1") ?? "XML"; + button1.Text = _localizationGetter("Forms.MassImportForm.button1") ?? "创建默认 XML"; + button2.Text = _localizationGetter("Forms.MassImportForm.button2") ?? "加载 XML"; + groupBox2.Text = _localizationGetter("Forms.MassImportForm.groupBox2") ?? "输出"; + button3.Text = _localizationGetter("Forms.MassImportForm.button3") ?? "开始"; + checkBoxDirectSave.Text = _localizationGetter("Forms.MassImportForm.checkBoxDirectSave") ?? "直接保存"; } private void DefaultXMLOnClick(object sender, EventArgs e) diff --git a/UoFiddler.Plugin.MassImport/MassImportPluginBase.cs b/UoFiddler.Plugin.MassImport/MassImportPluginBase.cs index da3d4b09..401d7c8e 100644 --- a/UoFiddler.Plugin.MassImport/MassImportPluginBase.cs +++ b/UoFiddler.Plugin.MassImport/MassImportPluginBase.cs @@ -76,6 +76,13 @@ private void ToolStripClick(object sender, EventArgs e) { TopMost = true }; + + // 应用汉化 + if (PluginBase.LocalizationGetter != null) + { + _importForm.SetLocalization(PluginBase.LocalizationGetter); + } + _importForm.Show(); } } diff --git a/UoFiddler.Plugin.MultiEditor/UserControls/MultiEditorControl.cs b/UoFiddler.Plugin.MultiEditor/UserControls/MultiEditorControl.cs index 720b9fd0..f17ab992 100644 --- a/UoFiddler.Plugin.MultiEditor/UserControls/MultiEditorControl.cs +++ b/UoFiddler.Plugin.MultiEditor/UserControls/MultiEditorControl.cs @@ -106,7 +106,66 @@ public MultiEditorControl() pictureBoxDrawTiles.MouseWheel += PictureBoxDrawTiles_OnMouseWheel; pictureBoxMulti.MouseWheel += PictureBoxMulti_OnMouseWheel; + + // 在加载完成时应用汉化 + this.Load += (s, e) => ApplyLocalization(); + } + /// + /// 应用汉化(内部方法) + /// + private void ApplyLocalization() + { + var getter = UoFiddler.Controls.Plugin.PluginBase.LocalizationGetter; + if (getter == null) return; + + tileTab.Text = getter("Forms.MultiEditorControl.tileTab") ?? "Tiles"; + designTab.Text = getter("Forms.MultiEditorControl.designTab") ?? "Design"; + importTab.Text = getter("Forms.MultiEditorControl.importTab") ?? "Import"; + Save.Text = getter("Forms.MultiEditorControl.Save") ?? "Save"; + groupBox1.Text = getter("Forms.MultiEditorControl.groupBox1") ?? "Multi Size"; + groupBox2.Text = getter("Forms.MultiEditorControl.groupBox2") ?? "Save"; + groupBox3.Text = getter("Forms.MultiEditorControl.groupBox3") ?? "Selected Tile X,Y,Z"; + groupBox4.Text = getter("Forms.MultiEditorControl.groupBox4") ?? "Export"; + BTN_CreateBlank.Text = getter("Forms.MultiEditorControl.BTN_CreateBlank") ?? "Create Blank"; + BTN_Resize.Text = getter("Forms.MultiEditorControl.BTN_Resize") ?? "Resize Multi"; + BTN_Save.Text = getter("Forms.MultiEditorControl.BTN_Save") ?? "Save to ID"; + BTN_Export_XML.Text = getter("Forms.MultiEditorControl.BTN_Export_XML") ?? ".xml"; + BTN_Export_CSV.Text = getter("Forms.MultiEditorControl.BTN_Export_CSV") ?? ".csv"; + BTN_Export_WSC.Text = getter("Forms.MultiEditorControl.BTN_Export_WSC") ?? ".wsc"; + BTN_Export_UOA.Text = getter("Forms.MultiEditorControl.BTN_Export_UOA") ?? ".uoa.txt"; + BTN_Export_UOX3.Text = getter("Forms.MultiEditorControl.BTN_Export_UOX3") ?? ".uox3"; + BTN_Export_Txt.Text = getter("Forms.MultiEditorControl.BTN_Export_Txt") ?? ".txt"; + brushSizeLabel.Text = getter("Forms.MultiEditorControl.brushSizeLabel") ?? "Brush size:"; + RB_Brush_S.Text = getter("Forms.MultiEditorControl.RB_Brush_S") ?? "S"; + RB_Brush_M.Text = getter("Forms.MultiEditorControl.RB_Brush_M") ?? "M"; + RB_Brush_L.Text = getter("Forms.MultiEditorControl.RB_Brush_L") ?? "L"; + DynamiccheckBox.Text = getter("Forms.MultiEditorControl.DynamiccheckBox") ?? "Invisible"; + toolStripDropDownButton1.Text = getter("Forms.MultiEditorControl.toolStripDropDownButton1") ?? "Misc"; + UndoItems.Text = getter("Forms.MultiEditorControl.UndoItems") ?? "Undo"; + showWalkablesToolStripMenuItem.Text = getter("Forms.MultiEditorControl.showWalkablesToolStripMenuItem") ?? "Show Walkable tiles"; + showDoubleSurfaceMenuItem.Text = getter("Forms.MultiEditorControl.showDoubleSurfaceMenuItem") ?? "Show double surface"; + removeAllTransToolStripMenuItem.Text = getter("Forms.MultiEditorControl.removeAllTransToolStripMenuItem") ?? "Reset transparent tiles"; + DrawTileLabel.Text = getter("Forms.MultiEditorControl.DrawTileLabel") ?? "Draw ID:"; + SelectedTileLabel.Text = getter("Forms.MultiEditorControl.SelectedTileLabel") ?? "ID:"; + toolStripBtnHelp.Text = getter("Forms.MultiEditorControl.toolStripBtnHelp") ?? "Help"; + + toolTip1.SetToolTip(textBox_Export, getter("Forms.MultiEditorControl.tooltip_textBox_Export") ?? "FileName"); + toolTip1.SetToolTip(BTN_RectFill, getter("Forms.MultiEditorControl.tooltip_BTN_RectFill") ?? "Rectangle Fill [B]"); + toolTip1.SetToolTip(BTN_LineDraw, getter("Forms.MultiEditorControl.tooltip_BTN_LineDraw") ?? "Line Draw [L]"); + toolTip1.SetToolTip(RB_Brush_S, getter("Forms.MultiEditorControl.tooltip_RB_Brush_S") ?? "Small brush (1x1)"); + toolTip1.SetToolTip(RB_Brush_M, getter("Forms.MultiEditorControl.tooltip_RB_Brush_M") ?? "Medium brush (3x3)"); + toolTip1.SetToolTip(RB_Brush_L, getter("Forms.MultiEditorControl.tooltip_RB_Brush_L") ?? "Large brush (5x5)"); + toolTip1.SetToolTip(BTN_Trans, getter("Forms.MultiEditorControl.tooltip_BTN_Trans") ?? "Switch Transparent [T]"); + toolTip1.SetToolTip(BTN_Pipette, getter("Forms.MultiEditorControl.tooltip_BTN_Pipette") ?? "Pick A Tile [P]"); + toolTip1.SetToolTip(BTN_Floor, getter("Forms.MultiEditorControl.tooltip_BTN_Floor") ?? "Draw Virtual Floor [F]"); + toolTip1.SetToolTip(BTN_Z, getter("Forms.MultiEditorControl.tooltip_BTN_Z") ?? "Apply Z Level [E]"); + toolTip1.SetToolTip(BTN_Remove, getter("Forms.MultiEditorControl.tooltip_BTN_Remove") ?? "Remove A Tile [R]"); + toolTip1.SetToolTip(BTN_Draw, getter("Forms.MultiEditorControl.tooltip_BTN_Draw") ?? "Draw A Tile [D]"); + toolTip1.SetToolTip(BTN_Select, getter("Forms.MultiEditorControl.tooltip_BTN_Select") ?? "Select A Tile [S]"); + toolTip1.SetToolTip(collapsibleSplitter1, getter("Forms.MultiEditorControl.tooltip_collapsibleSplitter1") ?? "Selected Tile Panel"); + toolTip1.SetToolTip(MaxHeightTrackBar, getter("Forms.MultiEditorControl.tooltip_MaxHeightTrackBar") ?? "Max Height Displayed"); + toolTip1.SetToolTip(pictureBoxMinimap, getter("Forms.MultiEditorControl.tooltip_pictureBoxMinimap") ?? "Minimap — click to pan"); } /// @@ -697,6 +756,7 @@ private void ApplyPreviewBackgroundColor() private void OnLoad(object sender, EventArgs e) { + ApplyLocalization(); ApplyDarkModeIfNeeded(); Options.LoadedUltimaClass["TileData"] = true; diff --git a/UoFiddler.Plugin.SendItem/Forms/SendItemOptionsForm.cs b/UoFiddler.Plugin.SendItem/Forms/SendItemOptionsForm.cs index 1fdbe78d..7897ec97 100644 --- a/UoFiddler.Plugin.SendItem/Forms/SendItemOptionsForm.cs +++ b/UoFiddler.Plugin.SendItem/Forms/SendItemOptionsForm.cs @@ -10,6 +10,8 @@ ***************************************************************************/ using System; +using System.Linq; +using System.Reflection; using System.Windows.Forms; using UoFiddler.Controls.Classes; @@ -24,6 +26,43 @@ public SendItemOptionsForm() cmdtext.Text = SendItemPluginBase.Cmd; argstext.Text = SendItemPluginBase.CmdArg; SendOnClick.Checked = SendItemPluginBase.OverrideClick; + + // 在 Load 事件中应用汉化 + this.Load += (s, e) => ApplyLocalization(); + } + + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + + // 如果窗体已显示,立即应用汉化 + if (this.Visible) + { + ApplyLocalization(); + } + } + + private Func _localizationGetter; + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.SendItemOptionsForm.Title") ?? "SendItem Options"; + groupBox1.Text = _localizationGetter("Forms.SendItemOptionsForm.groupBox1") ?? "Send Item"; + label7.Text = _localizationGetter("Forms.SendItemOptionsForm.label7") ?? "Cmd"; + label8.Text = _localizationGetter("Forms.SendItemOptionsForm.label8") ?? "Args"; + SendOnClick.Text = _localizationGetter("Forms.SendItemOptionsForm.SendOnClick") ?? "Send on DoubleClick"; + button1.Text = _localizationGetter("Forms.SendItemOptionsForm.button1") ?? "Save"; + + // ToolTips + var toolTip1 = this.components?.Components.OfType().FirstOrDefault(); + if (toolTip1 != null) + { + toolTip1.SetToolTip(label7, _localizationGetter("Forms.SendItemOptionsForm.tooltip_label7") ?? "Defines the cmd to send Client for selected Item"); + toolTip1.SetToolTip(label8, _localizationGetter("Forms.SendItemOptionsForm.tooltip_label8") ?? "{1} = Selected item ObjType"); + toolTip1.SetToolTip(SendOnClick, _localizationGetter("Forms.SendItemOptionsForm.tooltip_SendOnClick") ?? "Overrides DoubleClick"); + } } private void OnClickSave(object sender, EventArgs e) diff --git a/UoFiddler.Plugin.SendItem/SendItem.cs b/UoFiddler.Plugin.SendItem/SendItem.cs index 50dd0f52..19b0b3fb 100644 --- a/UoFiddler.Plugin.SendItem/SendItem.cs +++ b/UoFiddler.Plugin.SendItem/SendItem.cs @@ -131,12 +131,24 @@ private void ChangeOverrideClick(bool value, bool init) private static void ToolStripClick(object sender, EventArgs e) { - new SendItemOptionsForm().Show(); + SendItemOptionsForm form = new SendItemOptionsForm(); + + // 应用汉化 + if (PluginBase.LocalizationGetter != null) + { + form.SetLocalization(PluginBase.LocalizationGetter); + } + + form.Show(); } private void EventsModifyItemsControlContextMenuEvent(ContextMenuStrip strip) { - ToolStripMenuItem item = new ToolStripMenuItem { Text = "Send Item to Client" }; + // 类型 13:插件动态菜单项汉化 + string menuText = PluginBase.LocalizationGetter?.Invoke("Forms.ItemsControl.ContextMenu.sendItemToClientToolStripMenuItem") + ?? "Send Item to Client"; + + ToolStripMenuItem item = new ToolStripMenuItem { Text = menuText }; item.Click += ItemShowContextClicked; strip.Items.Add(item); } diff --git a/UoFiddler.Plugin.UopPacker/UopPacker.cs b/UoFiddler.Plugin.UopPacker/UopPacker.cs index 978a11ec..23934452 100644 --- a/UoFiddler.Plugin.UopPacker/UopPacker.cs +++ b/UoFiddler.Plugin.UopPacker/UopPacker.cs @@ -68,7 +68,16 @@ public override void ModifyTabPages(TabControl tabControl) Tag = 0, // at end used for undock/dock feature to define the order Text = "UOP Packer" }; - page.Controls.Add(new UopPackerControl(Version)); + + var control = new UopPackerControl(Version); + + // 应用汉化 + if (LocalizationGetter != null) + { + control.SetLocalization(LocalizationGetter); + } + + page.Controls.Add(control); tabControl.TabPages.Add(page); } } diff --git a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs index 111783be..d76775e4 100644 --- a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs +++ b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.cs @@ -26,6 +26,7 @@ namespace UoFiddler.Plugin.UopPacker.UserControls public partial class UopPackerControl : UserControl { private readonly LegacyMulFileConverter _conv; + private Func _localizationGetter; private UopPackerControl() { @@ -60,9 +61,17 @@ private UopPackerControl() ApplyDarkModeIfNeeded(); + // 在 Load 事件中应用汉化 + this.Load += (s, e) => ApplyLocalization(); + Dock = DockStyle.Fill; } + public void SetLocalization(Func localizationGetter) + { + _localizationGetter = localizationGetter; + } + private void ApplyDarkModeIfNeeded() { if (!Options.DarkMode) @@ -97,6 +106,92 @@ private void ApplyDarkModeIfNeeded() statustext.ForeColor = Color.OrangeRed; } + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + // 更新所有 UI 控件的文本 + label1.Text = _localizationGetter("Forms.UopPackerControl.label1") ?? "Convert from MUL to UOP"; + label2.Text = _localizationGetter("Forms.UopPackerControl.label2") ?? "Input MUL"; + label3.Text = _localizationGetter("Forms.UopPackerControl.label3") ?? "Input IDX"; + labelHousingBin.Text = _localizationGetter("Forms.UopPackerControl.labelHousingBin") ?? "housing.bin"; + label7.Text = _localizationGetter("Forms.UopPackerControl.label7") ?? "Type"; + label4.Text = _localizationGetter("Forms.UopPackerControl.label4") ?? "Map Index"; + label5.Text = _localizationGetter("Forms.UopPackerControl.label5") ?? "Type"; + label6.Text = _localizationGetter("Forms.UopPackerControl.label6") ?? "Map Index"; + label8.Text = _localizationGetter("Forms.UopPackerControl.label8") ?? "Input UOP"; + label9.Text = _localizationGetter("Forms.UopPackerControl.label9") ?? "Type"; + label11.Text = _localizationGetter("Forms.UopPackerControl.label11") ?? "Output files folder"; + label12.Text = _localizationGetter("Forms.UopPackerControl.label12") ?? "Convert from UOP to MUL"; + + // 调整输入框标签 - 右移以对齐其他标签 + label13.Text = _localizationGetter("Forms.UopPackerControl.label13") ?? "Input folder"; + label14.Text = _localizationGetter("Forms.UopPackerControl.label14") ?? "Output folder"; + + // 调整标签宽度和位置以适应中文 + label13.AutoSize = false; + label13.Width = 70; + label14.AutoSize = false; + label14.Width = 70; + + // 相应调整输入框位置 + inputfolder.Location = new System.Drawing.Point(80, inputfolder.Location.Y); + outputfolder.Location = new System.Drawing.Point(80, outputfolder.Location.Y); + SelectFolderButton.Location = new System.Drawing.Point(328, SelectFolderButton.Location.Y); + SelectOutputFolderButton.Location = new System.Drawing.Point(328, SelectOutputFolderButton.Location.Y); + + // 调整提取/打包单选按钮位置以对齐输入框(X=80) + extract.Location = new System.Drawing.Point(80, extract.Location.Y); + pack.Location = new System.Drawing.Point(80, pack.Location.Y); + + multouop.Text = _localizationGetter("Forms.UopPackerControl.multouop") ?? "Convert"; + uoptomul.Text = _localizationGetter("Forms.UopPackerControl.uoptomul") ?? "Convert"; + inmulbtn.Text = _localizationGetter("Forms.UopPackerControl.inmulbtn") ?? "..."; + inidxbtn.Text = _localizationGetter("Forms.UopPackerControl.inidxbtn") ?? "..."; + inhousingbinbtn.Text = _localizationGetter("Forms.UopPackerControl.inhousingbinbtn") ?? "..."; + outuopfolderbtn.Text = _localizationGetter("Forms.UopPackerControl.outuopfolderbtn") ?? "..."; + inuopbtn.Text = _localizationGetter("Forms.UopPackerControl.inuopbtn") ?? "..."; + outfolderbtn.Text = _localizationGetter("Forms.UopPackerControl.outfolderbtn") ?? "..."; + + // TabControl 和 RadioButtons + ExtractAllFilesTabPage.Text = _localizationGetter("Forms.UopPackerControl.ExtractAllFilesTabPage") ?? "Extract/Pack All Files"; + ExtractSingleFileTabPage.Text = _localizationGetter("Forms.UopPackerControl.ExtractSingleFileTabPage") ?? "Extract/Pack Single File"; + extract.Text = _localizationGetter("Forms.UopPackerControl.extract") ?? "Extract"; + pack.Text = _localizationGetter("Forms.UopPackerControl.pack") ?? "Pack"; + packAllGumpCompressionLabel.Text = _localizationGetter("Forms.UopPackerControl.packAllGumpCompressionLabel") ?? "Gump Compression"; + packAllHousingBinLabel.Text = _localizationGetter("Forms.UopPackerControl.packAllHousingBinLabel") ?? "housing.bin"; + + SelectFolderButton.Text = _localizationGetter("Forms.UopPackerControl.SelectFolderButton") ?? "Select Folder"; + SelectOutputFolderButton.Text = _localizationGetter("Forms.UopPackerControl.SelectOutputFolderButton") ?? "Select Folder"; + StartFolderButton.Text = _localizationGetter("Forms.UopPackerControl.StartFolderButton") ?? "Start"; + compressionLabel.Text = "(?)"; // 保持为帮助图标 + compressionInputLabel.Text = _localizationGetter("Forms.UopPackerControl.compressionInputLabel") ?? "Compression"; + + label10.Text = _localizationGetter("Forms.UopPackerControl.label10") ?? "Progress"; + + toolStripStatusLabel2.Text = _localizationGetter("Forms.UopPackerControl.toolStripStatusLabel2") ?? "Extracting/Packing may take some time."; + + // PlaceholderText 汉化 + inmul.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_inmul") ?? "art.mul"; + inidx.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_inidx") ?? "artidx.mul"; + inhousingbin.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_inhousingbin") ?? "housing.bin file path"; + outuopfolder.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_outuopfolder") ?? "folder where .uop will be written"; + inuop.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_inuop") ?? "MultiCollection.uop"; + outfolder.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_outfolder") ?? "(blank = same as input folder)"; + inputfolder.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_inputfolder") ?? "folder where files will be extracted"; + outputfolder.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_outputfolder") ?? "folder where files will be packed"; + packAllHousingBin.PlaceholderText = _localizationGetter("Forms.UopPackerControl.placeholder_packAllHousingBin") ?? "optional, defaults to folder/housing.bin"; + + // ToolTips + compressionTip.SetToolTip(packAllGumpCompressionBox, _localizationGetter("Forms.UopPackerControl.tooltip_compression") ?? "Compression type for gump files"); + compressionTip.SetToolTip(compressionBox, _localizationGetter("Forms.UopPackerControl.tooltip_compression_single") ?? "Compression type for the file"); + } + + private string GetLocalizedMessage(string key, string defaultValue = "") + { + return _localizationGetter?.Invoke(key) ?? defaultValue; + } + private void OnPackAllModeChanged(object sender, EventArgs e) => UpdatePackAllCompressionVisibility(); private void UpdatePackAllCompressionVisibility() diff --git a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx index fb0eeb27..16ab2b3c 100644 --- a/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx +++ b/UoFiddler.Plugin.UopPacker/UserControls/UopPackerControl.resx @@ -127,19 +127,19 @@ 17, 17 - Pick the compression that matches the original UOP for the file type: - Art / Map / Sound: None - Gumpart: None on older clients, Mythic on newer clients (New Legacy 7.0.104) + 选择与该文件类型的原始 UOP 匹配的压缩方式: + 美术/地图/音效: 无 + Gumpart: 旧客户端无压缩,较新客户端使用 Mythic(新遗留版 7.0.104) MultiCollection: Zlib -Picking the wrong option produces a UOP that is much larger than the original or unreadable by the client. +选择错误的选项会导致 UOP 文件大小远大于原始文件或无法被客户端读取。 - Pick the compression that matches the original UOP for the file type: - Art / Map / Sound: None - Gumpart: None on older clients, Mythic on newer clients (New Legacy 7.0.104) + 选择与该文件类型的原始 UOP 匹配的压缩方式: + 美术/地图/音效: 无 + Gumpart: 旧客户端无压缩,较新客户端使用 Mythic(新遗留版 7.0.104) MultiCollection: Zlib -Picking the wrong option produces a UOP that is much larger than the original or unreadable by the client. +选择错误的选项会导致 UOP 文件大小远大于原始文件或无法被客户端读取。 261, 17 diff --git a/UoFiddler/FiddlerAppContext.cs b/UoFiddler/FiddlerAppContext.cs index 0a8309dd..553f4a11 100644 --- a/UoFiddler/FiddlerAppContext.cs +++ b/UoFiddler/FiddlerAppContext.cs @@ -17,6 +17,7 @@ using UoFiddler.Controls.Classes; using UoFiddler.Controls.UserControls; using UoFiddler.Forms; +using UoFiddler.Localization; using Ultima.Helpers; namespace UoFiddler @@ -47,6 +48,7 @@ internal FiddlerAppContext(IServiceProvider services) _logger.LogInformation("Starting loading profile form..."); var profile = new LoadProfileForm(services.GetRequiredService>()) { TopMost = true }; + profile.SetLocalization(LocalizationService.GetString); var profileResult = profile.ShowDialog(); if (profileResult == DialogResult.Cancel) { @@ -80,9 +82,15 @@ internal FiddlerAppContext(IServiceProvider services) } _logger.LogInformation("Starting main form..."); + + // 确保 Options.ProfileName 有有效值 + string profileDisplayName = string.IsNullOrEmpty(Options.ProfileName) + ? "Default" + : Options.ProfileName.Replace("Options_", "").Replace(".xml", ""); + MainForm = new MainForm(services.GetRequiredService>()) { - Text = $"{Application.ProductName} (Profile: {Options.ProfileName.Replace("Options_", "").Replace(".xml", "")})" + Text = $"{Application.ProductName} (Profile: {profileDisplayName})" }; MainForm.Show(); } diff --git a/UoFiddler/Forms/AboutBoxForm.cs b/UoFiddler/Forms/AboutBoxForm.cs index 43545cbd..4da97295 100644 --- a/UoFiddler/Forms/AboutBoxForm.cs +++ b/UoFiddler/Forms/AboutBoxForm.cs @@ -14,6 +14,8 @@ using System.Windows.Forms; using UoFiddler.Classes; using UoFiddler.Controls.Classes; +using UoFiddler.Controls.Plugin; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -23,11 +25,30 @@ public AboutBoxForm() { InitializeComponent(); Icon = Options.GetFiddlerIcon(); + + // 汉化窗口 + LocalizationService.LocalizeForm(this); + + // 在 Load 事件中汉化更新日志 + this.Load += (s, e) => ApplyChangelogLocalization(); checkBoxCheckOnStart.Checked = FiddlerOptions.UpdateCheckOnStart; checkBoxFormState.Checked = FiddlerOptions.StoreFormState; } + private void ApplyChangelogLocalization() + { + var getter = PluginBase.LocalizationGetter; + if (getter == null) return; + + // 获取汉化的更新日志(如果有) + string changelog = getter("Forms.AboutBoxForm.changelog"); + if (!string.IsNullOrEmpty(changelog)) + { + richTextBox1.Text = changelog; + } + } + private void OnChangeCheck(object sender, EventArgs e) { FiddlerOptions.UpdateCheckOnStart = checkBoxCheckOnStart.Checked; diff --git a/UoFiddler/Forms/ExceptionForm.cs b/UoFiddler/Forms/ExceptionForm.cs index dc9a3ec6..76f9a087 100644 --- a/UoFiddler/Forms/ExceptionForm.cs +++ b/UoFiddler/Forms/ExceptionForm.cs @@ -12,6 +12,7 @@ using System; using System.Windows.Forms; using UoFiddler.Controls.Classes; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -21,6 +22,9 @@ public ExceptionForm(Exception err) { InitializeComponent(); Icon = Options.GetFiddlerIcon(); + + // 汉化窗口 + LocalizationService.LocalizeForm(this); richTextBox.Text = err.InnerException != null ? $"{err.InnerException.Message}\n{err.InnerException.GetType()}" diff --git a/UoFiddler/Forms/LoadProfileForm.cs b/UoFiddler/Forms/LoadProfileForm.cs index 38e74810..3e4194ea 100644 --- a/UoFiddler/Forms/LoadProfileForm.cs +++ b/UoFiddler/Forms/LoadProfileForm.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.Logging; using UoFiddler.Controls.Classes; using UoFiddler.Classes; +using UoFiddler.Localization; using Ultima.Helpers; namespace UoFiddler.Forms @@ -23,6 +24,7 @@ public partial class LoadProfileForm : Form { private readonly string[] _profiles; private readonly ILogger _log; + private Func? _localizationGetter; public LoadProfileForm() : this(AppLog.For()) { } @@ -44,6 +46,28 @@ public LoadProfileForm(ILogger logger) comboBoxLoad.SelectedIndex = 0; comboBoxBasedOn.SelectedIndex = 0; + + // 汉化 getter 在 SetLocalization() 时才初始化 + // 这样确保 LocalizationService 已完全初始化 + } + + public void SetLocalization(Func getLocalized) + { + if (getLocalized == null) return; + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + private void ApplyLocalization() + { + if (_localizationGetter == null) return; + + this.Text = _localizationGetter("Forms.LoadProfileForm.Title") ?? "Choose Profile"; + groupBox1.Text = _localizationGetter("Forms.LoadProfileForm.groupBox1") ?? "Load"; + groupBox2.Text = _localizationGetter("Forms.LoadProfileForm.groupBox2") ?? "Create"; + button1.Text = _localizationGetter("Forms.LoadProfileForm.button1") ?? "Load Profile"; + button2.Text = _localizationGetter("Forms.LoadProfileForm.button2") ?? "Create Profile"; + label1.Text = _localizationGetter("Forms.LoadProfileForm.label1") ?? "Based On"; } private static string[] GetProfiles() @@ -83,7 +107,11 @@ private void OnClickCreate(object sender, EventArgs e) { if (string.IsNullOrEmpty(textBoxCreate.Text)) { - MessageBox.Show("Profile name is missing", "New Profile", MessageBoxButtons.OK, MessageBoxIcon.Error); + string message = _localizationGetter?.Invoke("Forms.LoadProfileForm.Messages.ProfileNameMissing") ?? "Profile name is missing"; + string title = _localizationGetter?.Invoke("Forms.LoadProfileForm.Messages.NewProfileTitle") ?? "New Profile"; + MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error); + // 不关闭对话框,让用户重新输入 + textBoxCreate.Focus(); return; } diff --git a/UoFiddler/Forms/MainForm.cs b/UoFiddler/Forms/MainForm.cs index 4f228c7c..cc0211ad 100644 --- a/UoFiddler/Forms/MainForm.cs +++ b/UoFiddler/Forms/MainForm.cs @@ -20,8 +20,10 @@ using Ultima.Helpers; using UoFiddler.Classes; using UoFiddler.Controls.Classes; +using UoFiddler.Controls.UserControls; using UoFiddler.Properties; using UoFiddler.Controls.Plugin; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -52,6 +54,43 @@ public MainForm(ILogger logger) _log = logger; InitializeComponent(); + // 应用本地化 + LocalizationService.LocalizeForm(this); + + // 汉化右键菜单项 + unDockToolStripMenuItem.Text = LocalizationService.GetString("Forms.MainForm.ContextMenu.UnDock") ?? "UnDock"; + + // 为主标签页控件调用本地化(GumpControl等需要手动调用SetLocalization) + gumpsControl.SetLocalization(key => LocalizationService.GetString(key)); + hueControl.SetLocalization(key => LocalizationService.GetString(key)); + multisControl.SetLocalization(key => LocalizationService.GetString(key)); + animationsControl.SetLocalization(key => LocalizationService.GetString(key)); + landTilesControl.SetLocalization(key => LocalizationService.GetString(key)); + textureControl.SetLocalization(key => LocalizationService.GetString(key)); + soundControl.SetLocalization(key => LocalizationService.GetString(key)); + fontsControl.SetLocalization(key => LocalizationService.GetString(key)); + clilocControl.SetLocalization(key => LocalizationService.GetString(key)); + mapControl.SetLocalization(key => LocalizationService.GetString(key)); + lightControl.SetLocalization(key => LocalizationService.GetString(key)); + speechControl.SetLocalization(key => LocalizationService.GetString(key)); + skillsControl.SetLocalization(key => LocalizationService.GetString(key)); + animdataControl.SetLocalization(key => LocalizationService.GetString(key)); + multimapControl.SetLocalization(key => LocalizationService.GetString(key)); + landTilesControl.SetLocalization(key => LocalizationService.GetString(key)); + verdataControl.SetLocalization(key => LocalizationService.GetString(key)); + radarColControl.SetLocalization(key => LocalizationService.GetString(key)); + skillGroupControl.SetLocalization(key => LocalizationService.GetString(key)); + dressControl.SetLocalization(key => LocalizationService.GetString(key)); + + // 为 ItemsControl 设置汉化(通过遍历 TabPage 找到) + foreach (TabPage tab in TabPanel.TabPages) + { + if (tab.Controls[0] is ItemsControl itemsCtrl) + { + itemsCtrl.SetLocalization(key => LocalizationService.GetString(key)); + } + } + darkModeMenuItem.Checked = AppSettings.DarkMode; if (AppSettings.DarkMode) @@ -90,6 +129,9 @@ public MainForm(ILogger logger) LoadExternToolStripMenu(); GlobalPlugins.Plugins.FindPlugins($@"{Application.StartupPath}\plugins"); + // 为所有插件设置汉化 getter + UoFiddler.Controls.Plugin.PluginBase.LocalizationGetter = (key) => LocalizationService.GetString(key); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); @@ -102,7 +144,16 @@ public MainForm(ILogger logger) } plug.Instance.ModifyPluginToolStrip(toolStripDropDownButtonPlugins); + + // 汉化插件菜单项 + LocalizePluginMenuItems(toolStripDropDownButtonPlugins, plug.Instance.Name); + + // 记录添加标签页前的数量 + int tabCountBefore = TabPanel.TabPages.Count; plug.Instance.ModifyTabPages(TabPanel); + + // 汉化插件标签页标题(处理所有新添加的标签页) + LocalizePluginTabPages(TabPanel, tabCountBefore, plug.Instance.Name); } foreach (TabPage tab in TabPanel.TabPages) @@ -248,7 +299,7 @@ public void LoadExternToolStripMenu() ToolStripMenuItem item = new ToolStripMenuItem { - Text = "Manage.." + Text = LocalizationService.GetString("Forms.MainForm.Menus.manageToolStripMenuItem") ?? "Manage.." }; item.Click += OnClickToolManage; @@ -273,7 +324,7 @@ public void LoadExternToolStripMenu() ToolStripMenuItem sub = new ToolStripMenuItem { - Text = "Start", + Text = LocalizationService.GetString("Forms.MainForm.Menus.Start") ?? "Start", Tag = -1 }; @@ -438,6 +489,52 @@ private void OnClickUnDock(object sender, EventArgs e) TabPanel.TabPages.Remove(TabPanel.SelectedTab); } + /// + /// 汉化插件添加的菜单项(仅处理最后一个菜单项,即刚添加的) + /// + private void LocalizePluginMenuItems(ToolStripDropDownButton toolStrip, string pluginName) + { + // 仅处理最后添加的菜单项 + if (toolStrip.DropDownItems.Count > 0) + { + ToolStripItem lastItem = toolStrip.DropDownItems[toolStrip.DropDownItems.Count - 1]; + + if (lastItem is ToolStripMenuItem menuItem && !string.IsNullOrEmpty(menuItem.Text)) + { + // 尝试使用菜单项文本作为键查找汉化 + string lookupKey = menuItem.Text; + string? localizedText = LocalizationService.GetString($"Forms.MainForm.Plugins.{lookupKey}"); + if (!string.IsNullOrEmpty(localizedText)) + { + menuItem.Text = localizedText; + } + } + } + } + + /// + /// 汉化插件添加的标签页标题 + /// + private void LocalizePluginTabPages(TabControl tabControl, int startIndex, string pluginName) + { + // 处理从 startIndex 开始的所有新添加的标签页 + for (int i = startIndex; i < tabControl.TabPages.Count; i++) + { + TabPage tab = tabControl.TabPages[i]; + + if (!string.IsNullOrEmpty(tab.Text)) + { + // 尝试使用标签页文本作为键查找汉化 + string lookupKey = tab.Text; + string? localizedText = LocalizationService.GetString($"Forms.MainForm.PluginTabPages.{lookupKey}"); + if (!string.IsNullOrEmpty(localizedText)) + { + tab.Text = localizedText; + } + } + } + } + /// /// ReDocks closed Form /// @@ -490,9 +587,12 @@ private void OnClosing(object sender, FormClosingEventArgs e) if (files.Length > 0) { + var title = LocalizationService.GetString("Forms.MainForm.ExitConfirmDialog.Title") ?? "UnSaved Changes"; + var message = LocalizationService.GetString("Forms.MainForm.ExitConfirmDialog.Message") ?? "Are you sure you want to quit?\r\n\r\nThere are unsaved files:\r\n{0}"; + message = string.Format(message, files); + DialogResult result = - MessageBox.Show($"Are you sure you want to quit?\r\n\r\nThere are unsaved files:\r\n{files}", - "UnSaved Changes", MessageBoxButtons.YesNo, MessageBoxIcon.Question, + MessageBox.Show(message, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); if (result == DialogResult.No) { diff --git a/UoFiddler/Forms/ManagePluginsForm.cs b/UoFiddler/Forms/ManagePluginsForm.cs index 7738ddea..04667a39 100644 --- a/UoFiddler/Forms/ManagePluginsForm.cs +++ b/UoFiddler/Forms/ManagePluginsForm.cs @@ -10,11 +10,13 @@ ***************************************************************************/ using System; +using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using Microsoft.Extensions.Logging; using UoFiddler.Controls.Classes; using UoFiddler.Controls.Plugin; +using UoFiddler.Localization; using Ultima.Helpers; namespace UoFiddler.Forms @@ -22,11 +24,17 @@ namespace UoFiddler.Forms public partial class ManagePluginsForm : Form { private static readonly ILogger _log = AppLog.For(typeof(ManagePluginsForm)); + + // 保存显示名称到原始名称的映射 + private readonly Dictionary _displayNameToOriginalName = new Dictionary(); public ManagePluginsForm() { InitializeComponent(); Icon = Options.GetFiddlerIcon(); + + // 汉化窗口标题 + LocalizationService.LocalizeForm(this); foreach (AvailablePlugin plugin in GlobalPlugins.Plugins.AvailablePlugins) { @@ -37,7 +45,23 @@ public ManagePluginsForm() plugin.CreateInstance(); loaded = false; } - checkedListBox1.Items.Add(plugin.Instance.Name, loaded); + + // 获取原始名称 + string originalName = plugin.Instance.Name; + + // 尝试获取汉化名称 + string displayName = originalName; + string? localizedName = LocalizationService.GetString($"Forms.ManagePluginsForm.Plugins.{originalName}"); + if (!string.IsNullOrEmpty(localizedName)) + { + displayName = localizedName; + } + + // 保存映射关系 + _displayNameToOriginalName[displayName] = originalName; + + // 添加到列表(使用汉化后的显示名称) + checkedListBox1.Items.Add(displayName, loaded); } } @@ -49,7 +73,17 @@ private void OnSelect(object sender, EventArgs e) return; } - AvailablePlugin selPlugin = GlobalPlugins.Plugins.AvailablePlugins.Find(checkedListBox1.SelectedItem.ToString()); + // 获取显示的名称 + string displayName = checkedListBox1.SelectedItem.ToString(); + + // 转换为原始名称 + string originalName = displayName; + if (_displayNameToOriginalName.ContainsKey(displayName)) + { + originalName = _displayNameToOriginalName[displayName]; + } + + AvailablePlugin selPlugin = GlobalPlugins.Plugins.AvailablePlugins.Find(originalName); if (selPlugin == null) { return; @@ -74,9 +108,23 @@ private void OnClosing(object sender, FormClosingEventArgs e) { foreach (AvailablePlugin plug in GlobalPlugins.Plugins.AvailablePlugins) { + // 找到该插件在列表中的显示名称 + string originalName = plug.Instance.Name; + string displayName = originalName; + + // 查找对应的显示名称 + foreach (var kvp in _displayNameToOriginalName) + { + if (kvp.Value == originalName) + { + displayName = kvp.Key; + break; + } + } + if (Options.PluginsToLoad?.Contains(plug.Type.ToString()) == false) { - if (checkedListBox1.CheckedItems.Contains(plug.Instance.Name)) + if (checkedListBox1.CheckedItems.Contains(displayName)) { _log.LogInformation("ManagePlugins - adding plugin to profile: {Plugin}", plug.Type.ToString()); Options.PluginsToLoad.Add(plug.Type.ToString()); @@ -86,7 +134,7 @@ private void OnClosing(object sender, FormClosingEventArgs e) } else { - if (!checkedListBox1.CheckedItems.Contains(plug.Instance.Name)) + if (!checkedListBox1.CheckedItems.Contains(displayName)) { _log.LogInformation("ManagePlugins - removing plugin from profile: {Plugin}", plug.Type.ToString()); Options.PluginsToLoad.Remove(plug.Type.ToString()); diff --git a/UoFiddler/Forms/ManageToolsForm.cs b/UoFiddler/Forms/ManageToolsForm.cs index 79e7ec1d..9f4f7b8f 100644 --- a/UoFiddler/Forms/ManageToolsForm.cs +++ b/UoFiddler/Forms/ManageToolsForm.cs @@ -15,6 +15,7 @@ using System.Windows.Forms; using UoFiddler.Classes; using UoFiddler.Controls.Classes; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -29,6 +30,9 @@ public ManageToolsForm(Action loadExternToolStripMenuAction) Icon = Options.GetFiddlerIcon(); _loadExternToolStripMenuAction = loadExternToolStripMenuAction; + + // 应用中文汉化 + LocalizationService.LocalizeForm(this); } private void OnLoad(object sender, EventArgs e) diff --git a/UoFiddler/Forms/OptionsForm.cs b/UoFiddler/Forms/OptionsForm.cs index 44a68aa2..53cbac5d 100644 --- a/UoFiddler/Forms/OptionsForm.cs +++ b/UoFiddler/Forms/OptionsForm.cs @@ -17,6 +17,8 @@ using Ultima; using UoFiddler.Classes; using UoFiddler.Controls.Classes; +using UoFiddler.Controls.Plugin; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -97,6 +99,42 @@ public OptionsForm(Action updateAllTileViewsAction, } comboBoxSaveFormat.SelectedIndex = ClientFileSaveFormats.IndexOf(Options.SaveFormat); + + // 应用中文汉化 + LocalizationService.LocalizeForm(this); + + // 在 Load 事件中应用 ToolTip 汉化 + this.Load += (s, e) => ApplyLocalization(); + } + + private void ApplyLocalization() + { + var getter = PluginBase.LocalizationGetter; + if (getter == null) return; + + // ToolTips + toolTip1.SetToolTip(numericUpDownItemSizeHeight, getter("Forms.OptionsForm.tooltip_numericUpDownItemSizeHeight") ?? "Height"); + toolTip1.SetToolTip(checkBoxItemClip, getter("Forms.OptionsForm.tooltip_checkBoxItemClip") ?? "ItemClip images in items tab shrinked or clipped"); + toolTip1.SetToolTip(label1, getter("Forms.OptionsForm.tooltip_label1") ?? "ItemSize controls the size of images in items tab"); + toolTip1.SetToolTip(numericUpDownItemSizeWidth, getter("Forms.OptionsForm.tooltip_numericUpDownItemSizeWidth") ?? "Width"); + toolTip1.SetToolTip(checkBoxCacheData, getter("Forms.OptionsForm.tooltip_checkBoxCacheData") ?? "CacheData should mul entries be cached for faster load"); + toolTip1.SetToolTip(checkBoxNewMapSize, getter("Forms.OptionsForm.tooltip_checkBoxNewMapSize") ?? "NewMapSize Felucca/Trammel width 7168?"); + toolTip1.SetToolTip(checkBoxuseDiff, getter("Forms.OptionsForm.tooltip_checkBoxuseDiff") ?? "Should map diff files be used"); + toolTip1.SetToolTip(checkBoxPolSoundIdOffset, getter("Forms.OptionsForm.tooltip_checkBoxPolSoundIdOffset") ?? "UO Sounds are indexed from 0 but POL uses +1 offset.\r\nWhen this option is checked Sounds tab will display sound indexes starting from 1 instead of 0.\r\nThis option also affects the export sound list."); + toolTip1.SetToolTip(label2, getter("Forms.OptionsForm.tooltip_label2") ?? "Defines the map name"); + toolTip1.SetToolTip(label3, getter("Forms.OptionsForm.tooltip_label3") ?? "Defines the map name"); + toolTip1.SetToolTip(label4, getter("Forms.OptionsForm.tooltip_label4") ?? "Defines the map name"); + toolTip1.SetToolTip(label5, getter("Forms.OptionsForm.tooltip_label5") ?? "Defines the map name"); + toolTip1.SetToolTip(label6, getter("Forms.OptionsForm.tooltip_label6") ?? "Defines the map name"); + toolTip1.SetToolTip(label7, getter("Forms.OptionsForm.tooltip_label7") ?? "Defines the cmd to send Client to loc"); + toolTip1.SetToolTip(label8, getter("Forms.OptionsForm.tooltip_label8") ?? "{1} = x, {2} = y, {3} = z, {4} = mapid, {5} = mapname"); + toolTip1.SetToolTip(label9, getter("Forms.OptionsForm.tooltip_label9") ?? "Defines the map name"); + toolTip1.SetToolTip(FocusColorLabel, getter("Forms.OptionsForm.tooltip_FocusColorLabel") ?? "ItemSize controls the size of images in items tab"); + toolTip1.SetToolTip(SelectedColorLabel, getter("Forms.OptionsForm.tooltip_SelectedColorLabel") ?? "ItemSize controls the size of images in items tab"); + toolTip1.SetToolTip(radioExportFilenameHex, getter("Forms.OptionsForm.tooltip_radioExportFilenameHex") ?? "Exported filenames embed the ID in hexadecimal form."); + toolTip1.SetToolTip(radioExportFilenameDec, getter("Forms.OptionsForm.tooltip_radioExportFilenameDec") ?? "Exported filenames embed the ID in decimal form."); + toolTip1.SetToolTip(checkBoxExportFilenameDecPad, getter("Forms.OptionsForm.tooltip_checkBoxExportFilenameDecPad") ?? "When using decimal format, pad the ID with leading zeros so files sort correctly."); + toolTip1.SetToolTip(comboBoxSaveFormat, getter("Forms.OptionsForm.tooltip_comboBoxSaveFormat") ?? "Which container a save writes for the files the client ships as either. Affects art, gumpart, sound, multis and maps; everything else has only one format."); } private void OnClickApply(object sender, EventArgs e) diff --git a/UoFiddler/Forms/PathSettingsForm.cs b/UoFiddler/Forms/PathSettingsForm.cs index c321b11c..36ce9b4c 100644 --- a/UoFiddler/Forms/PathSettingsForm.cs +++ b/UoFiddler/Forms/PathSettingsForm.cs @@ -17,6 +17,7 @@ using Ultima; using Ultima.Helpers; using UoFiddler.Controls.Classes; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -29,6 +30,9 @@ public PathSettingsForm() pgPaths.SelectedObject = new DictionaryPropertyGridAdapter(Files.MulPath); tsTbRootPath.Text = Files.RootDir; + + // 应用中文汉化 + LocalizationService.LocalizeForm(this); } private void ReloadPath(object sender, EventArgs e) diff --git a/UoFiddler/Localization/Dictionaries/zh-CN.json b/UoFiddler/Localization/Dictionaries/zh-CN.json new file mode 100644 index 00000000..7a68e9f6 --- /dev/null +++ b/UoFiddler/Localization/Dictionaries/zh-CN.json @@ -0,0 +1,2486 @@ +{ + "version": "4.24.0", + "language": "zh-CN", + "description": "UOFiddler 简体中文汉化包", + "Forms": { + "MainForm": { + "Title": "UOFiddler", + "ExitConfirmDialog": { + "Title": "未保存更改", + "Message": "确定要退出吗?\r\n\r\n存在未保存的文件:\r\n{0}" + }, + "Tabs": { + "StartTab": "开始", + "MultisTab": "多重组件", + "AnimationTab": "动画", + "ItemsTab": "物品", + "LandTilesTab": "地形图块", + "TextureTab": "纹理", + "GumpsTab": "Gump界面", + "SoundsTab": "音效", + "HuesTab": "色调", + "FontsTab": "字体", + "ClilocTab": "本地化字符串", + "MapTab": "地图", + "LightTab": "光照", + "SpeechTab": "语音", + "SkillsTab": "技能", + "AnimDataTab": "动画数据", + "MultiMapTab": "多重地图/位面", + "DressTab": "装扮", + "TileDataTab": "图块数据", + "RadarColTab": "雷达颜色", + "SkillGrpTab": "技能组", + "VerdataTab": "Verdata 补丁" + }, + "Menus": { + "SettingsMenu": "设置", + "toolStripDropDownButtonView": "视图", + "ExternToolsDropDown": "外部工具", + "toolStripDropDownButtonPlugins": "插件", + "toolStripDropDownButtonHelp": "帮助", + "AlwaysOnTopMenuitem": "始终置顶", + "darkModeMenuItem": "暗黑模式", + "optionsToolStripMenuItem": "选项...", + "pathSettingsMenuItem": "路径设置...", + "reloadFilesMenuItem": "重新加载文件", + "manageToolStripMenuItem": "管理...", + "Start": "开始", + "ToggleViewStart": "开始", + "ToggleViewMulti": "多重组件", + "ToggleViewAnimations": "动画", + "ToggleViewItems": "物品", + "ToggleViewLandTiles": "地形图块", + "ToggleViewTexture": "纹理", + "ToggleViewGumps": "Gump界面", + "ToggleViewSounds": "音效", + "ToggleViewHue": "色调", + "ToggleViewFonts": "字体", + "ToggleViewCliloc": "本地化字符串", + "ToggleViewMap": "地图", + "ToggleViewLight": "光照", + "ToggleViewSpeech": "语音", + "ToggleViewSkills": "技能", + "ToggleViewAnimData": "动画数据", + "ToggleViewMultiMap": "多重地图/位面", + "ToggleViewDress": "装扮", + "ToggleViewTileData": "图块数据", + "ToggleViewRadarColor": "雷达颜色", + "ToggleViewSkillGrp": "技能组", + "ToggleViewVerdata": "Verdata 补丁", + "manageToolStripMenuItem1": "管理...", + "toolStripMenuItemHelp": "帮助", + "toolStripMenuItemAbout": "关于" + }, + "Plugins": { + "Mass Import": "批量导入", + "Send Item": "发送物品", + "PluginTest": "插件测试" + }, + "PluginTabPages": { + "Multi Editor": "Multi 编辑器", + "PluginTest": "插件测试", + "UOP Packer": "UOP打包器", + "Compare Items": "对比物品", + "Compare Land": "对比地形", + "Compare CliLocs": "对比CliLoc", + "Compare Hues": "对比色调", + "Compare Gumps": "对比Gump", + "Compare Map": "对比地图", + "Compare Texture": "对比纹理", + "Compare AnimData": "对比动画数据", + "Compare RadarCol": "对比雷达颜色", + "Compare TileData": "对比图块数据" + }, + "ContextMenu": { + "UnDock": "取消停靠" + } + }, + "LoadProfileForm": { + "Title": "选择配置文件", + "groupBox1": "加载", + "groupBox2": "创建", + "button1": "加载配置文件", + "button2": "创建配置文件", + "label1": "基于", + "Messages": { + "ProfileNameMissing": "配置文件名称不能为空", + "NewProfileTitle": "新配置文件" + } + }, + "ClientFileSaveCommand": { + "SaveConfirmTitle": "保存", + "SaveAnywayQuestion": "仍然保存吗?", + "SaveFailedTitle": "保存失败" + }, + "FileSavedDialog": { + "Title": "已保存", + "SaveSuccess": "保存成功", + "OpenFolder": "打开文件夹", + "OK": "确定", + "MultiSavedMessage": "多重组件保存成功", + "AllMultisSavedMessage": "所有多重组件保存成功", + "AllUopMultisSavedMessage": "所有 UOP 多重组件保存成功" + }, + "OptionsForm": { + "Title": "选项", + "groupBox1": "物品标签页", + "groupBox2": "杂项", + "groupBox3": "地图", + "groupBox4": "输出", + "ColorsGroupBox": "图块视图设置", + "ExportFilenamesGroupBox": "导出文件名", + "label1": "物品大小", + "label2": "map0 名称", + "label3": "map1 名称", + "label4": "map2 名称", + "label5": "map3 名称", + "label6": "map4 名称", + "label7": "命令", + "label8": "参数", + "label9": "map5 名称", + "label10": "输出路径", + "labelSaveFormat": "保存格式", + "FocusColorLabel": "图块焦点", + "SelectedColorLabel": "图块选择", + "PreviewBackgroundColorLabel": "背景颜色", + "checkBoxCacheData": "缓存数据", + "checkBoxNewMapSize": "新地图大小", + "checkBoxuseDiff": "使用地图差异文件", + "checkBoxPolSoundIdOffset": "偏移音效ID 1 (POL模拟器)", + "checkBoxItemClip": "物品裁剪", + "checkboxRemoveTileBorder": "移除图块边框", + "checkBoxExportFilenameDecPad": "十进制填充到5位数字", + "radioExportFilenameHex": "十六进制 (例如 Item 0x00FF.png)", + "radioExportFilenameDec": "十进制 (例如 Item 00255.png)", + "buttonApply": "应用", + "button2": "...", + "RestoreDefaultsButton": "恢复默认值", + "buttonClose": "关闭", + "tooltip_numericUpDownItemSizeHeight": "高度", + "tooltip_checkBoxItemClip": "物品裁剪:物品标签页中的物品图像是缩小还是裁剪", + "tooltip_label1": "物品大小控制物品标签页中图像的大小", + "tooltip_numericUpDownItemSizeWidth": "宽度", + "tooltip_checkBoxCacheData": "缓存数据:MUL条目是否应缓存以加快加载速度", + "tooltip_checkBoxNewMapSize": "新地图大小:Felucca/Trammel 宽度为 7168?", + "tooltip_checkBoxuseDiff": "是否应使用地图差异文件", + "tooltip_checkBoxPolSoundIdOffset": "UO 音效从 0 开始索引,但 POL 使用 +1 偏移。勾选此选项后,\"音效\"标签页将显示从 1 开始的音效索引,而不是从 0 开始。此选项也影响导出音效列表。", + "tooltip_label2": "定义地图名称", + "tooltip_label3": "定义地图名称", + "tooltip_label4": "定义地图名称", + "tooltip_label5": "定义地图名称", + "tooltip_label6": "定义地图名称", + "tooltip_label7": "定义发送客户端到位置的命令", + "tooltip_label8": "{1} = x, {2} = y, {3} = z, {4} = mapid, {5} = mapname", + "tooltip_label9": "定义地图名称", + "tooltip_FocusColorLabel": "物品大小控制物品标签页中图像的大小", + "tooltip_SelectedColorLabel": "物品大小控制物品标签页中图像的大小", + "tooltip_radioExportFilenameHex": "导出的文件名以十六进制形式嵌入 ID。", + "tooltip_radioExportFilenameDec": "导出的文件名以十进制形式嵌入 ID。", + "tooltip_checkBoxExportFilenameDecPad": "使用十进制格式时,用前导零填充 ID,以便文件正确排序。", + "tooltip_comboBoxSaveFormat": "选择保存文件时使用的容器格式。影响 art、gumpart、sound、multis 和 maps;其他所有内容只有一种格式。" + }, + "PathSettingsForm": { + "Title": "路径设置", + "tsBtnReloadPaths": "重新加载路径", + "tsBtnSetPathManual": "手动设置路径" + }, + "ManagePluginsForm": { + "Title": "管理插件(需要重新启动)", + "Plugins": { + "MassImportPlugin": "批量导入插件", + "ComparePlugin": "对比插件", + "MultiEditorPlugin": "Multi 编辑器插件", + "SendItemPlugin": "发送物品插件", + "UOP Packer": "UOP打包器", + "PluginTest": "插件测试" + } + }, + "ManageToolsForm": { + "Title": "管理工具", + "groupBox3": "工具列表", + "groupBox4": "参数列表", + "groupBox1": "工具", + "groupBox2": "参数", + "label1": "参数列表", + "button1": "...", + "button2": "添加", + "button3": "删除", + "button4": "删除", + "button5": "保存", + "button6": "添加", + "button7": "保存" + }, + "AboutBoxForm": { + "Title": "关于", + "checkBoxCheckOnStart": "启动时检查更新", + "button1": "检查更新", + "linkLabel1": "访问主页", + "label1": "项目作者和管理员", + "label3": "特殊贡献者", + "label5": "特别感谢", + "checkBoxFormState": "保存窗口状态" + }, + "ExceptionForm": { + "Title": "异常", + "OK": "确定" + }, + "MassImportForm": { + "Title": "批量导入", + "groupBox1": "XML", + "button1": "创建默认 XML", + "button2": "加载 XML", + "groupBox2": "输出", + "button3": "开始", + "checkBoxDirectSave": "直接保存" + }, + "MultiEditorControl": { + "tileTab": "图块", + "designTab": "设计", + "importTab": "导入", + "Save": "保存", + "groupBox1": "Multi 尺寸", + "groupBox2": "保存", + "groupBox3": "选中图块 X,Y,Z", + "groupBox4": "导出", + "BTN_CreateBlank": "创建空白", + "BTN_Resize": "调整 Multi", + "BTN_Save": "保存到 ID", + "BTN_Export_XML": ".xml", + "BTN_Export_CSV": ".csv", + "BTN_Export_WSC": ".wsc", + "BTN_Export_UOA": ".uoa.txt", + "BTN_Export_UOX3": ".uox3", + "BTN_Export_Txt": ".txt", + "brushSizeLabel": "笔刷大小:", + "RB_Brush_S": "S", + "RB_Brush_M": "M", + "RB_Brush_L": "L", + "DynamiccheckBox": "隐形", + "toolStripDropDownButton1": "其他", + "UndoItems": "撤销", + "showWalkablesToolStripMenuItem": "显示可行走图块", + "showDoubleSurfaceMenuItem": "显示双层表面", + "removeAllTransToolStripMenuItem": "重置透明图块", + "DrawTileLabel": "绘制 ID:", + "SelectedTileLabel": "ID:", + "toolStripBtnHelp": "帮助", + "tooltip_textBox_Export": "文件名", + "tooltip_BTN_RectFill": "矩形填充 [B]", + "tooltip_BTN_LineDraw": "直线绘制 [L]", + "tooltip_RB_Brush_S": "小笔刷 (1x1)", + "tooltip_RB_Brush_M": "中笔刷 (3x3)", + "tooltip_RB_Brush_L": "大笔刷 (5x5)", + "tooltip_BTN_Trans": "切换透明 [T]", + "tooltip_BTN_Pipette": "拾取图块 [P]", + "tooltip_BTN_Floor": "绘制虚拟地面 [F]", + "tooltip_BTN_Z": "应用 Z 级别 [E]", + "tooltip_BTN_Remove": "删除图块 [R]", + "tooltip_BTN_Draw": "绘制图块 [D]", + "tooltip_BTN_Select": "选择图块 [S]", + "tooltip_collapsibleSplitter1": "选中图块面板", + "tooltip_MaxHeightTrackBar": "最大显示高度", + "tooltip_pictureBoxMinimap": "小地图 — 点击平移", + "tooltip_toolStripBtnZoom": "缩放 — 点击或 Ctrl+0 重置为 100%" + }, + "SendItemOptionsForm": { + "Title": "发送物品选项", + "groupBox1": "发送物品", + "label7": "命令", + "label8": "参数", + "SendOnClick": "双击时发送", + "button1": "保存", + "tooltip_label7": "定义发送给客户端的命令,用于选定的物品", + "tooltip_label8": "{1} = 选定物品的对象类型", + "tooltip_SendOnClick": "覆盖双击行为" + }, + "PluginTestForm": { + "Title": "示例", + "ExampleForm": "示例", + "SayHello": "打招呼", + "PluginTest": "插件测试", + "messagebox_hello": "UO 客户端未运行,所以我在这里打个招呼。你好!", + "contextmenu_export_selected": "导出选定项到 itemdesc.cfg", + "contextmenu_export_all": "导出所有物品到 offset.cfg", + "messagebox_export_confirm": "你想要导出所有物品到 offset.cfg 吗?\r\n可能需要一些时间(大约 10-20 秒)。\r\n\r\n导出将替换位于以下位置的现有文件:{path}\r\n\r\n继续吗?\r\n", + "messagebox_export_title": "导出所有物品到 offset.cfg?", + "messagebox_export_done": "完成!" + }, + "UopPackerControl": { + "label1": "从 MUL 转换为 UOP", + "label2": "输入 MUL", + "label3": "输入 IDX", + "labelHousingBin": "housing.bin", + "label7": "类型", + "label4": "地图索引", + "label5": "类型", + "label6": "地图索引", + "label8": "输入 UOP", + "label9": "类型", + "label11": "输出文件夹", + "label12": "从 UOP 转换为 MUL", + "label10": "进度", + "label13": "输入文件夹", + "label14": "输出文件夹", + "multouop": "转换", + "uoptomul": "转换", + "inmulbtn": "...", + "inidxbtn": "...", + "inhousingbinbtn": "...", + "outuopfolderbtn": "...", + "inuopbtn": "...", + "outfolderbtn": "...", + "ExtractAllFilesTabPage": "提取/打包所有文件", + "ExtractSingleFileTabPage": "提取/打包单个文件", + "extract": "提取", + "pack": "打包", + "packAllGumpCompressionLabel": "Gump 压缩", + "packAllHousingBinLabel": "housing.bin", + "SelectFolderButton": "选择文件夹", + "SelectOutputFolderButton": "选择文件夹", + "StartFolderButton": "开始", + "compressionLabel": "压缩方式", + "compressionInputLabel": "压缩方式", + "toolStripStatusLabel2": "提取/打包可能需要一些时间。", + "tooltip_compression": "Gump 文件的压缩类型", + "tooltip_compression_single": "文件的压缩类型", + "msg_specify_input_type": "必须指定输入类型", + "msg_specify_input_mul": "必须指定输入 mul", + "msg_input_mul_not_exist": "输入的 mul 不存在", + "msg_specify_input_idx": "必须指定输入 idx", + "msg_input_idx_not_exist": "输入的 idx 不存在", + "msg_specify_output_folder": "必须指定输出文件夹", + "msg_output_folder_not_exist": "输出文件夹不存在", + "msg_specify_housing_bin": "必须指定输入 housing.bin。不使用它,MultiCollection.uop 是不完整的 - 首先从原始 UOP 中提取它。", + "msg_housing_bin_not_exist": "输入的 housing.bin 不存在", + "msg_file_exists_proceed": "{uopName} 已存在于输出文件夹中,将被覆盖。\n\n继续?", + "msg_overwrite_title": "覆盖现有文件", + "msg_unusual_compression": "每个已发货客户端中的每个 {fileType} 条目都未压缩存储。\n\n压缩的地图 UOP 根本无法由 UOFiddler 读回,压缩的美术或音效条目超出客户端观察到接受的任何内容。\n\n仍然使用 {compression} 吗?", + "msg_unusual_compression_title": "此文件类型的不寻常压缩", + "msg_specify_input_uop": "必须指定输入 uop", + "msg_input_file_not_exist": "输入文件不存在", + "msg_files_overwrite": "这些文件已存在于输出文件夹中,将被覆盖:\n\n {files}\n\n继续?", + "msg_overwrite_files_title": "覆盖现有文件", + "msg_error": "发生错误。\r\n{message}", + "placeholder_inmul": "art.mul", + "placeholder_inidx": "artidx.mul", + "placeholder_inhousingbin": "housing.bin 文件路径", + "placeholder_outuopfolder": "将写入 .uop 的文件夹", + "placeholder_inuop": "MultiCollection.uop", + "placeholder_outfolder": "(留空 = 与输入文件夹相同)", + "placeholder_inputfolder": "文件将被提取到的文件夹", + "placeholder_outputfolder": "文件将被打包到的文件夹", + "placeholder_packAllHousingBin": "可选,默认为 folder/housing.bin" + }, + "CompareItemControl": { + "extractAs": "导出图像..", + "asBmp": "作为 Bmp", + "asTiff": "作为 Tiff", + "asJpg": "作为 Jpg", + "asPng": "作为 Png", + "copyItem2To1": "复制物品到左侧", + "copyAllDiff": "复制所有不同", + "loadSecond": "加载第二个", + "showOnlyDifferences": "仅显示不同", + "multiSelect": "多选" + }, + "CompareLandControl": { + "exportImageToolStripMenuItem": "导出图像..", + "asBmpToolStripMenuItem": "作为 Bmp", + "asTiffToolStripMenuItem": "作为 Tiff", + "asJpgToolStripMenuItem": "作为 Jpg", + "asPngToolStripMenuItem": "作为 Png", + "copyLandTile2To1ToolStripMenuItem": "复制地形到左侧", + "btnCopyAllDiff": "复制所有差异", + "button1": "加载", + "checkBox1": "仅显示差异", + "chkMultiSelect": "多选" + }, + "CompareCliLocControl": { + "button1": "加载", + "button2": "加载", + "button3": "...", + "button4": "...", + "button5": "查找下一个差异", + "checkBox1": "仅显示不同", + "labelDiff1": "文件 1:", + "labelDiff2": "文件 2:" + }, + "CompareHuesControl": { + "button1": "加载", + "button2": "...", + "chkMultiSelect": "多选", + "applyHue1ToHue2": "应用色调到左侧" + }, + "HueEditForm": { + "Title": "色调编辑", + "ColorPickerButton": "颜色拾取器", + "SaveButton": "保存", + "Spread": "线性渐变", + "ExpGradientButton": "指数渐变", + "InverseButton": "反转", + "ModifyRangeButton": "修改范围", + "SetColorButton": "设置颜色", + "RLabel": "R:", + "GLabel": "G:", + "BLabel": "B:", + "itemToolStripMenuItem": "物品", + "animationToolStripMenuItem": "动画", + "gumpToolStripMenuItem": "Gump", + "hueOnlyGreyToolStripMenuItem": "仅色调灰度" + }, + "MultisControl": { + "listViewMultiColumn": "多重", + "toolStripMenuItem4": "显示空闲槽位", + "importToolStripMenuItem": "导入..", + "exportToolStripMenuItem": "导出..", + "removeToolStripMenuItem": "删除", + "saveToolStripMenuItem1": "保存", + "toTextfileToolStripMenuItem": "导出为文本文件", + "toUOAToolStripMenuItem": "导出为 UOA", + "toWscToolStripMenuItem": "导出为 WSC", + "toCsvToolStripMenuItem": "导出为 CSV", + "toUOX3ToolStripMenuItem": "导出为 UOX3", + "toolStripDropDownButton1": "杂项", + "exportAllImagesToolStripMenuItem": "导出所有图像", + "aToolStripMenuItem": "导出为 BMP", + "asTiffToolStripMenuItem1": "导出为 TIFF", + "asJpgToolStripMenuItem": "导出为 JPG", + "asPngToolStripMenuItem1": "导出为 PNG", + "ChangeBackgroundColorToolStripMenuItem": "改变背景颜色", + "UseTransparencyForPNGToolStripMenuItem": "PNG 使用透明背景", + "exportAllPartsToolStripMenuItem": "导出所有部分", + "toTextFileToolStripMenuItem1": "导出为文本文件", + "toUOAFileToolStripMenuItem": "导出为 UOA 文件", + "toWSCFileToolStripMenuItem": "导出为 WSC 文件", + "toCSVFileToolStripMenuItem": "导出为 CSV 文件", + "toUOX3FileToolStripMenuItem": "导出为 UOX3 文件", + "toXMLFileToolStripMenuItem": "导出为 XML 文件 (CentrED+)", + "fitModeToolStripMenuItem": "适应窗口", + "helpToolStripButton": "帮助", + "tabPage5": "图形", + "tabPage6": "组件", + "extractImageToolStripMenuItem": "导出图像..", + "asBmpToolStripMenuItem": "导出为 BMP", + "asTiffToolStripMenuItem": "导出为 TIFF", + "asJpgToolStripMenuItem1": "导出为 JPG", + "asPngToolStripMenuItem": "导出为 PNG", + "listViewUopColumn": "UOP", + "tabPageMul": "MUL", + "tabPageUop": "UOP", + "tabPageUopPreview": "预览", + "tabPageUopComponents": "组件", + "uopExportToolStripMenuItem": "导出", + "uopToUOAToolStripMenuItem": "导出为 UOA", + "uopToWscToolStripMenuItem": "导出为 WSC", + "uopToCsvToolStripMenuItem": "导出为 CSV", + "toolStripDropDownUop": "杂项", + "uopExportAllImagesToolStripMenuItem": "导出所有图像", + "uopAsBmpToolStripMenuItem": "导出为 BMP", + "uopAsTiffToolStripMenuItem": "导出为 TIFF", + "uopAsJpgToolStripMenuItem": "导出为 JPG", + "uopAsPngToolStripMenuItem": "导出为 PNG", + "uopExportAllPartsToolStripMenuItem": "导出所有部分", + "uopToTextfileToolStripMenuItem": "导出为文本文件", + "uopToUOAFileToolStripMenuItem": "导出为 UOA 文件", + "uopToWSCFileToolStripMenuItem": "导出为 WSC 文件", + "uopToCSVFileToolStripMenuItem": "导出为 CSV 文件", + "uopFitModeToolStripMenuItem": "适应窗口", + "uopHelpToolStripButton": "帮助", + "uopExtractImageToolStripMenuItem": "导出图像..", + "uopAsBmpPicToolStripMenuItem": "导出为 BMP", + "uopAsTiffPicToolStripMenuItem": "导出为 TIFF", + "uopAsJpgPicToolStripMenuItem": "导出为 JPG", + "uopAsPngPicToolStripMenuItem": "导出为 PNG", + "Status": { + "Size": "尺寸", + "MaxHeight": "最大高度", + "MultiRegion": "多重区域", + "Surface": "表面", + "Zoom": "缩放", + "Empty": "尺寸: 0,0 最大高度: 0 多重区域: 0,0,0,0" + }, + "HelpForm": { + "Title": "多重组件 — 键盘快捷方式和控制", + "ColumnShortcut": "快捷方式 / 控制", + "ColumnAction": "操作", + "ButtonClose": "关闭", + "HeaderPreview": "预览", + "PreviewFit": "适应窗口", + "PreviewFitDesc": "切换工具栏上的按钮 — 缩放以适应或以 100% 显示并显示滚动条", + "HeaderZoom": "缩放(仅 100% 模式)", + "ZoomCtrlWheel": "Ctrl + 鼠标滚轮", + "ZoomCtrlWheelDesc": "放大 / 缩小", + "ZoomShiftPlus": "Shift + = (加号键)", + "ZoomShiftPlusDesc": "放大", + "ZoomMinus": "- (减号键)", + "ZoomMinusDesc": "缩小", + "ZoomNumpadPlus": "数字键盘 +", + "ZoomNumpadPlusDesc": "放大", + "ZoomNumpadMinus": "数字键盘 -", + "ZoomNumpadMinusDesc": "缩小", + "ZoomCtrl0": "Ctrl + 0", + "ZoomCtrl0Desc": "重置缩放为 100%", + "HeaderPanning": "平移(仅 100% 模式)", + "PanningLeftDrag": "左键单击拖动", + "PanningLeftDragDesc": "平移视图" + } + }, + "GumpControl": { + "showFreeSlotsToolStripMenuItem": "显示空闲槽位", + "findNextFreeSlotToolStripMenuItem": "查找下一个空闲槽位", + "changeBackgroundColorToolStripMenuItem": "改变背景颜色", + "extractImageToolStripMenuItem": "导出图像..", + "asBmpToolStripMenuItem": "作为 Bmp", + "asTiffToolStripMenuItem": "作为 Tiff", + "asJpgToolStripMenuItem1": "作为 Jpg", + "asPngToolStripMenuItem1": "作为 Png", + "jumpToMaleFemale": "跳转到男/女角色", + "jumpToMale": "跳转到男角色", + "jumpToFemale": "跳转到女角色", + "copyImageToolStripMenuItem": "复制图像", + "pasteImageToolStripMenuItem": "粘贴图像", + "replaceGumpToolStripMenuItem": "替换", + "insertToolStripMenuItem": "插入到..", + "toolStripMenuItem1": "从以下位置开始插入", + "removeToolStripMenuItem": "删除", + "saveToolStripMenuItem": "保存", + "nameTagToolStripLabel": "名称:", + "tagFilterDropDownButton": "标签", + "IndexToolStripLabel": "索引:", + "toolStripDropDownButton1": "杂项", + "generateFromTileDataToolStripMenuItem": "从 TileData 装备生成 Gumps.xml 条目...", + "exportAllToolStripMenuItem": "全部导出..", + "IDLabel": "ID:", + "SizeLabel": "大小:", + "Preload": "预加载", + "saveToolStripButton": "保存", + "ClearAllTags": "全部清除", + "ExportSelected": "导出所选...", + "ExportSelectedAsBmp": "保存为 BMP", + "ExportSelectedAsTiff": "保存为 TIFF", + "ExportSelectedAsJpg": "保存为 JPG", + "ExportSelectedAsPng": "保存为 PNG", + "Messages": { + "CopyImageError": "复制图像失败", + "CopyImage": "复制图像", + "PasteImageError": "粘贴图像失败", + "PasteImage": "粘贴图像", + "InvalidGumpDimensions": "无效的 Gump 尺寸!\n\n剪贴板图像:{0}x{1}\nGump 可支持的最大尺寸为 65535x65535 像素。\n\n未做任何更改。", + "InvalidSize": "无效的尺寸", + "SaveConfirm": "确定要保存吗?这可能需要一些时间", + "Save": "保存", + "RemoveConfirm": "确定要删除 {0} 吗?", + "Remove": "删除" + }, + "Tags": { + "one-hand": "单手武器", + "two-hand": "双手武器", + "boots": "靴子", + "pants": "裤子", + "shirt": "衬衣", + "helmet": "头盔", + "gloves": "手套", + "ring": "戒指", + "talisman": "护身符", + "gorget": "喉甲", + "hair": "头发", + "waist": "腰部", + "chest-armor": "胸甲", + "bracelet": "手镯", + "facial-hair": "胡须", + "tunic": "束腰外衣", + "earring": "耳环", + "sleeves": "袖子", + "cloak": "斗篷", + "backpack": "背包", + "robe": "长袍", + "skirt": "裙子", + "leg-armor": "腿甲", + "container": "容器", + "paperdoll": "纸娃娃", + "equipment": "装备", + "male": "男性", + "female": "女性", + "player": "玩家", + "character": "角色" + } + }, + "ItemsControl": { + "showFreeSlotsToolStripMenuItem": "显示空闲槽位", + "findNextFreeSlotToolStripMenuItem": "查找下一个空闲槽位", + "ChangeBackgroundColorToolStripMenuItem": "改变背景颜色", + "changeBackgroundColorToolStripMenuItemDetail": "改变背景颜色", + "extractToolStripMenuItem": "导出图像..", + "bmpToolStripMenuItem": "导出为 BMP", + "tiffToolStripMenuItem": "导出为 TIFF", + "asJpgToolStripMenuItem1": "导出为 JPG", + "asPngToolStripMenuItem1": "导出为 PNG", + "selectInTileDataTabToolStripMenuItem": "在 图块数据 标签中选择", + "selectInRadarColorTabToolStripMenuItem": "在 雷达颜色 标签中选择", + "selectInGumpsTabMaleToolStripMenuItem": "在 Gump 界面 标签中选择 (男)", + "selectInGumpsTabFemaleToolStripMenuItem": "在 Gump 界面 标签中选择 (女)", + "copyImageToolStripMenuItem": "复制图像", + "pasteImageToolStripMenuItem": "粘贴图像", + "copyImageToolStripMenuItemDetail": "复制图像", + "pasteImageToolStripMenuItemDetail": "粘贴图像", + "replaceToolStripMenuItem": "替换...", + "replaceStartingFromToolStripMenuItem": "从...开始替换", + "replaceFromFolderToolStripMenuItem": "从文件夹替换...", + "insertAtToolStripMenuItem": "在...处插入", + "removeToolStripMenuItem": "移除", + "saveToolStripMenuItem": "保存", + "toolStripLabel1": "索引:", + "toolStripLabel2": "名称:", + "searchByNameToolStripButton": "查找下一个", + "PreloadItemsToolStripButton": "预加载物品", + "MiscToolStripDropDownButton": "其他", + "ExportAllToolStripMenuItem": "导出所有..", + "asBmpToolStripMenuItem": "导出为 BMP", + "asTiffToolStripMenuItem": "导出为 TIFF", + "asJpgToolStripMenuItem": "导出为 JPG", + "asPngToolStripMenuItem": "导出为 PNG", + "NameLabel": "名称:", + "GraphicLabel": "图形:", + "DetailLabels": { + "Name": "名称", + "Graphic": "图形", + "HeightCapacity": "高度/容量", + "Weight": "重量", + "Animation": "动画", + "QualityLayerLight": "品质/层/光", + "Quantity": "数量", + "Hue": "色调", + "StackingOffsetUnk4": "堆叠偏移/Unk4", + "Flags": "标志", + "GraphicPixelSizeWidthHeight": "图形像素尺寸 宽, 高", + "GraphicPixelOffsetXMinYMinXMaxYMax": "图形像素偏移 xMin, yMin, xMax, yMax", + "AnimationFrameCount": "动画帧数", + "AnimationInterval": "动画间隔" + }, + "DetailTextBoxContextMenu": { + "Copy": "复制", + "SelectAll": "全选" + }, + "ContextMenu": { + "sendItemToClientToolStripMenuItem": "发送物品到客户端" + }, + "Messages": { + "SaveConfirmation": "确定吗?这需要一些时间", + "SaveTitle": "保存" + }, + "ToolStripTextBoxContextMenu": { + "Copy": "复制", + "Paste": "粘贴", + "Cut": "剪切", + "SelectAll": "全选", + "Delete": "删除" + } + }, + "TexturesControl": { + "showFreeSlotsToolStripMenuItem": "显示空闲槽位", + "findNextFreeSlotToolStripMenuItem": "查找下一个空闲槽位", + "exportImageToolStripMenuItem": "导出图像..", + "asBmpToolStripMenuItem": "导出为 BMP", + "asTiffToolStripMenuItem": "导出为 TIFF", + "asJpgToolStripMenuItem": "导出为 JPG", + "asPngToolStripMenuItem": "导出为 PNG", + "selectInLandTilesTabToolStripMenuItem": "在 地形图块 标签中选择", + "copyImageToolStripMenuItem": "复制图像", + "pasteImageToolStripMenuItem": "粘贴图像", + "replaceToolStripMenuItem": "替换", + "replaceStartingFromToolStripMenuItem": "从...开始替换", + "insertAtToolStripMenuItem": "在...处插入", + "removeToolStripMenuItem": "移除", + "saveToolStripMenuItem": "保存", + "SaveButton": "保存", + "IndexToolStripLabel": "索引:", + "MiscToolStripDropDownButton": "其他", + "exportAllToolStripMenuItem": "导出所有..", + "ExportAllAsBmp": "全部导出为 BMP", + "ExportAllAsTiff": "全部导出为 TIFF", + "ExportAllAsJpeg": "全部导出为 JPG", + "ExportAllAsPng": "全部导出为 PNG", + "GraphicLabel": "图形:", + "Messages": { + "CopySuccess": "复制成功", + "PasteConfirm": "是否粘贴 {0} 个图像?", + "PasteSuccess": "粘贴成功", + "ReplaceConfirm": "是否替换 {0} 个图像?", + "ReplaceSuccess": "替换成功", + "InsertConfirm": "是否在位置 {0} 插入图像?", + "InsertSuccess": "插入成功", + "DeleteConfirm": "是否删除 {0} 个图像?", + "DeleteSuccess": "删除成功", + "SaveSuccess": "保存成功", + "ExportSuccess": "导出 {0} 个图像成功", + "InvalidSize": "图像尺寸无效。纹理必须是 64x64 或 128x128", + "InvalidIndex": "无效的索引", + "NoImage": "剪贴板中没有图像" + } + }, + "AnimationListControl": { + "searchToolStripLabel": "搜索:", + "removeToolStripMenuItem": "移除", + "tabPage1": "动画", + "tabPage2": "缩略图列表", + "extractImageToolStripMenuItem": "导出图像..", + "asBMpToolStripMenuItem": "导出为 BMP", + "asTiffToolStripMenuItem": "导出为 TIFF", + "asJpgToolStripMenuItem": "导出为 JPG", + "asPngToolStripMenuItem2": "导出为 PNG", + "extractAnimationToolStripMenuItem": "导出动画..", + "asBmpToolStripMenuItem1": "导出为 BMP", + "asTiffToolStripMenuItem1": "导出为 TIFF", + "asJpgToolStripMenuItem1": "导出为 JPG", + "asPngToolStripMenuItem1": "导出为 PNG", + "asAnimatedGifToolStripMenuItem": "导出为动态 GIF(循环)", + "asAnimatedGifnoLoopingToolStripMenuItem": "导出为动态 GIF(不循环)", + "exportFrameToolStripMenuItem": "导出帧..", + "asBmpToolStripMenuItem2": "导出为 BMP", + "asTiffToolStripMenuItem2": "导出为 TIFF", + "asJpgToolStripMenuItem2": "导出为 JPG", + "asPngToolStripMenuItem": "导出为 PNG", + "ExportDialog": { + "Title": "保存", + "Message": "{0} 已保存到 {1}" + }, + "ContextMenu": { + "exportAllThumbnailsToolStripMenuItem": "导出所有缩略图..", + "asBmpToolStripMenuItem3": "导出为 BMP", + "asTiffToolStripMenuItem3": "导出为 TIFF", + "asJpgToolStripMenuItem3": "导出为 JPG", + "asPngToolStripMenuItem3": "导出为 PNG" + }, + "groupBoxSettings": "帧", + "directionLabel": "方向", + "ShowFrameBoundsCheckBox": "显示帧边界", + "AnimateCheckBox": "播放动画", + "SettingsButton": "设置", + "sortAlphaToolStripMenuItem": "按字母排序", + "hueToolStripMenuItem": "色调", + "rewriteXmlToolStripMenuItem": "重写 XML", + "tryToFindNewGraphicsToolStripMenuItem": "尝试查找新图形", + "animationEditToolStripMenuItem": "动画编辑", + "GraphicLabel": "图形:", + "BaseGraphicLabel": "基础图形:", + "HueLabel": "色调:", + "ActionNames": { + "Walk": "行走", + "Idle": "闲置", + "Die1": "死亡1", + "Die2": "死亡2", + "Attack1": "攻击1", + "Attack2": "攻击2", + "Attack3": "攻击3", + "AttackBow": "弓攻击", + "AttackCrossBow": "弩攻击", + "AttackThrow": "投掷攻击", + "GetHit": "受伤", + "Pillage": "掠夺", + "Stomp": "踏脚", + "Cast2": "施法2", + "Cast3": "施法3", + "BlockRight": "右挡", + "BlockLeft": "左挡", + "Fidget": "躁动", + "Fly": "飞行", + "TakeOff": "起飞", + "GetHitInAir": "空中受伤", + "Run": "奔跑", + "Eat": "进食", + "Alert": "警戒", + "LieDown": "躺卧", + "Walk_01": "行走_01", + "WalkStaff_01": "持杖行走_01", + "Run_01": "奔跑_01", + "RunStaff_01": "持杖奔跑_01", + "Idle_01": "闲置_01", + "Fidget_Yawn_Stretch_01": "躁动_打哈欠_伸展_01", + "CombatIdle1H_01": "战斗闲置单手_01", + "AttackSlash1H_01": "斩击单手_01", + "AttackPierce1H_01": "刺击单手_01", + "AttackBash1H_01": "敲打单手_01", + "AttackBash2H_01": "敲打双手_01", + "AttackSlash2H_01": "斩击双手_01", + "AttackPierce2H_01": "刺击双手_01", + "CombatAdvance_1H_01": "战斗推进单手_01", + "Spell1": "法术1", + "Spell2": "法术2", + "AttackBow_01": "弓攻击_01", + "AttackCrossbow_01": "弩攻击_01", + "GetHit_Fr_Hi_01": "受伤_前_高_01", + "Die_Hard_Fwd_01": "死亡_硬_前_01", + "Die_Hard_Back_01": "死亡_硬_后_01", + "Horse_Walk_01": "马行走_01", + "Horse_Run_01": "马奔跑_01", + "Horse_Idle_01": "马闲置_01", + "Horse_Attack1H_SlashRight_01": "马攻击单手斩击右_01", + "Horse_AttackBow_01": "马弓攻击_01", + "Horse_AttackCrossbow_01": "马弩攻击_01", + "Horse_Attack2H_SlashRight_01": "马攻击双手斩击右_01", + "Block_Shield_Hard_01": "盾挡_硬_01", + "Punch_Punch_Jab_01": "拳击_拳击_刺拳_01", + "Bow_Lesser_01": "鞠躬_较小_01", + "Salute_Armed1h_01": "敬礼_持单手_01", + "Ingest_Eat_01": "进食_进食_01" + }, + "Messages": { + "ExportFramesMessage": "文件已成功保存", + "ExportFrameMessage": "{0} 已保存到 {1}", + "ExportFrameTitle": "已保存", + "ExportSingleFrameMessage": "{0} 已保存到 {1}", + "ExportSingleFrameTitle": "已保存", + "ExportAllThumbnailsMessage": "所有缩略图已成功保存", + "ExportAllThumbnailsTitle": "已保存", + "ExportAnimatedGifMessage": "动画已成功保存", + "ExportAnimatedGifTitle": "已保存", + "RewriteTitle": "重写", + "RewriteMessage": "XML 已保存" + }, + "NewEntriesForm": { + "Title": "动画列表新条目", + "AddButton": "添加", + "AnimateMenuitem": "播放动画", + "ActionTypes": { + "Monster": "怪物", + "SeaMonster": "海怪", + "Animal": "动物", + "HumanEquipment": "人类/装备" + } + } + }, + "AnimationEditForm": { + "Title": "动画编辑", + "Tabs": { + "Frame": "帧", + "PreviewEdit": "预览/编辑", + "Gallery": "画廊" + }, + "TreeViewContextMenu": { + "Replace": "替换", + "Remove": "移除", + "ExtractImages": "导出图像..", + "ExtractAsFormats": { + "Bmp": "导出为 BMP", + "Tiff": "导出为 TIFF", + "Jpg": "导出为 JPG", + "Png": "导出为 PNG" + }, + "Import": "导入..", + "ImportFromVD": "从 .vd 导入", + "Export": "导出..", + "ExportToVD": "导出到 .vd" + }, + "FileSelector": "选择动画文件", + "PaletteContextMenu": { + "Export": "导出..", + "ExportBmp": "BMP", + "ExportTiff": "TIFF", + "ExportText": "文本", + "Import": "导入..", + "ImportFromTxt": "从 TXT 导入", + "ImportFromGif": "从 GIF 导入", + "ImportFromImage": "从图像导入" + }, + "FramesListViewContextMenu": { + "AllDirectionsAdd": "添加全部方向", + "AllDirectionsAddWithCanvas": "添加全部方向(带画布)", + "AllDirectionsAddWithCanvasCv5GifStyle": "添加全部方向(带画布,CV5 GIF 风格)", + "AllDirectionsAddWithCanvasKRColorCorrector": "添加全部方向(带画布,KR 颜色校正)", + "Add": "添加", + "AddWithCanvas": "添加(带画布)", + "Replace": "替换", + "Remove": "移除" + }, + "FramePage": { + "ColorBalance": "色彩平衡", + "BackgroundRed": "R", + "BackgroundGreen": "G", + "BackgroundBlue": "B", + "ApplyButton": "应用", + "PaletteConverter": "调色板转换器", + "SetPallete": "设置", + "ColorModes": { + "RGB": "RGB", + "RBG": "RBG", + "GRB": "GRB", + "GBR": "GBR", + "BGR": "BGR", + "BRG": "BRG" + }, + "ExtraBackground": "额外背景", + "ColorRed": "R", + "ColorGreen": "G", + "ColorBlue": "B", + "LockColorControls": "锁定颜色控制" + }, + "AnimationEditPage": { + "Zoom": "缩放:", + "DrawFullRectangle": "绘制完整矩形", + "DrawEmptyRectangle": "绘制空矩形", + "DrawReferencialPoint": "绘制参考点", + "SecondAnimOverlay": "第二动画叠加层", + "ShowOverlay": "显示叠加层", + "SecondAnimFileId": "ID", + "SecondAnimOpacity": "不透明度", + "OnTop": "在顶部", + "Box": "方框", + "WarningLabel": "警告", + "AnimationFrames": "动画帧", + "AnimationSpeed": "动画速度", + "PlayButton": "播放", + "LocationCenter": "位置中心", + "CenterX": "X", + "CenterY": "Y", + "SameCenter": "相同中心", + "ReferencialPoint": "参考点", + "RefX": "X", + "RefY": "Y", + "LockButton": "锁定", + "Coordinates": "坐标", + "SetCoordinates": "设置", + "SaveCoordinates": "保存" + }, + "StatusBar": { + "MiscButton": "杂项", + "SaveMenuItem": "保存", + "ShowOnlyValidMenuItem": "仅显示有效", + "ExportAllToVD": "全部导出到 VD" + }, + "Tooltips": { + "AllDirectionsAddToolTip": "依次选择 5 个 GIF,分别添加到不同方向", + "AllDirectionsAddWithCanvasToolTip": "依次选择 5 个 GIF,分别添加到不同方向(使用画布收缩算法)", + "AllDirectionsAddWithCanvasKRToolTip": "添加图像时移除所有绿色" + } + }, + "CompareGumpControl": { + "checkBox1": "仅显示差异", + "chkMultiSelect": "多选", + "button1": "加载", + "button2": "加载", + "extractAs": "导出为..", + "copyGump2To1": "复制Gump到左侧" + }, + "CompareMapControl": { + "button1": "加载", + "button2": "...", + "zoomPlus": "放大", + "zoomMinus": "缩小", + "coordsPrefix": "坐标: ", + "zoomPrefix": "缩放: ", + "showDifferencesToolStripMenuItem": "显示差异", + "showMap1ToolStripMenuItem": "显示地图1", + "showMap2ToolStripMenuItem": "显示地图2", + "feluccaToolStripMenuItem": "Felucca", + "trammelToolStripMenuItem": "Trammel", + "ilshenarToolStripMenuItem": "Ilshenar", + "malasToolStripMenuItem": "Malas", + "tokunoToolStripMenuItem": "Tokuno", + "terMurToolStripMenuItem": "TerMur", + "markDiffToolStripMenuItem": "标记差异" + }, + "CompareTextureControl": { + "checkBox1": "仅显示差异", + "chkMultiSelect": "多选", + "button1": "加载", + "button2": "...", + "exportImageToolStripMenuItem": "导出图像..", + "copyLandTile2To1ToolStripMenuItem": "复制纹理到左侧", + "fromLeftToRight": "复制所有差异", + "copyAddOnly": "仅复制新增项" + }, + "CompareAnimDataControl": { + "copyEntry": "复制条目到左侧", + "buttonLoadSecond": "加载", + "buttonBrowse": "...", + "buttonCopySelected": "复制选定项", + "buttonCopyAllDiff": "复制所有差异", + "buttonCopyAddedOnly": "仅复制新增项", + "checkBoxShowDiff": "仅显示差异", + "chkMultiSelect": "多选", + "groupBoxOrg": "左侧 (原始)", + "groupBoxSec": "右侧 (第二个)", + "groupBoxLegend": "图例", + "labelOrgFrameCountCaption": "帧数:", + "labelOrgFrameIntervalCaption": "帧间隔:", + "labelOrgFrameStartCaption": "帧起始:", + "labelOrgFrameDataCaption": "帧数据:", + "labelSecFrameCountCaption": "帧数:", + "labelSecFrameIntervalCaption": "帧间隔:", + "labelSecFrameStartCaption": "帧起始:", + "labelSecFrameDataCaption": "帧数据:", + "legendLabelOnlyInOrg": "仅在原始中", + "legendLabelOnlyInSecond": "仅在第二个中", + "legendLabelDifferent": "不同值", + "legendLabelIdentical": "相同" + }, + "CompareRadarColControl": { + "copyEntry2To1": "复制条目到左侧", + "buttonLoadSecond": "加载", + "buttonBrowse": "...", + "buttonCopySelected": "复制选定项", + "buttonCopyAllDiff": "复制所有差异", + "checkBoxShowDiff": "仅显示差异", + "chkMultiSelect": "多选", + "groupBoxOrg": "左侧 (原始)", + "groupBoxSec": "右侧 (第二个)", + "groupBoxLegend": "图例", + "labelOrgColorCaption": "颜色:", + "labelSecColorCaption": "颜色:", + "legendLabelDifferent": "不同值", + "legendLabelIdentical": "相同" + }, + "CompareTileDataControl": { + "btnCopyLandSelected": "复制选定项", + "btnCopyLandAllDiff": "复制所有差异", + "btnCopyItemSelected": "复制选定项", + "btnCopyItemAllDiff": "复制所有差异", + "tabLand": "地形图块", + "tabItem": "静态图块", + "btnBrowse": "浏览...", + "btnLoad": "加载", + "chkShowDiff": "仅显示差异", + "chkMultiSelect": "多选", + "btnToggleRules": "规则 ▲", + "btnToggleRulesOpen": "规则 ▼", + "btnToggleRulesClose": "规则 ▲", + "labelDir": "目录:", + "chkLandName": "名称", + "chkLandTexId": "纹理ID", + "chkLandFlags": "标志", + "chkItemName": "名称", + "chkItemFlags": "标志", + "chkItemAnim": "动画", + "chkItemWeight": "重量", + "chkItemQuality": "品质", + "chkItemQty": "数量", + "chkItemHue": "色调", + "chkItemStack": "堆叠偏移", + "chkItemValue": "价值", + "chkItemHeight": "高度", + "chkItemMisc": "杂项数据", + "chkItemUnk2": "Unk2", + "chkItemUnk3": "Unk3", + "gbLandFields": "地形字段", + "gbItemFields": "物品字段", + "gbIgnoreFlags": "忽略标志 (勾选 = 从对比中排除)", + "btnResetRules": "恢复默认值", + "btnApplyRules": "应用", + "labelLandName": "名称", + "labelLandTexId": "纹理ID", + "labelItemName": "名称", + "labelItemAnim": "动画", + "labelItemWeight": "重量", + "labelItemQuality": "品质", + "labelItemQty": "数量", + "labelItemHue": "色调", + "labelItemStack": "堆叠偏移", + "labelItemValue": "价值", + "labelItemHeight": "高度", + "labelItemMisc": "杂项数据", + "labelItemUnk2": "Unk2", + "labelItemUnk3": "Unk3", + "flagHeaderFlag": "标志", + "flagHeaderOrg": "原始", + "flagHeaderSec": "第二个", + "flagBackground": "背景", + "flagWeapon": "武器", + "flagTransparent": "透明", + "flagTranslucent": "半透明", + "flagWall": "墙", + "flagDamaging": "伤害", + "flagImpassable": "不可通行", + "flagWet": "潮湿", + "flagUnknown1": "未知1", + "flagSurface": "表面", + "flagBridge": "桥", + "flagGeneric": "通用", + "flagWindow": "窗口", + "flagNoShoot": "不可射击", + "flagArticleA": "冠词A", + "flagArticleAn": "冠词An", + "flagArticleThe": "冠词The", + "flagFoliage": "植被", + "flagPartialHue": "部分色调", + "flagNoHouse": "无房屋", + "flagMap": "地图", + "flagContainer": "容器", + "flagWearable": "可穿戴", + "flagLightSource": "光源", + "flagAnimation": "动画", + "flagHoverOver": "悬停", + "flagNoDiagonal": "无对角线", + "flagArmor": "护甲", + "flagRoof": "屋顶", + "flagDoor": "门", + "flagStairBack": "楼梯背面", + "flagStairRight": "楼梯右面", + "flagAlphaBlend": "透明混合", + "flagUseNewArt": "使用新美术", + "flagArtUsed": "美术已使用", + "flagNoShadow": "无阴影", + "flagPixelBleed": "像素溢出", + "flagPlayAnimOnce": "播放一次动画", + "flagMultiMovable": "多可移动" + }, + "SoundsControl": { + "nameSortToolStripMenuItem": "按名称排序", + "showFreeSlotsToolStripMenuItem": "显示空闲插槽", + "nextFreeSlotToolStripMenuItem": "查找下一个空闲插槽", + "playSoundToolStripMenuItem": "播放", + "replaceToolStripMenuItem": "插入/替换", + "extractSoundToolStripMenuItem": "提取", + "removeSoundToolStripMenuItem": "移除", + "itemSave": "保存", + "groupBox1": "通用", + "SelectedSoundGroup": "当前音效", + "groupBox2": "搜索", + "groupBox3": "插入/替换", + "PlaySoundButton": "播放", + "StopSoundButton": "停止", + "ExtractSoundButton": "提取", + "RemoveSoundButton": "移除", + "SaveFileButton": "保存", + "exportAllSoundsButton": "导出所有音效", + "ExportSoundListCsvButton": "导出音效列表 (.csv)", + "WavChooseInsertButton": "...", + "AddInsertReplaceButton": "添加", + "GoPrevResultButton": "< 上一个", + "GoNextResultButton": "下一个 >", + "SearchByIdButton": "搜索", + "SearchByNameButton": "搜索", + "label1": "ID:", + "label3": "WAV 文件:", + "SortByNameCheckbox": "按名称排序树形", + "includeSoundIdCheckBox": "在文件名中包含音效ID", + "listViewColumn": "音效", + "stopButton": "停止", + "Messages": { + "emptySlot": "空闲插槽", + "replace": "替换", + "insert": "插入", + "currentSoundFormat": "当前音效", + "duration": "时长", + "soundSavedTo": "音效已保存到 {0}", + "saved": "已保存", + "extractAllComplete": "导出所有音效完成。", + "soundListSavedSuccessfully": "音效列表已成功保存。", + "invalidFilename": "无效的文件名", + "addReplaceTitle": "添加/替换", + "confirmReplace": "是否确定要替换 {0}?", + "replaceTitle": "替换", + "unexpectedWavFormat": "意外的 WAV 格式:", + "errorTitle": "错误", + "chooseWaveFile": "选择音效文件" + } + }, + "FontsControl": { + "writeTextToolStripMenuItem": "写入文本", + "setOffsetsToolStripMenuItem": "设置偏移", + "extractCharacterToolStripMenuItem": "提取字符", + "importCharacterToolStripMenuItem": "导入字符", + "saveToolStripMenuItem": "保存", + "LoadUnicodeFontsCheckBox": "加载 Unicode 字体", + "toolStripStatusLabel1": "<未选择>", + "Messages": { + "loadAllFontsConfirm": "是否要加载包括 Unicode 在内的所有字体?", + "loadAsciiOnlyConfirm": "只加载 ASCII 字体?", + "fontsReloadTitle": "字体重新加载", + "imageSizeExceeds": "图像高度或宽度超过 255", + "importTitle": "导入", + "characterSavedSuccess": "字符已成功保存。", + "unicodeFontsSavedSuccess": "Unicode 字体已成功保存。", + "fontsSavedSuccess": "字体已成功保存。" + } + }, + "CliLocControl": { + "GotoEntry": "输入编号", + "GotoButton": "跳转", + "FindEntry": "输入文本", + "FindButton": "查找", + "RegexToolStripButton": "使用正则表达式", + "SaveButton": "保存", + "ExportButton": "导出", + "ImportButton": "导入", + "LangComboBoxEnglish": "英文", + "LangComboBoxGerman": "德文", + "LangComboBoxCustom1": "自定义 1", + "LangComboBoxCustom2": "自定义 2", + "ColumnNumber": "编号", + "ColumnText": "文本", + "ColumnFlag": "标志", + "copyCliLocNumberToolStripMenuItem": "复制 CliLoc 编号", + "copyCliLocTextToolStripMenuItem": "复制 CliLoc 文本", + "addEntryToolStripMenuItem": "添加条目", + "deleteEntryToolStripMenuItem": "删除条目", + "cSVToolStripMenuItem": "CSV", + "tileDataToolStripMenuItem": "从 TileData 同步", + "Messages": { + "PleaseProvideSearchText": "请提供搜索文本", + "FindEntryTitle": "查找条目", + "InvalidRegex": "无效的正则表达式。", + "EntryNotFound": "未找到条目。", + "NumberNotFound": "未找到编号。", + "GotoTitle": "跳转", + "ClilocLoadWarningTitle": "Cliloc 分析警告", + "ImportChooseFileTitle": "选择要导入的 CSV 文件", + "ImportDoneTitle": "导入完成", + "ImportEntriesChanged": "{0} 个条目已更改。", + "ImportNoEntriesChanged": "没有条目被更改。" + } + }, + "TileDataSyncPreviewForm": { + "Title": "从 TileData 同步 — 预览", + "ColumnAction": "操作", + "ColumnNumber": "编号", + "ColumnCurrentText": "当前文本", + "ColumnNewText": "新文本", + "WillApplyTemplate": "将应用: 添加 {0}/{1} 更新 {2}/{3} 删除 {4}/{5}", + "CheckAllButton": "全选", + "UncheckAllButton": "取消全选", + "UncheckRemovesButton": "取消选中删除项", + "ApplyButton": "应用", + "CancelButton": "取消" + }, + "MapMarkerForm": { + "Title": "添加地图标记", + "TextLabel": "文本", + "ChooseColorButton": "选择颜色", + "SaveButton": "保存" + }, + "MapMeltStaticsForm": { + "Title": "融化静态物体", + "X1Label": "X1", + "Y1Label": "Y1", + "X2Label": "X2", + "Y2Label": "Y2", + "MeltButton": "融化", + "DoneTitle": "融化静态物体", + "DoneMessage": "完成" + }, + "MapReplaceTilesForm": { + "Title": "地图 - 替换地块", + "ReplaceFromLabel": "替换来源", + "ReplaceButton": "替换" + }, + "MapClearStaticsForm": { + "Title": "清除静态物体", + "X1Label": "X1", + "Y1Label": "Y1", + "X2Label": "X2", + "Y2Label": "Y2", + "ClearButton": "清除", + "DoneTitle": "清除静态物体", + "DoneMessage": "完成" + }, + "MapDefragStaticsForm": { + "Title": "整理静态物体", + "Source": "来源", + "AllowTruncation": "丢弃配置的地图大小之外的块", + "Filters": "过滤器", + "DropInvalidIds": "丢弃未知物品 id 的静态物体", + "Ceiling": "天花板:", + "OutOfBlock": "超出块 x/y 偏移:", + "DropInvalidZ": "丢弃 z = -128 的静态物体", + "NormalizeHue": "将负色调标准化为 0", + "BelowTerrain": "丢弃埋在地形瓦下的静态物体(从不绘制)", + "RemoveDuplicates": "移除重复项(相同 id、x、y 和 z)", + "DuplicatesHue": "也比较色调(旧版)", + "CollapseStacks": "将共享单元格的堆叠静态物体折叠为一个", + "CollapseFlags": "标志:", + "CollapseWet": "湿润", + "CollapseSurface": "表面", + "CollapseIgnoreZ": "忽略 z", + "CollapseIds": "物品 id:", + "SortTiles": "对每个块中的静态物体进行排序", + "Output": "输出文件夹", + "BrowseButton": "浏览...", + "AnalyzeButton": "分析", + "DefragButton": "整理", + "CancelButton": "取消", + "CloseButton": "关闭", + "Geometry": "几何:{0}", + "StatusAnalyzing": "分析中...", + "StatusDefragging": "整理中...", + "StatusCompleted": "完成", + "IdCeilingOptions": { + "tiledata": "TileData", + "art": "Art", + "legacy 0x3FFF": "Legacy 0x3FFF" + }, + "OutOfBlockOptions": { + "Drop": "丢弃", + "Mask to 0-7": "掩码到 0-7", + "Keep": "保留" + }, + "Messages": { + "Facet": "位面", + "MapSize": "地图大小", + "Blocks": "块", + "FilesNotFound": "staidx{0}.mul 或 statics{0}.mul 在加载的客户端中未找到。", + "OverridePresent": "staidx{0}x.mul 存在 - 客户端更倾向使用这些覆盖文件而不是这个对。", + "SurplusBlocks": "staidx 包含 {0:N0} 块但配置的地图大小仅覆盖 {1:N0} 块。多余块中的静态物体将被丢弃。", + "EmptyBlocks": "staidx 包含 {0:N0} 块,配置的地图大小覆盖 {1:N0} 块。索引末尾之后的块将被写入为空。", + "MatchingBlocks": "staidx 包含 {0:N0} 块,与配置的地图大小匹配。", + "Cancelled": "已取消。未写入任何内容。", + "Failed": "失败。", + "Analyzed": "已分析。未写入任何内容。", + "Done": "完成。{0:N0} 个静态物体已写入。" + } + }, + "MapDefragStaticsResultForm": { + "Title": "整理静态物体 - 结果", + "VerifyButton": "验证输出", + "SaveButton": "保存报告...", + "CopyButton": "复制", + "OpenFolderButton": "打开输出文件夹", + "CloseButton": "关闭" + }, + "MapDetailsForm": { + "Title": "地图详情", + "Labels": { + "Coordinate": "X", + "LandTile": "陆地图块", + "Altitude": "高度", + "Statics": "静态物体", + "Hue": "色调" + } + }, + "MapDiffApplyResultForm": { + "Title": "差异应用结果", + "buttonVerify": "验证输出", + "buttonSave": "保存报告...", + "buttonCopy": "复制", + "buttonOpenFolder": "打开输出文件夹", + "buttonClose": "关闭" + }, + "MapDiffInsertForm": { + "Title": "差异到地图复制", + "groupBoxWhat": "插入", + "groupBoxFrom": "区域,以地图图块为单位", + "groupBoxPreview": "将要插入的内容 - 拖动以选择区域", + "checkBoxMap": "地图", + "checkBoxStatics": "静态物品", + "RemoveDupl": "移除重复项", + "checkBoxDuplicatesHue": "也比较色调(旧版)", + "checkBoxPreviewStatics": "显示静态物品", + "checkBoxPreviewPatched": "标记差异涵盖的块", + "labelMapFormat": "写入为:", + "label1": "X1", + "label2": "Y1", + "label3": "X2", + "label4": "Y2", + "buttonCopy": "插入", + "buttonCancel": "取消", + "buttonClose": "关闭", + "Messages": { + "Inserting": "插入中...", + "Cancelled": "已取消。未写入任何内容。", + "Failed": "失败。", + "Done": "完成", + "Cancelling": "取消中...", + "RegionLabel": "区域", + "DiffDataLoaded": "差异数据已加载: {0:N0} 陆地块,{1:N0} 静态块", + "RequestWidened": "请求 {0},{1} - {2},{3} 已扩展为整个 8 图块块", + "BlocksLabel": "块 {0},{1} - {2},{3},{4} x {5}" + }, + "ComboBoxFormats": { + "SameFormatUOP": "与此客户端相同格式 (.uop)", + "SameFormatMUL": "与此客户端相同格式 (.mul)", + "LegacyMUL": "LegacyMUL" + } + }, + "MapRegionCopyResultForm": { + "Title": "地图和静态物品复制 - 结果", + "VerifyButton": "验证输出", + "SaveButton": "保存报告...", + "CopyButton": "复制", + "OpenFolderButton": "打开输出文件夹", + "CloseButton": "关闭" + }, + "MapReplaceForm": { + "Title": "地图和静态物品复制", + "GroupBoxSource": "复制来源", + "LabelFolder": "文件夹:", + "ButtonBrowse": "浏览...", + "LabelMap": "地图:", + "GroupBoxWhat": "复制内容", + "CheckBoxMap": "地图", + "LabelMapFormat": "写入为:", + "CheckBoxStatics": "静态物品", + "RemoveDupl": "移除重复项", + "CheckBoxDuplicatesHue": "也比较色调(旧版)", + "GroupBoxFrom": "来源区域,以源地图图块为单位", + "Label1": "X1", + "Label2": "Y1", + "Label3": "X2", + "Label4": "Y2", + "GroupBoxTo": "目标位置,以此地图的图块为单位", + "Label6": "X", + "Label7": "Y", + "LabelZAdjust": "Z 调整", + "CheckBoxZClamp": "保留超过限制的内容", + "GroupBoxPreview": "将要复制的内容", + "LabelSourcePreview": "来源 - 拖动选择区域", + "LabelTargetPreview": "目标 - 拖动放置它", + "CheckBoxPreviewStatics": "显示静态物品", + "CheckBoxPreviewOverlay": "在位置显示该片段", + "ButtonCopy": "复制", + "ButtonCancel": "取消", + "ButtonClose": "关闭", + "WindowTitle": "地图和静态物品复制", + "ComboBoxFormats": { + "SameFormatUOP": "与此客户端相同格式 (.uop)", + "SameFormatMUL": "与此客户端相同格式 (.mul)", + "MapMUL": "map{0}.mul", + "LegacyMUL": "map{0}LegacyMUL.uop" + }, + "PreviewMessages": { + "ChooseFolderAndMap": "选择文件夹和地图。", + "ChooseFolderToCopyFrom": "选择要复制的文件夹", + "SourceFormat": "从 {0},{1} - {2},{3} {4} x {5} 块", + "DestinationFormat": "到 {0},{1} - {2},{3}", + "WidenedFormat": "{0},{1} - {2},{3} 已扩展为整个块", + "SizeNotRecognised": "无法识别大小", + "FolderHolds": "文件夹包含 {0}", + "SizeCheckFailed": "无法确定该文件夹中地图 {0} 的大小。", + "SizeCheckMismatch": "该文件夹包含地图 {0} 的 {1},但选择了 {2}。选择匹配项或将读取错误的块。", + "PickMatchingEntry": "选择匹配的条目,或将读取错误的块", + "ChooseFolderForCopy": "选择包含要复制的地图文件的文件夹。", + "NothingSelected": "没有选择要复制的内容。", + "Copying": "复制中...", + "CopyingLand": "复制地形", + "CopyingStatics": "复制静态物品", + "Cancelled": "已取消。未写入任何内容。", + "Failed": "失败。", + "Done": "完成。", + "SelectFolderDialog": "选择包含要复制的地图文件的文件夹", + "SizeCouldNotBeDetermined": "无法确定该文件夹中地图 {0} 的大小。{1}{1}{2}", + "SizeMismatchError": "该文件夹包含地图 {0} 的 {1},但选择了 {2}。{3}{3}{4}{3}{3}选择匹配的条目,或将读取错误的块。", + "RegionZ": "区域 z 高度 {0},静态物品 {1}", + "RegionZAdjusted": " -> 高度 {0},静态物品 {1}", + "RegionZOutOfRange": " - {0:N0} 超过限制,{1}", + "HeldThere": "保留在此处", + "Refused": "拒绝", + "StaticsNone": "无", + "ReadingHeights": "读取此区域的高度..." + } + }, + "MapControl": { + "ZoomPlus": "+缩放", + "ZoomMinus": "-缩放", + "GetMapInfo": "获取地图信息", + "InsertMarker": "插入标记", + "Goto": "转到...", + "SendClientToPos": "发送客户端到位置", + "Felucca": "Felucca", + "Trammel": "Trammel", + "Ilshenar": "Ilshenar", + "Malas": "Malas", + "Tokuno": "Tokuno", + "TerMur": "TerMur", + "ExtractMap": "导出地图..", + "AsBmp": "导出为 BMP", + "AsTiff": "导出为 TIFF", + "AsJpg": "导出为 JPG", + "AsPng": "导出为 PNG", + "GotoMarker": "转到", + "RemoveMarker": "移除", + "SwitchVisibility": "切换可见性", + "View": "视图", + "ShowStatics": "显示静态物体", + "ShowCenterCross": "显示中心十字", + "ShowMarkers": "显示标记", + "ShowClientCross": "显示客户端十字", + "AltitudeModeNormal": "普通", + "AltitudeModeNormalWithAltitude": "普通 + 高度", + "AltitudeModeAltitude": "高度地图", + "ClientInteract": "客户端交互", + "ShowClientLoc": "显示客户端位置", + "GotoClientLoc": "转到客户端位置", + "SendClientToCenter": "发送客户端到中心", + "PreloadMap": "预加载地图", + "Misc": "其他", + "DefragStatics": "整理静态物体...", + "FreezeStatics": "冻结静态物体.. (在内存中)", + "MeltStatics": "融化静态物体.. (在内存中)", + "ClearStatics": "清除静态物体..(在内存中)", + "ReportStaticsUnderMap": "报告地图下方的静态物体(可能不可见)", + "ReportInvalidMapIDs": "报告无效的地图 ID", + "RewriteMap": "重写地图", + "MapAndStaticsCopy": "地图和静态物体复制...", + "DiffToMapCopy": "差异到地图复制...", + "ReplaceTiles": "替换图块..", + "CoordsLabel": "坐标: ", + "ClientLocLabel": "客户端位置: ", + "ZoomLabel": "缩放: ", + "AltitudePreset": { + "Sharp": "高对比度", + "Normal": "更多对比度", + "Soft": "柔和" + }, + "AltitudeIntensity": { + "Subtle": "微妙强度", + "Normal": "正常强度", + "Strong": "强烈强度" + } + }, + "LightControl": { + "Light": "光照", + "ExportImage": "导出图像..", + "AsBmp": "为 BMP", + "AsTiff": "为 TIFF", + "AsJpg": "为 JPG", + "Remove": "删除", + "Replace": "替换", + "InsertAt": "插入位置..", + "Save": "保存", + "IGPreview": "游戏内预览", + "BackgroundLandTile": "背景地形图块", + "LightTile": "光照图块", + "Dialogs": { + "ReplaceTitle": "选择要替换的图像文件", + "InsertTitle": "选择要插入位置 {0} (0x{0:X}) 的图像文件", + "FileFilter": "图像文件 (*.tif;*.tiff;*.bmp;*.png)|*.tif;*.tiff;*.bmp;*.png" + }, + "Messages": { + "LightSavedSuccessfully": "光照文件保存成功。", + "FilesSavedSuccessfully": "文件保存成功。" + } + }, + "SpeechControl": { + "IDEntryPlaceholder": "查找 ID...", + "KeyWordEntryPlaceholder": "关键词...", + "IDButton": "查找", + "IDNextButton": "查找下一个", + "KeyWordButton": "查找", + "KeyWordNextButton": "查找下一个", + "SaveButton": "保存", + "MiscMenu": "杂项", + "ImportFromCSV": "从 CSV 导入", + "ExportToCSV": "导出到 CSV", + "AddButton": "添加条目", + "DeleteButton": "删除条目", + "AddEntryMenu": "添加条目", + "DeleteEntryMenu": "删除条目", + "ColumnId": "标识号", + "ColumnKeyWord": "关键词", + "Dialogs": { + "ImportTitle": "选择要导入的 CSV 文件", + "CSVFilter": "CSV 文件 (*.csv)|*.csv" + }, + "Messages": { + "IDNotFound": "找不到 ID。", + "IDNotFoundTitle": "跳转", + "KeyWordNotFound": "找不到关键词。", + "KeyWordNotFoundTitle": "条目", + "SpeechSavedSuccessfully": "语音文件保存成功。" + } + }, + "SkillsControl": { + "SaveButton": "保存", + "AddButton": "添加", + "DeleteButton": "删除", + "ColumnId": "ID号", + "ColumnIsAction": "是否动作", + "ColumnName": "技能名称", + "FileSavedMessage": "技能已保存成功", + "FileSavedTitle": "保存" + }, + "AnimDataControl": { + "PreviewGroupBox": "预览", + "DataGroupBox": "数据", + "FramesGroupBox": "帧", + "StartDelayLabel": "开始延迟", + "FrameDelayLabel": "帧延迟", + "AddButton": "添加", + "RemoveButton": "移除", + "SaveButton": "保存", + "ImportButton": "导入...", + "ExportButton": "导出...", + "AddMenuItem": "添加", + "RemoveMenuItem": "移除", + "HueMenuItem": "色调", + "AnimateMenuItem": "播放动画", + "ShowFrameBoundsMenuItem": "显示帧边界", + "ExportGifMenuItem": "导出为动画 GIF", + "ExportGifDialog": { + "Title": "保存", + "Message": "已保存到 {0}" + }, + "IndexLabel": "索引:", + "SettingsButton": "设置", + "RelativeCheckBox": "相对坐标", + "BaseGraphicLabel": "基础图形:", + "GraphicLabel": "图形:", + "HueLabel": "色调:" + }, + "HuePopUpForm": { + "Title": "色调拾取器", + "OkButton": "确定", + "ClearButton": "清除", + "HueOnlyGrayCheckBox": "仅灰色色调 (+0x8000)" + }, + "HuesControl": { + "HueIndexToolStripLabel": "索引:", + "HueNameToolStripLabel": "名称:", + "SearchNameToolStripButton": "查找下一个", + "IndexOffsetButton": "显示游戏内编号", + "saveToolStripMenuItem": "保存", + "replaceToolStripMenuItem": "替换为..", + "exportToolStripMenuItem": "导出..", + "importToolStripMenuItem": "导入..", + "exportAllHueNamesListToolStripMenuItem": "导出所有色调名称列表" + }, + "AnimDataExportForm": { + "Title": "导出动画数据", + "lblAnimationsToExport": "导出动画:", + "btnExport": "导出", + "ExportSelection": { + "All": "全部(默认、蓝色、红色条目)", + "IncludeMissing": "包含缺失动画标志(默认和蓝色条目)", + "OnlyValid": "仅有效动画(默认条目)" + }, + "ExportSuccessMessage": "已导出 {0} 条 animdata 记录到:{1}", + "ExportErrorTitle": "AnimData 导出错误", + "ExportErrorMessage": "导出 animdata 时出错:{0}" + }, + "AnimDataImportForm": { + "Title": "导入动画数据", + "label5": "导入来源:", + "btnImport": "导入", + "label1": "冲突时:", + "cbErase": "导入前清空动画数据", + "ConflictAction": { + "Skip": "跳过", + "Overwrite": "覆盖" + }, + "ImportSuccessTitle": "导入成功", + "ImportSuccessMessage": "已从 {0} 成功导入 {1} 条动画数据\n\n请不要忘记保存您的更改!", + "ImportErrorTitle": "导入错误", + "ImportErrorMessage": "导入动画数据时出错: {0}" + }, + "LandTilesControl": { + "showFreeSlotsToolStripMenuItem": "显示空闲槽位", + "findNextFreeSlotToolStripMenuItem": "查找下一个空闲槽位", + "changeBackgroundColorToolStripMenuItem": "更改背景颜色", + "exportImageToolStripMenuItem": "导出图像..", + "asBmpToolStripMenuItem": "导出为 BMP", + "asTiffToolStripMenuItem": "导出为 TIFF", + "asJpgToolStripMenuItem1": "导出为 JPG", + "asPngToolStripMenuItem": "导出为 PNG", + "selectInTileDataTabToolStripMenuItem": "在图块数据标签中选择", + "selectInRadarColorTabToolStripMenuItem": "在雷达颜色标签中选择", + "selectInTexturesTabToolStripMenuItem": "在纹理标签中选择", + "copyImageToolStripMenuItem": "复制图像", + "pasteImageToolStripMenuItem": "粘贴图像", + "replaceToolStripMenuItem": "替换", + "replaceStartingFromToolStripMenuItem": "从..开始替换", + "replaceFromFolderToolStripMenuItem": "从文件夹替换...", + "insertAtToolStripMenuItem": "在..处插入", + "removeToolStripMenuItem": "移除", + "saveToolStripMenuItem": "保存", + "IndexToolStripLabel": "索引:", + "NameToolStripLabel": "名称:", + "searchByNameToolStripButton": "查找下一个", + "MiscToolStripDropDownButton": "杂项", + "exportAllToolStripMenuItem": "全部导出..", + "ExportAllAsBmp": "导出为 BMP", + "ExportAllAsTiff": "导出为 TIFF", + "asJpgToolStripMenuItem": "导出为 JPG", + "asPngToolStripMenuItem1": "导出为 PNG", + "SaveButton": "保存", + "NameLabel": "名称:", + "GraphicLabel": "图形:", + "FlagsLabel": "标志:", + "Messages": { + "LandtileSavedTo": "地形图块已保存到", + "Saved": "已保存", + "AllLandTilesSavedSuccessfully": "所有地形图块保存成功。", + "SelectDirectoryDescription": "选择目录", + "SelectFolderForReplaceDescription": "选择包含要替换的图像的文件夹" + } + }, + "MultiMapControl": { + "loadButton": "加载..", + "generateButton": "生成..", + "exportButton": "导出..", + "Menus": { + "multiMap": "多重地图", + "facet00": "位面 00", + "facet01": "位面 01", + "facet02": "位面 02", + "facet03": "位面 03", + "facet04": "位面 04", + "facet05": "位面 05", + "multiMapFromImage": "从图像生成多重地图", + "facetFromImage": "从图像生成位面", + "asBmp": "导出为 BMP..", + "asTiff": "导出为 TIFF..", + "asJpg": "导出为 JPG..", + "asPng": "导出为 PNG.." + }, + "Dialogs": { + "selectImageTitle": "选择要转换的图像", + "errorTitle": "错误" + }, + "Messages": { + "invalidImageSize": "图像高度或宽度无效", + "imageNotFound": "未找到图像", + "multiMapSavedSuccess": "多重地图保存成功。", + "facetSavedSuccess": "位面保存成功。" + } + }, + "VerdataControl": { + "buttonReload": "重新加载", + "buttonLoadFile": "加载文件…", + "buttonPlay": "播放", + "buttonStop": "停止", + "labelType": "补丁类型:", + "labelCount": "补丁:", + "labelFile": "文件:", + "labelIndex": "索引:", + "labelLookup": "查询:", + "labelLength": "长度:", + "labelExtra": "额外:", + "labelActions": "动作:", + "labelDir": "方向:", + "tabPageMultiPreview": "预览", + "tabPageMultiComponents": "组件", + "changeBackgroundColor": "更改背景颜色", + "openVerdataTitle": "打开 verdata.mul", + "noPatchesFound": "未找到补丁" + }, + "SkillGroupControl": { + "AddButton": "添加", + "RemoveButton": "删除", + "RenameButton": "重命名", + "SkillIDLabel": "技能ID", + "SaveButton": "保存", + "InvalidNameReserved": "无效的名称。该名称是保留的。", + "InvalidNameEmpty": "无效的名称。名称不能为空。", + "EditFormTitle": "技能组编辑", + "FileSavedMessage": "技能组保存成功。" + }, + "FileSavedDialog": { + "Title": "文件已保存", + "SaveSuccess": "文件保存成功。", + "OpenFolder": "打开文件夹", + "OK": "确定" + }, + "DressControl": { + "AsJpg": "JPG 格式", + "ExportMessages": { + "PaperdollSaved": "人物模型已成功保存。", + "InGameSaved": "游戏内图像已成功保存。", + "AnimSaved": "动画已成功保存。", + "ReportSaved": "报告已成功保存。" + }, + "FacingActionGroupBox": "方向/动作", + "DressItemButton": "穿戴物品", + "UnDress": "卸下", + "AsTiff": "TIFF 格式", + "ShowAnimation": "显示动画", + "UnDressAll": "卸下全部", + "FindNextItemButton": "查找下一个", + "ItemColumnHeader": "物品", + "FemaleCheckBox": "女性", + "ElfRadio": "精灵", + "ExportAnimation": "导出动画...", + "ExportImage": "导出图像...", + "HumanRadio": "人类", + "ExportReportButton": "导出报告", + "AsAnimatedGifNoLooping": "动画 GIF(非循环)", + "Animate": "动画", + "AsBmp": "BMP 格式", + "MountGroupBox": "坐骑", + "HueEllipsis": "色调...", + "AsPng": "PNG 格式", + "LayerSort": "按层排序", + "AsAnimatedGifLooping": "动画 GIF(循环)", + "Hue": "色调", + "GargoyleRadio": "石像鬼", + "ShowPaperdoll": "显示纸娃娃", + "InfoLabels": { + "ValidGump": "有效Gump", + "ValidAnim": "有效动画", + "ValidLayer": "有效层", + "ObjType": "对象类型", + "Animation": "动画", + "Hue": "色调", + "Layer": "层", + "GumpID": "Gump ID" + } + }, + "RadarColorControl": { + "selectInItemsTabToolStripMenuItem": "在 Items 标签页中选择", + "selectInTiledataTabToolStripMenuItem": "在 TileData 标签页中选择", + "setAsRangeFromToolStripMenuItem": "设置为范围\"起点\"", + "setAsRangeToToolStripMenuItem": "设置为范围\"终点\"", + "toolStripMenuItem1": "在 LandTiles 标签页中选择", + "toolStripMenuItem2": "在 TileData 标签页中选择", + "changeBackgroundColorToolStripMenuItem": "更改背景颜色", + "buttonSelectAllItems": "全选", + "buttonSelectNoneItems": "取消全选", + "buttonMean": "平均颜色", + "buttonRevert": "恢复", + "buttonStrategyHelp": "帮助", + "buttonSaveColor": "保存颜色", + "buttonCurrentToRangeAverage": "当前图形 → 选中集合平均", + "buttonRangeToIndividualAverage": "选中图形 → 单个平均", + "buttonRangeToRangeAverage": "选中图形 → 集合平均", + "buttonSaveFile": "保存文件", + "buttonRevertAll": "全部恢复", + "buttonExport": "导出...", + "buttonImport": "导入...", + "buttonAverageAll": "自动填充空项", + "groupColor": "颜色预览", + "groupColorValue": "颜色值", + "groupSingleTile": "单个图形", + "groupBatch": "批量处理", + "groupFile": "文件", + "labelHex": "十六进制:", + "labelRgb": "RGB:", + "label4": "→", + "labelItemsProgress": "物品:", + "labelLandProgress": "地形:", + "tabPageItems": "物品", + "tabPageLand": "地形", + "textFilterItems": "筛选", + "textFilterLand": "筛选", + "textBoxMeanFrom": "从", + "textBoxMeanTo": "至", + "textBoxMeanFromPlaceholder": "从", + "textBoxMeanToPlaceholder": "至", + "radioUseSelection": "选中的图形", + "radioUseRange": "范围:", + "toolTipStrategyHelp": "关于这些平均策略", + "Messages": { + "InvalidParametersRange": "参数无效。期望 [终点, 起点] 在 [0, {0}] 之间", + "InvalidParametersNoSelection": "参数无效。未选中任何图形。", + "ItemsTabNotLoaded": "需要先加载 Items 标签页。", + "LandTilesTabNotLoaded": "需要先加载 LandTiles 标签页。", + "ConfirmRevertAll": "确定要恢复所有物品和地形图形的修改吗?", + "ConfirmAutoFillEmpty": "确定要为所有黑色或缺失颜色的物品和地形图形条目计算并设置新的雷达颜色吗?", + "OperationDone": "完成!", + "RadarColorSaved": "雷达颜色保存成功。", + "RadarColSaved": "RadarCol 保存成功。" + }, + "AveragingStrategies": { + "Mean5": "均值 (5 位)", + "Mean8Shift": "均值 (8 位, >>3 打包)", + "Mean5Rounded": "均值 (5 位, 四舍五入)", + "Mean5RoundedIncludeAlpha": "均值 (5 位, 四舍五入, 含透明)", + "Mean5BankersRound": "均值 (5 位, 银行家舍入)", + "Mean5RoundedNoOutline": "均值 (5 位, 四舍五入, 无轮廓)", + "SnapToLandPalette": "对齐地形调色板", + "SnapToItemPalette": "对齐物品调色板", + "MeanRounded": "均值 (8 位, 四舍五入打包)", + "MeanLinear": "均值 (线性光)", + "Mode16": "众数 (主导像素)", + "MedianPerChannel": "每通道中位数", + "MeanNoOutline": "均值 (无轮廓)", + "Legacy": "旧版 (UOFiddler)" + }, + "DynamicButtonTexts": { + "buttonCurrentToRangeAverage_selection": "当前图形 → 选中集合平均", + "buttonCurrentToRangeAverage_range": "当前图形 → 范围平均", + "buttonRangeToIndividualAverage_selection": "选中图形 → 单个平均", + "buttonRangeToIndividualAverage_range": "范围内图形 → 单个平均", + "buttonRangeToRangeAverage_selection": "选中图形 → 集合平均", + "buttonRangeToRangeAverage_range": "范围内图形 → 范围平均" + }, + "MessageBoxTitles": { + "Error": "错误", + "Information": "信息", + "RevertAll": "全部恢复", + "AverageAll": "自动填充", + "AlgorithmBenchmark": "算法基准测试" + }, + "buttonSelectAllLand": "全选", + "buttonSelectNoneLand": "取消全选", + "setAsRangeFromToolStripMenuItem1": "设置为范围\"起点\"", + "setAsRangeToToolStripMenuItem1": "设置为范围\"终点\"" + }, + "TileDataControl": { + "TabPages": { + "tabPageItems": "物品", + "tabPageLand": "地形磁贴" + }, + "Labels": { + "nameLabel": "名称", + "animLabel": "动画", + "weightLabel": "重量", + "layerLabel": "层级", + "quantityLabel": "数量", + "valueLabel": "价值", + "stackOffLabel": "堆叠偏移", + "hueLabel": "色调", + "unknown2Label": "未知值2", + "miscDataLabel": "杂项数据", + "heightLabel": "高度", + "unknown3Label": "未知值3", + "landNameLabel": "名称", + "landTexIdLabel": "纹理 ID" + }, + "Toolbar": { + "toolStripLabel1": "索引:", + "toolStripLabel2": "名称:", + "searchByNameToolStripButton": "查找下一个", + "toolStripButton1": "导出为 CSV", + "toolStripButton5": "从 CSV 导入", + "toolStripButton4": "保存更改", + "toolStripButton3": "保存 TileData", + "helpToolStripButton": "帮助" + }, + "MiscMenu": { + "memorySaveWarning": "内存保存警告", + "saveDirectlyOnChanges": "更改时直接保存", + "setFilter": "设置过滤器", + "undoBulkApply": "撤销上次批量应用", + "setTextureOnDoubleClick": "双击时设置 TexID", + "setTextures": "设置纹理" + }, + "ItemsContextMenu": { + "copyTileData": "复制图块数据", + "pasteSpecial": "粘贴特殊...", + "selectInItems": "在物品标签中选择", + "selectInRadarColor": "在雷达颜色标签中选择", + "selectInGumpsMale": "在 Gumps 中选择 (男)", + "selectInGumpsFemale": "在 Gumps 中选择 (女)", + "selectInAnimData": "在动画数据标签中选择" + }, + "LandContextMenu": { + "copyTileData": "复制图块数据", + "pasteSpecial": "粘贴特殊...", + "selectInLandTiles": "在地形标签中选择", + "selectInRadarColor": "在雷达颜色标签中选择" + }, + "PictureBoxMenu": { + "changeBackgroundColor": "改变背景颜色" + }, + "FileDialogs": { + "importCsvTitle": "选择要导入的 csv 文件", + "importCsvFilter": "csv 文件 (*.csv)|*.csv", + "tileDataSavedMessage": "TileData 已成功保存。", + "itemDataSavedMessage": "ItemData 已成功保存。", + "landDataSavedMessage": "LandData 已成功保存。" + }, + "MessageBoxes": { + "SetTextures": { + "Title": "设置纹理", + "ConfirmMessage": "您想为所有地形设置 TexID 吗?\n\n此操作假设地形图块索引值等于纹理索引值。\n\n它只考虑 TexID 为 0 的地形磁贴。\n\n继续?", + "SuccessMessage": "已更新 {0} 个地形。", + "NothingUpdated": "未更新任何内容。" + }, + "BulkApply": { + "Title": "应用到选择", + "ConfirmMessage": "应用 {0} 到 {1} 项?", + "EmptyFieldsMessage": "没有要应用的内容 - 所有框都为空,所有标志保持不变。" + }, + "BulkApplyResult": { + "Title": "已保存", + "Message": "已在内存中应用 {0} 到 {1} 项。\r\n\r\n单击'保存 TileData'写入文件。" + }, + "MemorySaveWarning": { + "Title": "信息", + "ItemMessage": "编辑 0x{0:X4} ({0}) 已保存到内存。单击'保存 TileData'写入文件。", + "LandMessage": "编辑 0x{0:X4} ({0}) 已保存到内存。单击'保存 TileData'写入文件。" + }, + "UndoBulkApply": { + "Title": "撤销批量应用", + "ConfirmMessage": "将 {0} 项恢复到应用'{1}'之前的值?" + }, + "TabNotLoaded": { + "ItemsTabMessage": "您需要先加载物品标签页。", + "LandTabMessage": "您需要先加载地形标签页。" + }, + "SelectionError": { + "NoItemSelected": "您需要先选择一个物品。", + "NoLandSelected": "您需要先选择一个地形。", + "NothingToPaste": "您需要先复制某些内容才能粘贴。" + }, + "Messages": { + "emptyApply": "没有要应用的内容 - 所有框都为空,所有标志保持不变。", + "applyToSelection": "应用到选择", + "saved": "已保存", + "applyToMany": "应用 {0} 到 {1} 项?", + "appliedToMany": "已在内存中应用 {0} 到 {1} 项。\r\n\r\n单击'保存 TileData'写入文件。", + "undoConfirm": "将 {0} 项恢复到应用'{1}'之前的值?", + "undoBulkApply": "撤销批量应用", + "needLoadItems": "您需要先加载物品标签页。", + "needLoadLand": "您需要先加载地形标签页。", + "setTextures": "设置纹理", + "texturesUpdated": "已更新 {0} 个地形。", + "texturesNothing": "未更新任何内容。", + "information": "信息", + "setTexturesConfirm": "您想为所有地形设置 TexID 吗?\n\n此操作假设地形图块索引值等于纹理索引值。\n\n它只考虑 TexID 为 0 的地形磁贴。\n\n继续?" + } + }, + "TileFlags": { + "Background": "背景", + "Weapon": "武器", + "Transparent": "透明", + "Translucent": "半透明", + "Wall": "墙壁", + "Damaging": "伤害性", + "Impassable": "不可通行", + "Wet": "潮湿", + "Unknown1": "未知值1", + "Surface": "表面", + "Bridge": "桥梁", + "Generic": "通用", + "Window": "窗户", + "NoShoot": "不可射击", + "ArticleA": "冠词A", + "ArticleAn": "冠词An", + "ArticleThe": "冠词The", + "Foliage": "树叶", + "PartialHue": "部分色调", + "NoHouse": "不可建造房屋", + "Map": "地图", + "Container": "容器", + "Wearable": "可穿戴", + "LightSource": "光源", + "Animation": "动画", + "HoverOver": "悬停", + "NoDiagonal": "不可对角线", + "Armor": "护甲", + "Roof": "屋顶", + "Door": "门", + "StairBack": "后楼梯", + "StairRight": "右楼梯", + "AlphaBlend": "透明混合", + "UseNewArt": "使用新美术", + "ArtUsed": "已使用美术", + "Unused8": "未使用8", + "NoShadow": "无阴影", + "PixelBleed": "像素渗漏", + "PlayAnimOnce": "播放一次动画", + "MultiMovable": "多部件可移动", + "Unused10": "未使用10", + "Unused11": "未使用11", + "Unused12": "未使用12", + "Unused13": "未使用13", + "Unused14": "未使用14", + "Unused15": "未使用15", + "Unused16": "未使用16", + "Unused17": "未使用17", + "Unused18": "未使用18", + "Unused19": "未使用19", + "Unused20": "未使用20", + "Unused21": "未使用21", + "Unused22": "未使用22", + "Unused23": "未使用23", + "Unused24": "未使用24", + "Unused25": "未使用25", + "Unused26": "未使用26", + "Unused27": "未使用27", + "Unused28": "未使用28", + "Unused29": "未使用29", + "Unused30": "未使用30", + "Unused31": "未使用31", + "Unused32": "未使用32" + }, + "Tooltips": { + "name": "此字段用于物品名称,最多可为20个字符。", + "anim": "此字段用于与物品关联的动画ID。", + "weight": "此字段用于物品的重量。", + "layer": "此字段用于物品的层级:\n\n1 单手武器\n2 双手武器、盾牌或杂项\n3 鞋类\n4 裤子\n5 衬衣\n6 头盔/衣领\n7 手套\n8 戒指\n9 护身符\n10 项链\n11 头发\n12 腰部(半围裙)\n13 躯干(内侧)(胸甲)\n14 手镯\n15 未使用(但背包使用第21层)\n16 面部毛发\n17 躯干(中间)(超衣、束腰外衣、满围裙、腰带)\n18 耳环\n19 手臂\n20 背部(斗篷)\n21 背包\n22 躯干(外侧)(长袍)\n23 腿部(外侧)(裙子/短裙)\n24 腿部(内侧)(腿甲)\n25 坐骑(马、鸵鸟等)\n26 NPC购买补货容器\n27 NPC购买无补货容器\n28 NPC销售容器", + "quantity": "此字段用于物品的数量。", + "value": "此字段用于物品的价值。", + "stackOff": "堆叠偏移是指多个物品堆叠时以像素为单位的堆叠偏移。\n更高的堆叠偏移值意味着物品在堆栈中出现的距离会更远。", + "hue": "此字段用于物品的色调(颜色)。", + "unknown2": "此字段用于第二个未知值。", + "miscData": "旧UO演示武器模板定义", + "height": "此字段用于物品的高度。", + "unknown3": "此字段用于第三个未知值。", + "landName": "此字段用于地形图块名称,最多可为20个字符。", + "landTexId": "此字段用于与地形图块关联的纹理ID。" + } + }, + "TileDataFilterForm": { + "Title": "设置过滤器", + "ItemsTab": "物品过滤器", + "LandTab": "地形过滤器", + "ApplyButton": "应用过滤器", + "ResetButton": "重置过滤器", + "Labels": { + "Name": "名称", + "Anim": "动画", + "Weight": "重量", + "Quality": "品质", + "Quantity": "数量", + "Hue": "色调", + "StackOff": "堆叠偏移", + "Value": "价值", + "Height": "高度", + "MiscData": "杂项数据", + "Unk2": "未知值2", + "Unk3": "未知值3", + "NameLand": "名称", + "TexID": "纹理ID" + } + }, + "TileDataHelpForm": { + "Title": "TileData 帮助", + "FieldColumnHeader": "字段", + "CloseButton": "关闭", + "EditingManyEntries": { + "Title": "编辑多个条目", + "MultiSelect": { + "Title": "多选", + "Description": "Ctrl+点击和Shift+点击可以一次选择多个条目。右侧窗格保持显示您最后选择的条目,'保存更改'会写入到每个选定的条目。" + }, + "EmptyBoxes": { + "Title": "空框", + "Description": "选择多个条目时,条目之间不一致的框会显示为空。保存时会忽略空框——条目保留自己的值。填充它可以给它们全部相同的值。因为空意味着'保留不变',在多选中不能清除名称;单独选择条目来执行该操作。" + }, + "GreyedFlags": { + "Title": "灰化标志", + "Description": "在某些选定条目上设置但不是全部的标志显示为灰化。保存时灰化标志保留不变。单击它会循环:保留不变 -> 全部打开 -> 全部关闭。它们都已同意的标志只是切换。" + }, + "CopyPasteSpecial": { + "Title": "复制/特殊粘贴", + "Description": "右键单击条目并选择'复制图块数据'以记住它,然后选择任意数量的条目并选择'特殊粘贴...'来选择复制的字段和标志中的哪些写入它们。" + }, + "Undo": { + "Title": "撤销", + "Description": "杂项 -> '撤销最后批量应用'会恢复条目在最后应用前的值。仅保留最近的应用,当重新加载tiledata时会忘记。" + } + }, + "Items": { + "Title": "物品", + "Name": { + "Title": "名称", + "Description": "此字段用于物品名称,最多可为20个字符。" + }, + "Animation": { + "Title": "动画", + "Description": "此字段用于与物品关联的动画ID。" + }, + "Weight": { + "Title": "重量", + "Description": "此字段用于物品的重量。" + }, + "Layer": { + "Title": "层级", + "Description": "此字段用于物品的层级:\n\n1 单手武器\n2 双手武器、盾牌或杂项\n3 鞋类\n4 裤子\n5 衬衫\n6 头盔/颈部\n7 手套\n8 戒指\n9 护身符\n10 项链\n11 头发\n12 腰部(半围裙)\n13 躯干(内侧)(胸甲)\n14 手镯\n15 未使用(但背包使用第21层)\n16 面部毛发\n17 躯干(中间)(超衣、束腰外衣、满围裙、腰带)\n18 耳环\n19 手臂\n20 背部(斗篷)\n21 背包\n22 躯干(外侧)(长袍)\n23 腿部(外侧)(裙子/短裙)\n24 腿部(内侧)(腿甲)\n25 坐骑(马、鸵鸟等)\n26 NPC购买补货容器\n27 NPC购买无补货容器\n28 NPC销售容器" + }, + "Quantity": { + "Title": "数量", + "Description": "此字段用于物品的数量。" + }, + "Value": { + "Title": "价值", + "Description": "此字段用于物品的价值。" + }, + "StackingOffset": { + "Title": "堆叠偏移", + "Description": "堆叠偏移是指多个物品堆叠时以像素为单位的堆叠偏移。\n更高的堆叠偏移值意味着物品在堆栈中出现的距离会更远。" + }, + "Hue": { + "Title": "色调", + "Description": "此字段用于物品的色调(颜色)。" + }, + "Unknown2": { + "Title": "未知值2", + "Description": "此字段用于第二个未知值。" + }, + "MiscData": { + "Title": "杂项数据", + "Description": "旧UO演示武器模板定义" + }, + "Height": { + "Title": "高度", + "Description": "此字段用于物品的高度。" + }, + "Unknown3": { + "Title": "未知值3", + "Description": "此字段用于第三个未知值。" + } + }, + "LandTiles": { + "Title": "地形图块", + "Name": { + "Title": "名称", + "Description": "此字段用于地形图块名称,最多可为20个字符。" + }, + "TextureID": { + "Title": "纹理ID", + "Description": "此字段用于与地形图块关联的纹理ID。" + } + }, + "Flags": { + "Title": "标志", + "Background": { + "Description": "尚未记录。", + "Title": "背景" + }, + "Weapon": { + "Description": "尚未记录。", + "Title": "武器" + }, + "Transparent": { + "Description": "尚未记录。", + "Title": "透明" + }, + "Translucent": { + "Description": "图块以部分透明度渲染。", + "Title": "半透明" + }, + "Wall": { + "Description": "图块是墙。", + "Title": "墙" + }, + "Damaging": { + "Description": "图块在移动时可能造成伤害。", + "Title": "伤害" + }, + "Impassable": { + "Description": "图块不可移动或穿过。", + "Title": "不可通行" + }, + "Wet": { + "Description": "尚未记录。", + "Title": "湿润" + }, + "Unknown1": { + "Description": "未知。", + "Title": "未知1" + }, + "Surface": { + "Description": "图块是表面。它可以被移动,但不能穿过。", + "Title": "表面" + }, + "Bridge": { + "Description": "图块是楼梯、坡道或梯子。", + "Title": "桥梁" + }, + "Generic": { + "Description": "图块是可堆叠的。", + "Title": "通用" + }, + "Window": { + "Description": "图块是窗口。与NoShoot一样,带有此标志的图块阻挡视线。", + "Title": "窗户" + }, + "NoShoot": { + "Description": "图块阻挡视线。", + "Title": "不穿透" + }, + "ArticleA": { + "Description": "对于单数量图块,应在图块名称前加上\"一个 \"。", + "Title": "冠词A" + }, + "ArticleAn": { + "Description": "对于单数量图块,应在图块名称前加上\"一个 \"。", + "Title": "冠词An" + }, + "ArticleThe": { + "Description": "可能在图块名称前加上\"该\"。", + "Title": "冠词The" + }, + "Foliage": { + "Description": "走在后面时图块变为半透明。船桅也有此标志。", + "Title": "树叶" + }, + "PartialHue": { + "Description": "仅灰色像素会被着色。", + "Title": "部分着色" + }, + "NoHouse": { + "Description": "NoHouse或未知。需要进一步研究。", + "Title": "非房屋" + }, + "Map": { + "Description": "图块在制图学意义上是地图。未知用途。", + "Title": "地图" + }, + "Container": { + "Description": "图块是容器。", + "Title": "容器" + }, + "Wearable": { + "Description": "图块可以被装备。", + "Title": "可穿戴" + }, + "LightSource": { + "Description": "图块发出光。", + "Title": "光源" + }, + "Animation": { + "Description": "图块是动画的。", + "Title": "动画" + }, + "HoverOver": { + "Description": "石像鬼可以飞过,或NoDiagonal。", + "Title": "悬停通过" + }, + "NoDiagonal": { + "Description": "NoDiagonal(未知3)。", + "Title": "无对角线" + }, + "Armor": { + "Description": "尚未记录。", + "Title": "护甲" + }, + "Roof": { + "Description": "图块是斜面屋顶。", + "Title": "屋顶" + }, + "Door": { + "Description": "图块是门。带有此标志的图块可以被幽灵和GM穿过。", + "Title": "门" + }, + "StairBack": { + "Description": "尚未记录。", + "Title": "后楼梯" + }, + "StairRight": { + "Description": "尚未记录。", + "Title": "右楼梯" + }, + "AlphaBlend": { + "Description": "混合Alphas,图块混合。", + "Title": "透明混合" + }, + "UseNewArt": { + "Description": "使用新美术风格?与nodraw图块相关?", + "Title": "使用新美术" + }, + "ArtUsed": { + "Description": "有美术被使用?", + "Title": "已使用美术" + }, + "Unused8": { + "Description": "未使用或未知。", + "Title": "未使用8" + }, + "NoShadow": { + "Description": "不允许此图块上的阴影,光源?熔岩?", + "Title": "无阴影" + }, + "PixelBleed": { + "Description": "让像素渗透到其他图块?这是禁用纹理夹具?", + "Title": "像素渗漏" + }, + "PlayAnimOnce": { + "Description": "播放一次图块动画。", + "Title": "播放一次动画" + }, + "MultiMovable": { + "Description": "可移动多重部件?很酷的船和车辆等?与桅杆相关的东西?", + "Title": "多部件可移动" + }, + "Unused10": { + "Description": "未使用或未知。", + "Title": "未使用10" + }, + "Unused11": { + "Description": "未使用或未知。", + "Title": "未使用11" + }, + "Unused12": { + "Description": "未使用或未知。", + "Title": "未使用12" + }, + "Unused13": { + "Description": "未使用或未知。", + "Title": "未使用13" + }, + "Unused14": { + "Description": "未使用或未知。", + "Title": "未使用14" + }, + "Unused15": { + "Description": "未使用或未知。", + "Title": "未使用15" + }, + "Unused16": { + "Description": "未使用或未知。", + "Title": "未使用16" + }, + "Unused17": { + "Description": "未使用或未知。", + "Title": "未使用17" + }, + "Unused18": { + "Description": "未使用或未知。", + "Title": "未使用18" + }, + "Unused19": { + "Description": "未使用或未知。", + "Title": "未使用19" + }, + "Unused20": { + "Description": "未使用或未知。", + "Title": "未使用20" + }, + "Unused21": { + "Description": "未使用或未知。", + "Title": "未使用21" + }, + "Unused22": { + "Description": "未使用或未知。", + "Title": "未使用22" + }, + "Unused23": { + "Description": "未使用或未知。", + "Title": "未使用23" + }, + "Unused24": { + "Description": "未使用或未知。", + "Title": "未使用24" + }, + "Unused25": { + "Description": "未使用或未知。", + "Title": "未使用25" + }, + "Unused26": { + "Description": "未使用或未知。", + "Title": "未使用26" + }, + "Unused27": { + "Description": "未使用或未知。", + "Title": "未使用27" + }, + "Unused28": { + "Description": "未使用或未知。", + "Title": "未使用28" + }, + "Unused29": { + "Description": "未使用或未知。", + "Title": "未使用29" + }, + "Unused30": { + "Description": "未使用或未知。", + "Title": "未使用30" + }, + "Unused31": { + "Description": "未使用或未知。", + "Title": "未使用31" + }, + "Unused32": { + "Description": "未使用或未知。", + "Title": "未使用32" + } + } + }, + "StrategyHelpForm": { + "Title": "雷达颜色 — 平均策略", + "CloseButton": "关闭", + "HelpText": "雷达颜色平均策略\r\n================================\r\n\r\nradarcol.mul 中的每个条目都是一个 16 位 (RGB555) 颜色,用于渲染世界地图。当你点击\"平均颜色\"时,UOFiddler 会从图块的像素计算该颜色。有多种方法来计算平均值;它们在四舍五入、源像素选择和色彩空间方面有所不同。\r\n\r\n基准测试的发现\r\n-------------------------------\r\n每种策略都针对 radarcol.mul 中的值进行了评分。发现如下:\r\n\r\n - 产生文件值的 24->15 位缩放使用位移 (>>3),而不是旧版本 UOFiddler 使用的 *31/255 四舍五入。\r\n\r\n - 对于物品 (静态):值可以通过对图块 5 位像素的按通道算术平均(四舍五入)再现。\"均值 (5 位, 银行家舍入)\" 在约 96.6% 的 13,771 个物品条目上与文件逐字节匹配;余数是小于 1 步的误差。\r\n\r\n - 对于地形图块:4,239 个活跃条目总共只使用约 103 个独特颜色。这是手工调整的地形调色板,不是计算结果。没有像素平均算法与超过约 10% 的地形条目匹配。\"对齐地形调色板\"从文件中已有的颜色中选择最接近的,保持地形的连贯性。\r\n\r\n黄金标准 (推荐默认值)\r\n------------------------------------\r\n - 物品标签页 -> 均值 (5 位, 银行家舍入) [实际默认值]\r\n - 地形标签页 -> 对齐地形调色板\r\n\r\n切换标签页时手动切换下拉列表;选择在此会话中保持不变。\r\n\r\n详细策略\r\n--------------------\r\n\r\n均值 (5 位)\r\n 提取每个非透明像素的 5 位 R/G/B,求和,除以计数(截断)。简单但每通道有约半步的深色偏差。物品匹配约 13%。\r\n\r\n均值 (5 位, 四舍五入)\r\n 与均值 (5 位) 相同,但使用四舍五入 ((sum + n/2) / n)。物品匹配 96.4%。\r\n\r\n均值 (5 位, 银行家舍入) [默认]\r\n 相同,但使用银行家舍入 (四舍五入到最近的偶数)。经验上的赢家:物品匹配 96.6%。\r\n\r\n均值 (5 位, 四舍五入, 含透明)\r\n 在除数中包含透明像素 (值 0)。将平均值拖向 0;仅用于诊断。\r\n\r\n均值 (8 位, >>3 打包)\r\n 将每个 5 位像素扩展到 8 位 (通过 (c<<3)|(c>>2)),在 8 位空间中平均,然后用位移打包回 555。中间结果:物品匹配约 42%。\r\n\r\n均值 (8 位, 四舍五入打包)\r\n 与均值 (8 位) 相同,但用 ((c*31+127)/255) 打包。匹配约 69%。\r\n\r\n均值 (5 位, 四舍五入, 无轮廓)\r\n 在平均前排除接近黑色的像素 (5 位 Y < 2)。测试 90 年代精灵的常见技巧。实际上比四舍五入平均值更差 (约 44%)。\r\n\r\n均值 (线性光)\r\n 将 sRGB 伽马校正为线性 (x*x),平均,然后回到 sRGB。对照组;1997 年的美术管道几乎肯定没有伽马感知。低匹配率 (约 11%)。\r\n\r\n众数 (主导像素)\r\n 返回最常见的单个非透明像素。对于非常均匀的图块很有用,其他地方则很嘈杂。匹配约 5%。\r\n\r\n每通道中位数\r\n 在 5 位空间中独立的每通道中位数。对异常值稳健,但与文件值不匹配 (约 15%)。\r\n\r\n均值 (无轮廓)\r\n 纯 5 位截断平均值,带轮廓拒绝。(约 19%。)\r\n\r\n对齐地形调色板\r\n 计算银行家舍入 5 位平均值,然后将结果对齐到 radarcol.mul 中已有的约 103 个地形颜色中最接近的一个 (5 位欧几里得距离)。当你希望新的/编辑的地形图块看起来像邻近图块而不是产生自由形式颜色时的正确选择。\r\n\r\n对齐物品调色板\r\n 类似于对齐地形调色板,但使用约 2,200 个独特物品颜色。对物品的用处较小 (它们的值是真正计算出来的);为对称而保留。\r\n\r\n旧版 (UOFiddler)\r\n 逐位再现 UOFiddler 的早期行为:在 *31/255 截断 8 位空间中平均,用相同的截断重新打包。仅与约 1.9% 的文件值匹配。为了与较旧的 UOFiddler 版本保持连续性而保留。\r\n\r\n透明度和限制\r\n-----------------------\r\n所有策略 (除了\"含透明\") 都跳过等于 0 的像素 (透明)。限制规则应用于输出:如果所有分量缩放为 0 但输入非零,强制该通道为 1。纯黑不透明像素不能在 24->15 位缩放中幸存;它们在运行时和我们的平均中都显示为透明。\r\n\r\n工具\r\n-----\r\n - 在调试版本中,此控件上的\"算法基准 (CSV)\"按钮针对加载的 radarcol.mul 运行每个策略,并将每个策略的报告写入 Options.OutputPath\\radarcol_eval.csv。\r\n" + } + }, + "Ultima": { + "Maps": { + "MapSizes": { + "NoMapOrStaticsIndex": "无法找到地号 {0} 的地图或静态物品索引;假设 {1}", + "HoldsBlocks": "{0} 包含 {1:N0} 块,即 {2}", + "HoldsBlocksNoMatch": "{0} 包含 {1:N0} 块,与地号 {2} 的已知形状均不匹配;假设 {3}" + } + } + } +} diff --git a/UoFiddler/Localization/LocalizationExtensions.cs b/UoFiddler/Localization/LocalizationExtensions.cs new file mode 100644 index 00000000..4d619ff7 --- /dev/null +++ b/UoFiddler/Localization/LocalizationExtensions.cs @@ -0,0 +1,26 @@ +using System.Windows.Forms; + +namespace UoFiddler.Localization +{ + /// + /// 本地化扩展方法 + /// + public static class LocalizationExtensions + { + /// + /// 为Form启用本地化 + /// + public static void ApplyLocalization(this Form form) + { + LocalizationService.LocalizeForm(form); + } + + /// + /// 获取本地化字符串 + /// + public static string GetLocalized(this string key) + { + return LocalizationService.GetString(key) ?? key; + } + } +} diff --git a/UoFiddler/Localization/LocalizationService.cs b/UoFiddler/Localization/LocalizationService.cs new file mode 100644 index 00000000..35770579 --- /dev/null +++ b/UoFiddler/Localization/LocalizationService.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace UoFiddler.Localization +{ + /// + /// 本地化服务 - 管理UI控件的多语言翻译 + /// + public static class LocalizationService + { + private static StringDictionary? _currentDictionary; + private static string _dictionariesPath = ""; + private static string _currentLanguage = "en-US"; + + /// + /// 初始化本地化服务 + /// + public static void Initialize(string dictionariesPath) + { + _dictionariesPath = dictionariesPath; + Console.WriteLine($"[LocalizationService] ========================================"); + Console.WriteLine($"[LocalizationService] 初始化本地化服务"); + Console.WriteLine($"[LocalizationService] 字典路径: {_dictionariesPath}"); + Console.WriteLine($"[LocalizationService] 路径是否存在: {Directory.Exists(_dictionariesPath)}"); + Console.WriteLine($"[LocalizationService] ========================================"); + } + + /// + /// 设置当前语言并加载对应字典 + /// + public static void SetLanguage(string languageCode) + { + _currentLanguage = languageCode; + _currentDictionary = new StringDictionary(languageCode); + + string dictionaryFile = Path.Combine(_dictionariesPath, $"{languageCode}.json"); + Console.WriteLine($"[LocalizationService] 设置语言: {languageCode}"); + Console.WriteLine($"[LocalizationService] 字典文件: {dictionaryFile}"); + Console.WriteLine($"[LocalizationService] 文件是否存在: {File.Exists(dictionaryFile)}"); + + _currentDictionary.LoadFromFile(dictionaryFile); + + Console.WriteLine($"[LocalizationService] 字典加载完成"); + } + + /// + /// 获取翻译字符串 + /// + public static string? GetString(string key) + { + return _currentDictionary?.GetString(key); + } + + /// + /// 对整个Form进行本地化处理 + /// + public static void LocalizeForm(Form form) + { + if (_currentDictionary == null) + { + Console.WriteLine("[LocalizationService] 字典未初始化"); + return; + } + + try + { + Console.WriteLine($"[LocalizationService] 开始本地化Form: {form.Name}"); + + // 本地化Form标题 + string? formTitle = GetString($"Forms.{form.Name}.Title"); + if (!string.IsNullOrEmpty(formTitle)) + { + form.Text = formTitle; + Console.WriteLine($"[LocalizationService] Form标题已本地化: {formTitle}"); + } + + // 递归本地化所有控件 + LocalizeControls(form.Controls, form.Name); + + // 本地化菜单栏 + if (form.MainMenuStrip != null) + { + Console.WriteLine($"[LocalizationService] 检测到MainMenuStrip,开始本地化菜单"); + LocalizeMenuStrip(form.MainMenuStrip, form.Name); + } + else + { + Console.WriteLine($"[LocalizationService] 警告: Form没有MainMenuStrip"); + } + + // 本地化工具栏中的菜单按钮 (用于ToolStrip中的ToolStripDropDownButton) + foreach (var toolStrip in form.Controls.OfType()) + { + Console.WriteLine($"[LocalizationService] 检测到ToolStrip,开始本地化其中的菜单按钮"); + LocalizeToolStripItems(toolStrip.Items, form.Name); + } + } + catch (Exception ex) + { + Console.WriteLine($"[LocalizationService] 本地化Form失败: {ex.Message}\n{ex.StackTrace}"); + } + } + + /// + /// 本地化ToolStrip中的所有项 + /// + private static void LocalizeToolStripItems(ToolStripItemCollection items, string formName) + { + foreach (var item in items) + { + if (item is ToolStripDropDownButton dropDownBtn) + { + Console.WriteLine($"[LocalizationService] 处理ToolStripDropDownButton: {dropDownBtn.Name}"); + LocalizeToolStripItem(dropDownBtn, formName); + } + else if (item is ToolStripMenuItem menuItem) + { + Console.WriteLine($"[LocalizationService] 处理ToolStripMenuItem: {menuItem.Name}"); + LocalizeMenuStripItem(menuItem, formName); + } + else if (item is ToolStripButton button) + { + Console.WriteLine($"[LocalizationService] 处理ToolStripButton: {button.Name}"); + LocalizeToolStripButton(button, formName); + } + } + } + + /// + /// 本地化ToolStripDropDownButton和其他ToolStripItem + /// + private static void LocalizeToolStripItem(ToolStripDropDownButton item, string formName) + { + if (!string.IsNullOrEmpty(item.Name)) + { + string key = $"Forms.{formName}.Menus.{item.Name}"; + string? localizedText = GetString(key); + if (!string.IsNullOrEmpty(localizedText)) + { + Console.WriteLine($"[LocalizationService] 菜单按钮 {item.Name} → {localizedText}"); + item.Text = localizedText; + } + } + + // 递归处理子项 + foreach (ToolStripMenuItem subItem in item.DropDownItems.OfType()) + { + LocalizeMenuStripItem(subItem, formName); + } + } + + /// + /// 本地化ToolStripButton + /// + private static void LocalizeToolStripButton(ToolStripButton button, string formName) + { + if (!string.IsNullOrEmpty(button.Name)) + { + string? localizedText = GetString($"Forms.{formName}.{button.Name}"); + if (!string.IsNullOrEmpty(localizedText)) + { + Console.WriteLine($"[LocalizationService] ToolStripButton {button.Name} → {localizedText}"); + button.Text = localizedText; + } + } + } + + /// + /// 本地化菜单条 + /// + private static void LocalizeMenuStrip(MenuStrip menuStrip, string formName) + { + foreach (ToolStripMenuItem item in menuStrip.Items.OfType()) + { + LocalizeMenuStripItem(item, formName); + } + } + + /// + /// 递归本地化菜单项 + /// + private static void LocalizeMenuStripItem(ToolStripMenuItem menuItem, string formName) + { + if (!string.IsNullOrEmpty(menuItem.Name)) + { + string? localizedText = GetString($"Forms.{formName}.Menus.{menuItem.Name}"); + if (!string.IsNullOrEmpty(localizedText)) + { + menuItem.Text = localizedText; + } + } + + // 递归处理子菜单 + foreach (ToolStripMenuItem subItem in menuItem.DropDownItems.OfType()) + { + LocalizeMenuStripItem(subItem, formName); + } + } + + /// + /// 递归本地化控件集合 + /// + private static void LocalizeControls(Control.ControlCollection controls, string formName) + { + foreach (Control control in controls) + { + LocalizeControl(control, formName); + + // 递归处理子控件 + if (control.HasChildren) + { + LocalizeControls(control.Controls, formName); + } + } + } + + /// + /// 本地化单个控件 + /// + private static void LocalizeControl(Control control, string formName) + { + if (string.IsNullOrEmpty(control.Name) || control.Name.StartsWith("_")) + return; + + // 按类型处理不同的控件 + if (control is TabControl tabControl) + { + LocalizeTabPages(tabControl, formName); + } + else if (control is Button || control is CheckBox || control is Label || + control is GroupBox || control is RadioButton) + { + // 普通文本控件 - 尝试两种键格式 + string? localizedText = GetString($"Forms.{formName}.{control.Name}.Text"); + if (string.IsNullOrEmpty(localizedText)) + { + // 如果没有找到 .Text 后缀的键,尝试直接使用控件名 + localizedText = GetString($"Forms.{formName}.{control.Name}"); + } + + if (!string.IsNullOrEmpty(localizedText)) + { + Console.WriteLine($"[LocalizationService] 控件 {control.Name} → {localizedText}"); + control.Text = localizedText; + } + } + } + + /// + /// 本地化TabControl的标签页 + /// + private static void LocalizeTabPages(TabControl tabControl, string formName) + { + Console.WriteLine($"[LocalizationService] 本地化TabControl,共{tabControl.TabPages.Count}个标签页"); + + foreach (TabPage tabPage in tabControl.TabPages) + { + if (!string.IsNullOrEmpty(tabPage.Name)) + { + string key = $"Forms.{formName}.Tabs.{tabPage.Name}"; + string? localizedText = GetString(key); + if (!string.IsNullOrEmpty(localizedText)) + { + Console.WriteLine($"[LocalizationService] 标签页 {tabPage.Name} → {localizedText}"); + tabPage.Text = localizedText; + } + } + + // 本地化标签页内的控件 + LocalizeControls(tabPage.Controls, formName); + } + } + + /// + /// 获取当前语言代码 + /// + public static string GetCurrentLanguage() => _currentLanguage; + } +} diff --git a/UoFiddler/Localization/StringDictionary.cs b/UoFiddler/Localization/StringDictionary.cs new file mode 100644 index 00000000..14d81295 --- /dev/null +++ b/UoFiddler/Localization/StringDictionary.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +namespace UoFiddler.Localization +{ + /// + /// 字符串字典 - 从JSON文件加载多语言字符串 + /// + public class StringDictionary + { + private readonly Dictionary _strings = new(); + private readonly string _languageCode; + + public StringDictionary(string languageCode) + { + _languageCode = languageCode; + } + + /// + /// 从JSON文件加载字符串字典 + /// + public void LoadFromFile(string filePath) + { + if (!File.Exists(filePath)) + { + Console.WriteLine($"[LocalizationService] 字典文件不存在: {filePath}"); + return; + } + + try + { + string jsonContent = File.ReadAllText(filePath); + using (JsonDocument doc = JsonDocument.Parse(jsonContent)) + { + FlattenJsonElement(doc.RootElement, ""); + } + Console.WriteLine($"[LocalizationService] 已加载 {_languageCode} 字典,共 {_strings.Count} 个条目"); + } + catch (Exception ex) + { + Console.WriteLine($"[LocalizationService] 加载字典失败: {ex.Message}"); + } + } + + /// + /// 递归展开JSON对象,生成点号路径的键值对 + /// + private void FlattenJsonElement(JsonElement element, string prefix) + { + if (element.ValueKind == JsonValueKind.Object) + { + foreach (var property in element.EnumerateObject()) + { + string key = string.IsNullOrEmpty(prefix) ? property.Name : $"{prefix}.{property.Name}"; + FlattenJsonElement(property.Value, key); + } + } + else if (element.ValueKind == JsonValueKind.String) + { + _strings[prefix] = element.GetString() ?? ""; + } + } + + /// + /// 获取翻译字符串,如果不存在则返回null + /// + public string? GetString(string key) + { + return _strings.TryGetValue(key, out var value) ? value : null; + } + + /// + /// 获取所有字符串条目数 + /// + public int Count => _strings.Count; + } +} diff --git a/UoFiddler/Program.cs b/UoFiddler/Program.cs index 046eab36..35a4cb34 100644 --- a/UoFiddler/Program.cs +++ b/UoFiddler/Program.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection; using Serilog; using UoFiddler.Forms; +using UoFiddler.Localization; namespace UoFiddler { @@ -42,6 +43,13 @@ private static void Main() Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + // 初始化本地化服务 + LocalizationService.Initialize(Path.Combine(AppContext.BaseDirectory, "Localization", "Dictionaries")); + LocalizationService.SetLanguage("zh-CN"); + + // 为 MapSizes 设置汉化 getter + Ultima.Maps.MapSizesLocalization.LocalizationGetter = LocalizationService.GetString; + var services = new ServiceCollection(); services.AddLogging(b => b.AddSerilog(serilogLogger, dispose: true)); using var serviceProvider = services.BuildServiceProvider(); diff --git a/UoFiddler/UoFiddler.csproj b/UoFiddler/UoFiddler.csproj index 01b63757..2ab3a5f4 100644 --- a/UoFiddler/UoFiddler.csproj +++ b/UoFiddler/UoFiddler.csproj @@ -21,6 +21,7 @@ portable bin\$(Configuration)\ true + Exe none @@ -168,4 +169,9 @@ PreserveNewest + + + PreserveNewest + + \ No newline at end of file