summaryrefslogtreecommitdiffhomepage
path: root/packages/ui
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-13 06:48:38 -0500
committerGitHub <[email protected]>2026-03-13 06:48:38 -0500
commit05cb3c87ca387be41aceb5ccad978c6848a56f70 (patch)
tree2b592d2aa90d0fdb7ea72aa392507e2277b92ba5 /packages/ui
parent270cb0b8b4265ac0965ac8b94a58a3bca86fa558 (diff)
downloadopencode-05cb3c87ca387be41aceb5ccad978c6848a56f70.tar.gz
opencode-05cb3c87ca387be41aceb5ccad978c6848a56f70.zip
chore(app): i18n sync (#17283)
Diffstat (limited to 'packages/ui')
-rw-r--r--packages/ui/src/components/basic-tool.tsx5
-rw-r--r--packages/ui/src/components/file-search.tsx11
-rw-r--r--packages/ui/src/components/line-comment-annotations.tsx8
-rw-r--r--packages/ui/src/components/message-part.tsx20
-rw-r--r--packages/ui/src/components/tool-error-card.tsx16
-rw-r--r--packages/ui/src/i18n/ar.ts12
-rw-r--r--packages/ui/src/i18n/br.ts12
-rw-r--r--packages/ui/src/i18n/bs.ts12
-rw-r--r--packages/ui/src/i18n/da.ts12
-rw-r--r--packages/ui/src/i18n/de.ts12
-rw-r--r--packages/ui/src/i18n/en.ts13
-rw-r--r--packages/ui/src/i18n/es.ts12
-rw-r--r--packages/ui/src/i18n/fr.ts12
-rw-r--r--packages/ui/src/i18n/ja.ts12
-rw-r--r--packages/ui/src/i18n/ko.ts12
-rw-r--r--packages/ui/src/i18n/no.ts12
-rw-r--r--packages/ui/src/i18n/pl.ts12
-rw-r--r--packages/ui/src/i18n/ru.ts12
-rw-r--r--packages/ui/src/i18n/th.ts12
-rw-r--r--packages/ui/src/i18n/tr.ts12
-rw-r--r--packages/ui/src/i18n/zh.ts12
-rw-r--r--packages/ui/src/i18n/zht.ts12
-rw-r--r--packages/ui/src/pierre/selection-bridge.ts9
23 files changed, 248 insertions, 26 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>
diff --git a/packages/ui/src/i18n/ar.ts b/packages/ui/src/i18n/ar.ts
index 475496df0..f3e321ce5 100644
--- a/packages/ui/src/i18n/ar.ts
+++ b/packages/ui/src/i18n/ar.ts
@@ -145,4 +145,16 @@ export const dict = {
"ui.question.multiHint": "حدد كل ما ينطبق",
"ui.question.singleHint": "حدد إجابة واحدة",
"ui.question.custom.placeholder": "اكتب إجابتك...",
+
+ "ui.fileSearch.placeholder": "بحث",
+ "ui.fileSearch.previousMatch": "المطابقة السابقة",
+ "ui.fileSearch.nextMatch": "المطابقة التالية",
+ "ui.fileSearch.close": "إغلاق البحث",
+ "ui.tool.task": "مهمة",
+ "ui.tool.skill": "مهارة",
+ "ui.basicTool.called": "تم استدعاء `{{tool}}`",
+ "ui.toolErrorCard.failed": "فشل",
+ "ui.toolErrorCard.copyError": "نسخ الخطأ",
+ "ui.message.duration.seconds": "{{count}}ث",
+ "ui.message.duration.minutesSeconds": "{{minutes}}د {{seconds}}ث",
}
diff --git a/packages/ui/src/i18n/br.ts b/packages/ui/src/i18n/br.ts
index 8e68d0fef..d0ba67871 100644
--- a/packages/ui/src/i18n/br.ts
+++ b/packages/ui/src/i18n/br.ts
@@ -145,4 +145,16 @@ export const dict = {
"ui.question.multiHint": "Selecione todas que se aplicam",
"ui.question.singleHint": "Selecione uma resposta",
"ui.question.custom.placeholder": "Digite sua resposta...",
+
+ "ui.fileSearch.placeholder": "Localizar",
+ "ui.fileSearch.previousMatch": "Ocorrência anterior",
+ "ui.fileSearch.nextMatch": "Próxima ocorrência",
+ "ui.fileSearch.close": "Fechar busca",
+ "ui.tool.task": "Tarefa",
+ "ui.tool.skill": "Habilidade",
+ "ui.basicTool.called": "Chamou `{{tool}}`",
+ "ui.toolErrorCard.failed": "Falhou",
+ "ui.toolErrorCard.copyError": "Copiar erro",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
}
diff --git a/packages/ui/src/i18n/bs.ts b/packages/ui/src/i18n/bs.ts
index 60fa9c789..3707c7139 100644
--- a/packages/ui/src/i18n/bs.ts
+++ b/packages/ui/src/i18n/bs.ts
@@ -149,4 +149,16 @@ export const dict = {
"ui.question.multiHint": "Odaberi sve što važi",
"ui.question.singleHint": "Odaberi jedan odgovor",
"ui.question.custom.placeholder": "Unesi svoj odgovor...",
+
+ "ui.fileSearch.placeholder": "Pronađi",
+ "ui.fileSearch.previousMatch": "Prethodno",
+ "ui.fileSearch.nextMatch": "Sljedeće",
+ "ui.fileSearch.close": "Zatvori pretragu",
+ "ui.tool.task": "Zadatak",
+ "ui.tool.skill": "Vještina",
+ "ui.basicTool.called": "Pozvan `{{tool}}`",
+ "ui.toolErrorCard.failed": "Neuspješno",
+ "ui.toolErrorCard.copyError": "Kopiraj grešku",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/ui/src/i18n/da.ts b/packages/ui/src/i18n/da.ts
index 74e06b99d..60e3fb30f 100644
--- a/packages/ui/src/i18n/da.ts
+++ b/packages/ui/src/i18n/da.ts
@@ -144,4 +144,16 @@ export const dict = {
"ui.question.multiHint": "Vælg alle der gælder",
"ui.question.singleHint": "Vælg ét svar",
"ui.question.custom.placeholder": "Skriv dit svar...",
+
+ "ui.fileSearch.placeholder": "Find",
+ "ui.fileSearch.previousMatch": "Forrige match",
+ "ui.fileSearch.nextMatch": "Næste match",
+ "ui.fileSearch.close": "Luk søgning",
+ "ui.tool.task": "Opgave",
+ "ui.tool.skill": "Færdighed",
+ "ui.basicTool.called": "Kaldte `{{tool}}`",
+ "ui.toolErrorCard.failed": "Fejlede",
+ "ui.toolErrorCard.copyError": "Kopier fejl",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
}
diff --git a/packages/ui/src/i18n/de.ts b/packages/ui/src/i18n/de.ts
index 296417597..7fab33f56 100644
--- a/packages/ui/src/i18n/de.ts
+++ b/packages/ui/src/i18n/de.ts
@@ -150,4 +150,16 @@ export const dict = {
"ui.question.multiHint": "Alle zutreffenden auswählen",
"ui.question.singleHint": "Eine Antwort auswählen",
"ui.question.custom.placeholder": "Geben Sie Ihre Antwort ein...",
+
+ "ui.fileSearch.placeholder": "Suchen",
+ "ui.fileSearch.previousMatch": "Vorheriges Ergebnis",
+ "ui.fileSearch.nextMatch": "Nächstes Ergebnis",
+ "ui.fileSearch.close": "Suche schließen",
+ "ui.tool.task": "Aufgabe",
+ "ui.tool.skill": "Fähigkeit",
+ "ui.basicTool.called": "`{{tool}}` aufgerufen",
+ "ui.toolErrorCard.failed": "Fehlgeschlagen",
+ "ui.toolErrorCard.copyError": "Fehler kopieren",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/ui/src/i18n/en.ts b/packages/ui/src/i18n/en.ts
index 2b41bc03d..197dca445 100644
--- a/packages/ui/src/i18n/en.ts
+++ b/packages/ui/src/i18n/en.ts
@@ -80,6 +80,11 @@ export const dict: Record<string, string> = {
"ui.list.emptyWithFilter.prefix": "No results for",
"ui.list.emptyWithFilter.suffix": "",
+ "ui.fileSearch.placeholder": "Find",
+ "ui.fileSearch.previousMatch": "Previous match",
+ "ui.fileSearch.nextMatch": "Next match",
+ "ui.fileSearch.close": "Close search",
+
"ui.messageNav.newMessage": "New message",
"ui.textField.copyToClipboard": "Copy to clipboard",
@@ -94,6 +99,7 @@ export const dict: Record<string, string> = {
"ui.tool.list": "List",
"ui.tool.glob": "Glob",
"ui.tool.grep": "Grep",
+ "ui.tool.task": "Task",
"ui.tool.webfetch": "Webfetch",
"ui.tool.websearch": "Web Search",
"ui.tool.codesearch": "Code Search",
@@ -104,6 +110,11 @@ export const dict: Record<string, string> = {
"ui.tool.questions": "Questions",
"ui.tool.agent": "{{type}} Agent",
"ui.tool.agent.default": "Agent",
+ "ui.tool.skill": "Skill",
+
+ "ui.basicTool.called": "Called `{{tool}}`",
+ "ui.toolErrorCard.failed": "Failed",
+ "ui.toolErrorCard.copyError": "Copy error",
"ui.common.file.one": "file",
"ui.common.file.other": "files",
@@ -131,6 +142,8 @@ export const dict: Record<string, string> = {
"ui.message.revertMessage": "Revert message",
"ui.message.copyResponse": "Copy response",
"ui.message.copied": "Copied",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
"ui.message.interrupted": "Interrupted",
"ui.message.queued": "Queued",
"ui.message.attachment.alt": "attachment",
diff --git a/packages/ui/src/i18n/es.ts b/packages/ui/src/i18n/es.ts
index 6496c05db..77781e460 100644
--- a/packages/ui/src/i18n/es.ts
+++ b/packages/ui/src/i18n/es.ts
@@ -145,4 +145,16 @@ export const dict = {
"ui.question.multiHint": "Selecciona todas las que correspondan",
"ui.question.singleHint": "Selecciona una respuesta",
"ui.question.custom.placeholder": "Escribe tu respuesta...",
+
+ "ui.fileSearch.placeholder": "Buscar",
+ "ui.fileSearch.previousMatch": "Anterior",
+ "ui.fileSearch.nextMatch": "Siguiente",
+ "ui.fileSearch.close": "Cerrar búsqueda",
+ "ui.tool.task": "Tarea",
+ "ui.tool.skill": "Habilidad",
+ "ui.basicTool.called": "Llamado `{{tool}}`",
+ "ui.toolErrorCard.failed": "Falló",
+ "ui.toolErrorCard.copyError": "Copiar error",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
}
diff --git a/packages/ui/src/i18n/fr.ts b/packages/ui/src/i18n/fr.ts
index e278d0693..78a8ecf75 100644
--- a/packages/ui/src/i18n/fr.ts
+++ b/packages/ui/src/i18n/fr.ts
@@ -145,4 +145,16 @@ export const dict = {
"ui.question.multiHint": "Sélectionnez tout ce qui s'applique",
"ui.question.singleHint": "Sélectionnez une réponse",
"ui.question.custom.placeholder": "Tapez votre réponse...",
+
+ "ui.fileSearch.placeholder": "Rechercher",
+ "ui.fileSearch.previousMatch": "Précédent",
+ "ui.fileSearch.nextMatch": "Suivant",
+ "ui.fileSearch.close": "Fermer la recherche",
+ "ui.tool.task": "Tâche",
+ "ui.tool.skill": "Compétence",
+ "ui.basicTool.called": "Appelé `{{tool}}`",
+ "ui.toolErrorCard.failed": "Échoué",
+ "ui.toolErrorCard.copyError": "Copier l'erreur",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
}
diff --git a/packages/ui/src/i18n/ja.ts b/packages/ui/src/i18n/ja.ts
index b133a3282..59cf33ad2 100644
--- a/packages/ui/src/i18n/ja.ts
+++ b/packages/ui/src/i18n/ja.ts
@@ -144,4 +144,16 @@ export const dict = {
"ui.question.multiHint": "該当するものをすべて選択",
"ui.question.singleHint": "1 つ選択",
"ui.question.custom.placeholder": "回答を入力...",
+
+ "ui.fileSearch.placeholder": "検索",
+ "ui.fileSearch.previousMatch": "前の一致",
+ "ui.fileSearch.nextMatch": "次の一致",
+ "ui.fileSearch.close": "検索を閉じる",
+ "ui.tool.task": "タスク",
+ "ui.tool.skill": "スキル",
+ "ui.basicTool.called": "`{{tool}}` を呼び出しました",
+ "ui.toolErrorCard.failed": "失敗",
+ "ui.toolErrorCard.copyError": "エラーをコピー",
+ "ui.message.duration.seconds": "{{count}}秒",
+ "ui.message.duration.minutesSeconds": "{{minutes}}分 {{seconds}}秒",
}
diff --git a/packages/ui/src/i18n/ko.ts b/packages/ui/src/i18n/ko.ts
index 3e7af7140..1fed9e860 100644
--- a/packages/ui/src/i18n/ko.ts
+++ b/packages/ui/src/i18n/ko.ts
@@ -145,4 +145,16 @@ export const dict = {
"ui.question.multiHint": "해당하는 항목 모두 선택",
"ui.question.singleHint": "하나의 답변을 선택",
"ui.question.custom.placeholder": "답변 입력...",
+
+ "ui.fileSearch.placeholder": "찾기",
+ "ui.fileSearch.previousMatch": "이전 항목",
+ "ui.fileSearch.nextMatch": "다음 항목",
+ "ui.fileSearch.close": "검색 닫기",
+ "ui.tool.task": "작업",
+ "ui.tool.skill": "스킬",
+ "ui.basicTool.called": "`{{tool}}` 호출됨",
+ "ui.toolErrorCard.failed": "실패",
+ "ui.toolErrorCard.copyError": "오류 복사",
+ "ui.message.duration.seconds": "{{count}}초",
+ "ui.message.duration.minutesSeconds": "{{minutes}}분 {{seconds}}초",
}
diff --git a/packages/ui/src/i18n/no.ts b/packages/ui/src/i18n/no.ts
index 0c9fc82e5..f3a808658 100644
--- a/packages/ui/src/i18n/no.ts
+++ b/packages/ui/src/i18n/no.ts
@@ -148,4 +148,16 @@ export const dict: Record<Keys, string> = {
"ui.question.multiHint": "Velg alle som gjelder",
"ui.question.singleHint": "Velg ett svar",
"ui.question.custom.placeholder": "Skriv svaret ditt...",
+
+ "ui.fileSearch.placeholder": "Finn",
+ "ui.fileSearch.previousMatch": "Forrige treff",
+ "ui.fileSearch.nextMatch": "Neste treff",
+ "ui.fileSearch.close": "Lukk søk",
+ "ui.tool.task": "Oppgave",
+ "ui.tool.skill": "Ferdighet",
+ "ui.basicTool.called": "Kalte `{{tool}}`",
+ "ui.toolErrorCard.failed": "Mislyktes",
+ "ui.toolErrorCard.copyError": "Kopier feil",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
}
diff --git a/packages/ui/src/i18n/pl.ts b/packages/ui/src/i18n/pl.ts
index e63622a83..ea05e2f0e 100644
--- a/packages/ui/src/i18n/pl.ts
+++ b/packages/ui/src/i18n/pl.ts
@@ -144,4 +144,16 @@ export const dict = {
"ui.question.multiHint": "Zaznacz wszystkie pasujące",
"ui.question.singleHint": "Wybierz jedną odpowiedź",
"ui.question.custom.placeholder": "Wpisz swoją odpowiedź...",
+
+ "ui.fileSearch.placeholder": "Szukaj",
+ "ui.fileSearch.previousMatch": "Poprzednie",
+ "ui.fileSearch.nextMatch": "Następne",
+ "ui.fileSearch.close": "Zamknij wyszukiwanie",
+ "ui.tool.task": "Zadanie",
+ "ui.tool.skill": "Umiejętność",
+ "ui.basicTool.called": "Wywołano `{{tool}}`",
+ "ui.toolErrorCard.failed": "Błąd",
+ "ui.toolErrorCard.copyError": "Kopiuj błąd",
+ "ui.message.duration.seconds": "{{count}}s",
+ "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s",
}
diff --git a/packages/ui/src/i18n/ru.ts b/packages/ui/src/i18n/ru.ts
index b8c7a4a1f..9ae49bd9e 100644
--- a/packages/ui/src/i18n/ru.ts
+++ b/packages/ui/src/i18n/ru.ts
@@ -144,4 +144,16 @@ export const dict = {
"ui.question.multiHint": "Выберите все подходящие",
"ui.question.singleHint": "Выберите один ответ",
"ui.question.custom.placeholder": "Введите ваш ответ...",
+
+ "ui.fileSearch.placeholder": "Найти",
+ "ui.fileSearch.previousMatch": "Предыдущее",
+ "ui.fileSearch.nextMatch": "Следующее",
+ "ui.fileSearch.close": "Закрыть поиск",
+ "ui.tool.task": "Задача",
+ "ui.tool.skill": "Навык",
+ "ui.basicTool.called": "Вызван `{{tool}}`",
+ "ui.toolErrorCard.failed": "Ошибка",
+ "ui.toolErrorCard.copyError": "Скопировать ошибку",
+ "ui.message.duration.seconds": "{{count}}с",
+ "ui.message.duration.minutesSeconds": "{{minutes}}м {{seconds}}с",
}
diff --git a/packages/ui/src/i18n/th.ts b/packages/ui/src/i18n/th.ts
index 88236b644..8719596fe 100644
--- a/packages/ui/src/i18n/th.ts
+++ b/packages/ui/src/i18n/th.ts
@@ -146,4 +146,16 @@ export const dict = {
"ui.question.multiHint": "เลือกทั้งหมดที่ใช้",
"ui.question.singleHint": "เลือกหนึ่งคำตอบ",
"ui.question.custom.placeholder": "พิมพ์คำตอบของคุณ...",
+
+ "ui.fileSearch.placeholder": "ค้นหา",
+ "ui.fileSearch.previousMatch": "ก่อนหน้า",
+ "ui.fileSearch.nextMatch": "ถัดไป",
+ "ui.fileSearch.close": "ปิดการค้นหา",
+ "ui.tool.task": "งาน",
+ "ui.tool.skill": "ทักษะ",
+ "ui.basicTool.called": "เรียกใช้ `{{tool}}`",
+ "ui.toolErrorCard.failed": "ล้มเหลว",
+ "ui.toolErrorCard.copyError": "คัดลอกข้อผิดพลาด",
+ "ui.message.duration.seconds": "{{count}}วิ",
+ "ui.message.duration.minutesSeconds": "{{minutes}}นาที {{seconds}}วิ",
}
diff --git a/packages/ui/src/i18n/tr.ts b/packages/ui/src/i18n/tr.ts
index ff9de61b5..a17ba331e 100644
--- a/packages/ui/src/i18n/tr.ts
+++ b/packages/ui/src/i18n/tr.ts
@@ -151,4 +151,16 @@ export const dict = {
"ui.question.multiHint": "Geçerli tüm cevapları seçin",
"ui.question.singleHint": "Bir cevap seçin",
"ui.question.custom.placeholder": "Cevabınızı yazın...",
+
+ "ui.fileSearch.placeholder": "Bul",
+ "ui.fileSearch.previousMatch": "Önceki",
+ "ui.fileSearch.nextMatch": "Sonraki",
+ "ui.fileSearch.close": "Aramayı kapat",
+ "ui.tool.task": "Görev",
+ "ui.tool.skill": "Yetenek",
+ "ui.basicTool.called": "`{{tool}}` çağrıldı",
+ "ui.toolErrorCard.failed": "Başarısız",
+ "ui.toolErrorCard.copyError": "Hatayı kopyala",
+ "ui.message.duration.seconds": "{{count}}sn",
+ "ui.message.duration.minutesSeconds": "{{minutes}}dk {{seconds}}sn",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/ui/src/i18n/zh.ts b/packages/ui/src/i18n/zh.ts
index aaf17eee8..788cb3cfc 100644
--- a/packages/ui/src/i18n/zh.ts
+++ b/packages/ui/src/i18n/zh.ts
@@ -149,4 +149,16 @@ export const dict = {
"ui.question.multiHint": "可多选",
"ui.question.singleHint": "选择一个答案",
"ui.question.custom.placeholder": "输入你的答案...",
+
+ "ui.fileSearch.placeholder": "查找",
+ "ui.fileSearch.previousMatch": "上一个",
+ "ui.fileSearch.nextMatch": "下一个",
+ "ui.fileSearch.close": "关闭搜索",
+ "ui.tool.task": "任务",
+ "ui.tool.skill": "技能",
+ "ui.basicTool.called": "调用了 `{{tool}}`",
+ "ui.toolErrorCard.failed": "失败",
+ "ui.toolErrorCard.copyError": "复制错误",
+ "ui.message.duration.seconds": "{{count}}秒",
+ "ui.message.duration.minutesSeconds": "{{minutes}}分 {{seconds}}秒",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/ui/src/i18n/zht.ts b/packages/ui/src/i18n/zht.ts
index b8a9fa799..b4adeab79 100644
--- a/packages/ui/src/i18n/zht.ts
+++ b/packages/ui/src/i18n/zht.ts
@@ -149,4 +149,16 @@ export const dict = {
"ui.question.multiHint": "可多選",
"ui.question.singleHint": "選擇一個答案",
"ui.question.custom.placeholder": "輸入你的答案...",
+
+ "ui.fileSearch.placeholder": "搜尋",
+ "ui.fileSearch.previousMatch": "上一個",
+ "ui.fileSearch.nextMatch": "下一個",
+ "ui.fileSearch.close": "關閉搜尋",
+ "ui.tool.task": "任務",
+ "ui.tool.skill": "技能",
+ "ui.basicTool.called": "呼叫了 `{{tool}}`",
+ "ui.toolErrorCard.failed": "失敗",
+ "ui.toolErrorCard.copyError": "複製錯誤",
+ "ui.message.duration.seconds": "{{count}}秒",
+ "ui.message.duration.minutesSeconds": "{{minutes}}分 {{seconds}}秒",
} satisfies Partial<Record<Keys, string>>
diff --git a/packages/ui/src/pierre/selection-bridge.ts b/packages/ui/src/pierre/selection-bridge.ts
index d493ead3d..4055ec087 100644
--- a/packages/ui/src/pierre/selection-bridge.ts
+++ b/packages/ui/src/pierre/selection-bridge.ts
@@ -1,14 +1,17 @@
import { type SelectedLineRange } from "@pierre/diffs"
+type SelectionKey = "ui.sessionReview.selection.line" | "ui.sessionReview.selection.lines"
+type SelectionVars = Record<string, string | number>
+
type PointerMode = "none" | "text" | "numbers"
type Side = SelectedLineRange["side"]
type LineSpan = Pick<SelectedLineRange, "start" | "end">
-export function formatSelectedLineLabel(range: LineSpan) {
+export function formatSelectedLineLabel(range: LineSpan, t: (key: SelectionKey, params: SelectionVars) => string) {
const start = Math.min(range.start, range.end)
const end = Math.max(range.start, range.end)
- if (start === end) return `line ${start}`
- return `lines ${start}-${end}`
+ if (start === end) return t("ui.sessionReview.selection.line", { line: start })
+ return t("ui.sessionReview.selection.lines", { start, end })
}
export function previewSelectedLines(source: string, range: LineSpan) {