diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..b0db98e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+ "i18n-ally.localesPaths": [
+ "messages",
+ "src/i18n"
+ ]
+}
\ No newline at end of file
diff --git a/messages/en.json b/messages/en.json
new file mode 100644
index 0000000..483f0db
--- /dev/null
+++ b/messages/en.json
@@ -0,0 +1,130 @@
+{
+ "AppearanceSettings": {
+ "title": "Choose a theme",
+ "items": {
+ "System": "System",
+ "Light": "Light",
+ "Dark": "Dark"
+ }
+ },
+ "AvatarButton": {
+ "Settings": "Settings",
+ "LogIn": "LogIn",
+ "LogOut": "LogOut"
+ },
+ "Banner": {
+ "Text": "Star this project if you like it."
+ },
+ "BackButton": "Back",
+ "Bot": {
+ "title": "Ask Bot",
+ "description": "Powered by Vercel Ai SDK",
+ "placeholder": "Bot will automatically get your current code"
+ },
+ "BotVisibilityToggle": {
+ "open": "Open Bot",
+ "close": "Close Bot"
+ },
+ "DetailsPage": {
+ "BackButton": "All Submissions",
+ "Time": "Submitted on",
+ "Input": "Input",
+ "ExpectedOutput": "Expected Output",
+ "ActualOutput": "Acutal Output",
+ "Code": "Code"
+ },
+ "Difficulty": {
+ "EASY": "EASY",
+ "MEDIUM": "MEDIUM",
+ "HARD": "HARD"
+ },
+ "LanguageSettings": {
+ "en": {
+ "flag": "🇺🇸",
+ "name": "English"
+ },
+ "zh": {
+ "flag": "🇨🇳",
+ "name": "Chinese"
+ }
+ },
+ "PlaygroundHeader": {
+ "RunCodeButton": {
+ "TooltipTrigger": {
+ "loading": "Running...",
+ "ready": "Run"
+ },
+ "TooltipContent": "Run Code"
+ }
+ },
+ "ProblemPage": {
+ "Description": "Description",
+ "Solutions": "Solutions",
+ "Submissions": "Submissions",
+ "Details": "Details",
+ "Code": "Code",
+ "Testcase": "Testcase",
+ "Bot": "Bot"
+ },
+ "ProblemsetPage": {
+ "Status": "Status",
+ "Title": "Title",
+ "Difficulty": "Difficulty"
+ },
+ "SettingsDialog": {
+ "title": "Settings",
+ "description": "Customize your settings here.",
+ "breadcrumb": "Settings",
+ "nav": {
+ "Appearance": "Appearance",
+ "Language": "Language",
+ "CodeEditor": "CodeEditor",
+ "Advanced": "Advanced"
+ }
+ },
+ "StatusMessage": {
+ "PD": "Pending",
+ "QD": "Queued",
+ "CP": "Compiling",
+ "CE": "Compilation Error",
+ "CS": "Compilation Success",
+ "RU": "Running",
+ "TLE": "Time Limit Exceeded",
+ "MLE": "Memory Limit Exceeded",
+ "RE": "Runtime Error",
+ "AC": "Accepted",
+ "WA": "Wrong Answer",
+ "SE": "System Error"
+ },
+ "SubmissionsTable": {
+ "Index": "Index",
+ "Status": "Status",
+ "Language": "Language",
+ "Time": "Time",
+ "Memory": "Memory"
+ },
+ "WorkspaceEditorHeader": {
+ "LspStatusButton": {
+ "TooltipContent": "Language Server"
+ },
+ "ResetButton": {
+ "TooltipContent": "Reset Code"
+ },
+ "UndoButton": {
+ "TooltipContent": "Undo"
+ },
+ "RedoButton": {
+ "TooltipContent": "Redo"
+ },
+ "FormatButton": {
+ "TooltipContent": "Format"
+ },
+ "CopyButton": {
+ "TooltipContent": "Copy"
+ }
+ },
+ "WorkspaceEditorFooter": {
+ "Row": "Row",
+ "Column": "Column"
+ }
+}
\ No newline at end of file
diff --git a/messages/zh.json b/messages/zh.json
new file mode 100644
index 0000000..f15991d
--- /dev/null
+++ b/messages/zh.json
@@ -0,0 +1,130 @@
+{
+ "AppearanceSettings": {
+ "title": "选择一个主题",
+ "items": {
+ "System": "系统",
+ "Light": "浅色",
+ "Dark": "深色"
+ }
+ },
+ "AvatarButton": {
+ "Settings": "设置",
+ "LogIn": "登录",
+ "LogOut": "登出"
+ },
+ "Banner": {
+ "Text": "如果喜欢该项目不妨收藏一下"
+ },
+ "BackButton": "返回",
+ "Bot": {
+ "title": "询问AI助手",
+ "description": "由Vercel Ai SDK驱动",
+ "placeholder": "AI助手将自动获取您当前的代码"
+ },
+ "BotVisibilityToggle": {
+ "open": "打开AI助手",
+ "close": "关闭AI助手"
+ },
+ "DetailsPage": {
+ "BackButton": "所有提交记录",
+ "Time": "提交于",
+ "Input": "输入",
+ "ExpectedOutput": "期望输出",
+ "ActualOutput": "实际输出",
+ "Code": "代码"
+ },
+ "Difficulty": {
+ "EASY": "简单",
+ "MEDIUM": "中等",
+ "HARD": "困难"
+ },
+ "LanguageSettings": {
+ "en": {
+ "flag": "🇺🇸",
+ "name": "英语"
+ },
+ "zh": {
+ "flag": "🇨🇳",
+ "name": "中文"
+ }
+ },
+ "PlaygroundHeader": {
+ "RunCodeButton": {
+ "TooltipTrigger": {
+ "loading": "运行中...",
+ "ready": "运行"
+ },
+ "TooltipContent": "运行代码"
+ }
+ },
+ "ProblemPage": {
+ "Description": "题目描述",
+ "Solutions": "题解",
+ "Submissions": "提交记录",
+ "Details": "详情",
+ "Code": "代码",
+ "Testcase": "测试用例",
+ "Bot": "AI助手"
+ },
+ "ProblemsetPage": {
+ "Status": "状态",
+ "Title": "题目",
+ "Difficulty": "难度"
+ },
+ "SettingsDialog": {
+ "title": "设置",
+ "description": "在此处自定义设置。",
+ "breadcrumb": "设置",
+ "nav": {
+ "Appearance": "外观",
+ "Language": "语言",
+ "CodeEditor": "代码编辑器",
+ "Advanced": "高级设置"
+ }
+ },
+ "StatusMessage": {
+ "PD": "待处理",
+ "QD": "排队中",
+ "CP": "编译中",
+ "CE": "编译错误",
+ "CS": "编译成功",
+ "RU": "运行中",
+ "TLE": "超出时间限制",
+ "MLE": "超出内存限制",
+ "RE": "运行时错误",
+ "AC": "通过",
+ "WA": "解答错误",
+ "SE": "系统错误"
+ },
+ "SubmissionsTable": {
+ "Index": "序号",
+ "Status": "状态",
+ "Language": "语言",
+ "Time": "执行用时",
+ "Memory": "消耗内存"
+ },
+ "WorkspaceEditorHeader": {
+ "LspStatusButton": {
+ "TooltipContent": "语言服务"
+ },
+ "ResetButton": {
+ "TooltipContent": "重置代码"
+ },
+ "UndoButton": {
+ "TooltipContent": "撤销"
+ },
+ "RedoButton": {
+ "TooltipContent": "恢复"
+ },
+ "FormatButton": {
+ "TooltipContent": "格式化"
+ },
+ "CopyButton": {
+ "TooltipContent": "复制"
+ }
+ },
+ "WorkspaceEditorFooter": {
+ "Row": "行",
+ "Column": "列"
+ }
+}
\ No newline at end of file
diff --git a/src/app/(app)/problems/[id]/@Bot/page.tsx b/src/app/(app)/problems/[id]/@Bot/page.tsx
index 09bc148..d0b9020 100644
--- a/src/app/(app)/problems/[id]/@Bot/page.tsx
+++ b/src/app/(app)/problems/[id]/@Bot/page.tsx
@@ -9,6 +9,7 @@ import {
} from "@/components/ui/tooltip";
import { useCallback } from "react";
import { useChat } from "@ai-sdk/react";
+import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
import MdxPreview from "@/components/mdx-preview";
@@ -19,6 +20,7 @@ import { ChatMessageList } from "@/components/ui/chat/chat-message-list";
import { ChatBubble, ChatBubbleMessage } from "@/components/ui/chat/chat-bubble";
export default function Bot() {
+ const t = useTranslations("Bot");
const { problemId, problem, currentLang, currentValue } = useProblem();
const { messages, input, handleInputChange, setMessages, handleSubmit } = useChat({
@@ -58,12 +60,12 @@ export default function Bot() {
{!messages.some(
(message) => message.role === "user" || message.role === "assistant"
) && (
-
-
- Ask Bot
- Powered by Vercel Ai SDK
-
- )}
+
+
+ {t("title")}
+ {t("description")}
+
+ )}
@@ -100,7 +102,7 @@ export default function Bot() {
}
}}
className="h-full bg-muted border-transparent shadow-none rounded-lg"
- placeholder="Bot will automatically get your current code"
+ placeholder={t("placeholder")}
/>
diff --git a/src/app/(app)/problems/[id]/@Details/layout.tsx b/src/app/(app)/problems/[id]/@Details/layout.tsx
new file mode 100644
index 0000000..5cf90c9
--- /dev/null
+++ b/src/app/(app)/problems/[id]/@Details/layout.tsx
@@ -0,0 +1,8 @@
+import { getUserLocale } from "@/i18n/locale";
+import DetailsPage from "@/app/(app)/problems/[id]/@Details/page";
+
+export default async function DetailsLayout() {
+ const locale = await getUserLocale();
+
+ return ;
+}
diff --git a/src/app/(app)/problems/[id]/@Details/page.tsx b/src/app/(app)/problems/[id]/@Details/page.tsx
index 37f9348..07127f7 100644
--- a/src/app/(app)/problems/[id]/@Details/page.tsx
+++ b/src/app/(app)/problems/[id]/@Details/page.tsx
@@ -7,7 +7,10 @@ import {
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
+import { Locale } from "@/config/i18n";
+import { getLocale } from "@/lib/i18n";
import { useEffect, useState } from "react";
+import { useTranslations } from "next-intl";
import { ArrowLeftIcon } from "lucide-react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
@@ -20,7 +23,14 @@ import type { TestcaseResultWithTestcase } from "@/types/prisma";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { formatDistanceToNow, isBefore, subDays, format } from "date-fns";
-export default function DetailsPage() {
+interface DetailsPageProps {
+ locale: Locale;
+}
+
+export default function DetailsPage({ locale }: DetailsPageProps) {
+ const localeInstance = getLocale(locale);
+ const t = useTranslations("DetailsPage");
+ const s = useTranslations("StatusMessage");
const { api, submission } = useDockviewStore();
const { editorLanguageConfigs, problemId } = useProblem();
const [lastFailedTestcase, setLastFailedTestcase] =
@@ -53,7 +63,7 @@ export default function DetailsPage() {
const createdAt = new Date(submission.createdAt);
const submittedDisplay = isBefore(createdAt, subDays(new Date(), 1))
? format(createdAt, "yyyy-MM-dd")
- : formatDistanceToNow(createdAt, { addSuffix: true });
+ : formatDistanceToNow(createdAt, { addSuffix: true, locale: localeInstance });
const source = `\`\`\`${submission?.language}\n${submission?.code}\n\`\`\``;
@@ -76,7 +86,7 @@ export default function DetailsPage() {
className="h-8 w-auto p-2 hover:bg-transparent text-muted-foreground hover:text-foreground"
>
- All Submissions
+ {t("BackButton")}
@@ -92,10 +102,10 @@ export default function DetailsPage() {
getStatusColorClass(submission.status)
)}
>
-
{statusMap.get(submission.status)?.message}
+
{s(`${statusMap.get(submission.status)?.message}`)}
-
Submitted on
+
{t("Time")}
{submittedDisplay}
@@ -118,7 +128,7 @@ export default function DetailsPage() {
className="bg-background has-focus-visible:border-ring has-focus-visible:ring-ring/50 relative border px-4 py-1 outline-none first:rounded-t-md last:rounded-b-md last:border-b has-focus-visible:z-10 has-focus-visible:ring-[3px]"
>
- Input
+ {t("Input")}
@@ -142,7 +152,7 @@ export default function DetailsPage() {
-
Expected Output
+
{t("ExpectedOutput")}
-
Your Output
+
{t("ActualOutput")}
- )}
+
+ )}
- Code
+ {t("Code")}
-
+
diff --git a/src/app/(app)/problems/[id]/layout.tsx b/src/app/(app)/problems/[id]/layout.tsx
index 558c043..304f5b9 100644
--- a/src/app/(app)/problems/[id]/layout.tsx
+++ b/src/app/(app)/problems/[id]/layout.tsx
@@ -1,5 +1,6 @@
import prisma from "@/lib/prisma";
import { notFound } from "next/navigation";
+import { getUserLocale } from "@/i18n/locale";
import ProblemPage from "@/app/(app)/problems/[id]/page";
import { ProblemStoreProvider } from "@/providers/problem-store-provider";
import { PlaygroundHeader } from "@/components/features/playground/header";
@@ -59,6 +60,8 @@ export default async function ProblemLayout({
return notFound();
}
+ const locale = await getUserLocale();
+
return (
{
+ setKey((prevKey) => prevKey + 1);
+ }, [locale]);
+
return (
s.status === "AC").map(s => s.problemId));
const attemptedProblems = new Set(submissions.filter(s => s.status !== "AC").map(s => s.problemId));
+ const t = await getTranslations();
+
return (
- Status
- Title
- Difficulty
+ {t("ProblemsetPage.Status")}
+ {t("ProblemsetPage.Title")}
+ {t("ProblemsetPage.Difficulty")}
@@ -60,7 +63,7 @@ export default async function ProblemsetPage() {
- {problem.difficulty}
+ {t(`Difficulty.${problem.difficulty}`)}
))}
diff --git a/src/components/appearance-settings.tsx b/src/components/appearance-settings.tsx
index ad5c0a7..fc1429b 100644
--- a/src/components/appearance-settings.tsx
+++ b/src/components/appearance-settings.tsx
@@ -2,22 +2,25 @@
import Image from "next/image";
import { useTheme } from "next-themes";
+import { useTranslations } from "next-intl";
import { CheckIcon, MinusIcon } from "lucide-react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
-const items = [
- { value: "system", label: "System", image: "/ui-system.png" },
- { value: "light", label: "Light", image: "/ui-light.png" },
- { value: "dark", label: "Dark", image: "/ui-dark.png" },
-];
-
export default function AppearanceSettings() {
+ const t = useTranslations("AppearanceSettings");
+
+ const items = [
+ { value: "system", label: t("items.System"), image: "/ui-system.png" },
+ { value: "light", label: t("items.Light"), image: "/ui-light.png" },
+ { value: "dark", label: t("items.Dark"), image: "/ui-dark.png" },
+ ];
+
const { theme, setTheme } = useTheme();
return (