diff options
| author | Adam <[email protected]> | 2026-03-13 06:48:38 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-13 06:48:38 -0500 |
| commit | 05cb3c87ca387be41aceb5ccad978c6848a56f70 (patch) | |
| tree | 2b592d2aa90d0fdb7ea72aa392507e2277b92ba5 /packages/app/src/components | |
| parent | 270cb0b8b4265ac0965ac8b94a58a3bca86fa558 (diff) | |
| download | opencode-05cb3c87ca387be41aceb5ccad978c6848a56f70.tar.gz opencode-05cb3c87ca387be41aceb5ccad978c6848a56f70.zip | |
chore(app): i18n sync (#17283)
Diffstat (limited to 'packages/app/src/components')
8 files changed, 76 insertions, 69 deletions
diff --git a/packages/app/src/components/debug-bar.tsx b/packages/app/src/components/debug-bar.tsx index 6fde71f3b..cbb24f77b 100644 --- a/packages/app/src/components/debug-bar.tsx +++ b/packages/app/src/components/debug-bar.tsx @@ -2,6 +2,7 @@ import { useIsRouting, useLocation } from "@solidjs/router" import { batch, createEffect, onCleanup, onMount } from "solid-js" import { createStore } from "solid-js/store" import { Tooltip } from "@opencode-ai/ui/tooltip" +import { useLanguage } from "@/context/language" type Mem = Performance & { memory?: { @@ -27,17 +28,17 @@ type Obs = PerformanceObserverInit & { const span = 5000 const ms = (n?: number, d = 0) => { - if (n === undefined || Number.isNaN(n)) return "n/a" + if (n === undefined || Number.isNaN(n)) return return `${n.toFixed(d)}ms` } const time = (n?: number) => { - if (n === undefined || Number.isNaN(n)) return "n/a" + if (n === undefined || Number.isNaN(n)) return return `${Math.round(n)}` } const mb = (n?: number) => { - if (n === undefined || Number.isNaN(n)) return "n/a" + if (n === undefined || Number.isNaN(n)) return const v = n / 1024 / 1024 return `${v >= 1024 ? v.toFixed(0) : v.toFixed(1)}MB` } @@ -74,6 +75,7 @@ function Cell(props: { bad?: boolean; dim?: boolean; label: string; tip: string; } export function DebugBar() { + const language = useLanguage() const location = useLocation() const routing = useIsRouting() const [state, setState] = createStore({ @@ -98,14 +100,15 @@ export function DebugBar() { }, }) + const na = () => language.t("debugBar.na") const heap = () => (state.heap.limit ? (state.heap.used ?? 0) / state.heap.limit : undefined) const heapv = () => { const value = heap() - if (value === undefined) return "n/a" + if (value === undefined) return na() return `${Math.round(value * 100)}%` } - const longv = () => (state.long.count === undefined ? "n/a" : `${time(state.long.block)}/${state.long.count}`) - const navv = () => (state.nav.pending ? "..." : time(state.nav.dur)) + const longv = () => (state.long.count === undefined ? na() : `${time(state.long.block) ?? na()}/${state.long.count}`) + const navv = () => (state.nav.pending ? "..." : (time(state.nav.dur) ?? na())) let prev = "" let start = 0 @@ -359,7 +362,7 @@ export function DebugBar() { return ( <aside - aria-label="Development performance diagnostics" + aria-label={language.t("debugBar.ariaLabel")} class="pointer-events-auto fixed bottom-3 right-3 z-50 w-[308px] max-w-[calc(100vw-1.5rem)] overflow-hidden rounded-xl border p-0.5 text-text-on-interactive-base shadow-[var(--shadow-lg-border-base)] sm:bottom-4 sm:right-4 sm:w-[324px]" style={{ "background-color": "color-mix(in srgb, var(--icon-interactive-base) 42%, black)", @@ -368,67 +371,70 @@ export function DebugBar() { > <div class="grid grid-cols-5 gap-px font-mono"> <Cell - label="NAV" - tip="Last completed route transition touching a session page, measured from router start until the first paint after it settles." + label={language.t("debugBar.nav.label")} + tip={language.t("debugBar.nav.tip")} value={navv()} bad={bad(state.nav.dur, 400)} dim={state.nav.dur === undefined && !state.nav.pending} /> <Cell - label="FPS" - tip="Rolling frames per second over the last 5 seconds." - value={state.fps === undefined ? "n/a" : `${Math.round(state.fps)}`} + label={language.t("debugBar.fps.label")} + tip={language.t("debugBar.fps.tip")} + value={state.fps === undefined ? na() : `${Math.round(state.fps)}`} bad={bad(state.fps, 50, true)} dim={state.fps === undefined} /> <Cell - label="FRAME" - tip="Worst frame time over the last 5 seconds." - value={time(state.gap)} + label={language.t("debugBar.frame.label")} + tip={language.t("debugBar.frame.tip")} + value={time(state.gap) ?? na()} bad={bad(state.gap, 50)} dim={state.gap === undefined} /> <Cell - label="JANK" - tip="Frames over 32ms in the last 5 seconds." - value={state.jank === undefined ? "n/a" : `${state.jank}`} + label={language.t("debugBar.jank.label")} + tip={language.t("debugBar.jank.tip")} + value={state.jank === undefined ? na() : `${state.jank}`} bad={bad(state.jank, 8)} dim={state.jank === undefined} /> <Cell - label="LONG" - tip={`Blocked time and long-task count in the last 5 seconds. Max task: ${ms(state.long.max)}.`} + label={language.t("debugBar.long.label")} + tip={language.t("debugBar.long.tip", { max: ms(state.long.max) ?? na() })} value={longv()} bad={bad(state.long.block, 200)} dim={state.long.count === undefined} /> <Cell - label="DELAY" - tip="Worst observed input delay in the last 5 seconds." - value={time(state.delay)} + label={language.t("debugBar.delay.label")} + tip={language.t("debugBar.delay.tip")} + value={time(state.delay) ?? na()} bad={bad(state.delay, 100)} dim={state.delay === undefined} /> <Cell - label="INP" - tip="Approximate interaction duration over the last 5 seconds. This is INP-like, not the official Web Vitals INP." - value={time(state.inp)} + label={language.t("debugBar.inp.label")} + tip={language.t("debugBar.inp.tip")} + value={time(state.inp) ?? na()} bad={bad(state.inp, 200)} dim={state.inp === undefined} /> <Cell - label="CLS" - tip="Cumulative layout shift for the current app lifetime." - value={state.cls === undefined ? "n/a" : state.cls.toFixed(2)} + label={language.t("debugBar.cls.label")} + tip={language.t("debugBar.cls.tip")} + value={state.cls === undefined ? na() : state.cls.toFixed(2)} bad={bad(state.cls, 0.1)} dim={state.cls === undefined} /> <Cell - label="MEM" + label={language.t("debugBar.mem.label")} tip={ state.heap.used === undefined - ? "Used JS heap vs heap limit. Chromium only." - : `Used JS heap vs heap limit. ${mb(state.heap.used)} of ${mb(state.heap.limit)}.` + ? language.t("debugBar.mem.tipUnavailable") + : language.t("debugBar.mem.tip", { + used: mb(state.heap.used) ?? na(), + limit: mb(state.heap.limit) ?? na(), + }) } value={heapv()} bad={bad(heap(), 0.8)} diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 3d4dbecbd..e21be77fb 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -426,7 +426,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil </Show> </div> <Show when={item.keybind}> - <Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "")}</Keybind> + <Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "", language.t)}</Keybind> </Show> </div> </Match> diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx index 655aba0b0..eb039c14d 100644 --- a/packages/app/src/components/dialog-select-server.tsx +++ b/packages/app/src/components/dialog-select-server.tsx @@ -149,7 +149,7 @@ function ServerForm(props: ServerFormProps) { <TextField type="text" label={language.t("dialog.server.add.username")} - placeholder="username" + placeholder={language.t("dialog.server.add.usernamePlaceholder")} value={props.username} disabled={props.busy} onChange={props.onUsernameChange} @@ -158,7 +158,7 @@ function ServerForm(props: ServerFormProps) { <TextField type="password" label={language.t("dialog.server.add.password")} - placeholder="password" + placeholder={language.t("dialog.server.add.passwordPlaceholder")} value={props.password} disabled={props.busy} onChange={props.onPasswordChange} diff --git a/packages/app/src/components/server/server-row.tsx b/packages/app/src/components/server/server-row.tsx index 8a4b7be4d..63a40bac2 100644 --- a/packages/app/src/components/server/server-row.tsx +++ b/packages/app/src/components/server/server-row.tsx @@ -10,6 +10,7 @@ import { type ParentProps, Show, } from "solid-js" +import { useLanguage } from "@/context/language" import { type ServerConnection, serverName } from "@/context/server" import type { ServerHealth } from "@/utils/server-health" @@ -25,6 +26,7 @@ interface ServerRowProps extends ParentProps { } export function ServerRow(props: ServerRowProps) { + const language = useLanguage() const [truncated, setTruncated] = createSignal(false) let nameRef: HTMLSpanElement | undefined let versionRef: HTMLSpanElement | undefined @@ -100,7 +102,7 @@ export function ServerRow(props: ServerRowProps) { {conn().http.username ? ( <span class="text-text-weak">{conn().http.username}</span> ) : ( - <span class="text-text-weaker">no username</span> + <span class="text-text-weaker">{language.t("server.row.noUsername")}</span> )} </span> {conn().http.password && <span class="text-text-weak">••••••••</span>} diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 8cb704bf1..ae9d2800e 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -46,63 +46,63 @@ type OS = "macos" | "windows" | "linux" | "unknown" const MAC_APPS = [ { id: "vscode", - label: "VS Code", + label: "session.header.open.app.vscode", icon: "vscode", openWith: "Visual Studio Code", }, - { id: "cursor", label: "Cursor", icon: "cursor", openWith: "Cursor" }, - { id: "zed", label: "Zed", icon: "zed", openWith: "Zed" }, - { id: "textmate", label: "TextMate", icon: "textmate", openWith: "TextMate" }, + { id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "Cursor" }, + { id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "Zed" }, + { id: "textmate", label: "session.header.open.app.textmate", icon: "textmate", openWith: "TextMate" }, { id: "antigravity", - label: "Antigravity", + label: "session.header.open.app.antigravity", icon: "antigravity", openWith: "Antigravity", }, - { id: "terminal", label: "Terminal", icon: "terminal", openWith: "Terminal" }, - { id: "iterm2", label: "iTerm2", icon: "iterm2", openWith: "iTerm" }, - { id: "ghostty", label: "Ghostty", icon: "ghostty", openWith: "Ghostty" }, - { id: "warp", label: "Warp", icon: "warp", openWith: "Warp" }, - { id: "xcode", label: "Xcode", icon: "xcode", openWith: "Xcode" }, + { id: "terminal", label: "session.header.open.app.terminal", icon: "terminal", openWith: "Terminal" }, + { id: "iterm2", label: "session.header.open.app.iterm2", icon: "iterm2", openWith: "iTerm" }, + { id: "ghostty", label: "session.header.open.app.ghostty", icon: "ghostty", openWith: "Ghostty" }, + { id: "warp", label: "session.header.open.app.warp", icon: "warp", openWith: "Warp" }, + { id: "xcode", label: "session.header.open.app.xcode", icon: "xcode", openWith: "Xcode" }, { id: "android-studio", - label: "Android Studio", + label: "session.header.open.app.androidStudio", icon: "android-studio", openWith: "Android Studio", }, { id: "sublime-text", - label: "Sublime Text", + label: "session.header.open.app.sublimeText", icon: "sublime-text", openWith: "Sublime Text", }, ] as const const WINDOWS_APPS = [ - { id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" }, - { id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" }, - { id: "zed", label: "Zed", icon: "zed", openWith: "zed" }, + { id: "vscode", label: "session.header.open.app.vscode", icon: "vscode", openWith: "code" }, + { id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "cursor" }, + { id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "zed" }, { id: "powershell", - label: "PowerShell", + label: "session.header.open.app.powershell", icon: "powershell", openWith: "powershell", }, { id: "sublime-text", - label: "Sublime Text", + label: "session.header.open.app.sublimeText", icon: "sublime-text", openWith: "Sublime Text", }, ] as const const LINUX_APPS = [ - { id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" }, - { id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" }, - { id: "zed", label: "Zed", icon: "zed", openWith: "zed" }, + { id: "vscode", label: "session.header.open.app.vscode", icon: "vscode", openWith: "code" }, + { id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "cursor" }, + { id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "zed" }, { id: "sublime-text", - label: "Sublime Text", + label: "session.header.open.app.sublimeText", icon: "sublime-text", openWith: "Sublime Text", }, @@ -160,9 +160,9 @@ export function SessionHeader() { }) const fileManager = createMemo(() => { - if (os() === "macos") return { label: "Finder", icon: "finder" as const } - if (os() === "windows") return { label: "File Explorer", icon: "file-explorer" as const } - return { label: "File Manager", icon: "finder" as const } + if (os() === "macos") return { label: "session.header.open.finder", icon: "finder" as const } + if (os() === "windows") return { label: "session.header.open.fileExplorer", icon: "file-explorer" as const } + return { label: "session.header.open.fileManager", icon: "finder" as const } }) createEffect(() => { @@ -187,8 +187,10 @@ export function SessionHeader() { const options = createMemo(() => { return [ - { id: "finder", label: fileManager().label, icon: fileManager().icon }, - ...apps().filter((app) => exists[app.id]), + { id: "finder", label: language.t(fileManager().label), icon: fileManager().icon }, + ...apps() + .filter((app) => exists[app.id]) + .map((app) => ({ ...app, label: language.t(app.label) })), ] as const }) diff --git a/packages/app/src/components/session/session-sortable-terminal-tab.tsx b/packages/app/src/components/session/session-sortable-terminal-tab.tsx index 4f49911c1..898958742 100644 --- a/packages/app/src/components/session/session-sortable-terminal-tab.tsx +++ b/packages/app/src/components/session/session-sortable-terminal-tab.tsx @@ -6,6 +6,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button" import { Tabs } from "@opencode-ai/ui/tabs" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" import { Icon } from "@opencode-ai/ui/icon" +import { isDefaultTitle as isDefaultTerminalTitle } from "@/context/terminal-title" import { useTerminal, type LocalPTY } from "@/context/terminal" import { useLanguage } from "@/context/language" import { focusTerminalById } from "@/pages/session/helpers" @@ -27,11 +28,7 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () => const isDefaultTitle = () => { const number = props.terminal.titleNumber if (!Number.isFinite(number) || number <= 0) return false - const match = props.terminal.title.match(/^Terminal (\d+)$/) - if (!match) return false - const parsed = Number(match[1]) - if (!Number.isFinite(parsed) || parsed <= 0) return false - return parsed === number + return isDefaultTerminalTitle(props.terminal.title, number) } const label = () => { diff --git a/packages/app/src/components/settings-keybinds.tsx b/packages/app/src/components/settings-keybinds.tsx index 1e4244789..7e2a48110 100644 --- a/packages/app/src/components/settings-keybinds.tsx +++ b/packages/app/src/components/settings-keybinds.tsx @@ -239,7 +239,7 @@ function useKeyCapture(input: { showToast({ title: input.language.t("settings.shortcuts.conflict.title"), description: input.language.t("settings.shortcuts.conflict.description", { - keybind: formatKeybind(next), + keybind: formatKeybind(next, input.language.t), titles: [...conflicts.values()].join(", "), }), }) diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index 840903293..ff455ebe2 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -519,7 +519,7 @@ export const Terminal = (props: TerminalProps) => { if (event.code !== 1000) { if (once.value) return once.value = true - local.onConnectError?.(new Error(`WebSocket closed abnormally: ${event.code}`)) + local.onConnectError?.(new Error(language.t("terminal.connectionLost.abnormalClose", { code: event.code }))) } } socket.addEventListener("close", handleClose) |
