Translate admin panel to all 6 languages

This commit is contained in:
openhands committed 2026-07-04 21:21:15 +02:00
1 parent f381aa987f
commit befa4ec282
63 files changed
+9353 -1443

No files matched your search

+27 -28
View File
@@ -3,11 +3,10 @@ import { saveRadioSetting, saveRadioSettings } from '@/actions/admin-radio-extra
import { prisma } from '@/lib/prisma';
import { StatusCard } from '@/components/admin/dashboard';
import { Settings } from 'lucide-react';
import { getTranslations } from "next-intl/server";
export const dynamic = 'force-dynamic';
// Curated radio_* keys (faithful to AtomCMS RadioSettingsSeeder), grouped for
// the editor. Any extra radio_* rows found in the DB are appended under "Other".
type Field = { key: string; comment: string };
type Group = { title: string; fields: Field[] };
@@ -184,6 +183,8 @@ const GROUPS: Group[] = [
const CURATED_KEYS = new Set(GROUPS.flatMap((g) => g.fields.map((f) => f.key)));
export default async function AdminRadioSettingsPage() {
const t = await getTranslations("pages.admin.radio");
let values: Map<string, string> = new Map();
let dbError = false;
let extra: Field[] = [];
@@ -205,13 +206,12 @@ export default async function AdminRadioSettingsPage() {
}
const groups: Group[] = extra.length
? [...GROUPS, { title: 'Other radio settings', fields: extra }]
? [...GROUPS, { title: t("settingsForm.otherSettings"), fields: extra }]
: GROUPS;
const allKeys = [...CURATED_KEYS, ...extra.map((e) => e.key)].join(',');
const totalKeys = CURATED_KEYS.size + extra.length;
// Curated keys that already have a non-empty stored value.
const configuredKeys = [...CURATED_KEYS].filter((k) => {
const v = values.get(k);
return v != null && v !== '';
@@ -221,11 +221,11 @@ export default async function AdminRadioSettingsPage() {
return (
<main>
<nav className="text-xs text-gray-500 dark:text-gray-400 mb-2">
<Link href="/admin/radio">Radio</Link> ·{' '}
<Link href="/admin/radio/settings">Settings</Link> ·{' '}
<Link href="/admin/radio/banners">Banners</Link> ·{' '}
<Link href="/admin/radio/ranks">Ranks</Link> ·{' '}
<Link href="/admin/radio/history">History</Link>
<Link href="/admin/radio">{t("title")}</Link> ·{' '}
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{' '}
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{' '}
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{' '}
<Link href="/admin/radio/history">{t("history")}</Link>
</nav>
<div className="flex items-center gap-3 mb-6">
@@ -233,38 +233,37 @@ export default async function AdminRadioSettingsPage() {
<Settings size={20} className="text-[var(--color-primary)]" />
</div>
<div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">Radio Settings</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">Edits write to <code>website_settings</code> (radio_* keys) and bust the runtime settings cache. Booleans use <code>0</code> / <code>1</code>.</p>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("settingsForm.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("settingsForm.subtitle")}</p>
</div>
</div>
{dbError ? (
<div className="admin-card mb-6">
<p className="text-xs text-gray-500 dark:text-gray-400 m-0">
Could not load settings (database unavailable). Defaults will be
created on save.
{t("settingsForm.dbError")}
</p>
</div>
) : (
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
<StatusCard
label="Radio"
value={radioEnabled ? 'Enabled' : 'Disabled'}
value={radioEnabled ? t("settingsForm.enabled") : t("settingsForm.disabled")}
state={radioEnabled ? 'ok' : 'danger'}
icon="📻"
icon="\u{1F4BB}"
/>
<StatusCard label="Total keys" value={totalKeys} icon="🔑" />
<StatusCard label={t("settingsForm.totalKeys")} value={totalKeys} icon="\u{1F511}" />
<StatusCard
label="Configured"
label={t("settingsForm.configured")}
value={`${configuredKeys}/${CURATED_KEYS.size}`}
hint="curated keys with a value"
icon="⚙️"
hint={t("settingsForm.curatedHint")}
icon="\u{2699}\uFE0F"
/>
<StatusCard
label="Custom keys"
label={t("settingsForm.customKeys")}
value={extra.length}
state={extra.length > 0 ? 'warn' : 'neutral'}
icon="➕"
icon="\u{2795}"
/>
</div>
)}
@@ -273,7 +272,7 @@ export default async function AdminRadioSettingsPage() {
<input type="hidden" name="__keys" value={allKeys} />
<div className="flex justify-end mb-4">
<button type="submit" className="btn btn-primary">
Save all
{t("settingsForm.saveAll")}
</button>
</div>
@@ -308,19 +307,19 @@ export default async function AdminRadioSettingsPage() {
<div className="flex justify-end">
<button type="submit" className="btn btn-primary">
Save all
{t("settingsForm.saveAll")}
</button>
</div>
</form>
<section className="admin-card" style={{ marginTop: '1.5rem' }}>
<h3 style={{ marginTop: 0 }}>Add / overwrite a single key</h3>
<h3 style={{ marginTop: 0 }}>{t("settingsForm.singleKey")}</h3>
<form action={saveRadioSetting} className="flex gap-2 flex-wrap">
<input name="key" placeholder="radio_custom_key" required />
<input name="value" placeholder="value" className="flex-1 min-w-[160px]" />
<input name="comment" placeholder="comment (optional)" />
<input name="key" placeholder={t("settingsForm.keyPlaceholder")} required />
<input name="value" placeholder={t("settingsForm.valuePlaceholder")} className="flex-1 min-w-[160px]" />
<input name="comment" placeholder={t("settingsForm.commentPlaceholder")} />
<button type="submit" className="btn btn-primary">
Save
{t("settingsForm.saveSingle")}
</button>
</form>
</section>