feat: add Japanese localization with String Catalog (#819)
* Add i18n infrastructure with String Catalog and Japanese translations Introduce String Catalog (.xcstrings) for localization support: - Localizable.xcstrings: 195 UI string entries with en and ja translations - InfoPlist.xcstrings: Info.plist strings (microphone usage, Finder menu items) - project.pbxproj: add xcstrings to build phase and ja to knownRegions * Replace hardcoded UI strings with String(localized:defaultValue:) Migrate all user-facing strings across 11 source files to use String(localized:defaultValue:) API (macOS 13+). Each string references a key in Localizable.xcstrings, with the English text preserved as defaultValue for fallback. Files modified: - KeyboardShortcutSettings: 28 shortcut labels - SocketControlSettings: mode names and descriptions - TabManager: placement labels, color names, close dialogs - BrowserPanel/BrowserPanelView: error pages, context menus, tooltips - UpdateViewModel/UpdatePopoverView/UpdatePill: update UI states - NotificationsPage: notification panel labels - SurfaceSearchOverlay: search bar placeholder and tooltips - AppDelegate: menus, dialogs, command palette items * Fix localization gaps from review feedback Address review comments from CodeRabbit, Greptile, and Cubic Dev AI: - Use interpolated String(localized:) instead of concatenation for version/progress strings in UpdateViewModel - Localize remaining hardcoded strings in AppDelegate: window labels, rename dialog, status menu items, unread notification count - Localize insecure HTTP alert body in BrowserPanel - Add 12 new entries to Localizable.xcstrings with Japanese translations * Fix String(localized:defaultValue:) keys to use StaticString The localized: parameter requires StaticString when defaultValue: is used. Move string interpolation from the key to defaultValue only, and revert maxWidthText to plain strings since they are only used for layout width calculation. * Localize remaining UI strings across all source files Add String(localized:defaultValue:) to all user-facing strings in: - cmuxApp.swift: settings screen, menus, about panel, dialogs (~180 strings) - ContentView.swift: command palette, sidebar context menu, dialogs (~200 strings) - Workspace.swift: rename/move/close tab dialogs, tooltips (~20 strings) - UpdateTitlebarAccessory.swift: titlebar tooltips, notifications popover (~10 strings) - TerminalNotificationStore.swift: notification permission dialog (4 strings) - CmuxWebView.swift: browser context menu items (2 strings) - AppDelegate.swift: CLI install/uninstall alerts (6 strings) Add 418 new entries to Localizable.xcstrings with Japanese translations. Extract sidebar context menu into separate @ViewBuilder to fix Swift type-checker timeout in large body. Fix xcstrings format specifiers for interpolated strings (%lld, %@). Total: 624 localization entries covering the full UI. * Address review feedback: fix missing localizations and terminology - Localize javaScriptDialogTitle URL branch in BrowserPanel - Localize cantReach error message in BrowserPanel - Localize close other tabs dialog message in TabManager - Localize workspace accessibility label in ContentView - Fix unread notification singular/plural (split into two keys) - Fix insecure connection apostrophe inconsistency (unify to U+2019) - Rename socketControl.fullOpen.description to socketControl.allowAll.description - Remove dead code: renameTargetNoun function - Fix terminology inconsistencies in xcstrings: - Unify "Developer Tools" to デベロッパツール - Unify "Jump to Latest Unread" phrasing - Unify "Flash Focused Panel" terminology - Fix dialog.enableNotifications.notNow translation * fix: address remaining PR 819 review feedback * fix: use a single localized key for close-other-tabs * fix: avoid inflection markup in close-other-tabs message * Address review feedback: localize tooltip, fix subtitle concat, unify keys - Localize menubar tooltip unread count (hardcoded English -> localized) - Replace subtitle string concatenation anti-pattern with single localized keys containing interpolation placeholders - Unify workspace fallback key to workspace.displayName.fallback - Remove unused workspace.defaultName key from xcstrings - Add Japanese translations for new tooltip and subtitle keys
This commit is contained in:
parent
422c86e822
commit
2c330efb8a
20 changed files with 11643 additions and 789 deletions
|
|
@ -990,10 +990,10 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
|
||||
private static func currentSplitButtonTooltips() -> BonsplitConfiguration.SplitButtonTooltips {
|
||||
BonsplitConfiguration.SplitButtonTooltips(
|
||||
newTerminal: KeyboardShortcutSettings.Action.newSurface.tooltip("New Terminal"),
|
||||
newBrowser: KeyboardShortcutSettings.Action.openBrowser.tooltip("New Browser"),
|
||||
splitRight: KeyboardShortcutSettings.Action.splitRight.tooltip("Split Right"),
|
||||
splitDown: KeyboardShortcutSettings.Action.splitDown.tooltip("Split Down")
|
||||
newTerminal: KeyboardShortcutSettings.Action.newSurface.tooltip(String(localized: "workspace.tooltip.newTerminal", defaultValue: "New Terminal")),
|
||||
newBrowser: KeyboardShortcutSettings.Action.openBrowser.tooltip(String(localized: "workspace.tooltip.newBrowser", defaultValue: "New Browser")),
|
||||
splitRight: KeyboardShortcutSettings.Action.splitRight.tooltip(String(localized: "workspace.tooltip.splitRight", defaultValue: "Split Right")),
|
||||
splitDown: KeyboardShortcutSettings.Action.splitDown.tooltip(String(localized: "workspace.tooltip.splitDown", defaultValue: "Split Down"))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -3331,15 +3331,15 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
let panel = panels[panelId] else { return }
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Rename Tab"
|
||||
alert.informativeText = "Enter a custom name for this tab."
|
||||
alert.messageText = String(localized: "dialog.renameTab.title", defaultValue: "Rename Tab")
|
||||
alert.informativeText = String(localized: "dialog.renameTab.message", defaultValue: "Enter a custom name for this tab.")
|
||||
let currentTitle = panelCustomTitles[panelId] ?? panelTitles[panelId] ?? panel.displayTitle
|
||||
let input = NSTextField(string: currentTitle)
|
||||
input.placeholderString = "Tab name"
|
||||
input.placeholderString = String(localized: "dialog.renameTab.placeholder", defaultValue: "Tab name")
|
||||
input.frame = NSRect(x: 0, y: 0, width: 240, height: 22)
|
||||
alert.accessoryView = input
|
||||
alert.addButton(withTitle: "Rename")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: String(localized: "common.rename", defaultValue: "Rename"))
|
||||
alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel"))
|
||||
let alertWindow = alert.window
|
||||
alertWindow.initialFirstResponder = input
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -3368,24 +3368,24 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
)
|
||||
|
||||
var options: [(title: String, destination: PanelMoveDestination)] = [
|
||||
("New Workspace in Current Window", .newWorkspaceInCurrentWindow),
|
||||
("Selected Workspace in New Window", .selectedWorkspaceInNewWindow),
|
||||
(String(localized: "dialog.moveTab.newWorkspaceCurrentWindow", defaultValue: "New Workspace in Current Window"), .newWorkspaceInCurrentWindow),
|
||||
(String(localized: "dialog.moveTab.selectedWorkspaceNewWindow", defaultValue: "Selected Workspace in New Window"), .selectedWorkspaceInNewWindow),
|
||||
]
|
||||
options.append(contentsOf: workspaceTargets.map { target in
|
||||
(target.label, .existingWorkspace(target.workspaceId))
|
||||
})
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Move Tab"
|
||||
alert.informativeText = "Choose a destination for this tab."
|
||||
alert.messageText = String(localized: "dialog.moveTab.title", defaultValue: "Move Tab")
|
||||
alert.informativeText = String(localized: "dialog.moveTab.message", defaultValue: "Choose a destination for this tab.")
|
||||
let popup = NSPopUpButton(frame: NSRect(x: 0, y: 0, width: 320, height: 26), pullsDown: false)
|
||||
for option in options {
|
||||
popup.addItem(withTitle: option.title)
|
||||
}
|
||||
popup.selectItem(at: 0)
|
||||
alert.accessoryView = popup
|
||||
alert.addButton(withTitle: "Move")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: String(localized: "dialog.moveTab.move", defaultValue: "Move"))
|
||||
alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel"))
|
||||
|
||||
guard alert.runModal() == .alertFirstButtonReturn else { return }
|
||||
let selectedIndex = max(0, min(popup.indexOfSelectedItem, options.count - 1))
|
||||
|
|
@ -3431,9 +3431,9 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
if !moved {
|
||||
let failure = NSAlert()
|
||||
failure.alertStyle = .warning
|
||||
failure.messageText = "Move Failed"
|
||||
failure.informativeText = "cmux could not move this tab to the selected destination."
|
||||
failure.addButton(withTitle: "OK")
|
||||
failure.messageText = String(localized: "dialog.moveFailed.title", defaultValue: "Move Failed")
|
||||
failure.informativeText = String(localized: "dialog.moveFailed.message", defaultValue: "cmux could not move this tab to the selected destination.")
|
||||
failure.addButton(withTitle: String(localized: "common.ok", defaultValue: "OK"))
|
||||
_ = failure.runModal()
|
||||
}
|
||||
}
|
||||
|
|
@ -3500,11 +3500,11 @@ extension Workspace: BonsplitDelegate {
|
|||
@MainActor
|
||||
private func confirmClosePanel(for tabId: TabID) async -> Bool {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Close tab?"
|
||||
alert.informativeText = "This will close the current tab."
|
||||
alert.messageText = String(localized: "dialog.closeTab.title", defaultValue: "Close tab?")
|
||||
alert.informativeText = String(localized: "dialog.closeTab.message", defaultValue: "This will close the current tab.")
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "Close")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: String(localized: "dialog.closeTab.close", defaultValue: "Close"))
|
||||
alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel"))
|
||||
|
||||
// Prefer a sheet if we can find a window, otherwise fall back to modal.
|
||||
if let window = NSApp.keyWindow ?? NSApp.mainWindow {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue