added missing translations

This commit is contained in:
Codex Agent
2025-11-26 14:41:39 +01:00
parent ff168834b4
commit ecac9507a4
35 changed files with 2812 additions and 256 deletions

View File

@@ -294,12 +294,8 @@ function PageTabsNav({ tabs, currentKey }: { tabs: PageTab[]; currentKey?: strin
type="button"
className="flex w-full items-center justify-between rounded-2xl border border-slate-200/70 bg-white px-3 py-2 text-left text-sm font-semibold text-slate-700 shadow-sm dark:border-white/10 dark:bg-white/10 dark:text-slate-200"
>
<span>
{activeTab?.label ?? t('navigation.tabs.active', { defaultValue: 'Bereich wählen' })}
</span>
<span className="text-xs uppercase tracking-[0.3em] text-rose-500">
{t('navigation.tabs.open', { defaultValue: 'Tabs' })}
</span>
<span>{activeTab?.label ?? t('navigation.tabs.active')}</span>
<span className="text-xs uppercase tracking-[0.3em] text-rose-500">{t('navigation.tabs.open')}</span>
</button>
</SheetTrigger>
<SheetContent
@@ -308,10 +304,10 @@ function PageTabsNav({ tabs, currentKey }: { tabs: PageTab[]; currentKey?: strin
>
<SheetHeader className="px-4 pt-0 text-left">
<SheetTitle className="text-lg font-semibold text-slate-900 dark:text-white">
{t('navigation.tabs.title', { defaultValue: 'Bereich auswählen' })}
{t('navigation.tabs.title')}
</SheetTitle>
<SheetDescription>
{t('navigation.tabs.subtitle', { defaultValue: 'Wechsle schnell zwischen Event-Bereichen.' })}
{t('navigation.tabs.subtitle')}
</SheetDescription>
</SheetHeader>
<div className="mt-4 grid gap-2 px-4">

View File

@@ -34,7 +34,7 @@ interface TenantNotification {
export function NotificationCenter() {
const navigate = useNavigate();
const { t } = useTranslation('dashboard');
const { t } = useTranslation('management');
const [open, setOpen] = React.useState(false);
const [loading, setLoading] = React.useState(true);
const [notifications, setNotifications] = React.useState<TenantNotification[]>([]);
@@ -104,7 +104,7 @@ export function NotificationCenter() {
variant="ghost"
size="icon"
className="relative rounded-full border border-transparent text-slate-600 hover:text-rose-600 dark:text-slate-200"
aria-label={t('notifications.trigger', { defaultValue: 'Benachrichtigungen' })}
aria-label={t('notifications.trigger')}
>
<Bell className="h-5 w-5" />
{unreadCount > 0 ? (
@@ -116,9 +116,9 @@ export function NotificationCenter() {
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-80 space-y-1 p-0">
<DropdownMenuLabel className="flex items-center justify-between py-2">
<span>{t('notifications.title', { defaultValue: 'Notifications' })}</span>
<span>{t('notifications.title')}</span>
{!loading && unreadCount === 0 ? (
<Badge variant="outline">{t('notifications.empty', { defaultValue: 'Aktuell ruhig' })}</Badge>
<Badge variant="outline">{t('notifications.empty')}</Badge>
) : null}
</DropdownMenuLabel>
<DropdownMenuSeparator />
@@ -131,7 +131,7 @@ export function NotificationCenter() {
<div className="max-h-80 space-y-1 overflow-y-auto p-1">
{visibleNotifications.length === 0 ? (
<p className="px-3 py-4 text-sm text-slate-500">
{t('notifications.empty.message', { defaultValue: 'Alles erledigt wir melden uns bei Neuigkeiten.' })}
{t('notifications.empty.message')}
</p>
) : (
visibleNotifications.map((item) => (
@@ -163,7 +163,7 @@ export function NotificationCenter() {
className="h-7 rounded-full px-3 text-xs text-slate-500 hover:text-rose-600"
onClick={() => handleDismiss(item.id)}
>
{t('notifications.action.dismiss', { defaultValue: 'Ausblenden' })}
{t('notifications.action.dismiss')}
</Button>
</div>
</div>
@@ -183,7 +183,7 @@ export function NotificationCenter() {
}}
>
<Plus className="h-3.5 w-3.5" />
{t('notifications.action.refresh', { defaultValue: 'Neue Hinweise laden' })}
{t('notifications.action.refresh')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -208,13 +208,11 @@ function buildNotifications({
if (events.length === 0) {
items.push({
id: 'no-events',
title: t('notifications.noEvents.title', { defaultValue: 'Legen wir los' }),
description: t('notifications.noEvents.description', {
defaultValue: 'Erstelle dein erstes Event, um Uploads, Aufgaben und Einladungen freizuschalten.',
}),
title: t('notifications.noEvents.title'),
description: t('notifications.noEvents.description'),
tone: 'warning',
action: {
label: t('notifications.noEvents.cta', { defaultValue: 'Event erstellen' }),
label: t('notifications.noEvents.cta'),
onSelect: () => navigate(ADMIN_EVENT_CREATE_PATH),
},
});
@@ -226,14 +224,12 @@ function buildNotifications({
if (event.status !== 'published') {
items.push({
id: `draft-${event.id}`,
title: t('notifications.draftEvent.title', { defaultValue: 'Event noch als Entwurf' }),
description: t('notifications.draftEvent.description', {
defaultValue: 'Veröffentliche das Event, um Einladungen und Galerie freizugeben.',
}),
title: t('notifications.draftEvent.title'),
description: t('notifications.draftEvent.description'),
tone: 'info',
action: event.slug
? {
label: t('notifications.draftEvent.cta', { defaultValue: 'Event öffnen' }),
label: t('notifications.draftEvent.cta'),
onSelect: () => navigate(ADMIN_EVENT_VIEW_PATH(event.slug!)),
}
: undefined,
@@ -244,37 +240,33 @@ function buildNotifications({
if (eventDate && eventDate > now) {
const days = Math.round((eventDate - now) / (1000 * 60 * 60 * 24));
if (days <= 7) {
items.push({
id: `upcoming-${event.id}`,
title: t('notifications.upcomingEvent.title', { defaultValue: 'Event startet bald' }),
description: t('notifications.upcomingEvent.description', {
defaultValue: days === 0
? 'Heute findet ein Event statt checke Uploads und Tasks.'
: `Noch ${days} Tage bereite Einladungen und Aufgaben vor.`,
}),
tone: 'info',
action: event.slug
? {
label: t('notifications.upcomingEvent.cta', { defaultValue: 'Zum Event' }),
onSelect: () => navigate(ADMIN_EVENT_VIEW_PATH(event.slug!)),
}
: undefined,
});
}
items.push({
id: `upcoming-${event.id}`,
title: t('notifications.upcomingEvent.title'),
description: days === 0
? t('notifications.upcomingEvent.description_today')
: t('notifications.upcomingEvent.description_days', { count: days }),
tone: 'info',
action: event.slug
? {
label: t('notifications.upcomingEvent.cta'),
onSelect: () => navigate(ADMIN_EVENT_VIEW_PATH(event.slug!)),
}
: undefined,
});
}
}
const pendingUploads = Number(event.pending_photo_count ?? 0);
if (pendingUploads > 0) {
items.push({
id: `pending-uploads-${event.id}`,
title: t('notifications.pendingUploads.title', { defaultValue: 'Uploads warten auf Freigabe' }),
description: t('notifications.pendingUploads.description', {
defaultValue: `${pendingUploads} neue Uploads benötigen Moderation.`,
}),
title: t('notifications.pendingUploads.title'),
description: t('notifications.pendingUploads.description', { count: pendingUploads }),
tone: 'warning',
action: event.slug
? {
label: t('notifications.pendingUploads.cta', { defaultValue: 'Uploads öffnen' }),
label: t('notifications.pendingUploads.cta'),
onSelect: () => navigate(`${ADMIN_EVENT_VIEW_PATH(event.slug!)}#photos`),
}
: undefined,
@@ -285,18 +277,16 @@ function buildNotifications({
if ((summary?.new_photos ?? 0) > 0) {
items.push({
id: 'summary-new-photos',
title: t('notifications.newPhotos.title', { defaultValue: 'Neue Fotos eingetroffen' }),
description: t('notifications.newPhotos.description', {
defaultValue: `${summary?.new_photos ?? 0} Uploads warten auf dich.`,
}),
title: t('notifications.newPhotos.title'),
description: t('notifications.newPhotos.description', { count: summary?.new_photos ?? 0 }),
tone: 'success',
action: primary?.slug
? {
label: t('notifications.newPhotos.cta', { defaultValue: 'Galerie öffnen' }),
label: t('notifications.newPhotos.cta'),
onSelect: () => navigate(ADMIN_EVENT_VIEW_PATH(primary.slug!)),
}
: {
label: t('notifications.newPhotos.ctaFallback', { defaultValue: 'Events ansehen' }),
label: t('notifications.newPhotos.ctaFallback'),
onSelect: () => navigate(ADMIN_EVENTS_PATH),
},
});