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/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/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/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/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.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..cec569ff 100644 --- a/UoFiddler.Plugin.SendItem/SendItem.cs +++ b/UoFiddler.Plugin.SendItem/SendItem.cs @@ -131,7 +131,15 @@ 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) 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..0c06e0d4 100644 --- a/UoFiddler/Forms/MainForm.cs +++ b/UoFiddler/Forms/MainForm.cs @@ -22,6 +22,7 @@ using UoFiddler.Controls.Classes; using UoFiddler.Properties; using UoFiddler.Controls.Plugin; +using UoFiddler.Localization; namespace UoFiddler.Forms { @@ -52,6 +53,14 @@ public MainForm(ILogger logger) _log = logger; InitializeComponent(); + // 应用本地化 + LocalizationService.LocalizeForm(this); + + // 为主标签页控件调用本地化(GumpControl等需要手动调用SetLocalization) + gumpsControl.SetLocalization(key => LocalizationService.GetString(key)); + hueControl.SetLocalization(key => LocalizationService.GetString(key)); + multisControl.SetLocalization(key => LocalizationService.GetString(key)); + darkModeMenuItem.Checked = AppSettings.DarkMode; if (AppSettings.DarkMode) @@ -90,6 +99,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 +114,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 +269,7 @@ public void LoadExternToolStripMenu() ToolStripMenuItem item = new ToolStripMenuItem { - Text = "Manage.." + Text = LocalizationService.GetString("Forms.MainForm.Menus.manageToolStripMenuItem") ?? "Manage.." }; item.Click += OnClickToolManage; @@ -273,7 +294,7 @@ public void LoadExternToolStripMenu() ToolStripMenuItem sub = new ToolStripMenuItem { - Text = "Start", + Text = LocalizationService.GetString("Forms.MainForm.Menus.Start") ?? "Start", Tag = -1 }; @@ -438,6 +459,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 /// 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..811a75ca --- /dev/null +++ b/UoFiddler/Localization/Dictionaries/zh-CN.json @@ -0,0 +1,766 @@ +{ + "version": "4.24.0", + "language": "zh-CN", + "description": "UOFiddler 简体中文汉化包", + "Forms": { + "MainForm": { + "Title": "UOFiddler", + "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": "对比图块数据" + } + }, + "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": "应用色调到左侧" + }, + "HuesControl": { + "saveToolStripMenuItem": "保存", + "replaceToolStripMenuItem": "替换为..", + "exportToolStripMenuItem": "导出..", + "importToolStripMenuItem": "导入..", + "exportAllHueNamesListToolStripMenuItem": "导出所有色调名称列表", + "HueIndexToolStripLabel": "索引:", + "HueNameToolStripLabel": "名称:", + "SearchNameToolStripButton": "查找下一个", + "IndexOffsetButton": "显示游戏内数字" + }, + "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": "角色" + } + }, + "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": "多可移动" + } + } +} 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..0abe1004 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,10 @@ private static void Main() Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + // 初始化本地化服务 + LocalizationService.Initialize(Path.Combine(AppContext.BaseDirectory, "Localization", "Dictionaries")); + LocalizationService.SetLanguage("zh-CN"); + 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