feat(macos): 实现 macOS 系统菜单栏多语言支持与闪退修复
本次提交完成 macOS 端多项关键优化与修复: 1. 修复启动期竞态闪退问题,通过前移窗口属性初始化、串行化特效调用、跳过首次同步实现稳定启动 2. 实现系统菜单栏多语言本地化,支持中英日韩繁五种语言,软件内切换语言可同步更新菜单栏 3. 移除视图菜单中重复的全屏按钮,统一窗口标题栏逻辑 4. 新增 macOS App Store 打包配置与本地化资源
This commit is contained in:
14
macos/ExportOptions.plist
Normal file
14
macos/ExportOptions.plist
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>app-store</string>
|
||||
<key>teamID</key>
|
||||
<string>5V9NVUU6K5</string>
|
||||
<key>uploadSymbols</key>
|
||||
<false/>
|
||||
<key>uploadBitcode</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -29,6 +29,7 @@
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||
69AC2FD7F25C22464C6AA971 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EAD42DE88C17A1D7496A378 /* Pods_RunnerTests.framework */; };
|
||||
740419A3DA2BFE0F5E85D9AF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9097D4D2EB98F14D8827394A /* Pods_Runner.framework */; };
|
||||
A1B2C3D4E5F6000000000007 /* MainMenu.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F6000000000006 /* MainMenu.strings */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -66,7 +67,7 @@
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* s2ss.com */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = s2ss.com; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10ED2044A3C60003C045 /* xianyan.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = xianyan.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@@ -84,6 +85,11 @@
|
||||
9097D4D2EB98F14D8827394A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9DF906408CB20CD31706ACF0 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
A1B2C3D4E5F6000000000001 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
A1B2C3D4E5F6000000000002 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
A1B2C3D4E5F6000000000003 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
A1B2C3D4E5F6000000000004 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
A1B2C3D4E5F6000000000005 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
CC173CB9C4D834544F95E13C /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E124C85A506C3CD1E10E0BF4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
F3F87547C10EAEA1CDC2EAC9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -144,7 +150,7 @@
|
||||
33CC10EE2044A3C60003C045 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10ED2044A3C60003C045 /* s2ss.com */,
|
||||
33CC10ED2044A3C60003C045 /* xianyan.app */,
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
@@ -155,6 +161,7 @@
|
||||
children = (
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */,
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */,
|
||||
A1B2C3D4E5F6000000000006 /* MainMenu.strings */,
|
||||
33CC10F72044A3C60003C045 /* Info.plist */,
|
||||
);
|
||||
name = Resources;
|
||||
@@ -195,7 +202,6 @@
|
||||
475082748095A981E9B2017D /* Pods-RunnerTests.release.xcconfig */,
|
||||
9DF906408CB20CD31706ACF0 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -249,7 +255,7 @@
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* s2ss.com */;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* xianyan.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
@@ -289,6 +295,10 @@
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
"zh-Hans",
|
||||
"zh-Hant",
|
||||
ja,
|
||||
ko,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 33CC10E42044A3C60003C045;
|
||||
@@ -317,6 +327,7 @@
|
||||
files = (
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
|
||||
A1B2C3D4E5F6000000000007 /* MainMenu.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -468,6 +479,19 @@
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1B2C3D4E5F6000000000006 /* MainMenu.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
A1B2C3D4E5F6000000000001 /* en */,
|
||||
A1B2C3D4E5F6000000000002 /* zh-Hans */,
|
||||
A1B2C3D4E5F6000000000003 /* zh-Hant */,
|
||||
A1B2C3D4E5F6000000000004 /* ja */,
|
||||
A1B2C3D4E5F6000000000005 /* ko */,
|
||||
);
|
||||
name = MainMenu.strings;
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -729,12 +753,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
ARCHS = arm64;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = 5V9NVUU6K5;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
|
||||
@@ -289,12 +289,6 @@
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
<string>zh-Hans</string>
|
||||
<string>zh-Hant</string>
|
||||
<string>ja</string>
|
||||
<string>ko</string>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
||||
@@ -3,11 +3,24 @@
|
||||
/// 创建时间: 2026-06-02
|
||||
/// 更新时间: 2026-06-24
|
||||
/// 作用: 主窗口初始化,注册 apps.xy.xianyan/macos MethodChannel
|
||||
/// 上次更新: 监听全屏退出事件,修复退出全屏后窗口背景变黑的问题
|
||||
/// 上次更新: 实现菜单栏语言切换 — 新增 menuItemIds 静态数组(XIB objectId 列表)、
|
||||
/// assignMenuItemIdentifiers(为每个 menuItem 设置 identifier)、
|
||||
/// setMenuLanguage(根据 languageId 加载对应 lproj/MainMenu.strings
|
||||
/// 并递归更新 NSApp.mainMenu 所有菜单项标题)。
|
||||
/// 修复软件内切换语言后系统菜单栏不跟随切换的问题。
|
||||
/// 同时保留先前修复:在 awakeFromNib 提前调用
|
||||
/// MainFlutterWindowManipulator.start(mainFlutterWindow: self)
|
||||
/// 避免 reset() 走 start(nil) 分支触发竞态崩溃;
|
||||
/// contentViewController 由 FlutterViewController 改为
|
||||
/// MacOSWindowUtilsViewController 包装,使原生侧 setMaterial/
|
||||
/// setNSVisualEffectViewState 等方法的类型转换成功。
|
||||
/// 通过 setExtraFlag 保留 fullSizeContentView + titlebarAppearsTransparent
|
||||
/// 实现标题栏融合(红黄绿灯由 Flutter 侧 DesktopWindowTitleBar 自绘)
|
||||
/// ============================================================
|
||||
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import macos_window_utils
|
||||
|
||||
class MainFlutterWindow: NSWindow, NSTouchBarDelegate {
|
||||
// ============================================================
|
||||
@@ -23,9 +36,63 @@ class MainFlutterWindow: NSWindow, NSTouchBarDelegate {
|
||||
override func awakeFromNib() {
|
||||
let flutterViewController = FlutterViewController()
|
||||
let windowFrame = self.frame
|
||||
self.contentViewController = flutterViewController
|
||||
|
||||
// 关键修复:使用 MacOSWindowUtilsViewController 包装 FlutterViewController
|
||||
// macos_window_utils 插件的原生方法(setMaterial/setNSVisualEffectViewState/
|
||||
// addVisualEffectSubview 等)内部执行
|
||||
// `contentViewController as! MacOSWindowUtilsViewController`,
|
||||
// 若直接使用 FlutterViewController 作为 contentViewController 会触发
|
||||
// "Could not cast value of type 'FlutterViewController' to
|
||||
// 'MacOSWindowUtilsViewController'" 崩溃。
|
||||
// MacOSWindowUtilsViewController.loadView() 会创建 NSVisualEffectView 作为
|
||||
// 根视图并 addChild(flutterViewController),是侧边栏毛玻璃等特效的载体。
|
||||
let macOSWindowUtilsViewController = MacOSWindowUtilsViewController(
|
||||
flutterViewController: flutterViewController
|
||||
)
|
||||
self.contentViewController = macOSWindowUtilsViewController
|
||||
self.setFrame(windowFrame, display: true)
|
||||
|
||||
// 在 Flutter 引擎启动前预设窗口透明属性,消除 Impeller 渲染期竞态
|
||||
// 根因:macos_window_utils 的 setWindowBackgroundColorToClear() 在渲染期间调用
|
||||
// [NSWindow setBackgroundColor:] → NSThemeFrame._updateBackdropView → removeFromSuperview,
|
||||
// 与 Impeller raster 线程的 SetupRenderPass 产生竞态,导致空指针崩溃(Release 模式时序更紧)。
|
||||
// 修复:将不变属性前移到引擎启动前设置,Dart 侧不再在渲染期重复调用。
|
||||
self.isOpaque = false
|
||||
self.backgroundColor = NSColor.clear
|
||||
self.titlebarAppearsTransparent = true
|
||||
self.titleVisibility = .hidden
|
||||
if !self.styleMask.contains(.fullSizeContentView) {
|
||||
self.styleMask.insert(.fullSizeContentView)
|
||||
}
|
||||
|
||||
// 关键修复:提前调用 MainFlutterWindowManipulator.start(mainFlutterWindow: self)
|
||||
// 让 macos_window_utils 插件的 mainFlutterWindow 静态属性先被设置。
|
||||
// 否则后续 Dart 侧 WindowManipulator.initialize() → 原生 reset() 会因
|
||||
// mainFlutterWindow == nil 走 start(nil) 分支,触发
|
||||
// setWindowBackgroundColorToDefaultColor() → [NSWindow setBackgroundColor:]
|
||||
// → NSThemeFrame._updateBackdropView → removeFromSuperview,
|
||||
// 与 Impeller raster 线程的 SetupRenderPass 产生竞态导致 EXC_BAD_ACCESS 崩溃。
|
||||
//
|
||||
// 注意:start() 内部会调用 showTitle()/makeTitlebarOpaque()/disableFullSizeContentView()/
|
||||
// setWindowBackgroundColorToDefaultColor() 覆盖上述预设属性,因此必须在 start() 之后
|
||||
// 重新设置透明属性。此时 Flutter 引擎尚未启动,Impeller raster 线程未运行,
|
||||
// [NSWindow setBackgroundColor:] 不会与渲染线程竞态,安全无崩溃。
|
||||
//
|
||||
// 由于此处传入 mainFlutterWindow: self,start() 内部 isProvidedWindow=true,
|
||||
// 不会调用 configureMainFlutterWindow()(该方法会再次包装 contentViewController,
|
||||
// 但因已是 MacOSWindowUtilsViewController,重复包装会导致 FlutterViewController
|
||||
// 被嵌套两层,引发布局异常)。当前方案在上方已正确包装,无需 configure。
|
||||
MainFlutterWindowManipulator.start(mainFlutterWindow: self)
|
||||
|
||||
// start() 覆盖后重新设置透明属性(引擎启动前,无竞态风险)
|
||||
self.isOpaque = false
|
||||
self.backgroundColor = NSColor.clear
|
||||
self.titlebarAppearsTransparent = true
|
||||
self.titleVisibility = .hidden
|
||||
if !self.styleMask.contains(.fullSizeContentView) {
|
||||
self.styleMask.insert(.fullSizeContentView)
|
||||
}
|
||||
|
||||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||||
|
||||
// 在 Flutter 引擎启动前注册自定义 MethodChannel,避免 MissingPluginException
|
||||
@@ -40,6 +107,11 @@ class MainFlutterWindow: NSWindow, NSTouchBarDelegate {
|
||||
object: self
|
||||
)
|
||||
|
||||
// 为 mainMenu 中每个 menuItem 设置 identifier(XIB objectId)
|
||||
// 以便 setMenuLanguage 能根据 identifier 查找 .strings 中的本地化标题
|
||||
// 必须在 NSApp.mainMenu 从 XIB 加载后调用(awakeFromNib 时 mainMenu 已存在)
|
||||
self.assignMenuItemIdentifiers()
|
||||
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
@@ -204,6 +276,13 @@ class MainFlutterWindow: NSWindow, NSTouchBarDelegate {
|
||||
}
|
||||
result(nil)
|
||||
|
||||
// ---------- 菜单栏语言切换 ----------
|
||||
case "setMenuLanguage":
|
||||
let args = call.arguments as? [String: Any] ?? [:]
|
||||
let languageId = (args["languageId"] as? String) ?? "system"
|
||||
self.setMenuLanguage(languageId: languageId)
|
||||
result(nil)
|
||||
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
@@ -299,6 +378,159 @@ class MainFlutterWindow: NSWindow, NSTouchBarDelegate {
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MARK: - 菜单栏语言切换实现
|
||||
// ============================================================
|
||||
|
||||
/// XIB 中所有 menuItem 的 objectId,按深度优先遍历顺序排列
|
||||
/// 用于在 awakeFromNib 中为每个 menuItem 设置 identifier
|
||||
/// 顺序:顶层菜单项 → 其子菜单项(跳过 separatorItem)
|
||||
/// 与 MainMenu.xib 中 menuItem 的声明顺序一致
|
||||
private static let menuItemIds: [String] = [
|
||||
// App Menu(顶层 + 子菜单项)
|
||||
"1Xt-HY-uBw", "5kV-Vb-QxS", "BOF-NM-1cW", "NMo-om-nkz",
|
||||
"Olw-nP-bQN", "Vdr-fp-XzO", "Kd2-mp-pUS", "4sb-4s-VLi",
|
||||
// Edit Menu(顶层 + 子菜单项)
|
||||
"5QF-Oa-p0T", "dRJ-4n-Yzg", "6dh-zS-Vam", "uRl-iY-unG",
|
||||
"x3v-GG-iWU", "gVA-U4-sdL", "WeT-3V-zwk", "pa3-QI-u2k",
|
||||
"Ruw-6m-B2m",
|
||||
// Edit > Find
|
||||
"4EN-yA-p0u", "Xz5-n4-O0W", "YEy-JH-Tfz", "q09-fT-Sye",
|
||||
"OwM-mh-QMV", "buJ-ug-pKt", "S0p-oC-mLd",
|
||||
// Edit > Spelling and Grammar
|
||||
"Dv1-io-Yv7", "HFo-cy-zxI", "hz2-CU-CR7", "rbD-Rh-wIN",
|
||||
"mK6-2p-4JG", "78Y-hA-62v",
|
||||
// Edit > Substitutions
|
||||
"9ic-FL-obx", "z6F-FW-3nz", "9yt-4B-nSM", "hQb-2v-fYv",
|
||||
"rgM-f4-ycn", "cwL-P1-jid", "tRr-pd-1PS", "HFQ-gK-NFA",
|
||||
// Edit > Transformations
|
||||
"2oI-Rn-ZJC", "vmV-6d-7jI", "d9M-CD-aMd", "UEZ-Bs-lqG",
|
||||
// Edit > Speech
|
||||
"xrE-MZ-jX0", "Ynk-f8-cLZ", "Oyz-dy-DGm",
|
||||
// View Menu(顶层,子菜单项为空)
|
||||
"H8h-7b-M4v",
|
||||
// Window Menu(顶层 + 子菜单项)
|
||||
"aUF-d1-5bR", "OY7-WF-poV", "R4o-n2-Eq4", "LE2-aR-0XJ",
|
||||
// Help Menu(顶层,无子菜单项)
|
||||
"EPT-qC-fAb",
|
||||
]
|
||||
|
||||
/// 为 mainMenu 中每个 menuItem 设置 identifier(XIB objectId)
|
||||
///
|
||||
/// XIB 加载后,menuItem.identifier 默认为 nil,无法通过 objectId 查找。
|
||||
/// 此方法按深度优先顺序遍历 mainMenu,将 menuItemIds 中的 objectId
|
||||
/// 依次赋值给对应 menuItem.identifier,建立 objectId ↔ menuItem 映射。
|
||||
/// 跳过 separatorItem(XIB 中 separator 无 objectId)。
|
||||
private func assignMenuItemIdentifiers() {
|
||||
var index = 0
|
||||
func assign(_ menu: NSMenu) {
|
||||
for item in menu.items {
|
||||
if item.isSeparatorItem { continue }
|
||||
if index < MainFlutterWindow.menuItemIds.count {
|
||||
item.identifier = NSUserInterfaceItemIdentifier(
|
||||
rawValue: MainFlutterWindow.menuItemIds[index]
|
||||
)
|
||||
index += 1
|
||||
}
|
||||
if let submenu = item.submenu {
|
||||
assign(submenu)
|
||||
}
|
||||
}
|
||||
}
|
||||
guard let mainMenu = NSApp.mainMenu else {
|
||||
NSLog("[assignMenuItemIdentifiers] NSApp.mainMenu 为 nil")
|
||||
return
|
||||
}
|
||||
assign(mainMenu)
|
||||
NSLog("[assignMenuItemIdentifiers] 已为 \(index) 个菜单项设置 identifier")
|
||||
}
|
||||
|
||||
/// 切换 macOS 系统菜单栏语言
|
||||
///
|
||||
/// [languageId] 语言 ID:
|
||||
/// - 'system' / 'auto' → 跟随系统语言(使用 preferredLocalizations)
|
||||
/// - 'zh-CN' / 'zh-Hans' → 简体中文
|
||||
/// - 'zh-TW' / 'zh-Hant' → 繁体中文
|
||||
/// - 'en' → 英语
|
||||
/// - 'ja' → 日语
|
||||
/// - 'ko' → 韩语
|
||||
///
|
||||
/// 实现原理:
|
||||
/// 1. 根据 languageId 解析对应的 lproj 目录名
|
||||
/// 2. 加载对应语言的 MainMenu.strings 文件为 [String: String]
|
||||
/// 3. 递归遍历 NSApp.mainMenu,根据 menuItem.identifier(XIB objectId)
|
||||
/// 查找 .strings 中的 key("id.title")并更新标题
|
||||
/// 4. 替换 "APP_NAME" 占位符为应用名(CFBundleName)
|
||||
///
|
||||
/// 注意:此方法仅更新菜单项标题,不重建菜单结构,不影响 action 连接。
|
||||
private func setMenuLanguage(languageId: String) {
|
||||
// 1. 解析 lproj 目录名
|
||||
let lprojName: String
|
||||
switch languageId {
|
||||
case "system", "auto":
|
||||
// preferredLocalizations(from:) 是 Bundle 类的静态方法,
|
||||
// 返回系统首选语言在给定列表中的匹配顺序
|
||||
let preferred = Bundle.preferredLocalizations(from: [
|
||||
"en", "zh-Hans", "zh-Hant", "ja", "ko",
|
||||
])
|
||||
lprojName = preferred.first ?? "en"
|
||||
case "zh-CN", "zh-Hans":
|
||||
lprojName = "zh-Hans"
|
||||
case "zh-TW", "zh-Hant":
|
||||
lprojName = "zh-Hant"
|
||||
case "en":
|
||||
lprojName = "en"
|
||||
case "ja":
|
||||
lprojName = "ja"
|
||||
case "ko":
|
||||
lprojName = "ko"
|
||||
default:
|
||||
// 未知语言,回退到英语
|
||||
lprojName = "en"
|
||||
}
|
||||
|
||||
// 2. 加载对应语言的 MainMenu.strings
|
||||
guard let stringsPath = Bundle.main.path(
|
||||
forResource: "MainMenu",
|
||||
ofType: "strings",
|
||||
inDirectory: nil,
|
||||
forLocalization: lprojName
|
||||
) else {
|
||||
NSLog("[setMenuLanguage] 未找到 \(lprojName).lproj/MainMenu.strings")
|
||||
return
|
||||
}
|
||||
guard let strings = NSDictionary(contentsOfFile: stringsPath) as? [String: String] else {
|
||||
NSLog("[setMenuLanguage] 加载 \(lprojName).lproj/MainMenu.strings 失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 递归遍历 mainMenu,更新标题
|
||||
let appName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String) ?? "闲言"
|
||||
var updatedCount = 0
|
||||
func updateMenu(_ menu: NSMenu) {
|
||||
for item in menu.items {
|
||||
if item.isSeparatorItem { continue }
|
||||
if let identifier = item.identifier?.rawValue {
|
||||
let key = "\(identifier).title"
|
||||
if let localized = strings[key] {
|
||||
// 替换 APP_NAME 占位符为应用名
|
||||
item.title = localized.replacingOccurrences(of: "APP_NAME", with: appName)
|
||||
updatedCount += 1
|
||||
}
|
||||
}
|
||||
if let submenu = item.submenu {
|
||||
updateMenu(submenu)
|
||||
}
|
||||
}
|
||||
}
|
||||
guard let mainMenu = NSApp.mainMenu else {
|
||||
NSLog("[setMenuLanguage] NSApp.mainMenu 为 nil")
|
||||
return
|
||||
}
|
||||
updateMenu(mainMenu)
|
||||
NSLog("[setMenuLanguage] 菜单栏语言已切换为 \(lprojName),更新 \(updatedCount) 个菜单项")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MARK: - Touch Bar 实现
|
||||
// ============================================================
|
||||
|
||||
76
macos/Runner/en.lproj/MainMenu.strings
Normal file
76
macos/Runner/en.lproj/MainMenu.strings
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ============================================================
|
||||
* 闲言APP — macOS 主菜单本地化(英语)
|
||||
* 创建时间: 2026-06-24
|
||||
* 作用: macOS 系统菜单栏英文文案
|
||||
* ============================================================ */
|
||||
|
||||
/* ===== App Menu ===== */
|
||||
"1Xt-HY-uBw.title" = "xianyan";
|
||||
"5kV-Vb-QxS.title" = "About xianyan";
|
||||
"BOF-NM-1cW.title" = "Preferences…";
|
||||
"NMo-om-nkz.title" = "Services";
|
||||
"Olw-nP-bQN.title" = "Hide xianyan";
|
||||
"Vdr-fp-XzO.title" = "Hide Others";
|
||||
"Kd2-mp-pUS.title" = "Show All";
|
||||
"4sb-4s-VLi.title" = "Quit xianyan";
|
||||
|
||||
/* ===== Edit Menu ===== */
|
||||
"5QF-Oa-p0T.title" = "Edit";
|
||||
"dRJ-4n-Yzg.title" = "Undo";
|
||||
"6dh-zS-Vam.title" = "Redo";
|
||||
"uRl-iY-unG.title" = "Cut";
|
||||
"x3v-GG-iWU.title" = "Copy";
|
||||
"gVA-U4-sdL.title" = "Paste";
|
||||
"WeT-3V-zwk.title" = "Paste and Match Style";
|
||||
"pa3-QI-u2k.title" = "Delete";
|
||||
"Ruw-6m-B2m.title" = "Select All";
|
||||
|
||||
/* ===== Edit > Find ===== */
|
||||
"4EN-yA-p0u.title" = "Find";
|
||||
"Xz5-n4-O0W.title" = "Find…";
|
||||
"YEy-JH-Tfz.title" = "Find and Replace…";
|
||||
"q09-fT-Sye.title" = "Find Next";
|
||||
"OwM-mh-QMV.title" = "Find Previous";
|
||||
"buJ-ug-pKt.title" = "Use Selection for Find";
|
||||
"S0p-oC-mLd.title" = "Jump to Selection";
|
||||
|
||||
/* ===== Edit > Spelling and Grammar ===== */
|
||||
"Dv1-io-Yv7.title" = "Spelling and Grammar";
|
||||
"HFo-cy-zxI.title" = "Show Spelling and Grammar";
|
||||
"hz2-CU-CR7.title" = "Check Document Now";
|
||||
"rbD-Rh-wIN.title" = "Check Spelling While Typing";
|
||||
"mK6-2p-4JG.title" = "Check Grammar With Spelling";
|
||||
"78Y-hA-62v.title" = "Correct Spelling Automatically";
|
||||
|
||||
/* ===== Edit > Substitutions ===== */
|
||||
"9ic-FL-obx.title" = "Substitutions";
|
||||
"z6F-FW-3nz.title" = "Show Substitutions";
|
||||
"9yt-4B-nSM.title" = "Smart Copy/Paste";
|
||||
"hQb-2v-fYv.title" = "Smart Quotes";
|
||||
"rgM-f4-ycn.title" = "Smart Dashes";
|
||||
"cwL-P1-jid.title" = "Smart Links";
|
||||
"tRr-pd-1PS.title" = "Data Detectors";
|
||||
"HFQ-gK-NFA.title" = "Text Replacement";
|
||||
|
||||
/* ===== Edit > Transformations ===== */
|
||||
"2oI-Rn-ZJC.title" = "Transformations";
|
||||
"vmV-6d-7jI.title" = "Make Upper Case";
|
||||
"d9M-CD-aMd.title" = "Make Lower Case";
|
||||
"UEZ-Bs-lqG.title" = "Capitalize";
|
||||
|
||||
/* ===== Edit > Speech ===== */
|
||||
"xrE-MZ-jX0.title" = "Speech";
|
||||
"Ynk-f8-cLZ.title" = "Start Speaking";
|
||||
"Oyz-dy-DGm.title" = "Stop Speaking";
|
||||
|
||||
/* ===== View Menu ===== */
|
||||
"H8h-7b-M4v.title" = "View";
|
||||
|
||||
/* ===== Window Menu ===== */
|
||||
"aUF-d1-5bR.title" = "Window";
|
||||
"OY7-WF-poV.title" = "Minimize";
|
||||
"R4o-n2-Eq4.title" = "Zoom";
|
||||
"LE2-aR-0XJ.title" = "Bring All to Front";
|
||||
|
||||
/* ===== Help Menu ===== */
|
||||
"EPT-qC-fAb.title" = "Help";
|
||||
76
macos/Runner/ja.lproj/MainMenu.strings
Normal file
76
macos/Runner/ja.lproj/MainMenu.strings
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ============================================================
|
||||
* 闲言APP — macOS 主菜单本地化(日语)
|
||||
* 创建时间: 2026-06-24
|
||||
* 作用: macOS 系统菜单栏日本語文案
|
||||
* ============================================================ */
|
||||
|
||||
/* ===== App Menu ===== */
|
||||
"1Xt-HY-uBw.title" = "閑言";
|
||||
"5kV-Vb-QxS.title" = "閑言について";
|
||||
"BOF-NM-1cW.title" = "設定…";
|
||||
"NMo-om-nkz.title" = "サービス";
|
||||
"Olw-nP-bQN.title" = "閑言を隠す";
|
||||
"Vdr-fp-XzO.title" = "ほかを隠す";
|
||||
"Kd2-mp-pUS.title" = "すべてを表示";
|
||||
"4sb-4s-VLi.title" = "閑言を終了";
|
||||
|
||||
/* ===== Edit Menu ===== */
|
||||
"5QF-Oa-p0T.title" = "編集";
|
||||
"dRJ-4n-Yzg.title" = "取り消す";
|
||||
"6dh-zS-Vam.title" = "やり直す";
|
||||
"uRl-iY-unG.title" = "カット";
|
||||
"x3v-GG-iWU.title" = "コピー";
|
||||
"gVA-U4-sdL.title" = "ペースト";
|
||||
"WeT-3V-zwk.title" = "スタイルを適用してペースト";
|
||||
"pa3-QI-u2k.title" = "削除";
|
||||
"Ruw-6m-B2m.title" = "すべてを選択";
|
||||
|
||||
/* ===== Edit > Find ===== */
|
||||
"4EN-yA-p0u.title" = "検索";
|
||||
"Xz5-n4-O0W.title" = "検索…";
|
||||
"YEy-JH-Tfz.title" = "検索と置換…";
|
||||
"q09-fT-Sye.title" = "次を検索";
|
||||
"OwM-mh-QMV.title" = "前を検索";
|
||||
"buJ-ug-pKt.title" = "選択部分を検索に使用";
|
||||
"S0p-oC-mLd.title" = "選択部分へジャンプ";
|
||||
|
||||
/* ===== Edit > Spelling and Grammar ===== */
|
||||
"Dv1-io-Yv7.title" = "スペルと文法";
|
||||
"HFo-cy-zxI.title" = "スペルと文法を表示";
|
||||
"hz2-CU-CR7.title" = "今すぐ書類をチェック";
|
||||
"rbD-Rh-wIN.title" = "入力中にスペルをチェック";
|
||||
"mK6-2p-4JG.title" = "スペルと一緒に文法をチェック";
|
||||
"78Y-hA-62v.title" = "スペルを自動的に修正";
|
||||
|
||||
/* ===== Edit > Substitutions ===== */
|
||||
"9ic-FL-obx.title" = "置換";
|
||||
"z6F-FW-3nz.title" = "置換を表示";
|
||||
"9yt-4B-nSM.title" = "スマートコピー/ペースト";
|
||||
"hQb-2v-fYv.title" = "スマート引用符";
|
||||
"rgM-f4-ycn.title" = "スマートダッシュ";
|
||||
"cwL-P1-jid.title" = "スマートリンク";
|
||||
"tRr-pd-1PS.title" = "データ検出";
|
||||
"HFQ-gK-NFA.title" = "テキスト置換";
|
||||
|
||||
/* ===== Edit > Transformations ===== */
|
||||
"2oI-Rn-ZJC.title" = "変換";
|
||||
"vmV-6d-7jI.title" = "大文字にする";
|
||||
"d9M-CD-aMd.title" = "小文字にする";
|
||||
"UEZ-Bs-lqG.title" = "頭文字を大文字にする";
|
||||
|
||||
/* ===== Edit > Speech ===== */
|
||||
"xrE-MZ-jX0.title" = "スピーチ";
|
||||
"Ynk-f8-cLZ.title" = "読み上げを開始";
|
||||
"Oyz-dy-DGm.title" = "読み上げを停止";
|
||||
|
||||
/* ===== View Menu ===== */
|
||||
"H8h-7b-M4v.title" = "表示";
|
||||
|
||||
/* ===== Window Menu ===== */
|
||||
"aUF-d1-5bR.title" = "ウインドウ";
|
||||
"OY7-WF-poV.title" = "しまう";
|
||||
"R4o-n2-Eq4.title" = "拡大/縮小";
|
||||
"LE2-aR-0XJ.title" = "すべてを手前に移動";
|
||||
|
||||
/* ===== Help Menu ===== */
|
||||
"EPT-qC-fAb.title" = "ヘルプ";
|
||||
76
macos/Runner/ko.lproj/MainMenu.strings
Normal file
76
macos/Runner/ko.lproj/MainMenu.strings
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ============================================================
|
||||
* 闲言APP — macOS 主菜单本地化(韩语)
|
||||
* 创建时间: 2026-06-24
|
||||
* 作用: macOS 系统菜单栏한국어文案
|
||||
* ============================================================ */
|
||||
|
||||
/* ===== App Menu ===== */
|
||||
"1Xt-HY-uBw.title" = "한언";
|
||||
"5kV-Vb-QxS.title" = "한언 정보";
|
||||
"BOF-NM-1cW.title" = "환경설정…";
|
||||
"NMo-om-nkz.title" = "서비스";
|
||||
"Olw-nP-bQN.title" = "한언 숨기기";
|
||||
"Vdr-fp-XzO.title" = "기타 숨기기";
|
||||
"Kd2-mp-pUS.title" = "모두 보기";
|
||||
"4sb-4s-VLi.title" = "한언 종료";
|
||||
|
||||
/* ===== Edit Menu ===== */
|
||||
"5QF-Oa-p0T.title" = "편집";
|
||||
"dRJ-4n-Yzg.title" = "실행 취소";
|
||||
"6dh-zS-Vam.title" = "다시 실행";
|
||||
"uRl-iY-unG.title" = "잘라내기";
|
||||
"x3v-GG-iWU.title" = "복사";
|
||||
"gVA-U4-sdL.title" = "붙여넣기";
|
||||
"WeT-3V-zwk.title" = "스타일 적용하여 붙여넣기";
|
||||
"pa3-QI-u2k.title" = "삭제";
|
||||
"Ruw-6m-B2m.title" = "모두 선택";
|
||||
|
||||
/* ===== Edit > Find ===== */
|
||||
"4EN-yA-p0u.title" = "찾기";
|
||||
"Xz5-n4-O0W.title" = "찾기…";
|
||||
"YEy-JH-Tfz.title" = "찾기 및 바꾸기…";
|
||||
"q09-fT-Sye.title" = "다음 찾기";
|
||||
"OwM-mh-QMV.title" = "이전 찾기";
|
||||
"buJ-ug-pKt.title" = "선택 항목으로 찾기";
|
||||
"S0p-oC-mLd.title" = "선택 항목으로 이동";
|
||||
|
||||
/* ===== Edit > Spelling and Grammar ===== */
|
||||
"Dv1-io-Yv7.title" = "맞춤법 및 문법";
|
||||
"HFo-cy-zxI.title" = "맞춤법 및 문법 표시";
|
||||
"hz2-CU-CR7.title" = "지금 문서 검사";
|
||||
"rbD-Rh-wIN.title" = "입력하는 동안 맞춤법 검사";
|
||||
"mK6-2p-4JG.title" = "맞춤법과 함께 문법 검사";
|
||||
"78Y-hA-62v.title" = "맞춤법 자동 수정";
|
||||
|
||||
/* ===== Edit > Substitutions ===== */
|
||||
"9ic-FL-obx.title" = "대체";
|
||||
"z6F-FW-3nz.title" = "대체 표시";
|
||||
"9yt-4B-nSM.title" = "스마트 복사/붙여넣기";
|
||||
"hQb-2v-fYv.title" = "스마트 인용 부호";
|
||||
"rgM-f4-ycn.title" = "스마트 대시";
|
||||
"cwL-P1-jid.title" = "스마트 링크";
|
||||
"tRr-pd-1PS.title" = "데이터 감지기";
|
||||
"HFQ-gK-NFA.title" = "텍스트 대체";
|
||||
|
||||
/* ===== Edit > Transformations ===== */
|
||||
"2oI-Rn-ZJC.title" = "변환";
|
||||
"vmV-6d-7jI.title" = "대문자로 만들기";
|
||||
"d9M-CD-aMd.title" = "소문자로 만들기";
|
||||
"UEZ-Bs-lqG.title" = "첫 글자를 대문자로";
|
||||
|
||||
/* ===== Edit > Speech ===== */
|
||||
"xrE-MZ-jX0.title" = "음성";
|
||||
"Ynk-f8-cLZ.title" = "말하기 시작";
|
||||
"Oyz-dy-DGm.title" = "말하기 중지";
|
||||
|
||||
/* ===== View Menu ===== */
|
||||
"H8h-7b-M4v.title" = "보기";
|
||||
|
||||
/* ===== Window Menu ===== */
|
||||
"aUF-d1-5bR.title" = "윈도우";
|
||||
"OY7-WF-poV.title" = "최소화";
|
||||
"R4o-n2-Eq4.title" = "확대/축소";
|
||||
"LE2-aR-0XJ.title" = "모두 앞으로 가져오기";
|
||||
|
||||
/* ===== Help Menu ===== */
|
||||
"EPT-qC-fAb.title" = "도움말";
|
||||
76
macos/Runner/zh-Hans.lproj/MainMenu.strings
Normal file
76
macos/Runner/zh-Hans.lproj/MainMenu.strings
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ============================================================
|
||||
* 闲言APP — macOS 主菜单本地化(简体中文)
|
||||
* 创建时间: 2026-06-24
|
||||
* 作用: macOS 系统菜单栏简体中文文案
|
||||
* ============================================================ */
|
||||
|
||||
/* ===== App Menu ===== */
|
||||
"1Xt-HY-uBw.title" = "闲言";
|
||||
"5kV-Vb-QxS.title" = "关于 闲言";
|
||||
"BOF-NM-1cW.title" = "偏好设置…";
|
||||
"NMo-om-nkz.title" = "服务";
|
||||
"Olw-nP-bQN.title" = "隐藏 闲言";
|
||||
"Vdr-fp-XzO.title" = "隐藏其他";
|
||||
"Kd2-mp-pUS.title" = "显示全部";
|
||||
"4sb-4s-VLi.title" = "退出 闲言";
|
||||
|
||||
/* ===== Edit Menu ===== */
|
||||
"5QF-Oa-p0T.title" = "编辑";
|
||||
"dRJ-4n-Yzg.title" = "撤销";
|
||||
"6dh-zS-Vam.title" = "重做";
|
||||
"uRl-iY-unG.title" = "剪切";
|
||||
"x3v-GG-iWU.title" = "复制";
|
||||
"gVA-U4-sdL.title" = "粘贴";
|
||||
"WeT-3V-zwk.title" = "粘贴并匹配样式";
|
||||
"pa3-QI-u2k.title" = "删除";
|
||||
"Ruw-6m-B2m.title" = "全选";
|
||||
|
||||
/* ===== Edit > Find ===== */
|
||||
"4EN-yA-p0u.title" = "查找";
|
||||
"Xz5-n4-O0W.title" = "查找…";
|
||||
"YEy-JH-Tfz.title" = "查找和替换…";
|
||||
"q09-fT-Sye.title" = "查找下一个";
|
||||
"OwM-mh-QMV.title" = "查找上一个";
|
||||
"buJ-ug-pKt.title" = "使用所选内容查找";
|
||||
"S0p-oC-mLd.title" = "跳到所选内容";
|
||||
|
||||
/* ===== Edit > Spelling and Grammar ===== */
|
||||
"Dv1-io-Yv7.title" = "拼写和语法";
|
||||
"HFo-cy-zxI.title" = "显示拼写和语法";
|
||||
"hz2-CU-CR7.title" = "立即检查文稿";
|
||||
"rbD-Rh-wIN.title" = "键入时检查拼写";
|
||||
"mK6-2p-4JG.title" = "随拼写检查语法";
|
||||
"78Y-hA-62v.title" = "自动纠正拼写";
|
||||
|
||||
/* ===== Edit > Substitutions ===== */
|
||||
"9ic-FL-obx.title" = "替换";
|
||||
"z6F-FW-3nz.title" = "显示替换";
|
||||
"9yt-4B-nSM.title" = "智能复制/粘贴";
|
||||
"hQb-2v-fYv.title" = "智能引号";
|
||||
"rgM-f4-ycn.title" = "智能破折号";
|
||||
"cwL-P1-jid.title" = "智能链接";
|
||||
"tRr-pd-1PS.title" = "数据检测器";
|
||||
"HFQ-gK-NFA.title" = "文本替换";
|
||||
|
||||
/* ===== Edit > Transformations ===== */
|
||||
"2oI-Rn-ZJC.title" = "变换";
|
||||
"vmV-6d-7jI.title" = "转为大写";
|
||||
"d9M-CD-aMd.title" = "转为小写";
|
||||
"UEZ-Bs-lqG.title" = "首字母大写";
|
||||
|
||||
/* ===== Edit > Speech ===== */
|
||||
"xrE-MZ-jX0.title" = "语音";
|
||||
"Ynk-f8-cLZ.title" = "开始朗读";
|
||||
"Oyz-dy-DGm.title" = "停止朗读";
|
||||
|
||||
/* ===== View Menu ===== */
|
||||
"H8h-7b-M4v.title" = "视图";
|
||||
|
||||
/* ===== Window Menu ===== */
|
||||
"aUF-d1-5bR.title" = "窗口";
|
||||
"OY7-WF-poV.title" = "最小化";
|
||||
"R4o-n2-Eq4.title" = "缩放";
|
||||
"LE2-aR-0XJ.title" = "前置全部窗口";
|
||||
|
||||
/* ===== Help Menu ===== */
|
||||
"EPT-qC-fAb.title" = "帮助";
|
||||
76
macos/Runner/zh-Hant.lproj/MainMenu.strings
Normal file
76
macos/Runner/zh-Hant.lproj/MainMenu.strings
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ============================================================
|
||||
* 闲言APP — macOS 主菜单本地化(繁体中文)
|
||||
* 创建时间: 2026-06-24
|
||||
* 作用: macOS 系统菜单栏繁體中文文案
|
||||
* ============================================================ */
|
||||
|
||||
/* ===== App Menu ===== */
|
||||
"1Xt-HY-uBw.title" = "閒言";
|
||||
"5kV-Vb-QxS.title" = "關於 閒言";
|
||||
"BOF-NM-1cW.title" = "偏好設定…";
|
||||
"NMo-om-nkz.title" = "服務";
|
||||
"Olw-nP-bQN.title" = "隱藏 閒言";
|
||||
"Vdr-fp-XzO.title" = "隱藏其他";
|
||||
"Kd2-mp-pUS.title" = "顯示全部";
|
||||
"4sb-4s-VLi.title" = "結束 閒言";
|
||||
|
||||
/* ===== Edit Menu ===== */
|
||||
"5QF-Oa-p0T.title" = "編輯";
|
||||
"dRJ-4n-Yzg.title" = "還原";
|
||||
"6dh-zS-Vam.title" = "重做";
|
||||
"uRl-iY-unG.title" = "剪下";
|
||||
"x3v-GG-iWU.title" = "拷貝";
|
||||
"gVA-U4-sdL.title" = "貼上";
|
||||
"WeT-3V-zwk.title" = "貼上並符合樣式";
|
||||
"pa3-QI-u2k.title" = "刪除";
|
||||
"Ruw-6m-B2m.title" = "全選";
|
||||
|
||||
/* ===== Edit > Find ===== */
|
||||
"4EN-yA-p0u.title" = "尋找";
|
||||
"Xz5-n4-O0W.title" = "尋找…";
|
||||
"YEy-JH-Tfz.title" = "尋找與取代…";
|
||||
"q09-fT-Sye.title" = "尋找下一個";
|
||||
"OwM-mh-QMV.title" = "尋找上一個";
|
||||
"buJ-ug-pKt.title" = "使用選取範圍尋找";
|
||||
"S0p-oC-mLd.title" = "跳至選取範圍";
|
||||
|
||||
/* ===== Edit > Spelling and Grammar ===== */
|
||||
"Dv1-io-Yv7.title" = "拼字和文法";
|
||||
"HFo-cy-zxI.title" = "顯示拼字和文法";
|
||||
"hz2-CU-CR7.title" = "立即檢查文件";
|
||||
"rbD-Rh-wIN.title" = "鍵入時檢查拼字";
|
||||
"mK6-2p-4JG.title" = "隨拼字檢查文法";
|
||||
"78Y-hA-62v.title" = "自動校正拼字";
|
||||
|
||||
/* ===== Edit > Substitutions ===== */
|
||||
"9ic-FL-obx.title" = "取代";
|
||||
"z6F-FW-3nz.title" = "顯示取代";
|
||||
"9yt-4B-nSM.title" = "智慧拷貝/貼上";
|
||||
"hQb-2v-fYv.title" = "智慧引號";
|
||||
"rgM-f4-ycn.title" = "智慧破折號";
|
||||
"cwL-P1-jid.title" = "智慧連結";
|
||||
"tRr-pd-1PS.title" = "資料偵測器";
|
||||
"HFQ-gK-NFA.title" = "文字取代";
|
||||
|
||||
/* ===== Edit > Transformations ===== */
|
||||
"2oI-Rn-ZJC.title" = "變換";
|
||||
"vmV-6d-7jI.title" = "設為大寫";
|
||||
"d9M-CD-aMd.title" = "設為小寫";
|
||||
"UEZ-Bs-lqG.title" = "首字母大寫";
|
||||
|
||||
/* ===== Edit > Speech ===== */
|
||||
"xrE-MZ-jX0.title" = "語音";
|
||||
"Ynk-f8-cLZ.title" = "開始朗讀";
|
||||
"Oyz-dy-DGm.title" = "停止朗讀";
|
||||
|
||||
/* ===== View Menu ===== */
|
||||
"H8h-7b-M4v.title" = "顯示方式";
|
||||
|
||||
/* ===== Window Menu ===== */
|
||||
"aUF-d1-5bR.title" = "視窗";
|
||||
"OY7-WF-poV.title" = "縮到最小";
|
||||
"R4o-n2-Eq4.title" = "縮放";
|
||||
"LE2-aR-0XJ.title" = "將此程式所有視窗移至最前";
|
||||
|
||||
/* ===== Help Menu ===== */
|
||||
"EPT-qC-fAb.title" = "輔助說明";
|
||||
Reference in New Issue
Block a user