diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx index 2e414a437..b6e2f822e 100644 --- a/packages/app/src/components/dialog-edit-project.tsx +++ b/packages/app/src/components/dialog-edit-project.tsx @@ -9,12 +9,14 @@ import { useGlobalSDK } from "@/context/global-sdk" import { type LocalProject, getAvatarColors } from "@/context/layout" import { getFilename } from "@opencode-ai/util/path" import { Avatar } from "@opencode-ai/ui/avatar" +import { useLanguage } from "@/context/language" const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const export function DialogEditProject(props: { project: LocalProject }) { const dialog = useDialog() const globalSDK = useGlobalSDK() + const language = useLanguage() const folderName = createMemo(() => getFilename(props.project.worktree)) const defaultName = createMemo(() => props.project.name || folderName()) @@ -81,20 +83,20 @@ export function DialogEditProject(props: { project: LocalProject }) { } return ( - +
setStore("name", v)} />
- +
setIconHover(true)} onMouseLeave={() => setIconHover(false)}>
} > - Project icon + {language.t("dialog.project.edit.icon.alt")}
- Recommended size 128x128px + {language.t("dialog.project.edit.icon.hint")} + {language.t("dialog.project.edit.icon.recommended")}
- +
{(color) => ( @@ -209,10 +216,10 @@ export function DialogEditProject(props: { project: LocalProject }) {
diff --git a/packages/app/src/components/dialog-fork.tsx b/packages/app/src/components/dialog-fork.tsx index 472a1994f..c4c52fc4d 100644 --- a/packages/app/src/components/dialog-fork.tsx +++ b/packages/app/src/components/dialog-fork.tsx @@ -9,6 +9,7 @@ import { List } from "@opencode-ai/ui/list" import { extractPromptFromParts } from "@/utils/prompt" import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" import { base64Encode } from "@opencode-ai/util/encode" +import { useLanguage } from "@/context/language" interface ForkableMessage { id: string @@ -27,6 +28,7 @@ export const DialogFork: Component = () => { const sdk = useSDK() const prompt = usePrompt() const dialog = useDialog() + const language = useLanguage() const messages = createMemo((): ForkableMessage[] => { const sessionID = params.id @@ -73,11 +75,11 @@ export const DialogFork: Component = () => { } return ( - + x.id} items={messages} filterKeys={["text"]} diff --git a/packages/app/src/components/dialog-manage-models.tsx b/packages/app/src/components/dialog-manage-models.tsx index 66d125288..1ecefa2cb 100644 --- a/packages/app/src/components/dialog-manage-models.tsx +++ b/packages/app/src/components/dialog-manage-models.tsx @@ -4,14 +4,16 @@ import { Switch } from "@opencode-ai/ui/switch" import type { Component } from "solid-js" import { useLocal } from "@/context/local" import { popularProviders } from "@/hooks/use-providers" +import { useLanguage } from "@/context/language" export const DialogManageModels: Component = () => { const local = useLocal() + const language = useLanguage() return ( - + `${x?.provider?.id}:${x?.id}`} items={local.model.list()} filterKeys={["provider.name", "name", "id"]} diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index bf4a1f9ed..1ee2501de 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -6,6 +6,7 @@ import { getDirectory, getFilename } from "@opencode-ai/util/path" import { createMemo } from "solid-js" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "@/context/global-sync" +import { useLanguage } from "@/context/language" interface DialogSelectDirectoryProps { title?: string @@ -17,6 +18,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { const sync = useGlobalSync() const sdk = useGlobalSDK() const dialog = useDialog() + const language = useLanguage() const home = createMemo(() => sync.data.path.home) const root = createMemo(() => sync.data.path.home || sync.data.path.directory) @@ -81,10 +83,11 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { } return ( - + x} onSelect={(path) => { diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 2e28c4d2e..7c3113a54 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -9,6 +9,7 @@ import { createMemo, createSignal, onCleanup, Show } from "solid-js" import { formatKeybind, useCommand, type CommandOption } from "@/context/command" import { useLayout } from "@/context/layout" import { useFile } from "@/context/file" +import { useLanguage } from "@/context/language" type EntryType = "command" | "file" @@ -18,13 +19,14 @@ type Entry = { title: string description?: string keybind?: string - category: "Commands" | "Files" + category: string option?: CommandOption path?: string } export function DialogSelectFile() { const command = useCommand() + const language = useLanguage() const layout = useLayout() const file = useFile() const dialog = useDialog() @@ -56,7 +58,7 @@ export function DialogSelectFile() { title: option.title, description: option.description, keybind: option.keybind, - category: "Commands", + category: language.t("palette.group.commands"), option, }) @@ -64,7 +66,7 @@ export function DialogSelectFile() { id: "file:" + path, type: "file", title: path, - category: "Files", + category: language.t("palette.group.files"), path, }) @@ -143,8 +145,14 @@ export function DialogSelectFile() { return ( item.id} filterKeys={["title", "description", "category"]} diff --git a/packages/app/src/components/dialog-select-mcp.tsx b/packages/app/src/components/dialog-select-mcp.tsx index c29cd827e..25ef8df01 100644 --- a/packages/app/src/components/dialog-select-mcp.tsx +++ b/packages/app/src/components/dialog-select-mcp.tsx @@ -4,10 +4,12 @@ import { useSDK } from "@/context/sdk" import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { Switch } from "@opencode-ai/ui/switch" +import { useLanguage } from "@/context/language" export const DialogSelectMcp: Component = () => { const sync = useSync() const sdk = useSDK() + const language = useLanguage() const [loading, setLoading] = createSignal(null) const items = createMemo(() => @@ -34,10 +36,13 @@ export const DialogSelectMcp: Component = () => { const totalCount = createMemo(() => items().length) return ( - + x?.name ?? ""} items={items} filterKeys={["name", "status"]} @@ -60,16 +65,16 @@ export const DialogSelectMcp: Component = () => {
{i.name} - connected + {language.t("mcp.status.connected")} - failed + {language.t("mcp.status.failed")} - needs auth + {language.t("mcp.status.needs_auth")} - disabled + {language.t("mcp.status.disabled")} ... diff --git a/packages/app/src/components/dialog-select-model-unpaid.tsx b/packages/app/src/components/dialog-select-model-unpaid.tsx index 24ec8092d..6ac1fa678 100644 --- a/packages/app/src/components/dialog-select-model-unpaid.tsx +++ b/packages/app/src/components/dialog-select-model-unpaid.tsx @@ -10,11 +10,13 @@ import { useLocal } from "@/context/local" import { popularProviders, useProviders } from "@/hooks/use-providers" import { DialogConnectProvider } from "./dialog-connect-provider" import { DialogSelectProvider } from "./dialog-select-provider" +import { useLanguage } from "@/context/language" export const DialogSelectModelUnpaid: Component = () => { const local = useLocal() const dialog = useDialog() const providers = useProviders() + const language = useLanguage() let listRef: ListRef | undefined const handleKey = (e: KeyboardEvent) => { @@ -30,9 +32,9 @@ export const DialogSelectModelUnpaid: Component = () => { }) return ( - +
-
Free models provided by OpenCode
+
{language.t("dialog.model.unpaid.freeModels.title")}
(listRef = ref)} items={local.model.list} @@ -48,9 +50,9 @@ export const DialogSelectModelUnpaid: Component = () => { {(i) => (
{i.name} - Free + {language.t("model.tag.free")} - Latest + {language.t("model.tag.latest")}
)} @@ -60,9 +62,9 @@ export const DialogSelectModelUnpaid: Component = () => {
-
-
Add more models from popular providers
-
+
+
{language.t("dialog.model.unpaid.addMore.title")}
+
x?.id} @@ -83,10 +85,10 @@ export const DialogSelectModelUnpaid: Component = () => { {i.name} - Recommended + {language.t("dialog.provider.tag.recommended")} -
Connect with Claude Pro/Max or API key
+
{language.t("dialog.provider.anthropic.note")}
)} @@ -99,7 +101,7 @@ export const DialogSelectModelUnpaid: Component = () => { dialog.show(() => ) }} > - View all providers + {language.t("dialog.provider.viewAll")}
diff --git a/packages/app/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx index d54f9369a..ba42ffdd6 100644 --- a/packages/app/src/components/dialog-select-model.tsx +++ b/packages/app/src/components/dialog-select-model.tsx @@ -9,6 +9,7 @@ import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { DialogSelectProvider } from "./dialog-select-provider" import { DialogManageModels } from "./dialog-manage-models" +import { useLanguage } from "@/context/language" const ModelList: Component<{ provider?: string @@ -16,6 +17,7 @@ const ModelList: Component<{ onSelect: () => void }> = (props) => { const local = useLocal() + const language = useLanguage() const models = createMemo(() => local.model @@ -27,8 +29,8 @@ const ModelList: Component<{ return ( `${x.provider.id}:${x.id}`} items={models} current={local.model.current()} @@ -55,10 +57,10 @@ const ModelList: Component<{
{i.name} - Free + {language.t("model.tag.free")} - Latest + {language.t("model.tag.latest")}
)} @@ -71,13 +73,14 @@ export const ModelSelectorPopover: Component<{ children: JSX.Element }> = (props) => { const [open, setOpen] = createSignal(false) + const language = useLanguage() return ( {props.children} - Select model + {language.t("dialog.model.select.title")} setOpen(false)} class="p-1" /> @@ -87,10 +90,11 @@ export const ModelSelectorPopover: Component<{ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { const dialog = useDialog() + const language = useLanguage() return ( = (props) => { tabIndex={-1} onClick={() => dialog.show(() => )} > - Connect provider + {language.t("command.provider.connect")} } > @@ -108,7 +112,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { class="ml-3 mt-5 mb-6 text-text-base self-start" onClick={() => dialog.show(() => )} > - Manage models + {language.t("dialog.model.manage")} ) diff --git a/packages/app/src/components/dialog-select-provider.tsx b/packages/app/src/components/dialog-select-provider.tsx index 5bbde5d41..1e059c219 100644 --- a/packages/app/src/components/dialog-select-provider.tsx +++ b/packages/app/src/components/dialog-select-provider.tsx @@ -7,28 +7,38 @@ import { Tag } from "@opencode-ai/ui/tag" import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { IconName } from "@opencode-ai/ui/icons/provider" import { DialogConnectProvider } from "./dialog-connect-provider" +import { useLanguage } from "@/context/language" export const DialogSelectProvider: Component = () => { const dialog = useDialog() const providers = useProviders() + const language = useLanguage() + + const popularGroup = () => language.t("dialog.provider.group.popular") + const otherGroup = () => language.t("dialog.provider.group.other") return ( - + x?.id} - items={providers.all} + items={() => { + language.locale() + return providers.all() + }} filterKeys={["id", "name"]} - groupBy={(x) => (popularProviders.includes(x.id) ? "Popular" : "Other")} + groupBy={(x) => (popularProviders.includes(x.id) ? popularGroup() : otherGroup())} sortBy={(a, b) => { if (popularProviders.includes(a.id) && popularProviders.includes(b.id)) return popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id) return a.name.localeCompare(b.name) }} sortGroupsBy={(a, b) => { - if (a.category === "Popular" && b.category !== "Popular") return -1 - if (b.category === "Popular" && a.category !== "Popular") return 1 + const popular = popularGroup() + if (a.category === popular && b.category !== popular) return -1 + if (b.category === popular && a.category !== popular) return 1 return 0 }} onSelect={(x) => { @@ -41,10 +51,10 @@ export const DialogSelectProvider: Component = () => { {i.name} - Recommended + {language.t("dialog.provider.tag.recommended")} -
Connect with Claude Pro/Max or API key
+
{language.t("dialog.provider.anthropic.note")}
)} diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx index 90f372128..0b3967b76 100644 --- a/packages/app/src/components/dialog-select-server.tsx +++ b/packages/app/src/components/dialog-select-server.tsx @@ -10,6 +10,7 @@ import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/serv import { usePlatform } from "@/context/platform" import { createOpencodeClient } from "@opencode-ai/sdk/v2/client" import { useNavigate } from "@solidjs/router" +import { useLanguage } from "@/context/language" type ServerStatus = { healthy: boolean; version?: string } @@ -30,6 +31,7 @@ export function DialogSelectServer() { const dialog = useDialog() const server = useServer() const platform = usePlatform() + const language = useLanguage() const [store, setStore] = createStore({ url: "", adding: false, @@ -109,7 +111,7 @@ export function DialogSelectServer() { setStore("adding", false) if (!result.healthy) { - setStore("error", "Could not connect to server") + setStore("error", language.t("dialog.server.add.error")) return } @@ -122,11 +124,11 @@ export function DialogSelectServer() { } return ( - +
x} current={current()} @@ -168,14 +170,14 @@ export function DialogSelectServer() {
-

Add a server

+

{language.t("dialog.server.add.title")}

@@ -197,9 +199,9 @@ export function DialogSelectServer() {
-

Default server

+

{language.t("dialog.server.default.title")}

- Connect to this server on app launch instead of starting a local server. Requires restart. + {language.t("dialog.server.default.description")}

@@ -208,7 +210,7 @@ export function DialogSelectServer() { fallback={ No server selected} + fallback={{language.t("dialog.server.default.none")}} > } @@ -234,7 +236,7 @@ export function DialogSelectServer() { defaultUrlActions.refetch() }} > - Clear + {language.t("dialog.server.default.clear")}
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 072ef0bdd..63e9dfbfb 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -49,6 +49,7 @@ import { Persist, persisted } from "@/utils/persist" import { Identifier } from "@/utils/id" import { SessionContextUsage } from "@/components/session-context-usage" import { usePermission } from "@/context/permission" +import { useLanguage } from "@/context/language" import { useGlobalSync } from "@/context/global-sync" import { usePlatform } from "@/context/platform" import { createOpencodeClient, type Message, type Part } from "@opencode-ai/sdk/v2/client" @@ -118,6 +119,7 @@ export const PromptInput: Component = (props) => { const providers = useProviders() const command = useCommand() const permission = usePermission() + const language = useLanguage() let editorRef!: HTMLDivElement let fileInputRef!: HTMLInputElement let scrollRef!: HTMLDivElement @@ -1560,8 +1562,8 @@ export const PromptInput: Component = (props) => {
{store.mode === "shell" - ? "Enter shell command..." - : `Ask anything... "${PLACEHOLDERS[store.placeholder]}"`} + ? language.t("prompt.placeholder.shell") + : language.t("prompt.placeholder.normal", { example: PLACEHOLDERS[store.placeholder] })}
@@ -1571,12 +1573,16 @@ export const PromptInput: Component = (props) => {
- Shell - esc to exit + {language.t("prompt.mode.shell")} + {language.t("prompt.mode.shell.exit")}
- +