refactor(i18n): replace hardcoded texts with i18n message keys

This commit is contained in:
2025-04-15 18:22:21 +08:00
parent 8b276ae91e
commit 4428a29306
32 changed files with 608 additions and 154 deletions
+10 -7
View File
@@ -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 (
<fieldset className="space-y-4">
<legend className="text-foreground text-sm leading-none font-medium">
Choose a theme
{t("title")}
</legend>
<RadioGroup className="flex gap-3" defaultValue={theme}>
{items.map((item) => (
+3 -1
View File
@@ -9,6 +9,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { auth, signOut } from "@/lib/auth";
import { getTranslations } from "next-intl/server";
import { Skeleton } from "@/components/ui/skeleton";
import LogInButton from "@/components/log-in-button";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
@@ -28,6 +29,7 @@ async function handleLogOut() {
export async function AvatarButton() {
const session = await auth();
const t = await getTranslations("AvatarButton");
const isLoggedIn = !!session?.user;
const image = session?.user?.image ?? "https://github.com/shadcn.png";
const name = session?.user?.name ?? "unknown";
@@ -64,7 +66,7 @@ export async function AvatarButton() {
<SettingsButton />
<DropdownMenuItem onClick={handleLogOut}>
<LogOut />
Log out
{t("LogOut")}
</DropdownMenuItem>
</DropdownMenuGroup>
</>
+6 -1
View File
@@ -1,5 +1,6 @@
import Link from "next/link";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
import { ArrowLeftIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
@@ -14,6 +15,8 @@ export default function BackButton({
className,
...props
}: BackButtonProps) {
const t = useTranslations();
return (
<TooltipProvider delayDuration={0}>
<Tooltip>
@@ -29,7 +32,9 @@ export default function BackButton({
</Link>
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">Back</TooltipContent>
<TooltipContent className="px-2 py-1 text-xs">
{t("BackButton")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
+5 -2
View File
@@ -1,5 +1,6 @@
import { cn } from "@/lib/utils";
import { siteConfig } from "@/config/site";
import { useTranslations } from 'next-intl';
import { ArrowRightIcon } from "lucide-react";
interface BannerProps {
@@ -11,9 +12,11 @@ interface BannerProps {
export function Banner({
className,
link = siteConfig.url.repo.github,
text = "Star this project if you like it.",
text,
...props
}: BannerProps) {
const t = useTranslations();
return (
<header
{...props}
@@ -25,7 +28,7 @@ export function Banner({
<p className="flex justify-center text-sm">
<a href={link} className="group">
<span className="me-1 text-base leading-none">✨</span>
{text}
{text || t("Banner.Text")}
<ArrowRightIcon
className="ms-2 -mt-0.5 inline-flex opacity-60 transition-transform group-hover:translate-x-0.5"
size={16}
+4 -2
View File
@@ -8,11 +8,13 @@ import {
} from "@/components/ui/tooltip";
import { BotIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import { Toggle } from "@/components/ui/toggle";
import { useDockviewStore } from "@/stores/dockview";
export default function BotVisibilityToggle() {
const { api } = useDockviewStore();
const t = useTranslations();
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isBotVisible, setBotVisible] = useState<boolean>(false);
@@ -37,7 +39,7 @@ export default function BotVisibilityToggle() {
id: "Bot",
component: "Bot",
tabComponent: "Bot",
title: "Bot",
title: t("ProblemPage.Bot"),
position: {
direction: "right",
},
@@ -70,7 +72,7 @@ export default function BotVisibilityToggle() {
</div>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
<p>{isBotVisible ? "Close Bot" : "Open Bot"}</p>
<p>{isBotVisible ? t("BotVisibilityToggle.close") : t("BotVisibilityToggle.open")}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -1,6 +1,7 @@
import { cn } from "@/lib/utils";
import { RunCode } from "@/components/run-code";
import { auth } from "@/lib/auth";
import BackButton from "@/components/back-button";
import { RunCodeButton } from "@/components/run-code";
import { AvatarButton } from "@/components/avatar-button";
import BotVisibilityToggle from "@/components/bot-visibility-toggle";
@@ -8,10 +9,12 @@ interface PlaygroundHeaderProps {
className?: string;
}
export function PlaygroundHeader({
export async function PlaygroundHeader({
className,
...props
}: PlaygroundHeaderProps) {
const session = await auth();
return (
<header
{...props}
@@ -33,7 +36,7 @@ export function PlaygroundHeader({
<div className="z-10 absolute left-1/2 top-0 h-full -translate-x-1/2 py-2">
<div className="relative flex">
<div className="relative flex overflow-hidden rounded">
<RunCode className="bg-muted text-muted-foreground hover:bg-muted/50" />
<RunCodeButton session={session} className="bg-muted text-muted-foreground hover:bg-muted/50" />
</div>
</div>
</div>
@@ -9,12 +9,14 @@ import {
import { cn } from "@/lib/utils";
import { useState } from "react";
import { Check, Copy } from "lucide-react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
export function CopyButton() {
const { editor } = useProblem();
const [copied, setCopied] = useState(false);
const t = useTranslations("WorkspaceEditorHeader.CopyButton");
const handleCopy = async () => {
try {
@@ -65,7 +67,7 @@ export function CopyButton() {
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
Click to Copy
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -2,6 +2,7 @@
import { cn } from "@/lib/utils";
import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import { useProblem } from "@/hooks/use-problem";
import { CircleXIcon, TriangleAlertIcon } from "lucide-react";
@@ -20,6 +21,7 @@ export function WorkspaceEditorFooter({
className,
...props
}: WorkspaceEditorFooterProps) {
const t = useTranslations("WorkspaceEditorFooter");
const { editor, markers } = useProblem();
const [position, setPosition] = useState<{ lineNumber: number; column: number } | null>(null);
@@ -62,8 +64,8 @@ export function WorkspaceEditorFooter({
</div>
<span className="truncate">
{position
? `Row ${position.lineNumber}, Column ${position.column}`
: "Row -, Column -"}
? `${t("Row")} ${position.lineNumber}, ${t("Column")} ${position.column}`
: `${t("Row")} -, ${t("Column")} -`}
</span>
</div>
</footer>
@@ -7,11 +7,13 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Paintbrush } from "lucide-react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
export function FormatButton() {
const { editor } = useProblem();
const t = useTranslations("WorkspaceEditorHeader.FormatButton");
return (
<TooltipProvider delayDuration={0}>
@@ -20,7 +22,7 @@ export function FormatButton() {
<Button
variant="outline"
size="icon"
aria-label="Format Code"
aria-label={t("TooltipContent")}
onClick={() => {
editor?.trigger("format", "editor.action.formatDocument", null);
}}
@@ -31,7 +33,7 @@ export function FormatButton() {
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
Format Code
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -6,6 +6,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
@@ -28,6 +29,7 @@ const getLspStatusColor = (webSocket: WebSocket | null) => {
export function LspStatusButton() {
const { webSocket } = useProblem();
const t = useTranslations("WorkspaceEditorHeader.LspStatusButton");
return (
<TooltipProvider delayDuration={0}>
@@ -48,7 +50,7 @@ export function LspStatusButton() {
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
Language Server
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -7,11 +7,13 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Redo2 } from "lucide-react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
export function RedoButton() {
const { editor } = useProblem();
const t = useTranslations("WorkspaceEditorHeader.RedoButton");
return (
<TooltipProvider delayDuration={0}>
@@ -20,7 +22,7 @@ export function RedoButton() {
<Button
variant="outline"
size="icon"
aria-label="Redo Code"
aria-label={t("TooltipContent")}
onClick={() => {
editor?.trigger("redo", "redo", null);
}}
@@ -31,7 +33,7 @@ export function RedoButton() {
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
Redo Code
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -7,11 +7,13 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { RotateCcw } from "lucide-react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
export function ResetButton() {
const { editor, currentTemplate } = useProblem();
const t = useTranslations("WorkspaceEditorHeader.ResetButton");
const handleReset = () => {
if (editor) {
@@ -38,7 +40,7 @@ export function ResetButton() {
<Button
variant="outline"
size="icon"
aria-label="Reset Code"
aria-label={t("TooltipContent")}
onClick={handleReset}
disabled={!editor}
className="h-6 w-6 px-1.5 py-0.5 border-none shadow-none hover:bg-muted"
@@ -47,7 +49,7 @@ export function ResetButton() {
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
Reset Code
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -7,11 +7,13 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Undo2 } from "lucide-react";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
export function UndoButton() {
const { editor } = useProblem();
const t = useTranslations("WorkspaceEditorHeader.UndoButton");
return (
<TooltipProvider delayDuration={0}>
@@ -20,7 +22,7 @@ export function UndoButton() {
<Button
variant="outline"
size="icon"
aria-label="Undo Code"
aria-label={t("TooltipContent")}
onClick={() => {
editor?.trigger("undo", "undo", null);
}}
@@ -31,7 +33,7 @@ export function UndoButton() {
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">
Undo Code
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
+65
View File
@@ -0,0 +1,65 @@
"use client";
import Flag from "react-world-flags";
import { Globe } from "lucide-react";
import { useTranslations } from "next-intl";
import { Locale, locales } from "@/config/i18n";
import { useState, useMemo, useEffect } from "react";
import { getUserLocale, setUserLocale } from "@/i18n/locale";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
export function LanguageSettings() {
const t = useTranslations();
const [selectedOption, setSelectedOption] = useState<Locale>();
useEffect(() => {
const fetchLocale = async () => {
const userLocale = await getUserLocale();
if (!userLocale) return;
setSelectedOption(userLocale);
};
fetchLocale();
}, []);
const localeOptions = useMemo(() => {
const options = locales.map((locale) => ({
value: locale,
label: `${t(`LanguageSettings.${locale}.name`)}`,
}));
return options.sort((a, b) => a.value.localeCompare(b.value));
}, [t]);
const handleValueChange = (value: Locale) => {
setSelectedOption(value);
setUserLocale(value);
};
const getIconForLocale = (locale: Locale) => {
switch (locale) {
case "en":
return <Flag code="US" className="h-4 w-4 mr-2" />;
case "zh":
return <Flag code="CN" className="h-4 w-4 mr-2" />;
default:
return <Globe size={16} className="mr-2" />;
}
};
return (
<Select value={selectedOption} onValueChange={handleValueChange}>
<SelectTrigger className="w-[200px] shadow-none focus:ring-0">
<SelectValue />
</SelectTrigger>
<SelectContent className="w-[200px]">
{localeOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className="flex items-center">
{getIconForLocale(option.value)}
<span className="truncate">{option.label}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
);
}
+4 -2
View File
@@ -1,6 +1,7 @@
"use client";
import { LogIn } from "lucide-react";
import { useTranslations } from "next-intl";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
@@ -8,6 +9,7 @@ export default function LogInButton() {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const t = useTranslations("AvatarButton");
const handleLogIn = () => {
const params = new URLSearchParams(searchParams.toString());
@@ -17,8 +19,8 @@ export default function LogInButton() {
return (
<DropdownMenuItem onClick={handleLogIn}>
<LogIn className="mr-2 h-4 w-4" />
Log In
<LogIn />
{t("LogIn")}
</DropdownMenuItem>
);
}
+33 -18
View File
@@ -8,24 +8,32 @@ import {
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { useState } from "react";
import { Session } from "next-auth";
import { judge } from "@/actions/judge";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { useProblem } from "@/hooks/use-problem";
import { useDockviewStore } from "@/stores/dockview";
import { LoaderCircleIcon, PlayIcon } from "lucide-react";
import { showStatusToast } from "@/hooks/show-status-toast";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
interface RunCodeProps {
interface RunCodeButtonProps {
className?: string;
session: Session | null;
}
export function RunCode({
export function RunCodeButton({
className,
...props
}: RunCodeProps) {
session,
}: RunCodeButtonProps) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { api } = useDockviewStore();
const { currentLang, editor, problemId } = useProblem();
const [isLoading, setIsLoading] = useState<boolean>(false);
const t = useTranslations("PlaygroundHeader.RunCodeButton");
const handleJudge = async () => {
if (!editor) return;
@@ -33,18 +41,24 @@ export function RunCode({
const code = editor.getValue() || "";
setIsLoading(true);
try {
const result = await judge(currentLang, code, problemId);
showStatusToast({ status: result.status });
const panel = api?.getPanel("Submissions");
if (panel && !panel.api.isActive) {
panel.api.setActive();
if (!session) {
const params = new URLSearchParams(searchParams.toString());
params.set("redirectTo", pathname);
router.push(`/sign-in?${params.toString()}`);
} else {
try {
const result = await judge(currentLang, code, problemId);
showStatusToast({ status: result.status });
const panel = api?.getPanel("Submissions");
if (panel && !panel.api.isActive) {
panel.api.setActive();
}
} catch (error) {
console.error("Error occurred while judging the code:");
console.error(error);
} finally {
setIsLoading(false);
}
} catch (error) {
console.error("Error occurred while judging the code:");
console.error(error);
} finally {
setIsLoading(false);
}
};
@@ -53,7 +67,6 @@ export function RunCode({
<Tooltip>
<TooltipTrigger asChild>
<Button
{...props}
variant="secondary"
className={cn("h-8 px-3 py-1.5", className)}
onClick={handleJudge}
@@ -72,10 +85,12 @@ export function RunCode({
aria-hidden="true"
/>
)}
{isLoading ? "Running..." : "Run"}
{isLoading ? t("TooltipTrigger.loading") : t("TooltipTrigger.ready")}
</Button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">Run Code</TooltipContent>
<TooltipContent className="px-2 py-1 text-xs">
{t("TooltipContent")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
+3 -1
View File
@@ -1,16 +1,18 @@
"use client";
import { Settings } from "lucide-react";
import { useTranslations } from "next-intl";
import { useSettingsStore } from "@/stores/useSettingsStore";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
export function SettingsButton() {
const t = useTranslations("AvatarButton");
const { setDialogOpen } = useSettingsStore();
return (
<DropdownMenuItem onClick={() => setDialogOpen(true)}>
<Settings />
Settings
{t("Settings")}
</DropdownMenuItem>
);
}
+21 -27
View File
@@ -25,29 +25,31 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import AppearanceSettings from "./appearance-settings";
import { useTranslations } from "next-intl";
import { ScrollArea } from "@/components/ui/scroll-area";
import { useSettingsStore } from "@/stores/useSettingsStore";
import AppearanceSettings from "@/components/appearance-settings";
import { LanguageSettings } from "@/components/language-settings";
import { CodeXml, Globe, Paintbrush, Settings } from "lucide-react";
const data = {
nav: [
{ name: "Appearance", icon: Paintbrush },
{ name: "Language & region", icon: Globe },
{ name: "Code Editor", icon: CodeXml },
{ name: "Advanced", icon: Settings },
],
};
export function SettingsDialog() {
const t = useTranslations("SettingsDialog");
const data = {
nav: [
{ id: "Appearance", name: t("nav.Appearance"), icon: Paintbrush },
{ id: "Language", name: t("nav.Language"), icon: Globe },
{ id: "CodeEditor", name: t("nav.CodeEditor"), icon: CodeXml },
{ id: "Advanced", name: t("nav.Advanced"), icon: Settings },
],
};
const { isDialogOpen, activeSetting, setDialogOpen, setActiveSetting } = useSettingsStore();
return (
<Dialog open={isDialogOpen} onOpenChange={setDialogOpen}>
<DialogContent className="overflow-hidden p-0 md:max-h-[500px] md:max-w-[700px] lg:max-w-[800px]">
<DialogTitle className="sr-only">Settings</DialogTitle>
<DialogTitle className="sr-only">{t("title")}</DialogTitle>
<DialogDescription className="sr-only">
Customize your settings here.
{t("description")}
</DialogDescription>
<SidebarProvider className="items-start">
<Sidebar collapsible="none" className="hidden md:flex">
@@ -59,10 +61,10 @@ export function SettingsDialog() {
<SidebarMenuItem key={item.name}>
<SidebarMenuButton
asChild
isActive={item.name === activeSetting}
onClick={() => setActiveSetting(item.name)}
isActive={item.id === activeSetting}
onClick={() => setActiveSetting(item.id)}
>
<a href="#">
<a>
<item.icon />
<span>{item.name}</span>
</a>
@@ -80,11 +82,11 @@ export function SettingsDialog() {
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">Settings</BreadcrumbLink>
<BreadcrumbLink>{t("breadcrumb")}</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>{activeSetting}</BreadcrumbPage>
<BreadcrumbPage>{t(`nav.${activeSetting}`)}</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
@@ -92,16 +94,8 @@ export function SettingsDialog() {
</header>
<ScrollArea className="flex-1 overflow-y-auto p-4 pt-0">
<div className="flex flex-col gap-4">
{activeSetting === "Appearance" ? (
<AppearanceSettings />
) : (
Array.from({ length: 10 }).map((_, i) => (
<div
key={i}
className="aspect-video max-w-3xl rounded-xl bg-muted/50"
/>
))
)}
{activeSetting === "Appearance" && <AppearanceSettings />}
{activeSetting === "Language" && <LanguageSettings />}
</div>
</ScrollArea>
</main>
+27 -9
View File
@@ -9,6 +9,9 @@ import {
TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";
import { Locale } from "@/config/i18n";
import { useTranslations } from "next-intl";
import { enUS, zhCN } from "date-fns/locale";
import { useProblem } from "@/hooks/use-problem";
import { Clock4Icon, CpuIcon } from "lucide-react";
import { useDockviewStore } from "@/stores/dockview";
@@ -18,10 +21,23 @@ import { EditorLanguageIcons } from "@/config/editor-language-icons";
import { formatDistanceToNow, isBefore, subDays, format } from "date-fns";
interface SubmissionsTableProps {
locale: Locale;
submissions: SubmissionWithTestcaseResult[];
}
export default function SubmissionsTable({ submissions }: SubmissionsTableProps) {
const getLocale = (locale: Locale) => {
switch (locale) {
case "zh":
return zhCN;
case "en":
default:
return enUS;
}
}
export default function SubmissionsTable({ locale, submissions }: SubmissionsTableProps) {
const s = useTranslations("StatusMessage");
const t = useTranslations("SubmissionsTable");
const { editorLanguageConfigs } = useProblem();
const { api, setSubmission } = useDockviewStore();
@@ -41,7 +57,7 @@ export default function SubmissionsTable({ submissions }: SubmissionsTableProps)
id: "Details",
component: "Details",
tabComponent: "Details",
title: submission.status,
title: s(`${submission.status}`),
position: {
referencePanel: "Submissions",
direction: "within",
@@ -54,11 +70,11 @@ export default function SubmissionsTable({ submissions }: SubmissionsTableProps)
<Table>
<TableHeader className="bg-transparent">
<TableRow className="hover:bg-transparent">
<TableHead className="w-[100px]">Index</TableHead>
<TableHead className="w-[170px]">Status</TableHead>
<TableHead className="w-[100px]">Language</TableHead>
<TableHead className="w-[100px]">Time</TableHead>
<TableHead className="w-[100px]">Memory</TableHead>
<TableHead className="w-[100px]">{t("Index")}</TableHead>
<TableHead className="w-[170px]">{t("Status")}</TableHead>
<TableHead className="w-[100px]">{t("Language")}</TableHead>
<TableHead className="w-[100px]">{t("Time")}</TableHead>
<TableHead className="w-[100px]">{t("Memory")}</TableHead>
</TableRow>
</TableHeader>
@@ -68,11 +84,13 @@ export default function SubmissionsTable({ submissions }: SubmissionsTableProps)
{sortedSubmissions.map((submission, index) => {
const Icon = EditorLanguageIcons[submission.language];
const createdAt = new Date(submission.createdAt);
const localeInstance = getLocale(locale);
const submittedDisplay = isBefore(createdAt, subDays(new Date(), 1))
? format(createdAt, "yyyy-MM-dd")
: formatDistanceToNow(createdAt, { addSuffix: true });
: formatDistanceToNow(createdAt, { addSuffix: true, locale: localeInstance });
const isEven = (submissions.length - index) % 2 === 0;
const message = statusMap.get(submission.status)?.message;
return (
<TableRow
@@ -89,7 +107,7 @@ export default function SubmissionsTable({ submissions }: SubmissionsTableProps)
<TableCell>
<div className="flex flex-col truncate">
<span className={getStatusColorClass(submission.status)}>
{statusMap.get(submission.status)?.message}
{s(`${message}`)}
</span>
<span className="text-xs">{submittedDisplay}</span>
</div>