wip(app): i18n
This commit is contained in:
@@ -14,6 +14,7 @@ import { iife } from "@opencode-ai/util/iife"
|
|||||||
import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js"
|
import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js"
|
||||||
import { createStore, produce } from "solid-js/store"
|
import { createStore, produce } from "solid-js/store"
|
||||||
import { Link } from "@/components/link"
|
import { Link } from "@/components/link"
|
||||||
|
import { useLanguage } from "@/context/language"
|
||||||
import { useGlobalSDK } from "@/context/global-sdk"
|
import { useGlobalSDK } from "@/context/global-sdk"
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
@@ -25,6 +26,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
const globalSync = useGlobalSync()
|
const globalSync = useGlobalSync()
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
|
const language = useLanguage()
|
||||||
const provider = createMemo(() => globalSync.data.provider.all.find((x) => x.id === props.provider)!)
|
const provider = createMemo(() => globalSync.data.provider.all.find((x) => x.id === props.provider)!)
|
||||||
const methods = createMemo(
|
const methods = createMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -44,6 +46,12 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
|
|
||||||
const method = createMemo(() => (store.methodIndex !== undefined ? methods().at(store.methodIndex!) : undefined))
|
const method = createMemo(() => (store.methodIndex !== undefined ? methods().at(store.methodIndex!) : undefined))
|
||||||
|
|
||||||
|
const methodLabel = (value?: { type?: string; label?: string }) => {
|
||||||
|
if (!value) return ""
|
||||||
|
if (value.type === "api") return language.t("provider.connect.method.apiKey")
|
||||||
|
return value.label ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
async function selectMethod(index: number) {
|
async function selectMethod(index: number) {
|
||||||
const method = methods()[index]
|
const method = methods()[index]
|
||||||
setStore(
|
setStore(
|
||||||
@@ -112,8 +120,8 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
showToast({
|
showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
icon: "circle-check",
|
icon: "circle-check",
|
||||||
title: `${provider().name} connected`,
|
title: language.t("provider.connect.toast.connected.title", { provider: provider().name }),
|
||||||
description: `${provider().name} models are now available to use.`,
|
description: language.t("provider.connect.toast.connected.description", { provider: provider().name }),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,16 +150,18 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<div class="text-16-medium text-text-strong">
|
<div class="text-16-medium text-text-strong">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={props.provider === "anthropic" && method()?.label?.toLowerCase().includes("max")}>
|
<Match when={props.provider === "anthropic" && method()?.label?.toLowerCase().includes("max")}>
|
||||||
Login with Claude Pro/Max
|
{language.t("provider.connect.title.anthropicProMax")}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>Connect {provider().name}</Match>
|
<Match when={true}>{language.t("provider.connect.title", { provider: provider().name })}</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-2.5 pb-10 flex flex-col gap-6">
|
<div class="px-2.5 pb-10 flex flex-col gap-6">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={store.methodIndex === undefined}>
|
<Match when={store.methodIndex === undefined}>
|
||||||
<div class="text-14-regular text-text-base">Select login method for {provider().name}.</div>
|
<div class="text-14-regular text-text-base">
|
||||||
|
{language.t("provider.connect.selectMethod", { provider: provider().name })}
|
||||||
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<List
|
<List
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
@@ -169,7 +179,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<div class="w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center">
|
<div class="w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center">
|
||||||
<div class="w-2.5 h-0.5 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
|
<div class="w-2.5 h-0.5 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
|
||||||
</div>
|
</div>
|
||||||
<span>{i.label}</span>
|
<span>{methodLabel(i)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
@@ -179,7 +189,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex items-center gap-x-2">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span>Authorization in progress...</span>
|
<span>{language.t("provider.connect.status.inProgress")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
@@ -187,7 +197,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex items-center gap-x-2">
|
||||||
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
|
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
|
||||||
<span>Authorization failed: {store.error}</span>
|
<span>{language.t("provider.connect.status.failed", { error: store.error ?? "" })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
@@ -206,7 +216,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
const apiKey = formData.get("apiKey") as string
|
const apiKey = formData.get("apiKey") as string
|
||||||
|
|
||||||
if (!apiKey?.trim()) {
|
if (!apiKey?.trim()) {
|
||||||
setFormStore("error", "API key is required")
|
setFormStore("error", language.t("provider.connect.apiKey.required"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,26 +236,20 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Match when={provider().id === "opencode"}>
|
<Match when={provider().id === "opencode"}>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="text-14-regular text-text-base">{language.t("provider.connect.opencodeZen.line1")}</div>
|
||||||
|
<div class="text-14-regular text-text-base">{language.t("provider.connect.opencodeZen.line2")}</div>
|
||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
OpenCode Zen gives you access to a curated set of reliable optimized models for coding
|
{language.t("provider.connect.opencodeZen.visit.prefix")}
|
||||||
agents.
|
|
||||||
</div>
|
|
||||||
<div class="text-14-regular text-text-base">
|
|
||||||
With a single API key you'll get access to models such as Claude, GPT, Gemini, GLM and more.
|
|
||||||
</div>
|
|
||||||
<div class="text-14-regular text-text-base">
|
|
||||||
Visit{" "}
|
|
||||||
<Link href="https://opencode.ai/zen" tabIndex={-1}>
|
<Link href="https://opencode.ai/zen" tabIndex={-1}>
|
||||||
opencode.ai/zen
|
opencode.ai/zen
|
||||||
</Link>{" "}
|
</Link>
|
||||||
to collect your API key.
|
{language.t("provider.connect.opencodeZen.visit.suffix")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
Enter your {provider().name} API key to connect your account and use {provider().name} models
|
{language.t("provider.connect.apiKey.description", { provider: provider().name })}
|
||||||
in OpenCode.
|
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -253,8 +257,8 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
<TextField
|
<TextField
|
||||||
autofocus
|
autofocus
|
||||||
type="text"
|
type="text"
|
||||||
label={`${provider().name} API key`}
|
label={language.t("provider.connect.apiKey.label", { provider: provider().name })}
|
||||||
placeholder="API key"
|
placeholder={language.t("provider.connect.apiKey.placeholder")}
|
||||||
name="apiKey"
|
name="apiKey"
|
||||||
value={formStore.value}
|
value={formStore.value}
|
||||||
onChange={setFormStore.bind(null, "value")}
|
onChange={setFormStore.bind(null, "value")}
|
||||||
@@ -262,7 +266,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
error={formStore.error}
|
error={formStore.error}
|
||||||
/>
|
/>
|
||||||
<Button class="w-auto" type="submit" size="large" variant="primary">
|
<Button class="w-auto" type="submit" size="large" variant="primary">
|
||||||
Submit
|
{language.t("common.submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -292,7 +296,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
const code = formData.get("code") as string
|
const code = formData.get("code") as string
|
||||||
|
|
||||||
if (!code?.trim()) {
|
if (!code?.trim()) {
|
||||||
setFormStore("error", "Authorization code is required")
|
setFormStore("error", language.t("provider.connect.oauth.code.required"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,21 +310,22 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
await complete()
|
await complete()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setFormStore("error", "Invalid authorization code")
|
setFormStore("error", language.t("provider.connect.oauth.code.invalid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
Visit <Link href={store.authorization!.url}>this link</Link> to collect your authorization
|
{language.t("provider.connect.oauth.code.visit.prefix")}
|
||||||
code to connect your account and use {provider().name} models in OpenCode.
|
<Link href={store.authorization!.url}>{language.t("provider.connect.oauth.code.visit.link")}</Link>
|
||||||
|
{language.t("provider.connect.oauth.code.visit.suffix", { provider: provider().name })}
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
|
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
|
||||||
<TextField
|
<TextField
|
||||||
autofocus
|
autofocus
|
||||||
type="text"
|
type="text"
|
||||||
label={`${method()?.label} authorization code`}
|
label={language.t("provider.connect.oauth.code.label", { method: method()?.label ?? "" })}
|
||||||
placeholder="Authorization code"
|
placeholder={language.t("provider.connect.oauth.code.placeholder")}
|
||||||
name="code"
|
name="code"
|
||||||
value={formStore.value}
|
value={formStore.value}
|
||||||
onChange={setFormStore.bind(null, "value")}
|
onChange={setFormStore.bind(null, "value")}
|
||||||
@@ -328,7 +333,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
error={formStore.error}
|
error={formStore.error}
|
||||||
/>
|
/>
|
||||||
<Button class="w-auto" type="submit" size="large" variant="primary">
|
<Button class="w-auto" type="submit" size="large" variant="primary">
|
||||||
Submit
|
{language.t("common.submit")}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -361,13 +366,20 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-6">
|
<div class="flex flex-col gap-6">
|
||||||
<div class="text-14-regular text-text-base">
|
<div class="text-14-regular text-text-base">
|
||||||
Visit <Link href={store.authorization!.url}>this link</Link> and enter the code below to
|
{language.t("provider.connect.oauth.auto.visit.prefix")}
|
||||||
connect your account and use {provider().name} models in OpenCode.
|
<Link href={store.authorization!.url}>{language.t("provider.connect.oauth.auto.visit.link")}</Link>
|
||||||
|
{language.t("provider.connect.oauth.auto.visit.suffix", { provider: provider().name })}
|
||||||
</div>
|
</div>
|
||||||
<TextField label="Confirmation code" class="font-mono" value={code()} readOnly copyable />
|
<TextField
|
||||||
|
label={language.t("provider.connect.oauth.auto.confirmationCode")}
|
||||||
|
class="font-mono"
|
||||||
|
value={code()}
|
||||||
|
readOnly
|
||||||
|
copyable
|
||||||
|
/>
|
||||||
<div class="text-14-regular text-text-base flex items-center gap-4">
|
<div class="text-14-regular text-text-base flex items-center gap-4">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span>Waiting for authorization...</span>
|
<span>{language.t("provider.connect.status.waiting")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -95,12 +95,47 @@ export const dict = {
|
|||||||
|
|
||||||
"dialog.provider.viewAll": "View all providers",
|
"dialog.provider.viewAll": "View all providers",
|
||||||
|
|
||||||
|
"provider.connect.title": "Connect {{provider}}",
|
||||||
|
"provider.connect.title.anthropicProMax": "Login with Claude Pro/Max",
|
||||||
|
"provider.connect.selectMethod": "Select login method for {{provider}}.",
|
||||||
|
"provider.connect.method.apiKey": "API key",
|
||||||
|
"provider.connect.status.inProgress": "Authorization in progress...",
|
||||||
|
"provider.connect.status.waiting": "Waiting for authorization...",
|
||||||
|
"provider.connect.status.failed": "Authorization failed: {{error}}",
|
||||||
|
"provider.connect.apiKey.description":
|
||||||
|
"Enter your {{provider}} API key to connect your account and use {{provider}} models in OpenCode.",
|
||||||
|
"provider.connect.apiKey.label": "{{provider}} API key",
|
||||||
|
"provider.connect.apiKey.placeholder": "API key",
|
||||||
|
"provider.connect.apiKey.required": "API key is required",
|
||||||
|
"provider.connect.opencodeZen.line1":
|
||||||
|
"OpenCode Zen gives you access to a curated set of reliable optimized models for coding agents.",
|
||||||
|
"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.suffix": " to collect your API key.",
|
||||||
|
"provider.connect.oauth.code.visit.prefix": "Visit ",
|
||||||
|
"provider.connect.oauth.code.visit.link": "this link",
|
||||||
|
"provider.connect.oauth.code.visit.suffix":
|
||||||
|
" to collect your authorization code to connect your account and use {{provider}} models in OpenCode.",
|
||||||
|
"provider.connect.oauth.code.label": "{{method}} authorization code",
|
||||||
|
"provider.connect.oauth.code.placeholder": "Authorization code",
|
||||||
|
"provider.connect.oauth.code.required": "Authorization code is required",
|
||||||
|
"provider.connect.oauth.code.invalid": "Invalid authorization code",
|
||||||
|
"provider.connect.oauth.auto.visit.prefix": "Visit ",
|
||||||
|
"provider.connect.oauth.auto.visit.link": "this link",
|
||||||
|
"provider.connect.oauth.auto.visit.suffix":
|
||||||
|
" and enter the code below to connect your account and use {{provider}} models in OpenCode.",
|
||||||
|
"provider.connect.oauth.auto.confirmationCode": "Confirmation code",
|
||||||
|
"provider.connect.toast.connected.title": "{{provider}} connected",
|
||||||
|
"provider.connect.toast.connected.description": "{{provider}} models are now available to use.",
|
||||||
|
|
||||||
"model.tag.free": "Free",
|
"model.tag.free": "Free",
|
||||||
"model.tag.latest": "Latest",
|
"model.tag.latest": "Latest",
|
||||||
|
|
||||||
"common.search.placeholder": "Search",
|
"common.search.placeholder": "Search",
|
||||||
"common.loading": "Loading",
|
"common.loading": "Loading",
|
||||||
"common.cancel": "Cancel",
|
"common.cancel": "Cancel",
|
||||||
|
"common.submit": "Submit",
|
||||||
"common.save": "Save",
|
"common.save": "Save",
|
||||||
"common.saving": "Saving...",
|
"common.saving": "Saving...",
|
||||||
"common.default": "Default",
|
"common.default": "Default",
|
||||||
|
|||||||
@@ -99,12 +99,42 @@ export const dict = {
|
|||||||
|
|
||||||
"dialog.provider.viewAll": "查看全部提供商",
|
"dialog.provider.viewAll": "查看全部提供商",
|
||||||
|
|
||||||
|
"provider.connect.title": "连接 {{provider}}",
|
||||||
|
"provider.connect.title.anthropicProMax": "使用 Claude Pro/Max 登录",
|
||||||
|
"provider.connect.selectMethod": "选择 {{provider}} 的登录方式。",
|
||||||
|
"provider.connect.method.apiKey": "API 密钥",
|
||||||
|
"provider.connect.status.inProgress": "正在授权...",
|
||||||
|
"provider.connect.status.waiting": "等待授权...",
|
||||||
|
"provider.connect.status.failed": "授权失败: {{error}}",
|
||||||
|
"provider.connect.apiKey.description": "输入你的 {{provider}} API 密钥以连接帐户,并在 OpenCode 中使用 {{provider}} 模型。",
|
||||||
|
"provider.connect.apiKey.label": "{{provider}} API 密钥",
|
||||||
|
"provider.connect.apiKey.placeholder": "API 密钥",
|
||||||
|
"provider.connect.apiKey.required": "API 密钥为必填项",
|
||||||
|
"provider.connect.opencodeZen.line1": "OpenCode Zen 为你提供一组精选的可靠优化模型,用于代码智能体。",
|
||||||
|
"provider.connect.opencodeZen.line2": "只需一个 API 密钥,你就能使用 Claude、GPT、Gemini、GLM 等模型。",
|
||||||
|
"provider.connect.opencodeZen.visit.prefix": "访问 ",
|
||||||
|
"provider.connect.opencodeZen.visit.suffix": " 获取你的 API 密钥。",
|
||||||
|
"provider.connect.oauth.code.visit.prefix": "访问 ",
|
||||||
|
"provider.connect.oauth.code.visit.link": "此链接",
|
||||||
|
"provider.connect.oauth.code.visit.suffix": " 获取授权码,以连接你的帐户并在 OpenCode 中使用 {{provider}} 模型。",
|
||||||
|
"provider.connect.oauth.code.label": "{{method}} 授权码",
|
||||||
|
"provider.connect.oauth.code.placeholder": "授权码",
|
||||||
|
"provider.connect.oauth.code.required": "授权码为必填项",
|
||||||
|
"provider.connect.oauth.code.invalid": "授权码无效",
|
||||||
|
"provider.connect.oauth.auto.visit.prefix": "访问 ",
|
||||||
|
"provider.connect.oauth.auto.visit.link": "此链接",
|
||||||
|
"provider.connect.oauth.auto.visit.suffix": " 并输入以下代码,以连接你的帐户并在 OpenCode 中使用 {{provider}} 模型。",
|
||||||
|
"provider.connect.oauth.auto.confirmationCode": "确认码",
|
||||||
|
"provider.connect.toast.connected.title": "{{provider}} 已连接",
|
||||||
|
"provider.connect.toast.connected.description": "现在可以使用 {{provider}} 模型了。",
|
||||||
|
|
||||||
"model.tag.free": "免费",
|
"model.tag.free": "免费",
|
||||||
"model.tag.latest": "最新",
|
"model.tag.latest": "最新",
|
||||||
|
|
||||||
"common.search.placeholder": "搜索",
|
"common.search.placeholder": "搜索",
|
||||||
"common.loading": "加载中",
|
"common.loading": "加载中",
|
||||||
"common.cancel": "取消",
|
"common.cancel": "取消",
|
||||||
|
"common.submit": "提交",
|
||||||
"common.save": "保存",
|
"common.save": "保存",
|
||||||
"common.saving": "保存中...",
|
"common.saving": "保存中...",
|
||||||
"common.default": "默认",
|
"common.default": "默认",
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ This report documents the remaining user-facing strings in `packages/app/src` th
|
|||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
- The app uses `useLanguage().t("...")` with dictionaries in `packages/app/src/i18n/en.ts` and `packages/app/src/i18n/zh.ts`.
|
- The app uses `useLanguage().t("...")` with dictionaries in `packages/app/src/i18n/en.ts` and `packages/app/src/i18n/zh.ts`.
|
||||||
- Recent progress (already translated): `packages/app/src/pages/home.tsx`, `packages/app/src/pages/layout.tsx`, `packages/app/src/pages/session.tsx`, `packages/app/src/components/prompt-input.tsx` (plus new keys added in both dictionaries).
|
- Recent progress (already translated): `packages/app/src/pages/home.tsx`, `packages/app/src/pages/layout.tsx`, `packages/app/src/pages/session.tsx`, `packages/app/src/components/prompt-input.tsx`, `packages/app/src/components/dialog-connect-provider.tsx` (plus new keys added in both dictionaries).
|
||||||
- Dictionary parity check: `en.ts` and `zh.ts` currently contain the same key set (285 keys each; no missing or extra keys).
|
- Dictionary parity check: `en.ts` and `zh.ts` currently contain the same key set (314 keys each; no missing or extra keys).
|
||||||
|
|
||||||
## Methodology
|
## Methodology
|
||||||
|
|
||||||
@@ -71,24 +71,11 @@ Completed (2026-01-20):
|
|||||||
|
|
||||||
File: `packages/app/src/components/dialog-connect-provider.tsx`
|
File: `packages/app/src/components/dialog-connect-provider.tsx`
|
||||||
|
|
||||||
This flow is copy-heavy and user-facing.
|
Completed (2026-01-20):
|
||||||
|
|
||||||
**Representative untranslated strings**
|
- Localized all user-visible copy via `provider.connect.*` keys (titles, statuses, validations, instructions, OpenCode Zen onboarding).
|
||||||
- Login method label: "API key"
|
- Added `common.submit` and used it for both API + OAuth submit buttons.
|
||||||
- Status text: "Authorization in progress...", "Waiting for authorization..."
|
- Localized the success toast via `provider.connect.toast.connected.*`.
|
||||||
- Validation: "API key is required", "Authorization code is required", "Invalid authorization code"
|
|
||||||
- Field labels/placeholders: "Confirmation code", placeholder "API key", placeholder "Authorization code"
|
|
||||||
- Instructional text:
|
|
||||||
- "Visit this link ..."
|
|
||||||
- Provider-specific guidance and OpenCode Zen onboarding paragraphs
|
|
||||||
- Buttons: "Submit"
|
|
||||||
- Success toast:
|
|
||||||
- "{provider} connected"
|
|
||||||
- "{provider} models are now available to use."
|
|
||||||
|
|
||||||
**Recommendation:**
|
|
||||||
- Add a `provider.connect.*` namespace.
|
|
||||||
- Consider adding shared common keys like `common.submit` if it is used elsewhere.
|
|
||||||
|
|
||||||
### 4) Session Header (Share/Publish UI)
|
### 4) Session Header (Share/Publish UI)
|
||||||
|
|
||||||
@@ -250,12 +237,11 @@ This is only thrown in DEV and is more of a developer diagnostic. Optional to tr
|
|||||||
|
|
||||||
## Prioritized Implementation Plan
|
## Prioritized Implementation Plan
|
||||||
|
|
||||||
1. `packages/app/src/components/dialog-connect-provider.tsx`
|
1. `packages/app/src/components/session/session-header.tsx`
|
||||||
2. `packages/app/src/components/session/session-header.tsx`
|
2. `packages/app/src/pages/error.tsx`
|
||||||
3. `packages/app/src/pages/error.tsx`
|
3. `packages/app/src/components/session/session-new-view.tsx`
|
||||||
4. `packages/app/src/components/session/session-new-view.tsx`
|
4. `packages/app/src/components/session-context-usage.tsx` + locale formatting improvements (also `packages/app/src/components/session/session-context-tab.tsx`)
|
||||||
5. `packages/app/src/components/session-context-usage.tsx` + locale formatting improvements (also `packages/app/src/components/session/session-context-tab.tsx`)
|
5. Small stragglers:
|
||||||
6. Small stragglers:
|
|
||||||
- `packages/app/src/components/session-lsp-indicator.tsx`
|
- `packages/app/src/components/session-lsp-indicator.tsx`
|
||||||
- `packages/app/src/components/session/session-sortable-tab.tsx`
|
- `packages/app/src/components/session/session-sortable-tab.tsx`
|
||||||
- `packages/app/src/components/titlebar.tsx`
|
- `packages/app/src/components/titlebar.tsx`
|
||||||
@@ -264,7 +250,7 @@ This is only thrown in DEV and is more of a developer diagnostic. Optional to tr
|
|||||||
- `packages/app/src/context/global-sync.tsx`
|
- `packages/app/src/context/global-sync.tsx`
|
||||||
- `packages/app/src/context/file.tsx` + `packages/app/src/context/local.tsx`
|
- `packages/app/src/context/file.tsx` + `packages/app/src/context/local.tsx`
|
||||||
- `packages/app/src/utils/prompt.ts`
|
- `packages/app/src/utils/prompt.ts`
|
||||||
7. Decide on the terminal naming approach (`packages/app/src/context/terminal.tsx`).
|
6. Decide on the terminal naming approach (`packages/app/src/context/terminal.tsx`).
|
||||||
|
|
||||||
## Suggested Key Naming Conventions
|
## Suggested Key Naming Conventions
|
||||||
|
|
||||||
@@ -287,7 +273,6 @@ Pages:
|
|||||||
- `packages/app/src/pages/error.tsx`
|
- `packages/app/src/pages/error.tsx`
|
||||||
|
|
||||||
Components:
|
Components:
|
||||||
- `packages/app/src/components/dialog-connect-provider.tsx`
|
|
||||||
- `packages/app/src/components/session/session-header.tsx`
|
- `packages/app/src/components/session/session-header.tsx`
|
||||||
- `packages/app/src/components/session/session-new-view.tsx`
|
- `packages/app/src/components/session/session-new-view.tsx`
|
||||||
- `packages/app/src/components/session-context-usage.tsx`
|
- `packages/app/src/components/session-context-usage.tsx`
|
||||||
|
|||||||
Reference in New Issue
Block a user