From befa4ec2825c9bd0aabfa809306b3306f379c819 Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 4 Jul 2026 21:21:15 +0200 Subject: [PATCH] Translate admin panel to all 6 languages --- src/app/admin/achievements/page.tsx | 29 +- src/app/admin/ads/page.tsx | 24 +- src/app/admin/alerts/page.tsx | 43 +- src/app/admin/applications/page.tsx | 18 +- src/app/admin/articles/[id]/page.tsx | 8 +- src/app/admin/articles/new/page.tsx | 6 +- src/app/admin/articles/page.tsx | 28 +- src/app/admin/badges/page.tsx | 40 +- src/app/admin/bans/page.tsx | 46 +- src/app/admin/calendar/[id]/page.tsx | 38 +- src/app/admin/calendar/page.tsx | 28 +- src/app/admin/catalog/[id]/page.tsx | 41 +- src/app/admin/catalog/page.tsx | 36 +- src/app/admin/commandocentrum/page.tsx | 248 ++--- src/app/admin/email-templates/page.tsx | 52 +- src/app/admin/emulator/page.tsx | 69 +- src/app/admin/help-questions/page.tsx | 22 +- src/app/admin/housekeeping/page.tsx | 53 +- src/app/admin/ip/page.tsx | 46 +- src/app/admin/layout.tsx | 161 +-- src/app/admin/logs/page.tsx | 48 +- src/app/admin/maintenance/page.tsx | 47 +- src/app/admin/media/page.tsx | 11 +- src/app/admin/navigation/page.tsx | 36 +- src/app/admin/page.tsx | 28 +- src/app/admin/permissions/page.tsx | 36 +- src/app/admin/photos/page.tsx | 24 +- src/app/admin/radio/api-keys/page.tsx | 85 +- src/app/admin/radio/autodj/page.tsx | 118 +- src/app/admin/radio/banners/page.tsx | 71 +- src/app/admin/radio/embed/page.tsx | 68 +- src/app/admin/radio/history/page.tsx | 61 +- src/app/admin/radio/moderation/page.tsx | 35 +- src/app/admin/radio/monitoring/page.tsx | 90 +- src/app/admin/radio/page.tsx | 86 +- src/app/admin/radio/points/page.tsx | 73 +- src/app/admin/radio/ranks/page.tsx | 55 +- src/app/admin/radio/settings/page.tsx | 55 +- src/app/admin/rare-values/page.tsx | 57 +- src/app/admin/rooms/[id]/page.tsx | 44 +- src/app/admin/rooms/page.tsx | 42 +- src/app/admin/settings/page.tsx | 27 +- src/app/admin/shop/[id]/page.tsx | 33 +- src/app/admin/shop/new/page.tsx | 33 +- src/app/admin/shop/page.tsx | 52 +- src/app/admin/subscriptions/page.tsx | 31 +- src/app/admin/tags/page.tsx | 70 +- src/app/admin/teams/page.tsx | 74 +- src/app/admin/theme/page.tsx | 34 +- src/app/admin/transactions/page.tsx | 68 +- src/app/admin/users/[id]/edit/page.tsx | 36 +- src/app/admin/users/[id]/page.tsx | 64 +- src/app/admin/users/page.tsx | 46 +- src/app/admin/vouchers/page.tsx | 54 +- src/app/admin/vpn/page.tsx | 44 +- src/app/admin/wordfilter/page.tsx | 25 +- src/app/admin/writeable-boxes/page.tsx | 48 +- src/messages/de.json | 1308 ++++++++++++++++++++++ src/messages/en.json | 1311 ++++++++++++++++++++++- src/messages/es.json | 1308 ++++++++++++++++++++++ src/messages/fr.json | 1308 ++++++++++++++++++++++ src/messages/it.json | 1308 ++++++++++++++++++++++ src/messages/nl.json | 1308 ++++++++++++++++++++++ 63 files changed, 9353 insertions(+), 1443 deletions(-) diff --git a/src/app/admin/achievements/page.tsx b/src/app/admin/achievements/page.tsx index e2e127dc..e4872d11 100644 --- a/src/app/admin/achievements/page.tsx +++ b/src/app/admin/achievements/page.tsx @@ -1,4 +1,5 @@ import { Award } from "lucide-react"; +import { getTranslations } from "next-intl/server"; import { StatusCard } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; @@ -15,6 +16,8 @@ type Achievement = { }; export default async function AdminAchievements() { + const t = await getTranslations("pages.admin.achievements"); + let achievements: Achievement[] = []; try { achievements = await prisma.achievements.findMany({ @@ -33,7 +36,6 @@ export default async function AdminAchievements() { achievements = []; } - // Group by category (already sorted by category, then name, then level). const groups = new Map(); for (const a of achievements) { const list = groups.get(a.category) ?? []; @@ -41,7 +43,6 @@ export default async function AdminAchievements() { groups.set(a.category, list); } - // Distinct achievement names (each name can have multiple level rows). const distinctNames = new Set(achievements.map((a) => a.name)).size; return ( @@ -51,19 +52,19 @@ export default async function AdminAchievements() {
-

Achievements

-

Read-only · {achievements.length} rows across {groups.size} categories.

+

{t("title")}

+

{t("subtitle", { rows: achievements.length, categories: groups.size })}

- - - + + +
{groups.size === 0 ? ( -
No achievements found.
+
{t("noAchievements")}
) : ( [...groups.entries()].map(([category, rows]) => (
@@ -75,12 +76,12 @@ export default async function AdminAchievements() { - - - - - - + + + + + + diff --git a/src/app/admin/ads/page.tsx b/src/app/admin/ads/page.tsx index 28310f4f..b5071fbe 100644 --- a/src/app/admin/ads/page.tsx +++ b/src/app/admin/ads/page.tsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import { getTranslations } from "next-intl/server"; import { Megaphone } from "lucide-react"; import { StatusCard } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; @@ -6,6 +7,7 @@ import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; export default async function AdminAds() { + const t = await getTranslations("pages.admin.ads"); let ads: Awaited> = []; try { ads = await prisma.websiteAds.findMany({ @@ -27,30 +29,30 @@ export default async function AdminAds() {
-

Advertisements

-

Website advertisement banners (website_ads). Each ad is an image URL shown across the site.

+

{t("title")}

+

{t("subtitle")}

- + New ad + + {t("newAd")}
- - + +
-

All ads

+

{t("allAds")}

{ads.length === 0 ? ( -
No advertisements yet.
+
{t("noAds")}
) : (
NameLevelProgress neededReward typeReward amountPoints{t("colName")}{t("colLevel")}{t("colProgress")}{t("colRewardType")}{t("colRewardAmount")}{t("colPoints")}
- - - + + + @@ -68,7 +70,7 @@ export default async function AdminAds() { {ad.createdAt ? ad.createdAt.toISOString().slice(0, 10) : "—"} ))} diff --git a/src/app/admin/alerts/page.tsx b/src/app/admin/alerts/page.tsx index 7ba4471b..47fdccfd 100644 --- a/src/app/admin/alerts/page.tsx +++ b/src/app/admin/alerts/page.tsx @@ -1,3 +1,4 @@ +import { getTranslations } from "next-intl/server"; import { Bell } from "lucide-react"; import { sendHotelAlert } from "@/actions/admin-alerts"; import { StatusCard } from "@/components/admin/dashboard"; @@ -8,16 +9,10 @@ export const dynamic = "force-dynamic"; const CRITICAL = new Set(["critical", "error", "danger"]); const WARNING = new Set(["warning", "warn"]); -// Read-only feed of alert_logs (prisma.alertLogs delegate): the emulator and -// CMS write rows here when hotel/user alerts fire. Auto-gated by the admin -// layout (requireStaff); only the mutation re-asserts staff in the action. - function fromDate(d: Date | null | undefined): string { return d ? d.toISOString().slice(0, 16).replace("T", " ") : "—"; } -// Map a free-text severity to a theme-token colour. Unknown severities fall -// back to muted grey rather than inventing a new palette. function severityColor(severity: string): string { switch (severity.toLowerCase()) { case "critical": @@ -59,6 +54,8 @@ function Badge({ label, color }: { label: string; color: string }) { } export default async function AdminAlerts() { + const t = await getTranslations("pages.admin.alerts"); + let alerts: Awaited> = []; try { alerts = await prisma.alertLogs.findMany({ @@ -80,27 +77,27 @@ export default async function AdminAlerts() {
-

Alerts

-

Read-only · 100 most recent alert_logs (newest first).

+

{t("title")}

+

{t("subtitle")}

- + 0 ? "danger" : "ok"} icon="🚨" /> 0 ? "warn" : "neutral"} icon="⚠️" /> 0 ? "warn" : "ok"} icon="📨" @@ -108,9 +105,11 @@ export default async function AdminAlerts() {
-

Send hotel alert

+

{t("form.send")}

- Broadcasts a message to every online user via RCON (hotelalert). + {t.rich("form.description", { + code: (chunks) => {chunks}, + })}

PreviewImage URLCreated{t("colPreview")}{t("colImage")}{t("colCreated")}
- Edit + {t("edit")}