From c18ca4693c59a604a0b477b4e201746b399a7fde Mon Sep 17 00:00:00 2001 From: m4 Date: Tue, 11 Aug 2026 17:44:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(webui):=20i18n=20main=20chat=20=E2=80=94?= =?UTF-8?q?=20chat=20+=20threadList=20domains?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- src/app/components/ActionGroup.tsx | 43 +- src/app/components/AskUserInterrupt.tsx | 17 +- src/app/components/ChatInterface.tsx | 417 ++++++++++--------- src/app/components/ChatMessage.tsx | 26 +- src/app/components/CompactionSummary.tsx | 39 +- src/app/components/ContextUsageIndicator.tsx | 24 +- src/app/components/SubAgentIndicator.tsx | 4 +- src/app/components/ThreadList.tsx | 169 +++++--- src/app/components/ToolApprovalInterrupt.tsx | 37 +- src/app/components/ToolCallBox.tsx | 16 +- src/app/hooks/useChat.ts | 43 +- src/app/hooks/useToolLabels.ts | 27 ++ src/i18n/messages/en/chat.ts | 252 ++++++++++- src/i18n/messages/en/threadList.ts | 63 ++- src/i18n/messages/zh/chat.ts | 239 ++++++++++- src/i18n/messages/zh/threadList.ts | 61 ++- src/lib/toolLabel.ts | 85 +++- 17 files changed, 1187 insertions(+), 375 deletions(-) create mode 100644 src/app/hooks/useToolLabels.ts diff --git a/src/app/components/ActionGroup.tsx b/src/app/components/ActionGroup.tsx index f3113b4..9af1a4e 100644 --- a/src/app/components/ActionGroup.tsx +++ b/src/app/components/ActionGroup.tsx @@ -29,8 +29,10 @@ import { summarizeActionGroup, } from "@/lib/actionGrouping"; import { mergeApprovalDecision } from "@/lib/approvalDecision"; -import { formatToolLabel, toolArgumentPreview } from "@/lib/toolLabel"; +import { formatToolLabelWith, toolArgumentPreview } from "@/lib/toolLabel"; import { autoApprovesTools, type ReviewMode } from "@/lib/reviewMode"; +import { useTranslations } from "next-intl"; +import { useToolLabels } from "@/app/hooks/useToolLabels"; import { extractStringFromMessageContent, extractSubAgentContent, @@ -114,6 +116,8 @@ export const ActionGroup = React.memo(function ActionGroup({ compactionAnchorId, summarizationEvent, }) { + const t = useTranslations("chat"); + const toolLabels = useToolLabels(); const [open, setOpen] = useState(() => !defaultCollapsed); const [expandedSubAgents, setExpandedSubAgents] = useState< Record @@ -234,7 +238,7 @@ export const ActionGroup = React.memo(function ActionGroup({
- Input + {t("subAgent.input")}
(function ActionGroup({ {steps.length > 0 && (
- Steps + {t("subAgent.steps")}
(function ActionGroup({ {subAgent.output && (
- Output + {t("subAgent.output")}
(function ActionGroup({ stream, subAgentSteps, submittedActionRequestKeys, + t, toggleSubAgent, ui, ] @@ -325,19 +330,21 @@ export const ActionGroup = React.memo(function ActionGroup({ const lastToolCall = summary.lastToolCall; const lastToolLabel = lastToolCall - ? formatToolLabel(lastToolCall.name, lastToolCall.args) - : "Working"; + ? formatToolLabelWith(toolLabels, lastToolCall.name, lastToolCall.args) + : t("actionGroup.working"); const lastToolPreview = lastToolCall ? toolArgumentPreview(lastToolCall.name, lastToolCall.args) : ""; - const actionNoun = summary.total === 1 ? "action" : "actions"; const primaryText = hasPendingApproval - ? "Approval required" + ? t("actionGroup.approvalRequired") : summary.failed > 0 - ? `${summary.failed} of ${summary.total} ${actionNoun} failed` + ? t("actionGroup.failed", { + failed: summary.failed, + total: summary.total, + }) : isStreaming || summary.pending > 0 || summary.interrupted > 0 - ? `Working · ${summary.total} ${actionNoun}` - : `${summary.total} ${actionNoun} completed`; + ? t("actionGroup.workingWithCount", { total: summary.total }) + : t("actionGroup.completed", { total: summary.total }); const headerText = `${primaryText} · ${lastToolLabel}${ lastToolPreview ? ` · ${lastToolPreview}` : "" }`; @@ -345,12 +352,16 @@ export const ActionGroup = React.memo(function ActionGroup({ return (
)} diff --git a/src/app/components/AskUserInterrupt.tsx b/src/app/components/AskUserInterrupt.tsx index b236607..dfdd174 100644 --- a/src/app/components/AskUserInterrupt.tsx +++ b/src/app/components/AskUserInterrupt.tsx @@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { MessageCircleQuestion } from "lucide-react"; import { cn } from "@/lib/utils"; +import { useTranslations } from "next-intl"; interface Choice { value: string; @@ -32,6 +33,8 @@ export function AskUserInterrupt({ onCancel, isLoading, }: AskUserInterruptProps) { + const t = useTranslations("chat"); + const tCommon = useTranslations("common"); // answers[i] = final answer string; picked[i] = selected choice value or // OTHER for multiple_choice (null for free-text questions). const [answers, setAnswers] = useState(() => @@ -62,7 +65,7 @@ export function AskUserInterrupt({ aria-hidden="true" /> - EvoScientist Needs Your Input + {t("askUser.title")}
@@ -114,13 +117,13 @@ export function AskUserInterrupt({ : "border-border bg-background hover:bg-accent" )} > - Other… + {t("askUser.other")} {picked[i] === OTHER && (