feat(i18n): make admin and catalog components translatable
CI / check (push) Failing after 30s
CI / preflight (push) Skipped
CI / deploy (push) Skipped

This commit is contained in:
openhands committed 2026-09-18 14:56:36 +02:00
1 parent d6111387e4
commit 03774bb411
9 files changed
+142 -87

No files matched your search

@@ -12,6 +12,7 @@ import {
Terminal,
UserCheck,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useMemo, useState } from "react";
import { toast } from "sonner";
import {
@@ -45,6 +46,7 @@ export function RankEditClient({
allCmsPermissions,
}: RankEditorProps) {
const { run, isPending } = useServerAction();
const t = useTranslations("pages.admin.permissions.id.rankEditClient");
const [general, setGeneral] = useState({
rank_name: initialRank.rank_name,
@@ -89,7 +91,7 @@ export function RankEditClient({
]),
);
run(() => saveRankAction({ id: initialRank.id, fields }), {
successMessage: "Rank saved",
successMessage: t("rankSaved"),
});
}
@@ -107,11 +109,11 @@ export function RankEditClient({
});
if (!r.ok) {
setCmsPermSlugs(prev);
toast.error("Failed");
toast.error(t("failed"));
}
} catch {
setCmsPermSlugs(prev);
toast.error("Failed");
toast.error(t("failed"));
}
}
@@ -195,7 +197,7 @@ export function RankEditClient({
setSectionAll(filtered, "1");
}}
>
All On
{t("allOn")}
</Button>
<Button
variant="ghost"
@@ -206,7 +208,7 @@ export function RankEditClient({
setSectionAll(filtered, "0");
}}
>
All Off
{t("allOff")}
</Button>
</div>
)}
@@ -262,11 +264,14 @@ export function RankEditClient({
)}
<div>
<h1 className="text-2xl font-bold">
{general.rank_name || `Rank ${initialRank.id}`}
{general.rank_name || t("rankFallback", { id: initialRank.id })}
</h1>
<p className="text-sm text-muted-foreground">
Rank #{initialRank.id} &middot; Level {initialRank.level} &middot;{" "}
{totalUsers} users
{t("rankMeta", {
id: initialRank.id,
level: initialRank.level,
count: totalUsers,
})}
</p>
</div>
</div>
@@ -275,28 +280,31 @@ export function RankEditClient({
<Tabs defaultValue="general">
<TabsList>
<TabsTrigger value="general">
<Settings className="w-3.5 h-3.5 mr-1" /> General
<Settings className="w-3.5 h-3.5 mr-1" /> {t("tabGeneral")}
</TabsTrigger>
<TabsTrigger value="emulator">
<Terminal className="w-3.5 h-3.5 mr-1" /> Emulator ({enabledCount})
<Terminal className="w-3.5 h-3.5 mr-1" />{" "}
{t("tabEmulator", { count: enabledCount })}
</TabsTrigger>
<TabsTrigger value="cms">
<UserCheck className="w-3.5 h-3.5 mr-1" /> CMS (
{cmsPermSlugs.length})
<UserCheck className="w-3.5 h-3.5 mr-1" />{" "}
{t("tabCms", { count: cmsPermSlugs.length })}
</TabsTrigger>
<TabsTrigger value="users">
{t("tabUsers", { count: totalUsers })}
</TabsTrigger>
<TabsTrigger value="users">Users ({totalUsers})</TabsTrigger>
</TabsList>
{/* ═══ General ═══ */}
<TabsContent value="general" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-base">Rank Settings</CardTitle>
<CardTitle className="text-base">{t("rankSettings")}</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
<div className="space-y-1.5">
<Label className="text-xs">Rank Name</Label>
<Label className="text-xs">{t("rankName")}</Label>
<Input
value={general.rank_name}
onChange={(e) =>
@@ -305,7 +313,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Badge</Label>
<Label className="text-xs">{t("badge")}</Label>
<Input
value={general.badge}
onChange={(e) =>
@@ -314,7 +322,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Prefix</Label>
<Label className="text-xs">{t("prefix")}</Label>
<Input
value={general.prefix}
onChange={(e) =>
@@ -323,7 +331,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Prefix Color</Label>
<Label className="text-xs">{t("prefixColor")}</Label>
<div className="flex gap-2">
<Input
value={general.prefix_color}
@@ -348,10 +356,10 @@ export function RankEditClient({
<div className="flex items-center justify-between rounded-lg border p-3">
<div>
<Label htmlFor="rank-hidden_rank" className="text-sm">
Hidden Rank
{t("hiddenRank")}
</Label>
<p className="text-xs text-muted-foreground">
Hide from public
{t("hideFromPublic")}
</p>
</div>
<Switch
@@ -365,10 +373,10 @@ export function RankEditClient({
<div className="flex items-center justify-between rounded-lg border p-3">
<div>
<Label htmlFor="rank-log_commands" className="text-sm">
Log Commands
{t("logCommands")}
</Label>
<p className="text-xs text-muted-foreground">
Record to audit log
{t("recordToAuditLog")}
</p>
</div>
<Switch
@@ -380,7 +388,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5 rounded-lg border p-3">
<Label className="text-xs">Room Effect ID</Label>
<Label className="text-xs">{t("roomEffectId")}</Label>
<Input
type="number"
value={general.room_effect}
@@ -390,7 +398,7 @@ export function RankEditClient({
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 mt-4">
<div className="space-y-1.5">
<Label className="text-xs">Auto Credits</Label>
<Label className="text-xs">{t("autoCredits")}</Label>
<Input
type="number"
value={general.auto_credits_amount}
@@ -400,7 +408,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Auto Pixels</Label>
<Label className="text-xs">{t("autoPixels")}</Label>
<Input
type="number"
value={general.auto_pixels_amount}
@@ -410,7 +418,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Auto GOTW</Label>
<Label className="text-xs">{t("autoGotw")}</Label>
<Input
type="number"
value={general.auto_gotw_amount}
@@ -418,7 +426,7 @@ export function RankEditClient({
/>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Auto Points</Label>
<Label className="text-xs">{t("autoPoints")}</Label>
<Input
type="number"
value={general.auto_points_amount}
@@ -435,7 +443,7 @@ export function RankEditClient({
) : (
<Save className="w-4 h-4 mr-1" />
)}
Save
{t("save")}
</Button>
</div>
</CardContent>
@@ -445,10 +453,13 @@ export function RankEditClient({
<Card>
<CardHeader>
<CardTitle className="text-base">
Users with this rank
{t("usersWithThisRank")}
{totalUsers > users.length && (
<span className="text-muted-foreground font-normal ml-1 text-sm">
(showing {users.length} of {totalUsers})
{t("showingUsers", {
shown: users.length,
total: totalUsers,
})}
</span>
)}
</CardTitle>
@@ -456,7 +467,7 @@ export function RankEditClient({
<CardContent>
{users.length === 0 ? (
<p className="text-sm text-muted-foreground">
No users with this rank.
{t("noUsersWithThisRank")}
</p>
) : (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-2">
@@ -490,7 +501,10 @@ export function RankEditClient({
<TabsContent value="emulator" className="space-y-4">
<div className="flex items-center justify-between flex-wrap gap-2">
<p className="text-sm text-muted-foreground">
{enabledCount} of {totalCount} permissions enabled
{t("permissionsEnabled", {
enabled: enabledCount,
total: totalCount,
})}
</p>
<div className="flex items-center gap-3">
<div className="flex gap-1">
@@ -500,7 +514,7 @@ export function RankEditClient({
className="h-7 text-xs"
onClick={() => setAllPerms("1")}
>
Enable All
{t("enableAll")}
</Button>
<Button
variant="outline"
@@ -508,36 +522,48 @@ export function RankEditClient({
className="h-7 text-xs"
onClick={() => setAllPerms("0")}
>
Disable All
{t("disableAll")}
</Button>
</div>
<div className="flex gap-2 text-[11px] text-muted-foreground">
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded bg-[var(--admin-text-muted)] inline-block" />{" "}
Off
{t("off")}
</span>
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded bg-[var(--admin-success-subtle)] inline-block" />{" "}
On
{t("on")}
</span>
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded bg-[var(--admin-warning-subtle)] inline-block" />{" "}
Owner
{t("owner")}
</span>
</div>
</div>
</div>
<SearchInput
placeholder="Search permissions..."
placeholder={t("searchPermissionsPlaceholder")}
value={permSearch}
onChange={setPermSearch}
/>
<PermSection title="Commands" keys={cats.commands} sectionKey="cmd" />
<PermSection title="Account" keys={cats.account} sectionKey="acc" />
<PermSection
title={t("commands")}
keys={cats.commands}
sectionKey="cmd"
/>
<PermSection
title={t("account")}
keys={cats.account}
sectionKey="acc"
/>
{cats.other.length > 0 && (
<PermSection title="Other" keys={cats.other} sectionKey="other" />
<PermSection
title={t("other")}
keys={cats.other}
sectionKey="other"
/>
)}
<div className="flex justify-end">
@@ -547,7 +573,7 @@ export function RankEditClient({
) : (
<Save className="w-4 h-4 mr-1" />
)}
Save Rank
{t("saveRank")}
</Button>
</div>
</TabsContent>
@@ -663,10 +689,13 @@ export function RankEditClient({
<Card>
<CardHeader>
<CardTitle className="text-base">
Users with this rank
{t("usersWithThisRank")}
{totalUsers > users.length && (
<span className="text-muted-foreground font-normal ml-1 text-sm">
(showing {users.length} of {totalUsers})
{t("showingUsers", {
shown: users.length,
total: totalUsers,
})}
</span>
)}
</CardTitle>
@@ -674,7 +703,7 @@ export function RankEditClient({
<CardContent>
{users.length === 0 ? (
<p className="text-sm text-muted-foreground">
No users with this rank.
{t("noUsersWithThisRank")}
</p>
) : (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-2">