diff options
| author | Adam <[email protected]> | 2026-01-20 17:56:53 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-20 17:58:06 -0600 |
| commit | 233d003b4926ec615ff15c1ddd54a1719a62ef13 (patch) | |
| tree | 7c96d92f8b7bbf2b0fa9d5976d338fe10ab611dc | |
| parent | 6037e88ddf3fd08191dfb5e136796e15e8bc163c (diff) | |
| download | opencode-233d003b4926ec615ff15c1ddd54a1719a62ef13.tar.gz opencode-233d003b4926ec615ff15c1ddd54a1719a62ef13.zip | |
wip(app): i18n
| -rw-r--r-- | packages/app/src/components/dialog-settings.tsx | 9 | ||||
| -rw-r--r-- | packages/app/src/components/settings-agents.tsx | 7 | ||||
| -rw-r--r-- | packages/app/src/components/settings-commands.tsx | 7 | ||||
| -rw-r--r-- | packages/app/src/components/settings-general.tsx | 94 | ||||
| -rw-r--r-- | packages/app/src/components/settings-keybinds.tsx | 49 | ||||
| -rw-r--r-- | packages/app/src/components/settings-mcp.tsx | 7 | ||||
| -rw-r--r-- | packages/app/src/components/settings-models.tsx | 7 | ||||
| -rw-r--r-- | packages/app/src/components/settings-permissions.tsx | 136 | ||||
| -rw-r--r-- | packages/app/src/components/settings-providers.tsx | 7 | ||||
| -rw-r--r-- | packages/app/src/i18n/en.ts | 103 | ||||
| -rw-r--r-- | packages/app/src/i18n/zh.ts | 103 |
11 files changed, 452 insertions, 77 deletions
diff --git a/packages/app/src/components/dialog-settings.tsx b/packages/app/src/components/dialog-settings.tsx index 5ef89b8bf..1e9575cb2 100644 --- a/packages/app/src/components/dialog-settings.tsx +++ b/packages/app/src/components/dialog-settings.tsx @@ -2,6 +2,7 @@ import { Component } from "solid-js" import { Dialog } from "@opencode-ai/ui/dialog" import { Tabs } from "@opencode-ai/ui/tabs" import { Icon } from "@opencode-ai/ui/icon" +import { useLanguage } from "@/context/language" import { SettingsGeneral } from "./settings-general" import { SettingsKeybinds } from "./settings-keybinds" import { SettingsPermissions } from "./settings-permissions" @@ -12,6 +13,8 @@ import { SettingsCommands } from "./settings-commands" import { SettingsMcp } from "./settings-mcp" export const DialogSettings: Component = () => { + const language = useLanguage() + return ( <Dialog size="x-large"> <Tabs orientation="vertical" variant="settings" defaultValue="general" class="h-full settings-dialog"> @@ -26,15 +29,15 @@ export const DialogSettings: Component = () => { "padding-bottom": "12px", }} > - <Tabs.SectionTitle>Desktop</Tabs.SectionTitle> + <Tabs.SectionTitle>{language.t("settings.section.desktop")}</Tabs.SectionTitle> <div style={{ display: "flex", "flex-direction": "column", gap: "6px", width: "100%" }}> <Tabs.Trigger value="general"> <Icon name="sliders" /> - General + {language.t("settings.tab.general")} </Tabs.Trigger> <Tabs.Trigger value="shortcuts"> <Icon name="keyboard" /> - Shortcuts + {language.t("settings.tab.shortcuts")} </Tabs.Trigger> </div> </div> diff --git a/packages/app/src/components/settings-agents.tsx b/packages/app/src/components/settings-agents.tsx index 892be152b..e68f1e59c 100644 --- a/packages/app/src/components/settings-agents.tsx +++ b/packages/app/src/components/settings-agents.tsx @@ -1,11 +1,14 @@ import { Component } from "solid-js" +import { useLanguage } from "@/context/language" export const SettingsAgents: Component = () => { + const language = useLanguage() + return ( <div class="flex flex-col h-full overflow-y-auto"> <div class="flex flex-col gap-6 p-6 max-w-[600px]"> - <h2 class="text-16-medium text-text-strong">Agents</h2> - <p class="text-14-regular text-text-weak">Agent settings will be configurable here.</p> + <h2 class="text-16-medium text-text-strong">{language.t("settings.agents.title")}</h2> + <p class="text-14-regular text-text-weak">{language.t("settings.agents.description")}</p> </div> </div> ) diff --git a/packages/app/src/components/settings-commands.tsx b/packages/app/src/components/settings-commands.tsx index e98c0eeb0..cf796d0aa 100644 --- a/packages/app/src/components/settings-commands.tsx +++ b/packages/app/src/components/settings-commands.tsx @@ -1,11 +1,14 @@ import { Component } from "solid-js" +import { useLanguage } from "@/context/language" export const SettingsCommands: Component = () => { + const language = useLanguage() + return ( <div class="flex flex-col h-full overflow-y-auto"> <div class="flex flex-col gap-6 p-6 max-w-[600px]"> - <h2 class="text-16-medium text-text-strong">Commands</h2> - <p class="text-14-regular text-text-weak">Command settings will be configurable here.</p> + <h2 class="text-16-medium text-text-strong">{language.t("settings.commands.title")}</h2> + <p class="text-14-regular text-text-weak">{language.t("settings.commands.description")}</p> </div> </div> ) diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index e8749cbde..5f3519a74 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -2,22 +2,33 @@ import { Component, createMemo, type JSX } from "solid-js" import { Select } from "@opencode-ai/ui/select" import { Switch } from "@opencode-ai/ui/switch" import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme" +import { useLanguage } from "@/context/language" import { useSettings, monoFontFamily } from "@/context/settings" import { playSound, SOUND_OPTIONS } from "@/utils/sound" export const SettingsGeneral: Component = () => { const theme = useTheme() + const language = useLanguage() const settings = useSettings() const themeOptions = createMemo(() => Object.entries(theme.themes()).map(([id, def]) => ({ id, name: def.name ?? id })), ) - const colorSchemeOptions: { value: ColorScheme; label: string }[] = [ - { value: "system", label: "System setting" }, - { value: "light", label: "Light" }, - { value: "dark", label: "Dark" }, - ] + const colorSchemeOptions = createMemo( + (): { value: ColorScheme; label: string }[] => [ + { value: "system", label: language.t("theme.scheme.system") }, + { value: "light", label: language.t("theme.scheme.light") }, + { value: "dark", label: language.t("theme.scheme.dark") }, + ], + ) + + const languageOptions = createMemo(() => + language.locales.map((locale) => ({ + value: locale, + label: language.label(locale), + })), + ) const fontOptions = [ { value: "ibm-plex-mono", label: "IBM Plex Mono" }, @@ -45,20 +56,39 @@ export const SettingsGeneral: Component = () => { }} > <div class="flex flex-col gap-1 pt-6 pb-8"> - <h2 class="text-16-medium text-text-strong">General</h2> + <h2 class="text-16-medium text-text-strong">{language.t("settings.tab.general")}</h2> </div> </div> <div class="flex flex-col gap-8 w-full"> {/* Appearance Section */} <div class="flex flex-col gap-1"> - <h3 class="text-14-medium text-text-strong pb-2">Appearance</h3> + <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.appearance")}</h3> <div class="bg-surface-raised-base px-4 rounded-lg"> - <SettingsRow title="Appearance" description="Customise how OpenCode looks on your device"> + <SettingsRow + title={language.t("settings.general.row.language.title")} + description={language.t("settings.general.row.language.description")} + > <Select - options={colorSchemeOptions} - current={colorSchemeOptions.find((o) => o.value === theme.colorScheme())} + options={languageOptions()} + current={languageOptions().find((o) => o.value === language.locale())} + value={(o) => o.value} + label={(o) => o.label} + onSelect={(option) => option && language.setLocale(option.value)} + variant="secondary" + size="small" + triggerVariant="settings" + /> + </SettingsRow> + + <SettingsRow + title={language.t("settings.general.row.appearance.title")} + description={language.t("settings.general.row.appearance.description")} + > + <Select + options={colorSchemeOptions()} + current={colorSchemeOptions().find((o) => o.value === theme.colorScheme())} value={(o) => o.value} label={(o) => o.label} onSelect={(option) => option && theme.setColorScheme(option.value)} @@ -74,12 +104,12 @@ export const SettingsGeneral: Component = () => { </SettingsRow> <SettingsRow - title="Theme" + title={language.t("settings.general.row.theme.title")} description={ <> - Customise how OpenCode is themed.{" "} + {language.t("settings.general.row.theme.description")} {" "} <a href="#" class="text-text-interactive-base"> - Learn more + {language.t("common.learnMore")} </a> </> } @@ -104,7 +134,10 @@ export const SettingsGeneral: Component = () => { /> </SettingsRow> - <SettingsRow title="Font" description="Customise the mono font used in code blocks"> + <SettingsRow + title={language.t("settings.general.row.font.title")} + description={language.t("settings.general.row.font.description")} + > <Select options={fontOptions} current={fontOptions.find((o) => o.value === settings.appearance.font())} @@ -124,12 +157,12 @@ export const SettingsGeneral: Component = () => { {/* System notifications Section */} <div class="flex flex-col gap-1"> - <h3 class="text-14-medium text-text-strong pb-2">System notifications</h3> + <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.notifications")}</h3> <div class="bg-surface-raised-base px-4 rounded-lg"> <SettingsRow - title="Agent" - description="Show system notification when the agent is complete or needs attention" + title={language.t("settings.general.notifications.agent.title")} + description={language.t("settings.general.notifications.agent.description")} > <Switch checked={settings.notifications.agent()} @@ -137,14 +170,20 @@ export const SettingsGeneral: Component = () => { /> </SettingsRow> - <SettingsRow title="Permissions" description="Show system notification when a permission is required"> + <SettingsRow + title={language.t("settings.general.notifications.permissions.title")} + description={language.t("settings.general.notifications.permissions.description")} + > <Switch checked={settings.notifications.permissions()} onChange={(checked) => settings.notifications.setPermissions(checked)} /> </SettingsRow> - <SettingsRow title="Errors" description="Show system notification when an error occurs"> + <SettingsRow + title={language.t("settings.general.notifications.errors.title")} + description={language.t("settings.general.notifications.errors.description")} + > <Switch checked={settings.notifications.errors()} onChange={(checked) => settings.notifications.setErrors(checked)} @@ -155,10 +194,13 @@ export const SettingsGeneral: Component = () => { {/* Sound effects Section */} <div class="flex flex-col gap-1"> - <h3 class="text-14-medium text-text-strong pb-2">Sound effects</h3> + <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.sounds")}</h3> <div class="bg-surface-raised-base px-4 rounded-lg"> - <SettingsRow title="Agent" description="Play sound when the agent is complete or needs attention"> + <SettingsRow + title={language.t("settings.general.sounds.agent.title")} + description={language.t("settings.general.sounds.agent.description")} + > <Select options={soundOptions} current={soundOptions.find((o) => o.id === settings.sounds.agent())} @@ -179,7 +221,10 @@ export const SettingsGeneral: Component = () => { /> </SettingsRow> - <SettingsRow title="Permissions" description="Play sound when a permission is required"> + <SettingsRow + title={language.t("settings.general.sounds.permissions.title")} + description={language.t("settings.general.sounds.permissions.description")} + > <Select options={soundOptions} current={soundOptions.find((o) => o.id === settings.sounds.permissions())} @@ -200,7 +245,10 @@ export const SettingsGeneral: Component = () => { /> </SettingsRow> - <SettingsRow title="Errors" description="Play sound when an error occurs"> + <SettingsRow + title={language.t("settings.general.sounds.errors.title")} + description={language.t("settings.general.sounds.errors.description")} + > <Select options={soundOptions} current={soundOptions.find((o) => o.id === settings.sounds.errors())} diff --git a/packages/app/src/components/settings-keybinds.tsx b/packages/app/src/components/settings-keybinds.tsx index bac727cd7..13a0042ff 100644 --- a/packages/app/src/components/settings-keybinds.tsx +++ b/packages/app/src/components/settings-keybinds.tsx @@ -2,6 +2,7 @@ import { Component, For, Show, createMemo, createSignal, onCleanup, onMount } fr import { Button } from "@opencode-ai/ui/button" import { showToast } from "@opencode-ai/ui/toast" import { formatKeybind, parseKeybind, useCommand } from "@/context/command" +import { useLanguage } from "@/context/language" import { useSettings } from "@/context/settings" const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) @@ -17,6 +18,23 @@ type KeybindMeta = { const GROUPS: KeybindGroup[] = ["General", "Session", "Navigation", "Model and agent", "Terminal", "Prompt"] +type GroupKey = + | "settings.shortcuts.group.general" + | "settings.shortcuts.group.session" + | "settings.shortcuts.group.navigation" + | "settings.shortcuts.group.modelAndAgent" + | "settings.shortcuts.group.terminal" + | "settings.shortcuts.group.prompt" + +const groupKey: Record<KeybindGroup, GroupKey> = { + General: "settings.shortcuts.group.general", + Session: "settings.shortcuts.group.session", + Navigation: "settings.shortcuts.group.navigation", + "Model and agent": "settings.shortcuts.group.modelAndAgent", + Terminal: "settings.shortcuts.group.terminal", + Prompt: "settings.shortcuts.group.prompt", +} + function groupFor(id: string): KeybindGroup { if (id === PALETTE_ID) return "General" if (id.startsWith("terminal.")) return "Terminal" @@ -86,6 +104,7 @@ function signatures(config: string | undefined) { export const SettingsKeybinds: Component = () => { const command = useCommand() + const language = useLanguage() const settings = useSettings() const [active, setActive] = createSignal<string | null>(null) @@ -117,12 +136,16 @@ export const SettingsKeybinds: Component = () => { const resetAll = () => { stop() settings.keybinds.resetAll() - showToast({ title: "Shortcuts reset", description: "Keyboard shortcuts have been reset to defaults." }) + showToast({ + title: language.t("settings.shortcuts.reset.toast.title"), + description: language.t("settings.shortcuts.reset.toast.description"), + }) } const list = createMemo(() => { + language.locale() const out = new Map<string, KeybindMeta>() - out.set(PALETTE_ID, { title: "Command palette", group: "General" }) + out.set(PALETTE_ID, { title: language.t("command.palette"), group: "General" }) for (const opt of command.catalog) { if (opt.id.startsWith("suggested.")) continue @@ -188,7 +211,7 @@ export const SettingsKeybinds: Component = () => { const palette = settings.keybinds.get(PALETTE_ID) ?? DEFAULT_PALETTE_KEYBIND for (const sig of signatures(palette)) { - add(sig, { id: PALETTE_ID, title: "Command palette" }) + add(sig, { id: PALETTE_ID, title: title(PALETTE_ID) }) } const valueFor = (id: string) => { @@ -258,8 +281,11 @@ export const SettingsKeybinds: Component = () => { if (conflicts.size > 0) { showToast({ - title: "Shortcut already in use", - description: `${formatKeybind(next)} is already assigned to ${[...conflicts.values()].join(", ")}.`, + title: language.t("settings.shortcuts.conflict.title"), + description: language.t("settings.shortcuts.conflict.description", { + keybind: formatKeybind(next), + titles: [...conflicts.values()].join(", "), + }), }) return } @@ -288,9 +314,9 @@ export const SettingsKeybinds: Component = () => { }} > <div class="flex items-center justify-between gap-4 pt-6 pb-8 max-w-[720px]"> - <h2 class="text-16-medium text-text-strong">Keyboard shortcuts</h2> + <h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2> <Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}> - Reset to defaults + {language.t("settings.shortcuts.reset.button")} </Button> </div> </div> @@ -300,7 +326,7 @@ export const SettingsKeybinds: Component = () => { {(group) => ( <Show when={(grouped().get(group) ?? []).length > 0}> <div class="flex flex-col gap-1"> - <h3 class="text-14-medium text-text-strong pb-2">{group}</h3> + <h3 class="text-14-medium text-text-strong pb-2">{language.t(groupKey[group])}</h3> <div class="bg-surface-raised-base px-4 rounded-lg"> <For each={grouped().get(group) ?? []}> {(id) => ( @@ -316,8 +342,11 @@ export const SettingsKeybinds: Component = () => { }} onClick={() => start(id)} > - <Show when={active() === id} fallback={command.keybind(id) || "Unassigned"}> - Press keys + <Show + when={active() === id} + fallback={command.keybind(id) || language.t("settings.shortcuts.unassigned")} + > + {language.t("settings.shortcuts.pressKeys")} </Show> </button> </div> diff --git a/packages/app/src/components/settings-mcp.tsx b/packages/app/src/components/settings-mcp.tsx index ea6bf350f..928464a51 100644 --- a/packages/app/src/components/settings-mcp.tsx +++ b/packages/app/src/components/settings-mcp.tsx @@ -1,11 +1,14 @@ import { Component } from "solid-js" +import { useLanguage } from "@/context/language" export const SettingsMcp: Component = () => { + const language = useLanguage() + return ( <div class="flex flex-col h-full overflow-y-auto"> <div class="flex flex-col gap-6 p-6 max-w-[600px]"> - <h2 class="text-16-medium text-text-strong">MCP</h2> - <p class="text-14-regular text-text-weak">MCP settings will be configurable here.</p> + <h2 class="text-16-medium text-text-strong">{language.t("settings.mcp.title")}</h2> + <p class="text-14-regular text-text-weak">{language.t("settings.mcp.description")}</p> </div> </div> ) diff --git a/packages/app/src/components/settings-models.tsx b/packages/app/src/components/settings-models.tsx index 5fbeb144e..6a636879d 100644 --- a/packages/app/src/components/settings-models.tsx +++ b/packages/app/src/components/settings-models.tsx @@ -1,11 +1,14 @@ import { Component } from "solid-js" +import { useLanguage } from "@/context/language" export const SettingsModels: Component = () => { + const language = useLanguage() + return ( <div class="flex flex-col h-full overflow-y-auto"> <div class="flex flex-col gap-6 p-6 max-w-[600px]"> - <h2 class="text-16-medium text-text-strong">Models</h2> - <p class="text-14-regular text-text-weak">Model settings will be configurable here.</p> + <h2 class="text-16-medium text-text-strong">{language.t("settings.models.title")}</h2> + <p class="text-14-regular text-text-weak">{language.t("settings.models.description")}</p> </div> </div> ) diff --git a/packages/app/src/components/settings-permissions.tsx b/packages/app/src/components/settings-permissions.tsx index d0551d247..1381515f5 100644 --- a/packages/app/src/components/settings-permissions.tsx +++ b/packages/app/src/components/settings-permissions.tsx @@ -2,6 +2,7 @@ import { Select } from "@opencode-ai/ui/select" import { showToast } from "@opencode-ai/ui/toast" import { Component, For, createMemo, type JSX } from "solid-js" import { useGlobalSync } from "@/context/global-sync" +import { useLanguage } from "@/context/language" type PermissionAction = "allow" | "ask" | "deny" @@ -15,30 +16,94 @@ type PermissionItem = { description: string } -const ACTIONS: Array<{ value: PermissionAction; label: string }> = [ - { value: "allow", label: "Allow" }, - { value: "ask", label: "Ask" }, - { value: "deny", label: "Deny" }, -] - -const ITEMS: PermissionItem[] = [ - { id: "read", title: "Read", description: "Reading a file (matches the file path)" }, - { id: "edit", title: "Edit", description: "Modify files, including edits, writes, patches, and multi-edits" }, - { id: "glob", title: "Glob", description: "Match files using glob patterns" }, - { id: "grep", title: "Grep", description: "Search file contents using regular expressions" }, - { id: "list", title: "List", description: "List files within a directory" }, - { id: "bash", title: "Bash", description: "Run shell commands" }, - { id: "task", title: "Task", description: "Launch sub-agents" }, - { id: "skill", title: "Skill", description: "Load a skill by name" }, - { id: "lsp", title: "LSP", description: "Run language server queries" }, - { id: "todoread", title: "Todo Read", description: "Read the todo list" }, - { id: "todowrite", title: "Todo Write", description: "Update the todo list" }, - { id: "webfetch", title: "Web Fetch", description: "Fetch content from a URL" }, - { id: "websearch", title: "Web Search", description: "Search the web" }, - { id: "codesearch", title: "Code Search", description: "Search code on the web" }, - { id: "external_directory", title: "External Directory", description: "Access files outside the project directory" }, - { id: "doom_loop", title: "Doom Loop", description: "Detect repeated tool calls with identical input" }, -] +const ACTIONS = [ + { value: "allow", label: "settings.permissions.action.allow" }, + { value: "ask", label: "settings.permissions.action.ask" }, + { value: "deny", label: "settings.permissions.action.deny" }, +] as const + +const ITEMS = [ + { + id: "read", + title: "settings.permissions.tool.read.title", + description: "settings.permissions.tool.read.description", + }, + { + id: "edit", + title: "settings.permissions.tool.edit.title", + description: "settings.permissions.tool.edit.description", + }, + { + id: "glob", + title: "settings.permissions.tool.glob.title", + description: "settings.permissions.tool.glob.description", + }, + { + id: "grep", + title: "settings.permissions.tool.grep.title", + description: "settings.permissions.tool.grep.description", + }, + { + id: "list", + title: "settings.permissions.tool.list.title", + description: "settings.permissions.tool.list.description", + }, + { + id: "bash", + title: "settings.permissions.tool.bash.title", + description: "settings.permissions.tool.bash.description", + }, + { + id: "task", + title: "settings.permissions.tool.task.title", + description: "settings.permissions.tool.task.description", + }, + { + id: "skill", + title: "settings.permissions.tool.skill.title", + description: "settings.permissions.tool.skill.description", + }, + { + id: "lsp", + title: "settings.permissions.tool.lsp.title", + description: "settings.permissions.tool.lsp.description", + }, + { + id: "todoread", + title: "settings.permissions.tool.todoread.title", + description: "settings.permissions.tool.todoread.description", + }, + { + id: "todowrite", + title: "settings.permissions.tool.todowrite.title", + description: "settings.permissions.tool.todowrite.description", + }, + { + id: "webfetch", + title: "settings.permissions.tool.webfetch.title", + description: "settings.permissions.tool.webfetch.description", + }, + { + id: "websearch", + title: "settings.permissions.tool.websearch.title", + description: "settings.permissions.tool.websearch.description", + }, + { + id: "codesearch", + title: "settings.permissions.tool.codesearch.title", + description: "settings.permissions.tool.codesearch.description", + }, + { + id: "external_directory", + title: "settings.permissions.tool.external_directory.title", + description: "settings.permissions.tool.external_directory.description", + }, + { + id: "doom_loop", + title: "settings.permissions.tool.doom_loop.title", + description: "settings.permissions.tool.doom_loop.description", + }, +] as const const VALID_ACTIONS = new Set<PermissionAction>(["allow", "ask", "deny"]) @@ -67,6 +132,15 @@ function getRuleDefault(value: unknown): PermissionAction | undefined { export const SettingsPermissions: Component = () => { const globalSync = useGlobalSync() + const language = useLanguage() + + const actions = createMemo( + (): Array<{ value: PermissionAction; label: string }> => + ACTIONS.map((action) => ({ + value: action.value, + label: language.t(action.label), + })), + ) const permission = createMemo(() => { return toMap(globalSync.data.config.permission) @@ -95,7 +169,7 @@ export const SettingsPermissions: Component = () => { globalSync.updateConfig({ permission: { [id]: nextValue } }).catch((err: unknown) => { globalSync.set("config", "permission", before) const message = err instanceof Error ? err.message : String(err) - showToast({ title: "Failed to update permissions", description: message }) + showToast({ title: language.t("settings.permissions.toast.updateFailed.title"), description: message }) }) } @@ -109,21 +183,21 @@ export const SettingsPermissions: Component = () => { }} > <div class="flex flex-col gap-1 p-8 max-w-[720px]"> - <h2 class="text-16-medium text-text-strong">Permissions</h2> - <p class="text-14-regular text-text-weak">Control what tools the server can use by default.</p> + <h2 class="text-16-medium text-text-strong">{language.t("settings.permissions.title")}</h2> + <p class="text-14-regular text-text-weak">{language.t("settings.permissions.description")}</p> </div> </div> <div class="flex flex-col gap-6 p-8 pt-6 max-w-[720px]"> <div class="flex flex-col gap-2"> - <h3 class="text-14-medium text-text-strong">Appearance</h3> + <h3 class="text-14-medium text-text-strong">{language.t("settings.permissions.section.tools")}</h3> <div class="border border-border-weak-base rounded-lg overflow-hidden"> <For each={ITEMS}> {(item) => ( - <SettingsRow title={item.title} description={item.description}> + <SettingsRow title={language.t(item.title)} description={language.t(item.description)}> <Select - options={ACTIONS} - current={ACTIONS.find((o) => o.value === actionFor(item.id))} + options={actions()} + current={actions().find((o) => o.value === actionFor(item.id))} value={(o) => o.value} label={(o) => o.label} onSelect={(option) => option && setPermission(item.id, option.value)} diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx index cf90b6c13..7b6ca1939 100644 --- a/packages/app/src/components/settings-providers.tsx +++ b/packages/app/src/components/settings-providers.tsx @@ -1,11 +1,14 @@ import { Component } from "solid-js" +import { useLanguage } from "@/context/language" export const SettingsProviders: Component = () => { + const language = useLanguage() + return ( <div class="flex flex-col h-full overflow-y-auto"> <div class="flex flex-col gap-6 p-6 max-w-[600px]"> - <h2 class="text-16-medium text-text-strong">Providers</h2> - <p class="text-14-regular text-text-weak">Provider settings will be configurable here.</p> + <h2 class="text-16-medium text-text-strong">{language.t("settings.providers.title")}</h2> + <p class="text-14-regular text-text-weak">{language.t("settings.providers.description")}</p> </div> </div> ) diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 9487dc0ef..5db336330 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -26,6 +26,8 @@ export const dict = { "command.session.next": "Next session", "command.session.archive": "Archive session", + "command.palette": "Command palette", + "command.theme.cycle": "Cycle theme", "command.theme.set": "Use theme: {{theme}}", "command.theme.scheme.cycle": "Cycle color scheme", @@ -395,6 +397,7 @@ export const dict = { "common.dismiss": "Dismiss", "common.requestFailed": "Request failed", "common.moreOptions": "More options", + "common.learnMore": "Learn more", "common.rename": "Rename", "common.reset": "Reset", "common.delete": "Delete", @@ -412,6 +415,106 @@ export const dict = { "sidebar.project.recentSessions": "Recent sessions", "sidebar.project.viewAllSessions": "View all sessions", + "settings.section.desktop": "Desktop", + "settings.tab.general": "General", + "settings.tab.shortcuts": "Shortcuts", + + "settings.general.section.appearance": "Appearance", + "settings.general.section.notifications": "System notifications", + "settings.general.section.sounds": "Sound effects", + + "settings.general.row.language.title": "Language", + "settings.general.row.language.description": "Change the display language for OpenCode", + "settings.general.row.appearance.title": "Appearance", + "settings.general.row.appearance.description": "Customise how OpenCode looks on your device", + "settings.general.row.theme.title": "Theme", + "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", + + "settings.general.notifications.agent.title": "Agent", + "settings.general.notifications.agent.description": "Show system notification when the agent is complete or needs attention", + "settings.general.notifications.permissions.title": "Permissions", + "settings.general.notifications.permissions.description": "Show system notification when a permission is required", + "settings.general.notifications.errors.title": "Errors", + "settings.general.notifications.errors.description": "Show system notification when an error occurs", + + "settings.general.sounds.agent.title": "Agent", + "settings.general.sounds.agent.description": "Play sound when the agent is complete or needs attention", + "settings.general.sounds.permissions.title": "Permissions", + "settings.general.sounds.permissions.description": "Play sound when a permission is required", + "settings.general.sounds.errors.title": "Errors", + "settings.general.sounds.errors.description": "Play sound when an error occurs", + + "settings.shortcuts.title": "Keyboard shortcuts", + "settings.shortcuts.reset.button": "Reset to defaults", + "settings.shortcuts.reset.toast.title": "Shortcuts reset", + "settings.shortcuts.reset.toast.description": "Keyboard shortcuts have been reset to defaults.", + "settings.shortcuts.conflict.title": "Shortcut already in use", + "settings.shortcuts.conflict.description": "{{keybind}} is already assigned to {{titles}}.", + "settings.shortcuts.unassigned": "Unassigned", + "settings.shortcuts.pressKeys": "Press keys", + + "settings.shortcuts.group.general": "General", + "settings.shortcuts.group.session": "Session", + "settings.shortcuts.group.navigation": "Navigation", + "settings.shortcuts.group.modelAndAgent": "Model and agent", + "settings.shortcuts.group.terminal": "Terminal", + "settings.shortcuts.group.prompt": "Prompt", + + "settings.providers.title": "Providers", + "settings.providers.description": "Provider settings will be configurable here.", + "settings.models.title": "Models", + "settings.models.description": "Model settings will be configurable here.", + "settings.agents.title": "Agents", + "settings.agents.description": "Agent settings will be configurable here.", + "settings.commands.title": "Commands", + "settings.commands.description": "Command settings will be configurable here.", + "settings.mcp.title": "MCP", + "settings.mcp.description": "MCP settings will be configurable here.", + + "settings.permissions.title": "Permissions", + "settings.permissions.description": "Control what tools the server can use by default.", + "settings.permissions.section.tools": "Tools", + "settings.permissions.toast.updateFailed.title": "Failed to update permissions", + + "settings.permissions.action.allow": "Allow", + "settings.permissions.action.ask": "Ask", + "settings.permissions.action.deny": "Deny", + + "settings.permissions.tool.read.title": "Read", + "settings.permissions.tool.read.description": "Reading a file (matches the file path)", + "settings.permissions.tool.edit.title": "Edit", + "settings.permissions.tool.edit.description": "Modify files, including edits, writes, patches, and multi-edits", + "settings.permissions.tool.glob.title": "Glob", + "settings.permissions.tool.glob.description": "Match files using glob patterns", + "settings.permissions.tool.grep.title": "Grep", + "settings.permissions.tool.grep.description": "Search file contents using regular expressions", + "settings.permissions.tool.list.title": "List", + "settings.permissions.tool.list.description": "List files within a directory", + "settings.permissions.tool.bash.title": "Bash", + "settings.permissions.tool.bash.description": "Run shell commands", + "settings.permissions.tool.task.title": "Task", + "settings.permissions.tool.task.description": "Launch sub-agents", + "settings.permissions.tool.skill.title": "Skill", + "settings.permissions.tool.skill.description": "Load a skill by name", + "settings.permissions.tool.lsp.title": "LSP", + "settings.permissions.tool.lsp.description": "Run language server queries", + "settings.permissions.tool.todoread.title": "Todo Read", + "settings.permissions.tool.todoread.description": "Read the todo list", + "settings.permissions.tool.todowrite.title": "Todo Write", + "settings.permissions.tool.todowrite.description": "Update the todo list", + "settings.permissions.tool.webfetch.title": "Web Fetch", + "settings.permissions.tool.webfetch.description": "Fetch content from a URL", + "settings.permissions.tool.websearch.title": "Web Search", + "settings.permissions.tool.websearch.description": "Search the web", + "settings.permissions.tool.codesearch.title": "Code Search", + "settings.permissions.tool.codesearch.description": "Search code on the web", + "settings.permissions.tool.external_directory.title": "External Directory", + "settings.permissions.tool.external_directory.description": "Access files outside the project directory", + "settings.permissions.tool.doom_loop.title": "Doom Loop", + "settings.permissions.tool.doom_loop.description": "Detect repeated tool calls with identical input", + "workspace.new": "New workspace", "workspace.type.local": "local", "workspace.type.sandbox": "sandbox", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index e066dbcd9..bd809bdb9 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -30,6 +30,8 @@ export const dict = { "command.session.next": "下一个会话", "command.session.archive": "归档会话", + "command.palette": "命令面板", + "command.theme.cycle": "切换主题", "command.theme.set": "使用主题: {{theme}}", "command.theme.scheme.cycle": "切换配色方案", @@ -391,6 +393,7 @@ export const dict = { "common.dismiss": "忽略", "common.requestFailed": "请求失败", "common.moreOptions": "更多选项", + "common.learnMore": "了解更多", "common.rename": "重命名", "common.reset": "重置", "common.delete": "删除", @@ -408,6 +411,106 @@ export const dict = { "sidebar.project.recentSessions": "最近会话", "sidebar.project.viewAllSessions": "查看全部会话", + "settings.section.desktop": "桌面", + "settings.tab.general": "通用", + "settings.tab.shortcuts": "快捷键", + + "settings.general.section.appearance": "外观", + "settings.general.section.notifications": "系统通知", + "settings.general.section.sounds": "音效", + + "settings.general.row.language.title": "语言", + "settings.general.row.language.description": "更改 OpenCode 的显示语言", + "settings.general.row.appearance.title": "外观", + "settings.general.row.appearance.description": "自定义 OpenCode 在你的设备上的外观", + "settings.general.row.theme.title": "主题", + "settings.general.row.theme.description": "自定义 OpenCode 的主题。", + "settings.general.row.font.title": "字体", + "settings.general.row.font.description": "自定义代码块使用的等宽字体", + + "settings.general.notifications.agent.title": "智能体", + "settings.general.notifications.agent.description": "当智能体完成或需要注意时显示系统通知", + "settings.general.notifications.permissions.title": "权限", + "settings.general.notifications.permissions.description": "当需要权限时显示系统通知", + "settings.general.notifications.errors.title": "错误", + "settings.general.notifications.errors.description": "发生错误时显示系统通知", + + "settings.general.sounds.agent.title": "智能体", + "settings.general.sounds.agent.description": "当智能体完成或需要注意时播放声音", + "settings.general.sounds.permissions.title": "权限", + "settings.general.sounds.permissions.description": "当需要权限时播放声音", + "settings.general.sounds.errors.title": "错误", + "settings.general.sounds.errors.description": "发生错误时播放声音", + + "settings.shortcuts.title": "键盘快捷键", + "settings.shortcuts.reset.button": "重置为默认值", + "settings.shortcuts.reset.toast.title": "快捷键已重置", + "settings.shortcuts.reset.toast.description": "键盘快捷键已重置为默认设置。", + "settings.shortcuts.conflict.title": "快捷键已被占用", + "settings.shortcuts.conflict.description": "{{keybind}} 已分配给 {{titles}}。", + "settings.shortcuts.unassigned": "未设置", + "settings.shortcuts.pressKeys": "按下按键", + + "settings.shortcuts.group.general": "通用", + "settings.shortcuts.group.session": "会话", + "settings.shortcuts.group.navigation": "导航", + "settings.shortcuts.group.modelAndAgent": "模型与智能体", + "settings.shortcuts.group.terminal": "终端", + "settings.shortcuts.group.prompt": "提示", + + "settings.providers.title": "提供商", + "settings.providers.description": "提供商设置将在此处可配置。", + "settings.models.title": "模型", + "settings.models.description": "模型设置将在此处可配置。", + "settings.agents.title": "智能体", + "settings.agents.description": "智能体设置将在此处可配置。", + "settings.commands.title": "命令", + "settings.commands.description": "命令设置将在此处可配置。", + "settings.mcp.title": "MCP", + "settings.mcp.description": "MCP 设置将在此处可配置。", + + "settings.permissions.title": "权限", + "settings.permissions.description": "控制服务器默认可以使用哪些工具。", + "settings.permissions.section.tools": "工具", + "settings.permissions.toast.updateFailed.title": "更新权限失败", + + "settings.permissions.action.allow": "允许", + "settings.permissions.action.ask": "询问", + "settings.permissions.action.deny": "拒绝", + + "settings.permissions.tool.read.title": "读取", + "settings.permissions.tool.read.description": "读取文件(匹配文件路径)", + "settings.permissions.tool.edit.title": "编辑", + "settings.permissions.tool.edit.description": "修改文件,包括编辑、写入、补丁和多重编辑", + "settings.permissions.tool.glob.title": "Glob", + "settings.permissions.tool.glob.description": "使用 glob 模式匹配文件", + "settings.permissions.tool.grep.title": "Grep", + "settings.permissions.tool.grep.description": "使用正则表达式搜索文件内容", + "settings.permissions.tool.list.title": "列表", + "settings.permissions.tool.list.description": "列出目录中的文件", + "settings.permissions.tool.bash.title": "Bash", + "settings.permissions.tool.bash.description": "运行 shell 命令", + "settings.permissions.tool.task.title": "Task", + "settings.permissions.tool.task.description": "启动子智能体", + "settings.permissions.tool.skill.title": "Skill", + "settings.permissions.tool.skill.description": "按名称加载技能", + "settings.permissions.tool.lsp.title": "LSP", + "settings.permissions.tool.lsp.description": "运行语言服务器查询", + "settings.permissions.tool.todoread.title": "读取待办", + "settings.permissions.tool.todoread.description": "读取待办列表", + "settings.permissions.tool.todowrite.title": "更新待办", + "settings.permissions.tool.todowrite.description": "更新待办列表", + "settings.permissions.tool.webfetch.title": "Web Fetch", + "settings.permissions.tool.webfetch.description": "从 URL 获取内容", + "settings.permissions.tool.websearch.title": "Web Search", + "settings.permissions.tool.websearch.description": "搜索网页", + "settings.permissions.tool.codesearch.title": "Code Search", + "settings.permissions.tool.codesearch.description": "在网上搜索代码", + "settings.permissions.tool.external_directory.title": "外部目录", + "settings.permissions.tool.external_directory.description": "访问项目目录之外的文件", + "settings.permissions.tool.doom_loop.title": "Doom Loop", + "settings.permissions.tool.doom_loop.description": "检测具有相同输入的重复工具调用", + "workspace.new": "新建工作区", "workspace.type.local": "本地", "workspace.type.sandbox": "沙盒", |
