From 32a45eb043e2f0023d2b18bf21a448e4a363dbe4 Mon Sep 17 00:00:00 2001 From: Henry Li Date: Tue, 20 Jan 2026 14:06:47 +0800 Subject: [PATCH] feat: implement i18n --- .../app/workspace/chats/[thread_id]/page.tsx | 5 +- frontend/src/app/workspace/chats/page.tsx | 4 +- .../artifacts/artifact-file-detail.tsx | 21 ++--- .../artifacts/artifact-file-list.tsx | 4 +- .../src/components/workspace/copy-button.tsx | 4 +- .../src/components/workspace/input-box.tsx | 21 +++-- .../workspace/messages/message-group.tsx | 46 +++++----- .../components/workspace/recent-chat-list.tsx | 8 +- .../components/workspace/settings/index.ts | 0 .../workspace/settings/settings-dialog.tsx | 0 frontend/src/components/workspace/welcome.tsx | 20 ++--- .../workspace/workspace-container.tsx | 19 ++-- .../components/workspace/workspace-header.tsx | 4 +- .../workspace/workspace-nav-menu.tsx | 4 +- .../workspace/workspace-sidebar.tsx | 26 +++++- frontend/src/core/i18n/hooks.ts | 57 ++++++++++++ frontend/src/core/i18n/index.ts | 23 +++++ frontend/src/core/i18n/locales/en-US.ts | 86 +++++++++++++++++++ frontend/src/core/i18n/locales/index.ts | 3 + frontend/src/core/i18n/locales/types.ts | 83 ++++++++++++++++++ frontend/src/core/i18n/locales/zh-CN.ts | 86 +++++++++++++++++++ 21 files changed, 455 insertions(+), 69 deletions(-) create mode 100644 frontend/src/components/workspace/settings/index.ts create mode 100644 frontend/src/components/workspace/settings/settings-dialog.tsx create mode 100644 frontend/src/core/i18n/hooks.ts create mode 100644 frontend/src/core/i18n/index.ts create mode 100644 frontend/src/core/i18n/locales/en-US.ts create mode 100644 frontend/src/core/i18n/locales/index.ts create mode 100644 frontend/src/core/i18n/locales/types.ts create mode 100644 frontend/src/core/i18n/locales/zh-CN.ts diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 5eef71f..88237ab 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -23,6 +23,7 @@ import { ThreadContext } from "@/components/workspace/messages/context"; import { ThreadTitle } from "@/components/workspace/thread-title"; import { Tooltip } from "@/components/workspace/tooltip"; import { Welcome } from "@/components/workspace/welcome"; +import { useI18n } from "@/core/i18n/hooks"; import { useLocalSettings } from "@/core/settings"; import { type AgentThread } from "@/core/threads"; import { useSubmitThread, useThreadStream } from "@/core/threads/hooks"; @@ -31,6 +32,7 @@ import { uuid } from "@/core/utils/uuid"; import { cn } from "@/lib/utils"; export default function ChatPage() { + const { t } = useI18n(); const router = useRouter(); const [settings, setSettings] = useLocalSettings(); const { setOpen: setSidebarOpen } = useSidebar(); @@ -41,7 +43,6 @@ export default function ChatPage() { setArtifacts, selectedArtifact, } = useArtifacts(); - const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>(); const isNewThread = useMemo( () => threadIdFromPath === "new", @@ -117,7 +118,7 @@ export default function ChatPage() { }} > - Artifacts + {t.common.artifacts} )} diff --git a/frontend/src/app/workspace/chats/page.tsx b/frontend/src/app/workspace/chats/page.tsx index c53f085..d72ee0c 100644 --- a/frontend/src/app/workspace/chats/page.tsx +++ b/frontend/src/app/workspace/chats/page.tsx @@ -10,11 +10,13 @@ import { WorkspaceContainer, WorkspaceHeader, } from "@/components/workspace/workspace-container"; +import { useI18n } from "@/core/i18n/hooks"; import { useThreads } from "@/core/threads/hooks"; import { pathOfThread, titleOfThread } from "@/core/threads/utils"; import { formatTimeAgo } from "@/core/utils/datetime"; export default function ChatsPage() { + const { t } = useI18n(); const { data: threads } = useThreads(); const [search, setSearch] = useState(""); const filteredThreads = useMemo(() => { @@ -31,7 +33,7 @@ export default function ChatsPage() { setSearch(e.target.value)} diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index fea4377..16716a0 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -27,6 +27,7 @@ import { Textarea } from "@/components/ui/textarea"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { useArtifactContent } from "@/core/artifacts/hooks"; import { urlOfArtifact } from "@/core/artifacts/utils"; +import { useI18n } from "@/core/i18n/hooks"; import { checkCodeFile, getFileName } from "@/core/utils/files"; import { cn } from "@/lib/utils"; @@ -41,6 +42,7 @@ export function ArtifactFileDetail({ filepath: string; threadId: string; }) { + const { t } = useI18n(); const { artifacts, setOpen, select } = useArtifacts(); const isWriteFile = useMemo(() => { return filepathFromProps.startsWith("write-file:"); @@ -124,26 +126,26 @@ export function ArtifactFileDetail({ )} {isCodeFile && ( { try { await navigator.clipboard.writeText(content ?? ""); - toast.success("Copied to clipboard"); + toast.success(t.clipboard.copiedToClipboard); } catch (error) { toast.error("Failed to copy to clipboard"); console.error(error); } }} - tooltip="Copy content to clipboard" + tooltip={t.clipboard.copyToClipboard} /> )} {!isWriteFile && ( @@ -153,17 +155,16 @@ export function ArtifactFileDetail({ > console.log("Download")} - tooltip="Download file" + label={t.common.download} + tooltip={t.common.download} /> )} setOpen(false)} - tooltip="Close" + tooltip={t.common.close} /> diff --git a/frontend/src/components/workspace/artifacts/artifact-file-list.tsx b/frontend/src/components/workspace/artifacts/artifact-file-list.tsx index 8f7f1f8..072c7db 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-list.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-list.tsx @@ -10,6 +10,7 @@ import { CardTitle, } from "@/components/ui/card"; import { urlOfArtifact } from "@/core/artifacts/utils"; +import { useI18n } from "@/core/i18n/hooks"; import { getFileExtensionDisplayName, getFileName } from "@/core/utils/files"; import { cn } from "@/lib/utils"; @@ -24,6 +25,7 @@ export function ArtifactFileList({ files: string[]; threadId: string; }) { + const { t } = useI18n(); const { select: selectArtifact, setOpen } = useArtifacts(); const handleClick = useCallback( (filepath: string) => { @@ -57,7 +59,7 @@ export function ArtifactFileList({ > diff --git a/frontend/src/components/workspace/copy-button.tsx b/frontend/src/components/workspace/copy-button.tsx index b710939..13d331b 100644 --- a/frontend/src/components/workspace/copy-button.tsx +++ b/frontend/src/components/workspace/copy-button.tsx @@ -2,6 +2,7 @@ import { CheckIcon, CopyIcon } from "lucide-react"; import { useCallback, useState, type ComponentProps } from "react"; import { Button } from "@/components/ui/button"; +import { useI18n } from "@/core/i18n/hooks"; import { Tooltip } from "./tooltip"; @@ -11,6 +12,7 @@ export function CopyButton({ }: ComponentProps & { clipboardData: string; }) { + const { t } = useI18n(); const [copied, setCopied] = useState(false); const handleCopy = useCallback(() => { void navigator.clipboard.writeText(clipboardData); @@ -18,7 +20,7 @@ export function CopyButton({ setTimeout(() => setCopied(false), 2000); }, [clipboardData]); return ( - +