+
{user ? (
- <>
-
-
- {user.username}
-
-
-
- 退出登录
-
- >
+
+
+
+
+
+ {userInitial}
+
+
+ {user.username}
+
+
+
+
+
+
+
+
+
+
+ {userInitial}
+
+
+
+
+ {pickAppText(locale, `${user.username},你好!`, `Hi, ${user.username}`)}
+
+
+ {pickAppText(locale, '当前已登录到你的工作区实例。', 'You are currently signed in to your workspace instance.')}
+
+
+
+
+
+
+
+ {pickAppText(locale, '退出登录', 'Sign Out')}
+
+
+
+
+
) : !isAuthLoading ? (
AUTH_ITEMS.map((item) => {
const isActive = pathname.startsWith(item.href);
@@ -154,7 +225,7 @@ const Header = () => {
}`}
>
- {item.name}
+ {authLabel(item.key)}
);
})
diff --git a/app-instance/frontend/components/LanguageSwitcher.tsx b/app-instance/frontend/components/LanguageSwitcher.tsx
new file mode 100644
index 0000000..6e9605b
--- /dev/null
+++ b/app-instance/frontend/components/LanguageSwitcher.tsx
@@ -0,0 +1,41 @@
+'use client';
+
+import { Languages } from 'lucide-react';
+
+import { useAppI18n } from '@/lib/i18n/provider';
+import { cn } from '@/lib/utils';
+
+const OPTIONS = [
+ { value: 'zh-CN', label: 'ZH' },
+ { value: 'en-US', label: 'EN' },
+] as const;
+
+export function LanguageSwitcher({ className }: { className?: string }) {
+ const { locale, setLocale } = useAppI18n();
+
+ return (
+
+
+ {OPTIONS.map((option) => (
+ setLocale(option.value)}
+ className={cn(
+ 'rounded px-2 py-1 text-xs font-medium transition-colors',
+ locale === option.value
+ ? 'bg-background text-foreground shadow-sm'
+ : 'text-muted-foreground hover:text-foreground'
+ )}
+ >
+ {option.label}
+
+ ))}
+
+ );
+}
diff --git a/app-instance/frontend/components/chat-workbench/AgentTeamBlock.tsx b/app-instance/frontend/components/chat-workbench/AgentTeamBlock.tsx
index fcc58ec..bfe5136 100644
--- a/app-instance/frontend/components/chat-workbench/AgentTeamBlock.tsx
+++ b/app-instance/frontend/components/chat-workbench/AgentTeamBlock.tsx
@@ -6,6 +6,9 @@ import { CheckCircle2, Loader2, Sparkles, Square } from 'lucide-react';
import type { ProcessArtifact, ProcessEvent, ProcessRun } from '@/types';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
+import { appArtifactPreview, appFeedRoleLabel, appStatusLabel } from '@/lib/i18n/common';
+import { pickAppText } from '@/lib/i18n/core';
+import { useAppI18n } from '@/lib/i18n/provider';
import { cn } from '@/lib/utils';
type RunCardPhase = 'live' | 'exiting' | 'collapsed';
@@ -51,15 +54,6 @@ function accentFor(index: number) {
return AGENT_ACCENTS[index % AGENT_ACCENTS.length];
}
-function statusLabel(status: ProcessRun['status']) {
- if (status === 'done') return '已完成';
- if (status === 'error') return '失败';
- if (status === 'cancelled') return '已取消';
- if (status === 'waiting') return '等待中';
- if (status === 'queued') return '排队中';
- return '进行中';
-}
-
function statusTone(status: ProcessRun['status']) {
if (status === 'done') return 'border-emerald-500/20 bg-emerald-500/10 text-emerald-300';
if (status === 'error') return 'border-rose-500/20 bg-rose-500/10 text-rose-300';
@@ -69,13 +63,6 @@ function statusTone(status: ProcessRun['status']) {
return 'border-sky-500/20 bg-sky-500/10 text-sky-300';
}
-function roleLabel(role: AgentFeedItem['role']) {
- if (role === 'user') return '主 agent';
- if (role === 'tool') return '工具输出';
- if (role === 'system') return '状态';
- return '子 agent';
-}
-
function feedTone(role: AgentFeedItem['role']) {
if (role === 'user') {
return 'ml-6 border-border/70 bg-muted/60 text-foreground';
@@ -89,22 +76,6 @@ function feedTone(role: AgentFeedItem['role']) {
return 'mr-6 border-border/70 bg-background/80 text-foreground';
}
-function artifactPreview(artifact: ProcessArtifact): string {
- if (artifact.artifact_type === 'link' && artifact.url) {
- return `${artifact.title}\n${artifact.url}`;
- }
- if ((artifact.artifact_type === 'text' || artifact.artifact_type === 'markdown') && artifact.content) {
- return `${artifact.title}\n${artifact.content}`;
- }
- if (artifact.artifact_type === 'json') {
- return `${artifact.title}\n已生成结构化结果`;
- }
- if (artifact.file_id) {
- return `${artifact.title}\n已生成文件输出`;
- }
- return artifact.title;
-}
-
function delegatedTask(run: ProcessRun): string | null {
const value = run.metadata?.delegated_task;
return typeof value === 'string' && value.trim() ? value.trim() : null;
@@ -114,6 +85,7 @@ function buildFeed(
run: ProcessRun,
events: ProcessEvent[],
artifacts: ProcessArtifact[],
+ locale: 'zh-CN' | 'en-US',
): AgentFeedItem[] {
const items: AgentFeedItem[] = [];
let hasLeadBubble = false;
@@ -160,7 +132,7 @@ function buildFeed(
key: artifact.artifact_id,
created_at: artifact.created_at,
role: artifact.actor_type === 'mcp' ? 'tool' : 'assistant',
- text: artifactPreview(artifact),
+ text: appArtifactPreview(artifact, locale),
});
}
@@ -181,12 +153,12 @@ function buildFeed(
.slice(-8);
}
-function runSummary(run: ProcessRun, feed: AgentFeedItem[]): string {
+function runSummary(run: ProcessRun, feed: AgentFeedItem[], locale: 'zh-CN' | 'en-US'): string {
if (run.summary?.trim()) {
return run.summary.trim();
}
const latestAssistant = [...feed].reverse().find((item) => item.role === 'assistant' || item.role === 'tool');
- return latestAssistant?.text || '已完成子任务处理';
+ return latestAssistant?.text || pickAppText(locale, '已完成子任务处理', 'Subtask processing completed');
}
function useRunCardPhases(runs: ProcessRun[]) {
@@ -256,7 +228,13 @@ function useRunCardPhases(runs: ProcessRun[]) {
return phases;
}
-function AgentBubble({ item }: { item: AgentFeedItem }) {
+function AgentBubble({
+ item,
+ locale,
+}: {
+ item: AgentFeedItem;
+ locale: 'zh-CN' | 'en-US';
+}) {
return (
- {roleLabel(item.role)}
+ {appFeedRoleLabel(item.role, locale)}
{item.text}
@@ -281,6 +259,7 @@ function LiveAgentCard({
phase,
accentIndex,
onSelect,
+ locale,
}: {
run: ProcessRun;
feed: AgentFeedItem[];
@@ -289,6 +268,7 @@ function LiveAgentCard({
phase: RunCardPhase;
accentIndex: number;
onSelect: () => void;
+ locale: 'zh-CN' | 'en-US';
}) {
const showSpinner = !TERMINAL_STATUSES.has(run.status);
const accent = accentFor(accentIndex);
@@ -308,13 +288,13 @@ function LiveAgentCard({
- Sub-Agent
+ {pickAppText(locale, '子 Agent', 'Sub-agent')}
{run.actor_name}
{run.title}
- {statusLabel(run.status)}
+ {appStatusLabel(run.status, locale)}