From 850d50eb645b4a0a92ba673b1d0af88887f92178 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:42:26 -0600 Subject: [PATCH] fix(app): missing i18n keys --- .../components/dialog-connect-provider.tsx | 4 +- .../app/src/components/dialog-select-mcp.tsx | 4 +- .../src/components/dialog-select-model.tsx | 4 +- packages/app/src/components/model-tooltip.tsx | 62 +++++++++++++------ packages/app/src/components/prompt-input.tsx | 4 +- .../session/session-sortable-terminal-tab.tsx | 4 +- .../app/src/components/settings-general.tsx | 43 +++++++------ packages/app/src/components/titlebar.tsx | 6 +- packages/app/src/i18n/en.ts | 46 ++++++++++++++ packages/app/src/pages/layout.tsx | 28 ++++----- packages/app/src/pages/session.tsx | 10 +-- packages/app/src/utils/sound.ts | 24 +++---- 12 files changed, 159 insertions(+), 80 deletions(-) diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index 63f3c5dbe..66c5bd0b8 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -33,7 +33,7 @@ export function DialogConnectProvider(props: { provider: string }) { globalSync.data.provider_auth[props.provider] ?? [ { type: "api", - label: "API key", + label: language.t("provider.connect.method.apiKey"), }, ], ) @@ -245,7 +245,7 @@ export function DialogConnectProvider(props: { provider: string }) {
{language.t("provider.connect.opencodeZen.visit.prefix")} - opencode.ai/zen + {language.t("provider.connect.opencodeZen.visit.link")} {language.t("provider.connect.opencodeZen.visit.suffix")}
diff --git a/packages/app/src/components/dialog-select-mcp.tsx b/packages/app/src/components/dialog-select-mcp.tsx index 25ef8df01..b91699561 100644 --- a/packages/app/src/components/dialog-select-mcp.tsx +++ b/packages/app/src/components/dialog-select-mcp.tsx @@ -77,7 +77,9 @@ export const DialogSelectMcp: Component = () => { {language.t("mcp.status.disabled")} - ... + + {language.t("common.loading.ellipsis")} + diff --git a/packages/app/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx index a030e952b..4fd8af1b3 100644 --- a/packages/app/src/components/dialog-select-model.tsx +++ b/packages/app/src/components/dialog-select-model.tsx @@ -115,8 +115,8 @@ export const ModelSelectorPopover: Component<{ variant="ghost" iconSize="normal" class="size-6" - aria-label="Manage models" - title="Manage models" + aria-label={language.t("dialog.model.manage")} + title={language.t("dialog.model.manage")} onClick={handleManage} /> } diff --git a/packages/app/src/components/model-tooltip.tsx b/packages/app/src/components/model-tooltip.tsx index 14b4ba799..54550cf76 100644 --- a/packages/app/src/components/model-tooltip.tsx +++ b/packages/app/src/components/model-tooltip.tsx @@ -1,4 +1,5 @@ import { Show, type Component } from "solid-js" +import { useLanguage } from "@/context/language" type InputKey = "text" | "image" | "audio" | "video" | "pdf" type InputMap = Record @@ -22,23 +23,31 @@ type ModelInfo = { } } -function sourceName(model: ModelInfo) { - const value = `${model.id} ${model.name}`.toLowerCase() - - if (/claude|anthropic/.test(value)) return "Anthropic" - if (/gpt|o[1-4]|codex|openai/.test(value)) return "OpenAI" - if (/gemini|palm|bard|google/.test(value)) return "Google" - if (/grok|xai/.test(value)) return "xAI" - if (/llama|meta/.test(value)) return "Meta" - - return model.provider.name -} - export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free?: boolean }> = (props) => { + const language = useLanguage() + const sourceName = (model: ModelInfo) => { + const value = `${model.id} ${model.name}`.toLowerCase() + + if (/claude|anthropic/.test(value)) return language.t("model.provider.anthropic") + if (/gpt|o[1-4]|codex|openai/.test(value)) return language.t("model.provider.openai") + if (/gemini|palm|bard|google/.test(value)) return language.t("model.provider.google") + if (/grok|xai/.test(value)) return language.t("model.provider.xai") + if (/llama|meta/.test(value)) return language.t("model.provider.meta") + + return model.provider.name + } + const inputLabel = (value: string) => { + if (value === "text") return language.t("model.input.text") + if (value === "image") return language.t("model.input.image") + if (value === "audio") return language.t("model.input.audio") + if (value === "video") return language.t("model.input.video") + if (value === "pdf") return language.t("model.input.pdf") + return value + } const title = () => { const tags: Array = [] - if (props.latest) tags.push("Latest") - if (props.free) tags.push("Free") + if (props.latest) tags.push(language.t("model.tag.latest")) + if (props.free) tags.push(language.t("model.tag.free")) const suffix = tags.length ? ` (${tags.join(", ")})` : "" return `${sourceName(props.model)} ${props.model.name}${suffix}` } @@ -46,22 +55,35 @@ export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free? if (props.model.capabilities) { const input = props.model.capabilities.input const order: Array = ["text", "image", "audio", "video", "pdf"] - const entries = order.filter((key) => input[key]) + const entries = order.filter((key) => input[key]).map((key) => inputLabel(key)) return entries.length ? entries.join(", ") : undefined } - return props.model.modalities?.input?.join(", ") + const raw = props.model.modalities?.input + if (!raw) return + const entries = raw.map((value) => inputLabel(value)) + return entries.length ? entries.join(", ") : undefined } const reasoning = () => { - if (props.model.capabilities) return props.model.capabilities.reasoning ? "Allows reasoning" : "No reasoning" - return props.model.reasoning ? "Allows reasoning" : "No reasoning" + if (props.model.capabilities) + return props.model.capabilities.reasoning + ? language.t("model.tooltip.reasoning.allowed") + : language.t("model.tooltip.reasoning.none") + return props.model.reasoning + ? language.t("model.tooltip.reasoning.allowed") + : language.t("model.tooltip.reasoning.none") } - const context = () => `Context limit ${props.model.limit.context.toLocaleString()}` + const context = () => + language.t("model.tooltip.context", { limit: props.model.limit.context.toLocaleString() }) return (
{title()}
- {(value) =>
Allows: {value()}
} + {(value) => ( +
+ {language.t("model.tooltip.allows", { inputs: value() })} +
+ )}
{reasoning()}
{context()}
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 35a74f43e..d80abf0c3 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1696,7 +1696,9 @@ export const PromptInput: Component = (props) => {
{language.t("prompt.action.stop")} - ESC + + {language.t("common.key.esc")} +
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 63efa54a8..03f07fa56 100644 --- a/packages/app/src/components/session/session-sortable-terminal-tab.tsx +++ b/packages/app/src/components/session/session-sortable-terminal-tab.tsx @@ -150,11 +150,11 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () => > - Rename + {language.t("common.rename")} - Close + {language.t("common.close")} diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index c356b269b..69d180f9d 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -29,18 +29,19 @@ export const SettingsGeneral: Component = () => { ) const fontOptions = [ - { value: "ibm-plex-mono", label: "IBM Plex Mono" }, - { value: "cascadia-code", label: "Cascadia Code" }, - { value: "fira-code", label: "Fira Code" }, - { value: "hack", label: "Hack" }, - { value: "inconsolata", label: "Inconsolata" }, - { value: "intel-one-mono", label: "Intel One Mono" }, - { value: "jetbrains-mono", label: "JetBrains Mono" }, - { value: "meslo-lgs", label: "Meslo LGS" }, - { value: "roboto-mono", label: "Roboto Mono" }, - { value: "source-code-pro", label: "Source Code Pro" }, - { value: "ubuntu-mono", label: "Ubuntu Mono" }, - ] + { value: "ibm-plex-mono", label: "font.option.ibmPlexMono" }, + { value: "cascadia-code", label: "font.option.cascadiaCode" }, + { value: "fira-code", label: "font.option.firaCode" }, + { value: "hack", label: "font.option.hack" }, + { value: "inconsolata", label: "font.option.inconsolata" }, + { value: "intel-one-mono", label: "font.option.intelOneMono" }, + { value: "jetbrains-mono", label: "font.option.jetbrainsMono" }, + { value: "meslo-lgs", label: "font.option.mesloLgs" }, + { value: "roboto-mono", label: "font.option.robotoMono" }, + { value: "source-code-pro", label: "font.option.sourceCodePro" }, + { value: "ubuntu-mono", label: "font.option.ubuntuMono" }, + ] as const + const fontOptionsList = [...fontOptions] const soundOptions = [...SOUND_OPTIONS] @@ -137,17 +138,21 @@ export const SettingsGeneral: Component = () => { description={language.t("settings.general.row.font.description")} >
@@ -203,7 +208,7 @@ export const SettingsGeneral: Component = () => { options={soundOptions} current={soundOptions.find((o) => o.id === settings.sounds.agent())} value={(o) => o.id} - label={(o) => o.label} + label={(o) => language.t(o.label)} onHighlight={(option) => { if (!option) return playSound(option.src) @@ -227,7 +232,7 @@ export const SettingsGeneral: Component = () => { options={soundOptions} current={soundOptions.find((o) => o.id === settings.sounds.permissions())} value={(o) => o.id} - label={(o) => o.label} + label={(o) => language.t(o.label)} onHighlight={(option) => { if (!option) return playSound(option.src) @@ -251,7 +256,7 @@ export const SettingsGeneral: Component = () => { options={soundOptions} current={soundOptions.find((o) => o.id === settings.sounds.errors())} value={(o) => o.id} - label={(o) => o.label} + label={(o) => language.t(o.label)} onHighlight={(option) => { if (!option) return playSound(option.src) diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index a5103d90d..877fe8492 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -94,7 +94,7 @@ export function Titlebar() { variant="ghost" class="size-8 rounded-md" onClick={layout.mobileSidebar.toggle} - aria-label="Toggle menu" + aria-label={language.t("sidebar.menu.toggle")} />
@@ -105,7 +105,7 @@ export function Titlebar() { variant="ghost" class="size-8 rounded-md" onClick={layout.mobileSidebar.toggle} - aria-label="Toggle menu" + aria-label={language.t("sidebar.menu.toggle")} /> @@ -119,7 +119,7 @@ export function Titlebar() { variant="ghost" class="group/sidebar-toggle size-6 p-0" onClick={layout.sidebar.toggle} - aria-label="Toggle sidebar" + aria-label={language.t("command.sidebar.toggle")} aria-expanded={layout.sidebar.opened()} >
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 8e4c9aa50..27361a2e6 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -14,6 +14,7 @@ export const dict = { "command.category.agent": "Agent", "command.category.permissions": "Permissions", "command.category.workspace": "Workspace", + "command.category.settings": "Settings", "theme.scheme.system": "System", "theme.scheme.light": "Light", @@ -23,6 +24,7 @@ export const dict = { "command.project.open": "Open project", "command.provider.connect": "Connect provider", "command.server.switch": "Switch server", + "command.settings.open": "Open settings", "command.session.previous": "Previous session", "command.session.next": "Next session", "command.session.archive": "Archive session", @@ -115,6 +117,7 @@ export const dict = { "provider.connect.opencodeZen.line2": "With a single API key you'll get access to models such as Claude, GPT, Gemini, GLM and more.", "provider.connect.opencodeZen.visit.prefix": "Visit ", + "provider.connect.opencodeZen.visit.link": "opencode.ai/zen", "provider.connect.opencodeZen.visit.suffix": " to collect your API key.", "provider.connect.oauth.code.visit.prefix": "Visit ", "provider.connect.oauth.code.visit.link": "this link", @@ -134,9 +137,24 @@ export const dict = { "model.tag.free": "Free", "model.tag.latest": "Latest", + "model.provider.anthropic": "Anthropic", + "model.provider.openai": "OpenAI", + "model.provider.google": "Google", + "model.provider.xai": "xAI", + "model.provider.meta": "Meta", + "model.input.text": "text", + "model.input.image": "image", + "model.input.audio": "audio", + "model.input.video": "video", + "model.input.pdf": "pdf", + "model.tooltip.allows": "Allows: {{inputs}}", + "model.tooltip.reasoning.allowed": "Allows reasoning", + "model.tooltip.reasoning.none": "No reasoning", + "model.tooltip.context": "Context limit {{limit}}", "common.search.placeholder": "Search", "common.loading": "Loading", + "common.loading.ellipsis": "...", "common.cancel": "Cancel", "common.submit": "Submit", "common.save": "Save", @@ -371,6 +389,7 @@ export const dict = { "session.messages.loadingEarlier": "Loading earlier messages...", "session.messages.loadEarlier": "Load earlier messages", "session.messages.loading": "Loading messages...", + "session.messages.jumpToLatest": "Jump to latest", "session.context.addToContext": "Add {{selection}} to context", @@ -402,6 +421,8 @@ export const dict = { "terminal.loading": "Loading terminal...", "terminal.title": "Terminal", "terminal.title.numbered": "Terminal {{number}}", + "terminal.connectionLost.title": "Connection Lost", + "terminal.connectionLost.description": "The terminal connection was interrupted. This can happen when the server restarts.", "common.closeTab": "Close tab", "common.dismiss": "Dismiss", @@ -414,7 +435,9 @@ export const dict = { "common.close": "Close", "common.edit": "Edit", "common.loadMore": "Load more", + "common.key.esc": "ESC", + "sidebar.menu.toggle": "Toggle menu", "sidebar.settings": "Settings", "sidebar.help": "Help", "sidebar.workspaces.enable": "Enable workspaces", @@ -441,6 +464,29 @@ export const dict = { "settings.general.row.theme.description": "Customise how OpenCode is themed.", "settings.general.row.font.title": "Font", "settings.general.row.font.description": "Customise the mono font used in code blocks", + "font.option.ibmPlexMono": "IBM Plex Mono", + "font.option.cascadiaCode": "Cascadia Code", + "font.option.firaCode": "Fira Code", + "font.option.hack": "Hack", + "font.option.inconsolata": "Inconsolata", + "font.option.intelOneMono": "Intel One Mono", + "font.option.jetbrainsMono": "JetBrains Mono", + "font.option.mesloLgs": "Meslo LGS", + "font.option.robotoMono": "Roboto Mono", + "font.option.sourceCodePro": "Source Code Pro", + "font.option.ubuntuMono": "Ubuntu Mono", + "sound.option.staplebops01": "Boopy", + "sound.option.staplebops02": "Beepy", + "sound.option.staplebops03": "Staplebops 03", + "sound.option.staplebops04": "Staplebops 04", + "sound.option.staplebops05": "Staplebops 05", + "sound.option.staplebops06": "Staplebops 06", + "sound.option.staplebops07": "Staplebops 07", + "sound.option.nope01": "Nope 01", + "sound.option.nope02": "Nope 02", + "sound.option.nope03": "Oopsie", + "sound.option.nope04": "Nope 04", + "sound.option.nope05": "Nope 05", "settings.general.notifications.agent.title": "Agent", "settings.general.notifications.agent.description": diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index e00928cfc..1318217b5 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -847,8 +847,8 @@ export default function Layout(props: ParentProps) { }, { id: "settings.open", - title: "Open settings", - category: "Settings", + title: language.t("command.settings.open"), + category: language.t("command.category.settings"), keybind: "mod+comma", onSelect: () => openSettings(), }, @@ -1573,12 +1573,12 @@ export default function Layout(props: ParentProps) { keybind={command.keybind("session.archive")} gutter={8} > - archiveSession(props.session)} - aria-label="Archive session" - /> + archiveSession(props.session)} + aria-label={language.t("command.session.archive")} + />
@@ -1759,7 +1759,7 @@ export default function Layout(props: ParentProps) { icon="dot-grid" variant="ghost" class="size-6 rounded-md" - aria-label="More options" + aria-label={language.t("common.moreOptions")} /> @@ -1808,7 +1808,7 @@ export default function Layout(props: ParentProps) { variant="ghost" class="size-6 rounded-md" onClick={() => navigate(`/${slug()}/session`)} - aria-label="New session" + aria-label={language.t("command.session.new")} /> @@ -2122,7 +2122,7 @@ export default function Layout(props: ParentProps) { variant="ghost" size="large" onClick={chooseProject} - aria-label="Open project" + aria-label={language.t("command.project.open")} /> @@ -2142,7 +2142,7 @@ export default function Layout(props: ParentProps) { variant="ghost" size="large" onClick={openSettings} - aria-label="Settings" + aria-label={language.t("sidebar.settings")} /> @@ -2151,7 +2151,7 @@ export default function Layout(props: ParentProps) { variant="ghost" size="large" onClick={() => platform.openLink("https://opencode.ai/desktop-feedback")} - aria-label="Help" + aria-label={language.t("sidebar.help")} /> @@ -2202,7 +2202,7 @@ export default function Layout(props: ParentProps) { icon="dot-grid" variant="ghost" class="shrink-0 size-6 rounded-md opacity-0 group-hover/project:opacity-100 data-[expanded]:opacity-100 data-[expanded]:bg-surface-base-active" - aria-label="Project options" + aria-label={language.t("common.moreOptions")} /> diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 8d6122819..48b059459 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -1366,7 +1366,7 @@ export default function Page() { window.history.replaceState(null, "", window.location.href.replace(/#.*$/, "")) }} > - Jump to latest + {language.t("session.messages.jumpToLatest")} @@ -2017,9 +2017,11 @@ export default function Page() { style={{ color: "rgba(239, 68, 68, 0.8)" }} />
-
Connection Lost
+
+ {language.t("terminal.connectionLost.title")} +
- The terminal connection was interrupted. This can happen when the server restarts. + {language.t("terminal.connectionLost.description")}
diff --git a/packages/app/src/utils/sound.ts b/packages/app/src/utils/sound.ts index e8db0bf7b..fa65f1d61 100644 --- a/packages/app/src/utils/sound.ts +++ b/packages/app/src/utils/sound.ts @@ -12,18 +12,18 @@ import staplebops06 from "@opencode-ai/ui/audio/staplebops-06.aac" import staplebops07 from "@opencode-ai/ui/audio/staplebops-07.aac" export const SOUND_OPTIONS = [ - { id: "staplebops-01", label: "Boopy", src: staplebops01 }, - { id: "staplebops-02", label: "Beepy", src: staplebops02 }, - { id: "staplebops-03", label: "Staplebops 03", src: staplebops03 }, - { id: "staplebops-04", label: "Staplebops 04", src: staplebops04 }, - { id: "staplebops-05", label: "Staplebops 05", src: staplebops05 }, - { id: "staplebops-06", label: "Staplebops 06", src: staplebops06 }, - { id: "staplebops-07", label: "Staplebops 07", src: staplebops07 }, - { id: "nope-01", label: "Nope 01", src: nope01 }, - { id: "nope-02", label: "Nope 02", src: nope02 }, - { id: "nope-03", label: "Oopsie", src: nope03 }, - { id: "nope-04", label: "Nope 04", src: nope04 }, - { id: "nope-05", label: "Nope 05", src: nope05 }, + { id: "staplebops-01", label: "sound.option.staplebops01", src: staplebops01 }, + { id: "staplebops-02", label: "sound.option.staplebops02", src: staplebops02 }, + { id: "staplebops-03", label: "sound.option.staplebops03", src: staplebops03 }, + { id: "staplebops-04", label: "sound.option.staplebops04", src: staplebops04 }, + { id: "staplebops-05", label: "sound.option.staplebops05", src: staplebops05 }, + { id: "staplebops-06", label: "sound.option.staplebops06", src: staplebops06 }, + { id: "staplebops-07", label: "sound.option.staplebops07", src: staplebops07 }, + { id: "nope-01", label: "sound.option.nope01", src: nope01 }, + { id: "nope-02", label: "sound.option.nope02", src: nope02 }, + { id: "nope-03", label: "sound.option.nope03", src: nope03 }, + { id: "nope-04", label: "sound.option.nope04", src: nope04 }, + { id: "nope-05", label: "sound.option.nope05", src: nope05 }, ] as const export type SoundOption = (typeof SOUND_OPTIONS)[number]