diff options
| author | Adam <[email protected]> | 2026-01-20 07:53:46 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-20 17:58:06 -0600 |
| commit | 835fea6bb135b2d2757445fc647618c828a91f0b (patch) | |
| tree | 5ae60b8f4a4453a8a4ae64dd7c858c05040b000a /packages/app/src | |
| parent | 7138bd021c4aa73204b2c74e3c954b38d20acd75 (diff) | |
| download | opencode-835fea6bb135b2d2757445fc647618c828a91f0b.tar.gz opencode-835fea6bb135b2d2757445fc647618c828a91f0b.zip | |
wip(app): i18n prompt input
Diffstat (limited to 'packages/app/src')
| -rw-r--r-- | packages/app/src/components/prompt-input.tsx | 102 | ||||
| -rw-r--r-- | packages/app/src/i18n/en.ts | 46 | ||||
| -rw-r--r-- | packages/app/src/i18n/zh.ts | 46 |
3 files changed, 143 insertions, 51 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 63e9dfbfb..35a74f43e 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -67,33 +67,33 @@ interface PromptInputProps { onNewSessionWorktreeReset?: () => void } -const PLACEHOLDERS = [ - "Fix a TODO in the codebase", - "What is the tech stack of this project?", - "Fix broken tests", - "Explain how authentication works", - "Find and fix security vulnerabilities", - "Add unit tests for the user service", - "Refactor this function to be more readable", - "What does this error mean?", - "Help me debug this issue", - "Generate API documentation", - "Optimize database queries", - "Add input validation", - "Create a new component for...", - "How do I deploy this project?", - "Review my code for best practices", - "Add error handling to this function", - "Explain this regex pattern", - "Convert this to TypeScript", - "Add logging throughout the codebase", - "What dependencies are outdated?", - "Help me write a migration script", - "Implement caching for this endpoint", - "Add pagination to this list", - "Create a CLI command for...", - "How do environment variables work here?", -] +const EXAMPLES = [ + "prompt.example.1", + "prompt.example.2", + "prompt.example.3", + "prompt.example.4", + "prompt.example.5", + "prompt.example.6", + "prompt.example.7", + "prompt.example.8", + "prompt.example.9", + "prompt.example.10", + "prompt.example.11", + "prompt.example.12", + "prompt.example.13", + "prompt.example.14", + "prompt.example.15", + "prompt.example.16", + "prompt.example.17", + "prompt.example.18", + "prompt.example.19", + "prompt.example.20", + "prompt.example.21", + "prompt.example.22", + "prompt.example.23", + "prompt.example.24", + "prompt.example.25", +] as const interface SlashCommand { id: string @@ -186,7 +186,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { popover: null, historyIndex: -1, savedPrompt: null, - placeholder: Math.floor(Math.random() * PLACEHOLDERS.length), + placeholder: Math.floor(Math.random() * EXAMPLES.length), dragging: false, mode: "normal", applyingHistory: false, @@ -259,7 +259,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { params.id if (params.id) return const interval = setInterval(() => { - setStore("placeholder", (prev) => (prev + 1) % PLACEHOLDERS.length) + setStore("placeholder", (prev) => (prev + 1) % EXAMPLES.length) }, 6500) onCleanup(() => clearInterval(interval)) }) @@ -314,8 +314,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => { if (fileItems.length > 0) { showToast({ - title: "Unsupported paste", - description: "Only images or PDFs can be pasted here.", + title: language.t("prompt.toast.pasteUnsupported.title"), + description: language.t("prompt.toast.pasteUnsupported.description"), }) return } @@ -999,8 +999,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => { const currentAgent = local.agent.current() if (!currentModel || !currentAgent) { showToast({ - title: "Select an agent and model", - description: "Choose an agent and model before sending a prompt.", + title: language.t("prompt.toast.modelAgentRequired.title"), + description: language.t("prompt.toast.modelAgentRequired.description"), }) return } @@ -1011,7 +1011,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { if (data?.message) return data.message } if (err instanceof Error) return err.message - return "Request failed" + return language.t("common.requestFailed") } addToHistory(currentPrompt, mode) @@ -1032,7 +1032,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { .then((x) => x.data) .catch((err) => { showToast({ - title: "Failed to create worktree", + title: language.t("prompt.toast.worktreeCreateFailed.title"), description: errorMessage(err), }) return undefined @@ -1040,8 +1040,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => { if (!createdWorktree?.directory) { showToast({ - title: "Failed to create worktree", - description: "Request failed", + title: language.t("prompt.toast.worktreeCreateFailed.title"), + description: language.t("common.requestFailed"), }) return } @@ -1072,7 +1072,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { .then((x) => x.data ?? undefined) .catch((err) => { showToast({ - title: "Failed to create session", + title: language.t("prompt.toast.sessionCreateFailed.title"), description: errorMessage(err), }) return undefined @@ -1116,7 +1116,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { }) .catch((err) => { showToast({ - title: "Failed to send shell command", + title: language.t("prompt.toast.shellSendFailed.title"), description: errorMessage(err), }) restoreInput() @@ -1148,7 +1148,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { }) .catch((err) => { showToast({ - title: "Failed to send command", + title: language.t("prompt.toast.commandSendFailed.title"), description: errorMessage(err), }) restoreInput() @@ -1316,7 +1316,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { }) .catch((err) => { showToast({ - title: "Failed to send prompt", + title: language.t("prompt.toast.promptSendFailed.title"), description: errorMessage(err), }) removeOptimisticMessage() @@ -1340,7 +1340,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <Match when={store.popover === "at"}> <Show when={atFlat().length > 0} - fallback={<div class="text-text-weak px-2 py-1">No matching results</div>} + fallback={<div class="text-text-weak px-2 py-1">{language.t("prompt.popover.emptyResults")}</div>} > <For each={atFlat().slice(0, 10)}> {(item) => ( @@ -1386,7 +1386,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <Match when={store.popover === "slash"}> <Show when={slashFlat().length > 0} - fallback={<div class="text-text-weak px-2 py-1">No matching commands</div>} + fallback={<div class="text-text-weak px-2 py-1">{language.t("prompt.popover.emptyCommands")}</div>} > <For each={slashFlat()}> {(cmd) => ( @@ -1408,7 +1408,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <div class="flex items-center gap-2 shrink-0"> <Show when={cmd.type === "custom"}> <span class="text-11-regular text-text-subtle px-1.5 py-0.5 bg-surface-base rounded"> - custom + {language.t("prompt.slash.badge.custom")} </span> </Show> <Show when={command.keybind(cmd.id)}> @@ -1437,7 +1437,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <div class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised-stronger-non-alpha/90 pointer-events-none"> <div class="flex flex-col items-center gap-2 text-text-weak"> <Icon name="photo" class="size-8" /> - <span class="text-14-regular">Drop images or PDFs here</span> + <span class="text-14-regular">{language.t("prompt.dropzone.label")}</span> </div> </div> </Show> @@ -1450,7 +1450,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <div class="flex items-center text-12-regular min-w-0"> <span class="text-text-weak whitespace-nowrap truncate min-w-0">{getDirectory(path())}</span> <span class="text-text-strong whitespace-nowrap">{getFilename(path())}</span> - <span class="text-text-weak whitespace-nowrap ml-1">active</span> + <span class="text-text-weak whitespace-nowrap ml-1">{language.t("prompt.context.active")}</span> </div> <IconButton type="button" @@ -1469,7 +1469,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { onClick={() => prompt.context.addActive()} > <Icon name="plus-small" size="small" /> - <span>Include active file</span> + <span>{language.t("prompt.context.includeActiveFile")}</span> </button> </Show> <For each={prompt.context.items()}> @@ -1563,7 +1563,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <div class="absolute top-0 inset-x-0 px-5 py-3 pr-12 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate"> {store.mode === "shell" ? language.t("prompt.placeholder.shell") - : language.t("prompt.placeholder.normal", { example: PLACEHOLDERS[store.placeholder] })} + : language.t("prompt.placeholder.normal", { example: language.t(EXAMPLES[store.placeholder]) })} </div> </Show> </div> @@ -1681,7 +1681,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <div class="flex items-center gap-2"> <SessionContextUsage /> <Show when={store.mode === "normal"}> - <Tooltip placement="top" value="Attach file"> + <Tooltip placement="top" value={language.t("prompt.action.attachFile")}> <Button type="button" variant="ghost" class="size-6" onClick={() => fileInputRef.click()}> <Icon name="photo" class="size-4.5" /> </Button> @@ -1695,13 +1695,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <Switch> <Match when={working()}> <div class="flex items-center gap-2"> - <span>Stop</span> + <span>{language.t("prompt.action.stop")}</span> <span class="text-icon-base text-12-medium text-[10px]!">ESC</span> </div> </Match> <Match when={true}> <div class="flex items-center gap-2"> - <span>Send</span> + <span>{language.t("prompt.action.send")}</span> <Icon name="enter" size="small" class="text-icon-base" /> </div> </Match> diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index cd900e1c0..9e100d520 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -110,6 +110,52 @@ export const dict = { "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "esc to exit", + "prompt.example.1": "Fix a TODO in the codebase", + "prompt.example.2": "What is the tech stack of this project?", + "prompt.example.3": "Fix broken tests", + "prompt.example.4": "Explain how authentication works", + "prompt.example.5": "Find and fix security vulnerabilities", + "prompt.example.6": "Add unit tests for the user service", + "prompt.example.7": "Refactor this function to be more readable", + "prompt.example.8": "What does this error mean?", + "prompt.example.9": "Help me debug this issue", + "prompt.example.10": "Generate API documentation", + "prompt.example.11": "Optimize database queries", + "prompt.example.12": "Add input validation", + "prompt.example.13": "Create a new component for...", + "prompt.example.14": "How do I deploy this project?", + "prompt.example.15": "Review my code for best practices", + "prompt.example.16": "Add error handling to this function", + "prompt.example.17": "Explain this regex pattern", + "prompt.example.18": "Convert this to TypeScript", + "prompt.example.19": "Add logging throughout the codebase", + "prompt.example.20": "What dependencies are outdated?", + "prompt.example.21": "Help me write a migration script", + "prompt.example.22": "Implement caching for this endpoint", + "prompt.example.23": "Add pagination to this list", + "prompt.example.24": "Create a CLI command for...", + "prompt.example.25": "How do environment variables work here?", + + "prompt.popover.emptyResults": "No matching results", + "prompt.popover.emptyCommands": "No matching commands", + "prompt.dropzone.label": "Drop images or PDFs here", + "prompt.slash.badge.custom": "custom", + "prompt.context.active": "active", + "prompt.context.includeActiveFile": "Include active file", + "prompt.action.attachFile": "Attach file", + "prompt.action.send": "Send", + "prompt.action.stop": "Stop", + + "prompt.toast.pasteUnsupported.title": "Unsupported paste", + "prompt.toast.pasteUnsupported.description": "Only images or PDFs can be pasted here.", + "prompt.toast.modelAgentRequired.title": "Select an agent and model", + "prompt.toast.modelAgentRequired.description": "Choose an agent and model before sending a prompt.", + "prompt.toast.worktreeCreateFailed.title": "Failed to create worktree", + "prompt.toast.sessionCreateFailed.title": "Failed to create session", + "prompt.toast.shellSendFailed.title": "Failed to send shell command", + "prompt.toast.commandSendFailed.title": "Failed to send command", + "prompt.toast.promptSendFailed.title": "Failed to send prompt", + "dialog.mcp.title": "MCPs", "dialog.mcp.description": "{{enabled}} of {{total}} enabled", "dialog.mcp.empty": "No MCPs configured", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index e8b37fd63..28a39612a 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -114,6 +114,52 @@ export const dict = { "prompt.mode.shell": "Shell", "prompt.mode.shell.exit": "按 esc 退出", + "prompt.example.1": "修复代码库中的一个 TODO", + "prompt.example.2": "这个项目的技术栈是什么?", + "prompt.example.3": "修复失败的测试", + "prompt.example.4": "解释认证是如何工作的", + "prompt.example.5": "查找并修复安全漏洞", + "prompt.example.6": "为用户服务添加单元测试", + "prompt.example.7": "重构这个函数,让它更易读", + "prompt.example.8": "这个错误是什么意思?", + "prompt.example.9": "帮我调试这个问题", + "prompt.example.10": "生成 API 文档", + "prompt.example.11": "优化数据库查询", + "prompt.example.12": "添加输入校验", + "prompt.example.13": "创建一个新的组件用于...", + "prompt.example.14": "我该如何部署这个项目?", + "prompt.example.15": "审查我的代码并给出最佳实践建议", + "prompt.example.16": "为这个函数添加错误处理", + "prompt.example.17": "解释这个正则表达式", + "prompt.example.18": "把它转换成 TypeScript", + "prompt.example.19": "在整个代码库中添加日志", + "prompt.example.20": "哪些依赖已经过期?", + "prompt.example.21": "帮我写一个迁移脚本", + "prompt.example.22": "为这个接口实现缓存", + "prompt.example.23": "给这个列表添加分页", + "prompt.example.24": "创建一个 CLI 命令用于...", + "prompt.example.25": "这里的环境变量是怎么工作的?", + + "prompt.popover.emptyResults": "没有匹配的结果", + "prompt.popover.emptyCommands": "没有匹配的命令", + "prompt.dropzone.label": "将图片或 PDF 拖到这里", + "prompt.slash.badge.custom": "自定义", + "prompt.context.active": "当前", + "prompt.context.includeActiveFile": "包含当前文件", + "prompt.action.attachFile": "附加文件", + "prompt.action.send": "发送", + "prompt.action.stop": "停止", + + "prompt.toast.pasteUnsupported.title": "不支持的粘贴", + "prompt.toast.pasteUnsupported.description": "这里只能粘贴图片或 PDF 文件。", + "prompt.toast.modelAgentRequired.title": "请选择智能体和模型", + "prompt.toast.modelAgentRequired.description": "发送提示前请先选择智能体和模型。", + "prompt.toast.worktreeCreateFailed.title": "创建工作树失败", + "prompt.toast.sessionCreateFailed.title": "创建会话失败", + "prompt.toast.shellSendFailed.title": "发送 shell 命令失败", + "prompt.toast.commandSendFailed.title": "发送命令失败", + "prompt.toast.promptSendFailed.title": "发送提示失败", + "dialog.mcp.title": "MCPs", "dialog.mcp.description": "已启用 {{enabled}} / {{total}}", "dialog.mcp.empty": "未配置 MCPs", |
