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
|
|
@ -19,22 +19,22 @@ enum NewWorkspacePlacement: String, CaseIterable, Identifiable {
|
|||
var displayName: String {
|
||||
switch self {
|
||||
case .top:
|
||||
return "Top"
|
||||
return String(localized: "workspace.placement.top", defaultValue: "Top")
|
||||
case .afterCurrent:
|
||||
return "After current"
|
||||
return String(localized: "workspace.placement.afterCurrent", defaultValue: "After current")
|
||||
case .end:
|
||||
return "End"
|
||||
return String(localized: "workspace.placement.end", defaultValue: "End")
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .top:
|
||||
return "Insert new workspaces at the top of the list."
|
||||
return String(localized: "workspace.placement.top.description", defaultValue: "Insert new workspaces at the top of the list.")
|
||||
case .afterCurrent:
|
||||
return "Insert new workspaces directly after the active workspace."
|
||||
return String(localized: "workspace.placement.afterCurrent.description", defaultValue: "Insert new workspaces directly after the active workspace.")
|
||||
case .end:
|
||||
return "Append new workspaces to the bottom of the list."
|
||||
return String(localized: "workspace.placement.end.description", defaultValue: "Append new workspaces to the bottom of the list.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,9 +72,9 @@ enum SidebarActiveTabIndicatorStyle: String, CaseIterable, Identifiable {
|
|||
var displayName: String {
|
||||
switch self {
|
||||
case .leftRail:
|
||||
return "Left Rail"
|
||||
return String(localized: "sidebar.indicator.leftRail", defaultValue: "Left Rail")
|
||||
case .solidFill:
|
||||
return "Solid Fill"
|
||||
return String(localized: "sidebar.indicator.solidFill", defaultValue: "Solid Fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1078,9 +1078,13 @@ class TabManager: ObservableObject {
|
|||
|
||||
let count = plan.panelIds.count
|
||||
let titleLines = plan.titles.map { "• \($0)" }.joined(separator: "\n")
|
||||
let message = "This is about to close \(count) tab\(count == 1 ? "" : "s") in this pane:\n\(titleLines)"
|
||||
let message = if count == 1 {
|
||||
String(localized: "dialog.closeOtherTabs.message.one", defaultValue: "This will close 1 tab in this pane:\n\(titleLines)")
|
||||
} else {
|
||||
String(localized: "dialog.closeOtherTabs.message.other", defaultValue: "This will close \(count) tabs in this pane:\n\(titleLines)")
|
||||
}
|
||||
guard confirmClose(
|
||||
title: "Close other tabs?",
|
||||
title: String(localized: "dialog.closeOtherTabs.title", defaultValue: "Close other tabs?"),
|
||||
message: message,
|
||||
acceptCmdD: false
|
||||
) else { return }
|
||||
|
|
@ -1120,8 +1124,8 @@ class TabManager: ObservableObject {
|
|||
alert.messageText = title
|
||||
alert.informativeText = message
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "Close")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: String(localized: "common.close", defaultValue: "Close"))
|
||||
alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel"))
|
||||
|
||||
// macOS convention: Cmd+D = confirm destructive close (e.g. "Don't Save").
|
||||
// We only opt into this for the "close last workspace => close window" path to avoid
|
||||
|
|
@ -1182,15 +1186,15 @@ class TabManager: ObservableObject {
|
|||
if let collapsed, !collapsed.isEmpty {
|
||||
return collapsed
|
||||
}
|
||||
return "Untitled Tab"
|
||||
return String(localized: "tab.untitled", defaultValue: "Untitled Tab")
|
||||
}
|
||||
|
||||
private func closeWorkspaceIfRunningProcess(_ workspace: Workspace) {
|
||||
let willCloseWindow = tabs.count <= 1
|
||||
if workspaceNeedsConfirmClose(workspace),
|
||||
!confirmClose(
|
||||
title: "Close workspace?",
|
||||
message: "This will close the workspace and all of its panels.",
|
||||
title: String(localized: "dialog.closeWorkspace.title", defaultValue: "Close workspace?"),
|
||||
message: String(localized: "dialog.closeWorkspace.message", defaultValue: "This will close the workspace and all of its panels."),
|
||||
acceptCmdD: willCloseWindow
|
||||
) {
|
||||
return
|
||||
|
|
@ -1231,8 +1235,8 @@ class TabManager: ObservableObject {
|
|||
let needsConfirm = workspaceNeedsConfirmClose(tab)
|
||||
if needsConfirm {
|
||||
let message = willCloseWindow
|
||||
? "This will close the last tab and close the window."
|
||||
: "This will close the last tab and close its workspace."
|
||||
? String(localized: "dialog.closeLastTabWindow.message", defaultValue: "This will close the last tab and close the window.")
|
||||
: String(localized: "dialog.closeLastTabWorkspace.message", defaultValue: "This will close the last tab and close its workspace.")
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"surface.close.shortcut.confirm tab=\(tab.id.uuidString.prefix(5)) " +
|
||||
|
|
@ -1240,7 +1244,7 @@ class TabManager: ObservableObject {
|
|||
)
|
||||
#endif
|
||||
guard confirmClose(
|
||||
title: "Close tab?",
|
||||
title: String(localized: "dialog.closeTab.title", defaultValue: "Close tab?"),
|
||||
message: message,
|
||||
acceptCmdD: willCloseWindow
|
||||
) else {
|
||||
|
|
@ -1272,8 +1276,8 @@ class TabManager: ObservableObject {
|
|||
)
|
||||
#endif
|
||||
guard confirmClose(
|
||||
title: "Close tab?",
|
||||
message: "This will close the current tab.",
|
||||
title: String(localized: "dialog.closeTab.title", defaultValue: "Close tab?"),
|
||||
message: String(localized: "dialog.closeTab.message", defaultValue: "This will close the current tab."),
|
||||
acceptCmdD: false
|
||||
) else {
|
||||
#if DEBUG
|
||||
|
|
@ -1311,8 +1315,8 @@ class TabManager: ObservableObject {
|
|||
if let terminalPanel = tab.terminalPanel(for: surfaceId),
|
||||
terminalPanel.needsConfirmClose() {
|
||||
guard confirmClose(
|
||||
title: "Close tab?",
|
||||
message: "This will close the current tab.",
|
||||
title: String(localized: "dialog.closeTab.title", defaultValue: "Close tab?"),
|
||||
message: String(localized: "dialog.closeTab.message", defaultValue: "This will close the current tab."),
|
||||
acceptCmdD: false
|
||||
) else { return }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue