Skip to content

Commit 05cb3c8

Browse files
authored
chore(app): i18n sync (#17283)
1 parent 270cb0b commit 05cb3c8

65 files changed

Lines changed: 1776 additions & 156 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/app/src/app.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { type BaseRouterProps, Navigate, Route, Router } from "@solidjs/router"
1212
import { type Duration, Effect } from "effect"
1313
import {
1414
type Component,
15+
createMemo,
1516
createResource,
1617
createSignal,
1718
ErrorBoundary,
@@ -67,7 +68,7 @@ const SessionIndexRoute = () => <Navigate href="session" />
6768

6869
function UiI18nBridge(props: ParentProps) {
6970
const language = useLanguage()
70-
return <I18nProvider value={{ locale: language.locale, t: language.t }}>{props.children}</I18nProvider>
71+
return <I18nProvider value={{ locale: language.intl, t: language.t }}>{props.children}</I18nProvider>
7172
}
7273

7374
declare global {
@@ -218,8 +219,12 @@ function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean }>) {
218219
}
219220

220221
function ConnectionError(props: { onRetry?: () => void; onServerSelected?: (key: ServerConnection.Key) => void }) {
222+
const language = useLanguage()
221223
const server = useServer()
222224
const others = () => server.list.filter((s) => ServerConnection.key(s) !== server.key)
225+
const name = createMemo(() => server.name || server.key)
226+
const serverToken = "\u0000server\u0000"
227+
const unreachable = createMemo(() => language.t("app.server.unreachable", { server: serverToken }).split(serverToken))
223228

224229
const timer = setInterval(() => props.onRetry?.(), 1000)
225230
onCleanup(() => clearInterval(timer))
@@ -229,13 +234,15 @@ function ConnectionError(props: { onRetry?: () => void; onServerSelected?: (key:
229234
<div class="flex flex-col items-center max-w-md text-center">
230235
<Splash class="w-12 h-15 mb-4" />
231236
<p class="text-14-regular text-text-base">
232-
Could not reach <span class="text-text-strong font-medium">{server.name || server.key}</span>
237+
{unreachable()[0]}
238+
<span class="text-text-strong font-medium">{name()}</span>
239+
{unreachable()[1]}
233240
</p>
234-
<p class="mt-1 text-12-regular text-text-weak">Retrying automatically...</p>
241+
<p class="mt-1 text-12-regular text-text-weak">{language.t("app.server.retrying")}</p>
235242
</div>
236243
<Show when={others().length > 0}>
237244
<div class="flex flex-col gap-2 w-full max-w-sm">
238-
<span class="text-12-regular text-text-base text-center">Other servers</span>
245+
<span class="text-12-regular text-text-base text-center">{language.t("app.server.otherServers")}</span>
239246
<div class="flex flex-col gap-1 bg-surface-base rounded-lg p-2">
240247
<For each={others()}>
241248
{(conn) => {

packages/app/src/components/debug-bar.tsx

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useIsRouting, useLocation } from "@solidjs/router"
22
import { batch, createEffect, onCleanup, onMount } from "solid-js"
33
import { createStore } from "solid-js/store"
44
import { Tooltip } from "@opencode-ai/ui/tooltip"
5+
import { useLanguage } from "@/context/language"
56

67
type Mem = Performance & {
78
memory?: {
@@ -27,17 +28,17 @@ type Obs = PerformanceObserverInit & {
2728
const span = 5000
2829

2930
const ms = (n?: number, d = 0) => {
30-
if (n === undefined || Number.isNaN(n)) return "n/a"
31+
if (n === undefined || Number.isNaN(n)) return
3132
return `${n.toFixed(d)}ms`
3233
}
3334

3435
const time = (n?: number) => {
35-
if (n === undefined || Number.isNaN(n)) return "n/a"
36+
if (n === undefined || Number.isNaN(n)) return
3637
return `${Math.round(n)}`
3738
}
3839

3940
const mb = (n?: number) => {
40-
if (n === undefined || Number.isNaN(n)) return "n/a"
41+
if (n === undefined || Number.isNaN(n)) return
4142
const v = n / 1024 / 1024
4243
return `${v >= 1024 ? v.toFixed(0) : v.toFixed(1)}MB`
4344
}
@@ -74,6 +75,7 @@ function Cell(props: { bad?: boolean; dim?: boolean; label: string; tip: string;
7475
}
7576

7677
export function DebugBar() {
78+
const language = useLanguage()
7779
const location = useLocation()
7880
const routing = useIsRouting()
7981
const [state, setState] = createStore({
@@ -98,14 +100,15 @@ export function DebugBar() {
98100
},
99101
})
100102

103+
const na = () => language.t("debugBar.na")
101104
const heap = () => (state.heap.limit ? (state.heap.used ?? 0) / state.heap.limit : undefined)
102105
const heapv = () => {
103106
const value = heap()
104-
if (value === undefined) return "n/a"
107+
if (value === undefined) return na()
105108
return `${Math.round(value * 100)}%`
106109
}
107-
const longv = () => (state.long.count === undefined ? "n/a" : `${time(state.long.block)}/${state.long.count}`)
108-
const navv = () => (state.nav.pending ? "..." : time(state.nav.dur))
110+
const longv = () => (state.long.count === undefined ? na() : `${time(state.long.block) ?? na()}/${state.long.count}`)
111+
const navv = () => (state.nav.pending ? "..." : (time(state.nav.dur) ?? na()))
109112

110113
let prev = ""
111114
let start = 0
@@ -359,7 +362,7 @@ export function DebugBar() {
359362

360363
return (
361364
<aside
362-
aria-label="Development performance diagnostics"
365+
aria-label={language.t("debugBar.ariaLabel")}
363366
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]"
364367
style={{
365368
"background-color": "color-mix(in srgb, var(--icon-interactive-base) 42%, black)",
@@ -368,67 +371,70 @@ export function DebugBar() {
368371
>
369372
<div class="grid grid-cols-5 gap-px font-mono">
370373
<Cell
371-
label="NAV"
372-
tip="Last completed route transition touching a session page, measured from router start until the first paint after it settles."
374+
label={language.t("debugBar.nav.label")}
375+
tip={language.t("debugBar.nav.tip")}
373376
value={navv()}
374377
bad={bad(state.nav.dur, 400)}
375378
dim={state.nav.dur === undefined && !state.nav.pending}
376379
/>
377380
<Cell
378-
label="FPS"
379-
tip="Rolling frames per second over the last 5 seconds."
380-
value={state.fps === undefined ? "n/a" : `${Math.round(state.fps)}`}
381+
label={language.t("debugBar.fps.label")}
382+
tip={language.t("debugBar.fps.tip")}
383+
value={state.fps === undefined ? na() : `${Math.round(state.fps)}`}
381384
bad={bad(state.fps, 50, true)}
382385
dim={state.fps === undefined}
383386
/>
384387
<Cell
385-
label="FRAME"
386-
tip="Worst frame time over the last 5 seconds."
387-
value={time(state.gap)}
388+
label={language.t("debugBar.frame.label")}
389+
tip={language.t("debugBar.frame.tip")}
390+
value={time(state.gap) ?? na()}
388391
bad={bad(state.gap, 50)}
389392
dim={state.gap === undefined}
390393
/>
391394
<Cell
392-
label="JANK"
393-
tip="Frames over 32ms in the last 5 seconds."
394-
value={state.jank === undefined ? "n/a" : `${state.jank}`}
395+
label={language.t("debugBar.jank.label")}
396+
tip={language.t("debugBar.jank.tip")}
397+
value={state.jank === undefined ? na() : `${state.jank}`}
395398
bad={bad(state.jank, 8)}
396399
dim={state.jank === undefined}
397400
/>
398401
<Cell
399-
label="LONG"
400-
tip={`Blocked time and long-task count in the last 5 seconds. Max task: ${ms(state.long.max)}.`}
402+
label={language.t("debugBar.long.label")}
403+
tip={language.t("debugBar.long.tip", { max: ms(state.long.max) ?? na() })}
401404
value={longv()}
402405
bad={bad(state.long.block, 200)}
403406
dim={state.long.count === undefined}
404407
/>
405408
<Cell
406-
label="DELAY"
407-
tip="Worst observed input delay in the last 5 seconds."
408-
value={time(state.delay)}
409+
label={language.t("debugBar.delay.label")}
410+
tip={language.t("debugBar.delay.tip")}
411+
value={time(state.delay) ?? na()}
409412
bad={bad(state.delay, 100)}
410413
dim={state.delay === undefined}
411414
/>
412415
<Cell
413-
label="INP"
414-
tip="Approximate interaction duration over the last 5 seconds. This is INP-like, not the official Web Vitals INP."
415-
value={time(state.inp)}
416+
label={language.t("debugBar.inp.label")}
417+
tip={language.t("debugBar.inp.tip")}
418+
value={time(state.inp) ?? na()}
416419
bad={bad(state.inp, 200)}
417420
dim={state.inp === undefined}
418421
/>
419422
<Cell
420-
label="CLS"
421-
tip="Cumulative layout shift for the current app lifetime."
422-
value={state.cls === undefined ? "n/a" : state.cls.toFixed(2)}
423+
label={language.t("debugBar.cls.label")}
424+
tip={language.t("debugBar.cls.tip")}
425+
value={state.cls === undefined ? na() : state.cls.toFixed(2)}
423426
bad={bad(state.cls, 0.1)}
424427
dim={state.cls === undefined}
425428
/>
426429
<Cell
427-
label="MEM"
430+
label={language.t("debugBar.mem.label")}
428431
tip={
429432
state.heap.used === undefined
430-
? "Used JS heap vs heap limit. Chromium only."
431-
: `Used JS heap vs heap limit. ${mb(state.heap.used)} of ${mb(state.heap.limit)}.`
433+
? language.t("debugBar.mem.tipUnavailable")
434+
: language.t("debugBar.mem.tip", {
435+
used: mb(state.heap.used) ?? na(),
436+
limit: mb(state.heap.limit) ?? na(),
437+
})
432438
}
433439
value={heapv()}
434440
bad={bad(heap(), 0.8)}

packages/app/src/components/dialog-select-file.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
426426
</Show>
427427
</div>
428428
<Show when={item.keybind}>
429-
<Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "")}</Keybind>
429+
<Keybind class="rounded-[4px]">{formatKeybind(item.keybind ?? "", language.t)}</Keybind>
430430
</Show>
431431
</div>
432432
</Match>

packages/app/src/components/dialog-select-server.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function ServerForm(props: ServerFormProps) {
149149
<TextField
150150
type="text"
151151
label={language.t("dialog.server.add.username")}
152-
placeholder="username"
152+
placeholder={language.t("dialog.server.add.usernamePlaceholder")}
153153
value={props.username}
154154
disabled={props.busy}
155155
onChange={props.onUsernameChange}
@@ -158,7 +158,7 @@ function ServerForm(props: ServerFormProps) {
158158
<TextField
159159
type="password"
160160
label={language.t("dialog.server.add.password")}
161-
placeholder="password"
161+
placeholder={language.t("dialog.server.add.passwordPlaceholder")}
162162
value={props.password}
163163
disabled={props.busy}
164164
onChange={props.onPasswordChange}

packages/app/src/components/server/server-row.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
type ParentProps,
1111
Show,
1212
} from "solid-js"
13+
import { useLanguage } from "@/context/language"
1314
import { type ServerConnection, serverName } from "@/context/server"
1415
import type { ServerHealth } from "@/utils/server-health"
1516

@@ -25,6 +26,7 @@ interface ServerRowProps extends ParentProps {
2526
}
2627

2728
export function ServerRow(props: ServerRowProps) {
29+
const language = useLanguage()
2830
const [truncated, setTruncated] = createSignal(false)
2931
let nameRef: HTMLSpanElement | undefined
3032
let versionRef: HTMLSpanElement | undefined
@@ -100,7 +102,7 @@ export function ServerRow(props: ServerRowProps) {
100102
{conn().http.username ? (
101103
<span class="text-text-weak">{conn().http.username}</span>
102104
) : (
103-
<span class="text-text-weaker">no username</span>
105+
<span class="text-text-weaker">{language.t("server.row.noUsername")}</span>
104106
)}
105107
</span>
106108
{conn().http.password && <span class="text-text-weak">••••••••</span>}

packages/app/src/components/session/session-header.tsx

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,63 +46,63 @@ type OS = "macos" | "windows" | "linux" | "unknown"
4646
const MAC_APPS = [
4747
{
4848
id: "vscode",
49-
label: "VS Code",
49+
label: "session.header.open.app.vscode",
5050
icon: "vscode",
5151
openWith: "Visual Studio Code",
5252
},
53-
{ id: "cursor", label: "Cursor", icon: "cursor", openWith: "Cursor" },
54-
{ id: "zed", label: "Zed", icon: "zed", openWith: "Zed" },
55-
{ id: "textmate", label: "TextMate", icon: "textmate", openWith: "TextMate" },
53+
{ id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "Cursor" },
54+
{ id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "Zed" },
55+
{ id: "textmate", label: "session.header.open.app.textmate", icon: "textmate", openWith: "TextMate" },
5656
{
5757
id: "antigravity",
58-
label: "Antigravity",
58+
label: "session.header.open.app.antigravity",
5959
icon: "antigravity",
6060
openWith: "Antigravity",
6161
},
62-
{ id: "terminal", label: "Terminal", icon: "terminal", openWith: "Terminal" },
63-
{ id: "iterm2", label: "iTerm2", icon: "iterm2", openWith: "iTerm" },
64-
{ id: "ghostty", label: "Ghostty", icon: "ghostty", openWith: "Ghostty" },
65-
{ id: "warp", label: "Warp", icon: "warp", openWith: "Warp" },
66-
{ id: "xcode", label: "Xcode", icon: "xcode", openWith: "Xcode" },
62+
{ id: "terminal", label: "session.header.open.app.terminal", icon: "terminal", openWith: "Terminal" },
63+
{ id: "iterm2", label: "session.header.open.app.iterm2", icon: "iterm2", openWith: "iTerm" },
64+
{ id: "ghostty", label: "session.header.open.app.ghostty", icon: "ghostty", openWith: "Ghostty" },
65+
{ id: "warp", label: "session.header.open.app.warp", icon: "warp", openWith: "Warp" },
66+
{ id: "xcode", label: "session.header.open.app.xcode", icon: "xcode", openWith: "Xcode" },
6767
{
6868
id: "android-studio",
69-
label: "Android Studio",
69+
label: "session.header.open.app.androidStudio",
7070
icon: "android-studio",
7171
openWith: "Android Studio",
7272
},
7373
{
7474
id: "sublime-text",
75-
label: "Sublime Text",
75+
label: "session.header.open.app.sublimeText",
7676
icon: "sublime-text",
7777
openWith: "Sublime Text",
7878
},
7979
] as const
8080

8181
const WINDOWS_APPS = [
82-
{ id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" },
83-
{ id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" },
84-
{ id: "zed", label: "Zed", icon: "zed", openWith: "zed" },
82+
{ id: "vscode", label: "session.header.open.app.vscode", icon: "vscode", openWith: "code" },
83+
{ id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "cursor" },
84+
{ id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "zed" },
8585
{
8686
id: "powershell",
87-
label: "PowerShell",
87+
label: "session.header.open.app.powershell",
8888
icon: "powershell",
8989
openWith: "powershell",
9090
},
9191
{
9292
id: "sublime-text",
93-
label: "Sublime Text",
93+
label: "session.header.open.app.sublimeText",
9494
icon: "sublime-text",
9595
openWith: "Sublime Text",
9696
},
9797
] as const
9898

9999
const LINUX_APPS = [
100-
{ id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" },
101-
{ id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" },
102-
{ id: "zed", label: "Zed", icon: "zed", openWith: "zed" },
100+
{ id: "vscode", label: "session.header.open.app.vscode", icon: "vscode", openWith: "code" },
101+
{ id: "cursor", label: "session.header.open.app.cursor", icon: "cursor", openWith: "cursor" },
102+
{ id: "zed", label: "session.header.open.app.zed", icon: "zed", openWith: "zed" },
103103
{
104104
id: "sublime-text",
105-
label: "Sublime Text",
105+
label: "session.header.open.app.sublimeText",
106106
icon: "sublime-text",
107107
openWith: "Sublime Text",
108108
},
@@ -160,9 +160,9 @@ export function SessionHeader() {
160160
})
161161

162162
const fileManager = createMemo(() => {
163-
if (os() === "macos") return { label: "Finder", icon: "finder" as const }
164-
if (os() === "windows") return { label: "File Explorer", icon: "file-explorer" as const }
165-
return { label: "File Manager", icon: "finder" as const }
163+
if (os() === "macos") return { label: "session.header.open.finder", icon: "finder" as const }
164+
if (os() === "windows") return { label: "session.header.open.fileExplorer", icon: "file-explorer" as const }
165+
return { label: "session.header.open.fileManager", icon: "finder" as const }
166166
})
167167

168168
createEffect(() => {
@@ -187,8 +187,10 @@ export function SessionHeader() {
187187

188188
const options = createMemo(() => {
189189
return [
190-
{ id: "finder", label: fileManager().label, icon: fileManager().icon },
191-
...apps().filter((app) => exists[app.id]),
190+
{ id: "finder", label: language.t(fileManager().label), icon: fileManager().icon },
191+
...apps()
192+
.filter((app) => exists[app.id])
193+
.map((app) => ({ ...app, label: language.t(app.label) })),
192194
] as const
193195
})
194196

packages/app/src/components/session/session-sortable-terminal-tab.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
66
import { Tabs } from "@opencode-ai/ui/tabs"
77
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
88
import { Icon } from "@opencode-ai/ui/icon"
9+
import { isDefaultTitle as isDefaultTerminalTitle } from "@/context/terminal-title"
910
import { useTerminal, type LocalPTY } from "@/context/terminal"
1011
import { useLanguage } from "@/context/language"
1112
import { focusTerminalById } from "@/pages/session/helpers"
@@ -27,11 +28,7 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () =>
2728
const isDefaultTitle = () => {
2829
const number = props.terminal.titleNumber
2930
if (!Number.isFinite(number) || number <= 0) return false
30-
const match = props.terminal.title.match(/^Terminal (\d+)$/)
31-
if (!match) return false
32-
const parsed = Number(match[1])
33-
if (!Number.isFinite(parsed) || parsed <= 0) return false
34-
return parsed === number
31+
return isDefaultTerminalTitle(props.terminal.title, number)
3532
}
3633

3734
const label = () => {

0 commit comments

Comments
 (0)