diff options
| author | Adam <[email protected]> | 2026-03-13 06:48:38 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-13 06:48:38 -0500 |
| commit | 05cb3c87ca387be41aceb5ccad978c6848a56f70 (patch) | |
| tree | 2b592d2aa90d0fdb7ea72aa392507e2277b92ba5 /packages/ui/src/components | |
| parent | 270cb0b8b4265ac0965ac8b94a58a3bca86fa558 (diff) | |
| download | opencode-05cb3c87ca387be41aceb5ccad978c6848a56f70.tar.gz opencode-05cb3c87ca387be41aceb5ccad978c6848a56f70.zip | |
chore(app): i18n sync (#17283)
Diffstat (limited to 'packages/ui/src/components')
| -rw-r--r-- | packages/ui/src/components/basic-tool.tsx | 5 | ||||
| -rw-r--r-- | packages/ui/src/components/file-search.tsx | 11 | ||||
| -rw-r--r-- | packages/ui/src/components/line-comment-annotations.tsx | 8 | ||||
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 20 | ||||
| -rw-r--r-- | packages/ui/src/components/tool-error-card.tsx | 16 |
5 files changed, 37 insertions, 23 deletions
diff --git a/packages/ui/src/components/basic-tool.tsx b/packages/ui/src/components/basic-tool.tsx index 3f009f4e0..0b2c1e1ce 100644 --- a/packages/ui/src/components/basic-tool.tsx +++ b/packages/ui/src/components/basic-tool.tsx @@ -1,5 +1,6 @@ import { createEffect, For, Match, on, onCleanup, Show, Switch, type JSX } from "solid-js" import { animate, type AnimationPlaybackControls } from "motion" +import { useI18n } from "../context/i18n" import { createStore } from "solid-js/store" import { Collapsible } from "./collapsible" import type { IconProps } from "./icon" @@ -233,12 +234,14 @@ export function GenericTool(props: { hideDetails?: boolean input?: Record<string, unknown> }) { + const i18n = useI18n() + return ( <BasicTool icon="mcp" status={props.status} trigger={{ - title: `Called \`${props.tool}\``, + title: i18n.t("ui.basicTool.called", { tool: props.tool }), subtitle: label(props.input), args: args(props.input), }} diff --git a/packages/ui/src/components/file-search.tsx b/packages/ui/src/components/file-search.tsx index d83fdb16a..244e9c273 100644 --- a/packages/ui/src/components/file-search.tsx +++ b/packages/ui/src/components/file-search.tsx @@ -1,4 +1,5 @@ import { Portal } from "solid-js/web" +import { useI18n } from "../context/i18n" import { Icon } from "./icon" export function FileSearchBar(props: { @@ -13,6 +14,8 @@ export function FileSearchBar(props: { onPrev: () => void onNext: () => void }) { + const i18n = useI18n() + return ( <Portal> <div @@ -26,7 +29,7 @@ export function FileSearchBar(props: { <Icon name="magnifying-glass" size="small" class="text-text-weak shrink-0" /> <input ref={props.setInput} - placeholder="Find" + placeholder={i18n.t("ui.fileSearch.placeholder")} value={props.query()} class="w-40 bg-transparent outline-none text-14-regular text-text-strong placeholder:text-text-weak" onInput={(e) => props.onInput(e.currentTarget.value)} @@ -40,7 +43,7 @@ export function FileSearchBar(props: { type="button" class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none" disabled={props.count() === 0} - aria-label="Previous match" + aria-label={i18n.t("ui.fileSearch.previousMatch")} onClick={props.onPrev} > <Icon name="chevron-down" size="small" class="rotate-180" /> @@ -49,7 +52,7 @@ export function FileSearchBar(props: { type="button" class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none" disabled={props.count() === 0} - aria-label="Next match" + aria-label={i18n.t("ui.fileSearch.nextMatch")} onClick={props.onNext} > <Icon name="chevron-down" size="small" /> @@ -58,7 +61,7 @@ export function FileSearchBar(props: { <button type="button" class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong" - aria-label="Close search" + aria-label={i18n.t("ui.fileSearch.close")} onClick={props.onClose} > <Icon name="close-small" size="small" /> diff --git a/packages/ui/src/components/line-comment-annotations.tsx b/packages/ui/src/components/line-comment-annotations.tsx index 3505487eb..a4870074d 100644 --- a/packages/ui/src/components/line-comment-annotations.tsx +++ b/packages/ui/src/components/line-comment-annotations.tsx @@ -2,6 +2,7 @@ import { type DiffLineAnnotation, type SelectedLineRange } from "@pierre/diffs" import { createEffect, createMemo, createSignal, onCleanup, Show, type Accessor, type JSX } from "solid-js" import { createStore } from "solid-js/store" import { render as renderSolid } from "solid-js/web" +import { useI18n } from "../context/i18n" import { createHoverCommentUtility } from "../pierre/comment-hover" import { cloneSelectedLineRange, formatSelectedLineLabel, lineInSelectedRange } from "../pierre/selection-bridge" import { LineComment, LineCommentEditor } from "./line-comment" @@ -341,6 +342,7 @@ export function createLineCommentController<T extends LineCommentShape>( export function createLineCommentController<T extends LineCommentShape>( props: LineCommentControllerProps<T> | LineCommentControllerWithSideProps<T>, ) { + const i18n = useI18n() const note = createLineCommentState<string>(props.state) const annotations = @@ -376,7 +378,7 @@ export function createLineCommentController<T extends LineCommentShape>( return note.isOpen(comment.id) || note.isEditing(comment.id) }, comment: comment.comment, - selection: formatSelectedLineLabel(comment.selection), + selection: formatSelectedLineLabel(comment.selection, i18n.t), get actions() { return props.renderCommentActions?.(comment, { edit, remove }) }, @@ -386,7 +388,7 @@ export function createLineCommentController<T extends LineCommentShape>( get value() { return note.draft() }, - selection: formatSelectedLineLabel(comment.selection), + selection: formatSelectedLineLabel(comment.selection, i18n.t), onInput: note.setDraft, onCancel: note.cancelDraft, onSubmit: (value: string) => { @@ -412,7 +414,7 @@ export function createLineCommentController<T extends LineCommentShape>( get value() { return note.draft() }, - selection: formatSelectedLineLabel(range), + selection: formatSelectedLineLabel(range, i18n.t), onInput: note.setDraft, onCancel: note.cancelDraft, onSubmit: (comment) => { diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index a89d97272..b580998b6 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -322,7 +322,7 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo { case "skill": return { icon: "brain", - title: input.name || "skill", + title: input.name || i18n.t("ui.tool.skill"), } default: return { @@ -924,15 +924,12 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp const match = data.store.provider?.all?.find((p) => p.id === providerID) return match?.models?.[modelID]?.name ?? modelID }) + const timefmt = createMemo(() => new Intl.DateTimeFormat(i18n.locale(), { timeStyle: "short" })) const stamp = createMemo(() => { const created = props.message.time?.created if (typeof created !== "number") return "" - const date = new Date(created) - const hours = date.getHours() - const hour12 = hours % 12 || 12 - const minute = String(date.getMinutes()).padStart(2, "0") - return `${hour12}:${minute} ${hours < 12 ? "AM" : "PM"}` + return timefmt().format(created) }) const metaHead = createMemo(() => { @@ -1318,6 +1315,7 @@ PART_MAPPING["compaction"] = function CompactionPartDisplay() { PART_MAPPING["text"] = function TextPartDisplay(props) { const data = useData() const i18n = useI18n() + const numfmt = createMemo(() => new Intl.NumberFormat(i18n.locale())) const part = () => props.part as TextPart const interrupted = createMemo( () => @@ -1343,10 +1341,13 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { : -1 if (!(ms >= 0)) return "" const total = Math.round(ms / 1000) - if (total < 60) return `${total}s` + if (total < 60) return i18n.t("ui.message.duration.seconds", { count: numfmt().format(total) }) const minutes = Math.floor(total / 60) const seconds = total % 60 - return `${minutes}m ${seconds}s` + return i18n.t("ui.message.duration.minutesSeconds", { + minutes: numfmt().format(minutes), + seconds: numfmt().format(seconds), + }) }) const meta = createMemo(() => { @@ -2206,7 +2207,8 @@ ToolRegistry.register({ ToolRegistry.register({ name: "skill", render(props) { - const title = createMemo(() => props.input.name || "skill") + const i18n = useI18n() + const title = createMemo(() => props.input.name || i18n.t("ui.tool.skill")) const running = createMemo(() => props.status === "pending" || props.status === "running") const titleContent = () => <TextShimmer text={title()} active={running()} /> diff --git a/packages/ui/src/components/tool-error-card.tsx b/packages/ui/src/components/tool-error-card.tsx index 0c99924de..038870d38 100644 --- a/packages/ui/src/components/tool-error-card.tsx +++ b/packages/ui/src/components/tool-error-card.tsx @@ -30,7 +30,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) { list: "ui.tool.list", glob: "ui.tool.glob", grep: "ui.tool.grep", - task: "Task", + task: "ui.tool.task", webfetch: "ui.tool.webfetch", websearch: "ui.tool.websearch", codesearch: "ui.tool.codesearch", @@ -54,10 +54,10 @@ export function ToolErrorCard(props: ToolErrorCardProps) { const subtitle = createMemo(() => { if (split.subtitle) return split.subtitle const parts = tail().split(": ") - if (parts.length <= 1) return "Failed" + if (parts.length <= 1) return i18n.t("ui.toolErrorCard.failed") const head = (parts[0] ?? "").trim() - if (!head) return "Failed" - return head[0] ? head[0].toUpperCase() + head.slice(1) : "Failed" + if (!head) return i18n.t("ui.toolErrorCard.failed") + return head[0] ? head[0].toUpperCase() + head.slice(1) : i18n.t("ui.toolErrorCard.failed") }) const body = createMemo(() => { @@ -116,7 +116,11 @@ export function ToolErrorCard(props: ToolErrorCardProps) { <div data-slot="tool-error-card-content"> <Show when={open()}> <div data-slot="tool-error-card-copy"> - <Tooltip value={copied() ? i18n.t("ui.message.copied") : "Copy error"} placement="top" gutter={4}> + <Tooltip + value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.toolErrorCard.copyError")} + placement="top" + gutter={4} + > <IconButton icon={copied() ? "check" : "copy"} size="normal" @@ -126,7 +130,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) { e.stopPropagation() copy() }} - aria-label={copied() ? i18n.t("ui.message.copied") : "Copy error"} + aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.toolErrorCard.copyError")} /> </Tooltip> </div> |
