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..232c24ae 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; } 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/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/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..3d0fa585 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(); } @@ -1345,6 +1355,95 @@ private void ExportAnimatedGif(bool looping) FileSavedDialog.Show(FindForm(), outputFile, "InGame Anim saved successfully."); } + /// + /// 设置本地化获取器 + /// + 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) { ExportAnimatedGif(true); 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..af403d09 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(); @@ -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); } @@ -1626,6 +1671,108 @@ private void OnClick_SaveAllToXML(object sender, EventArgs e) FileSavedDialog.Show(FindForm(), fileName, "All Multis saved successfully."); } + /// + /// 设置本地化函数 + /// + public void SetLocalization(Func getLocalized) + { + _localizationGetter = getLocalized; + ApplyLocalization(); + } + + 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 { private const int _wmMouseWheel = 0x020A; 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/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..93f5c915 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 @@ -44,6 +45,9 @@ public LoadProfileForm(ILogger logger) comboBoxLoad.SelectedIndex = 0; comboBoxBasedOn.SelectedIndex = 0; + + // 应用中文汉化 + LocalizationService.LocalizeForm(this); } private static string[] GetProfiles() diff --git a/UoFiddler/Forms/MainForm.cs b/UoFiddler/Forms/MainForm.cs index 4f228c7c..60b3cb83 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,42 @@ 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)); + 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 +128,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 +143,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 +298,7 @@ public void LoadExternToolStripMenu() ToolStripMenuItem item = new ToolStripMenuItem { - Text = "Manage.." + Text = LocalizationService.GetString("Forms.MainForm.Menus.manageToolStripMenuItem") ?? "Manage.." }; item.Click += OnClickToolManage; @@ -273,7 +323,7 @@ public void LoadExternToolStripMenu() ToolStripMenuItem sub = new ToolStripMenuItem { - Text = "Start", + Text = LocalizationService.GetString("Forms.MainForm.Menus.Start") ?? "Start", Tag = -1 }; @@ -438,6 +488,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 +586,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..c03cbe2e --- /dev/null +++ b/UoFiddler/Localization/Dictionaries/zh-CN.json @@ -0,0 +1,1680 @@ +{ + "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": "基于" + }, + "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" + } + }, + "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" + } + } + }, + "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