Internationalization: 7 languages with live switching
Custom signal-based runtime (no i18n dependency): shared src/shared/i18n dictionaries, ui I18nService.t() reactive by construction so language switches live, and a backend i18n.ts for native menus + thrown errors. Every user-facing string keyed across templates, toasts, dialogs, file kinds, columns, macOS menu/context menus, and errors. Language picker in Options (auto-detects OS locale, falls back to English); switching relabels the webview and native chrome instantly. English/Spanish/French/ German fully drafted; Chinese/Japanese/Basque drafted, flagged for native review. Docs updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+15
-14
@@ -1,6 +1,7 @@
|
||||
import { ApplicationMenu, type ApplicationMenuItemConfig } from "electrobun/bun";
|
||||
import { BRANDING } from "../shared/branding";
|
||||
import type { SavedConnection } from "../shared/types";
|
||||
import { t } from "./i18n";
|
||||
import type { ConnectionStore } from "./store/connections";
|
||||
|
||||
/**
|
||||
@@ -27,37 +28,37 @@ export async function applyApplicationMenu(store: ConnectionStore): Promise<void
|
||||
...looseItems,
|
||||
...stackMenus,
|
||||
...(looseItems.length || stackMenus.length ? [{ type: "separator" } as const] : []),
|
||||
{ label: "Connection Manager…", action: "manage-connections" },
|
||||
{ label: t("menu.connectionManager"), action: "manage-connections" },
|
||||
];
|
||||
|
||||
ApplicationMenu.setApplicationMenu([
|
||||
{
|
||||
submenu: [
|
||||
{ label: `About ${BRANDING.appName}`, action: "about" },
|
||||
{ label: t("menu.about", { app: BRANDING.appName }), action: "about" },
|
||||
{ type: "separator" },
|
||||
{ role: "hide" },
|
||||
{ role: "hideOthers" },
|
||||
{ role: "showAll" },
|
||||
{ type: "separator" },
|
||||
{ label: "Quit", role: "quit" },
|
||||
{ label: t("menu.quit"), role: "quit" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "File",
|
||||
label: t("menu.file"),
|
||||
submenu: [
|
||||
{ label: "New Tab", action: "new-tab", accelerator: "t" },
|
||||
{ label: "Close Tab", action: "close-tab", accelerator: "w" },
|
||||
{ label: t("menu.newTab"), action: "new-tab", accelerator: "t" },
|
||||
{ label: t("menu.closeTab"), action: "close-tab", accelerator: "w" },
|
||||
{ type: "separator" },
|
||||
{ label: "New Folder", action: "new-folder" },
|
||||
{ label: "New File", action: "new-file" },
|
||||
{ label: t("menu.newFolder"), action: "new-folder" },
|
||||
{ label: t("menu.newFile"), action: "new-file" },
|
||||
{ type: "separator" },
|
||||
{ label: "Edit File", action: "edit-file", accelerator: "e" },
|
||||
{ label: t("menu.editFile"), action: "edit-file", accelerator: "e" },
|
||||
{ type: "separator" },
|
||||
{ label: "Connect", submenu: connectSubmenu },
|
||||
{ label: t("menu.connect"), submenu: connectSubmenu },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
label: t("menu.edit"),
|
||||
submenu: [
|
||||
{ role: "undo" },
|
||||
{ role: "redo" },
|
||||
@@ -71,15 +72,15 @@ export async function applyApplicationMenu(store: ConnectionStore): Promise<void
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
label: t("menu.view"),
|
||||
submenu: [
|
||||
{ label: "Refresh", action: "refresh-active", accelerator: "r" },
|
||||
{ label: t("menu.refresh"), action: "refresh-active", accelerator: "r" },
|
||||
{ type: "separator" },
|
||||
{ role: "toggleFullScreen" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Window",
|
||||
label: t("menu.window"),
|
||||
submenu: [
|
||||
{ role: "minimize" },
|
||||
{ role: "zoom" },
|
||||
|
||||
Reference in New Issue
Block a user