mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-25 12:44:39 +02:00
feat(i18n): wire user settings locale, add nav translations
- Sync svelte-i18n locale from userSettings.locale (backend) via $effect - Persist language changes to backend via userSettings.updateGlobal - Add nav/ locale module with navigation labels in 5 languages - Replace 6 hardcoded German strings in app layout with $_() calls: "Alle Themes", "Menü", "Geschenke", "Profil", "Einstellungen", etc. - Make baseNavItems reactive ($derived) so labels update on language change Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e152098829
commit
bfe11d91ed
7 changed files with 114 additions and 20 deletions
|
|
@ -15,20 +15,21 @@ const defaultLocale = 'de';
|
||||||
|
|
||||||
function registerLocale(lang: SupportedLocale) {
|
function registerLocale(lang: SupportedLocale) {
|
||||||
register(lang, async () => {
|
register(lang, async () => {
|
||||||
const [common, dashboard, credits, profile, subscription, todo, app_slider] = await Promise.all(
|
const [common, nav, dashboard, credits, profile, subscription, todo, app_slider] =
|
||||||
[
|
await Promise.all([
|
||||||
import(`./locales/common/${lang}.json`),
|
import(`./locales/common/${lang}.json`),
|
||||||
|
import(`./locales/nav/${lang}.json`),
|
||||||
import(`./locales/dashboard/${lang}.json`),
|
import(`./locales/dashboard/${lang}.json`),
|
||||||
import(`./locales/credits/${lang}.json`),
|
import(`./locales/credits/${lang}.json`),
|
||||||
import(`./locales/profile/${lang}.json`),
|
import(`./locales/profile/${lang}.json`),
|
||||||
import(`./locales/subscription/${lang}.json`),
|
import(`./locales/subscription/${lang}.json`),
|
||||||
import(`./locales/todo/${lang}.json`),
|
import(`./locales/todo/${lang}.json`),
|
||||||
import(`./locales/app_slider/${lang}.json`),
|
import(`./locales/app_slider/${lang}.json`),
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
common: common.default,
|
common: common.default,
|
||||||
|
nav: nav.default,
|
||||||
dashboard: dashboard.default,
|
dashboard: dashboard.default,
|
||||||
credits: credits.default,
|
credits: credits.default,
|
||||||
profile: profile.default,
|
profile: profile.default,
|
||||||
|
|
|
||||||
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/de.json
Normal file
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/de.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"home": "Home",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"spiral": "Spiral",
|
||||||
|
"observatory": "Observatory",
|
||||||
|
"credits": "Credits",
|
||||||
|
"gifts": "Geschenke",
|
||||||
|
"api_keys": "API Keys",
|
||||||
|
"profile": "Profil",
|
||||||
|
"settings": "Einstellungen",
|
||||||
|
"tags": "Tags",
|
||||||
|
"admin": "Admin",
|
||||||
|
"all_themes": "Alle Themes",
|
||||||
|
"menu": "Menü",
|
||||||
|
"sign_out": "Abmelden"
|
||||||
|
}
|
||||||
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/en.json
Normal file
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/en.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"home": "Home",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"spiral": "Spiral",
|
||||||
|
"observatory": "Observatory",
|
||||||
|
"credits": "Credits",
|
||||||
|
"gifts": "Gifts",
|
||||||
|
"api_keys": "API Keys",
|
||||||
|
"profile": "Profile",
|
||||||
|
"settings": "Settings",
|
||||||
|
"tags": "Tags",
|
||||||
|
"admin": "Admin",
|
||||||
|
"all_themes": "All Themes",
|
||||||
|
"menu": "Menu",
|
||||||
|
"sign_out": "Sign out"
|
||||||
|
}
|
||||||
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/es.json
Normal file
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/es.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"home": "Inicio",
|
||||||
|
"dashboard": "Panel",
|
||||||
|
"spiral": "Spiral",
|
||||||
|
"observatory": "Observatorio",
|
||||||
|
"credits": "Créditos",
|
||||||
|
"gifts": "Regalos",
|
||||||
|
"api_keys": "API Keys",
|
||||||
|
"profile": "Perfil",
|
||||||
|
"settings": "Ajustes",
|
||||||
|
"tags": "Tags",
|
||||||
|
"admin": "Admin",
|
||||||
|
"all_themes": "Todos los temas",
|
||||||
|
"menu": "Menú",
|
||||||
|
"sign_out": "Cerrar sesión"
|
||||||
|
}
|
||||||
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/fr.json
Normal file
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/fr.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"home": "Accueil",
|
||||||
|
"dashboard": "Tableau de bord",
|
||||||
|
"spiral": "Spiral",
|
||||||
|
"observatory": "Observatoire",
|
||||||
|
"credits": "Crédits",
|
||||||
|
"gifts": "Cadeaux",
|
||||||
|
"api_keys": "Clés API",
|
||||||
|
"profile": "Profil",
|
||||||
|
"settings": "Paramètres",
|
||||||
|
"tags": "Tags",
|
||||||
|
"admin": "Admin",
|
||||||
|
"all_themes": "Tous les thèmes",
|
||||||
|
"menu": "Menu",
|
||||||
|
"sign_out": "Déconnexion"
|
||||||
|
}
|
||||||
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/it.json
Normal file
16
apps/manacore/apps/web/src/lib/i18n/locales/nav/it.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"home": "Home",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"spiral": "Spiral",
|
||||||
|
"observatory": "Osservatorio",
|
||||||
|
"credits": "Crediti",
|
||||||
|
"gifts": "Regali",
|
||||||
|
"api_keys": "Chiavi API",
|
||||||
|
"profile": "Profilo",
|
||||||
|
"settings": "Impostazioni",
|
||||||
|
"tags": "Tag",
|
||||||
|
"admin": "Admin",
|
||||||
|
"all_themes": "Tutti i temi",
|
||||||
|
"menu": "Menu",
|
||||||
|
"sign_out": "Esci"
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
import { onDestroy, setContext } from 'svelte';
|
import { onDestroy, setContext } from 'svelte';
|
||||||
import KeyboardShortcutsModal from '$lib/components/KeyboardShortcutsModal.svelte';
|
import KeyboardShortcutsModal from '$lib/components/KeyboardShortcutsModal.svelte';
|
||||||
import SessionWarning from '$lib/components/SessionWarning.svelte';
|
import SessionWarning from '$lib/components/SessionWarning.svelte';
|
||||||
import { locale } from 'svelte-i18n';
|
import { locale, _ } from 'svelte-i18n';
|
||||||
import {
|
import {
|
||||||
PillNavigation,
|
PillNavigation,
|
||||||
TagStrip,
|
TagStrip,
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
id: 'all-themes',
|
id: 'all-themes',
|
||||||
label: 'Alle Themes',
|
label: $_('nav.all_themes'),
|
||||||
icon: 'palette',
|
icon: 'palette',
|
||||||
onClick: () => goto('/themes'),
|
onClick: () => goto('/themes'),
|
||||||
active: false,
|
active: false,
|
||||||
|
|
@ -95,15 +95,28 @@
|
||||||
let currentLocale = $derived($locale || 'de');
|
let currentLocale = $derived($locale || 'de');
|
||||||
function handleLocaleChange(newLocale: string) {
|
function handleLocaleChange(newLocale: string) {
|
||||||
setLocale(newLocale as any);
|
setLocale(newLocale as any);
|
||||||
|
userSettings.updateGlobal({ locale: newLocale });
|
||||||
AppEvents.languageChanged(newLocale);
|
AppEvents.languageChanged(newLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync locale from user settings (backend) after login
|
||||||
|
$effect(() => {
|
||||||
|
if (userSettings.loaded && userSettings.locale) {
|
||||||
|
const settingsLocale = userSettings.locale;
|
||||||
|
if (supportedLocales.includes(settingsLocale as any) && settingsLocale !== $locale) {
|
||||||
|
setLocale(settingsLocale as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
let languageItems = $derived(
|
let languageItems = $derived(
|
||||||
getLanguageDropdownItems(supportedLocales, currentLocale, handleLocaleChange)
|
getLanguageDropdownItems(supportedLocales, currentLocale, handleLocaleChange)
|
||||||
);
|
);
|
||||||
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
||||||
|
|
||||||
// ── User / Guest awareness ──────────────────────────────
|
// ── User / Guest awareness ──────────────────────────────
|
||||||
let userEmail = $derived(authStore.isAuthenticated ? authStore.user?.email || 'Menü' : '');
|
let userEmail = $derived(
|
||||||
|
authStore.isAuthenticated ? authStore.user?.email || $_('nav.menu') : ''
|
||||||
|
);
|
||||||
|
|
||||||
// ── Tags ────────────────────────────────────────────────
|
// ── Tags ────────────────────────────────────────────────
|
||||||
const allTags = useAllTags();
|
const allTags = useAllTags();
|
||||||
|
|
@ -124,30 +137,30 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Navigation ──────────────────────────────────────────
|
// ── Navigation ──────────────────────────────────────────
|
||||||
const baseNavItems: PillNavItem[] = [
|
let baseNavItems = $derived<PillNavItem[]>([
|
||||||
{ href: '/home', label: 'Home', icon: 'home' },
|
{ href: '/home', label: $_('nav.home'), icon: 'home' },
|
||||||
{ href: '/dashboard', label: 'Dashboard', icon: 'grid' },
|
{ href: '/dashboard', label: $_('nav.dashboard'), icon: 'grid' },
|
||||||
{ href: '/spiral', label: 'Spiral', icon: 'spiral' },
|
{ href: '/spiral', label: $_('nav.spiral'), icon: 'spiral' },
|
||||||
{ href: '/observatory', label: 'Observatory', icon: 'eye' },
|
{ href: '/observatory', label: $_('nav.observatory'), icon: 'eye' },
|
||||||
{ href: '/credits', label: 'Credits', icon: 'creditCard' },
|
{ href: '/credits', label: $_('nav.credits'), icon: 'creditCard' },
|
||||||
{ href: '/gifts', label: 'Geschenke', icon: 'gift' },
|
{ href: '/gifts', label: $_('nav.gifts'), icon: 'gift' },
|
||||||
{ href: '/api-keys', label: 'API Keys', icon: 'key' },
|
{ href: '/api-keys', label: $_('nav.api_keys'), icon: 'key' },
|
||||||
{ href: '/profile', label: 'Profil', icon: 'user' },
|
{ href: '/profile', label: $_('nav.profile'), icon: 'user' },
|
||||||
{ href: '/settings', label: 'Settings', icon: 'settings' },
|
{ href: '/settings', label: $_('nav.settings'), icon: 'settings' },
|
||||||
{
|
{
|
||||||
href: '/',
|
href: '/',
|
||||||
label: 'Tags',
|
label: $_('nav.tags'),
|
||||||
icon: 'tag',
|
icon: 'tag',
|
||||||
onClick: handleTagStripToggle,
|
onClick: handleTagStripToggle,
|
||||||
active: isTagStripVisible,
|
active: isTagStripVisible,
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
let isAdmin = $derived(authStore.user?.role === 'admin');
|
let isAdmin = $derived(authStore.user?.role === 'admin');
|
||||||
let navItems = $derived<PillNavItem[]>(
|
let navItems = $derived<PillNavItem[]>(
|
||||||
isAdmin ? [...baseNavItems, { href: '/admin', label: 'Admin', icon: 'shield' }] : baseNavItems
|
isAdmin ? [...baseNavItems, { href: '/admin', label: 'Admin', icon: 'shield' }] : baseNavItems
|
||||||
);
|
);
|
||||||
const navRoutes = navItems.map((item) => item.href);
|
let navRoutes = $derived(navItems.map((item) => item.href));
|
||||||
|
|
||||||
function handleKeydown(event: KeyboardEvent) {
|
function handleKeydown(event: KeyboardEvent) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue