diff --git a/src/app/globals.css b/src/app/globals.css index f89f3acd..fd630c57 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1011,6 +1011,21 @@ tbody tr:hover { .content-card-body { padding: 0.85rem; } + .nav-item { + white-space: normal; + text-wrap: balance; + font-size: 12px; + padding: 0.5rem 0.75rem; + height: auto; + min-height: 44px; + } + .nav-item::after { + display: none; + } + .dropdown-item { + font-size: 13px; + padding: 0.6rem 0.85rem; + } } /* make tables scroll on small screens */ diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index 2d22ddae..bca1be86 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -34,7 +34,8 @@ export async function Navigation() { - +
+
+
diff --git a/src/i18n/request.ts b/src/i18n/request.ts index eefe19ee..c82e31fb 100644 --- a/src/i18n/request.ts +++ b/src/i18n/request.ts @@ -1,9 +1,8 @@ -import { cookies } from "next/headers"; +import { cookies, headers } from "next/headers"; import { getRequestConfig } from "next-intl/server"; -// i18n WITHOUT routing: the locale is chosen by a `NEXT_LOCALE` cookie (set by -// the language switcher) rather than a URL segment, so every existing route and -// the access-guard middleware keep working unchanged. English is the fallback. +// i18n WITHOUT routing: the locale is chosen by a `NEXT_LOCALE` cookie, then +// the Accept-Language header, and finally English as fallback. export const SUPPORTED_LOCALES = ["en", "it", "nl", "de", "fr", "es", "pt", "pl", "sv", "tr", "ro", "hu", "cs", "sk", "da", "no", "el", "bg", "hr", "sr", "uk", "ru"] as const; export type AppLocale = (typeof SUPPORTED_LOCALES)[number]; export const DEFAULT_LOCALE: AppLocale = "en"; @@ -12,10 +11,37 @@ export function isSupportedLocale(value: string | undefined): value is AppLocale return !!value && (SUPPORTED_LOCALES as readonly string[]).includes(value); } +function detectLocaleFromAcceptLanguage(acceptLanguage: string | null): AppLocale | null { + if (!acceptLanguage) return null; + const locales = acceptLanguage + .split(",") + .map((entry) => { + const [tag, qPart] = entry.split(";"); + const quality = qPart ? parseFloat(qPart.replace("q=", "")) : 1; + const lang = tag?.trim().split("-")[0] ?? ""; + return { lang, quality }; + }) + .filter((l) => l.lang) + .sort((a, b) => b.quality - a.quality); + + for (const { lang } of locales) { + if (isSupportedLocale(lang)) return lang; + } + return null; +} + export default getRequestConfig(async () => { const store = await cookies(); const cookieLocale = store.get("NEXT_LOCALE")?.value; - const locale: AppLocale = isSupportedLocale(cookieLocale) ? cookieLocale : DEFAULT_LOCALE; + + let locale: AppLocale; + if (isSupportedLocale(cookieLocale)) { + locale = cookieLocale; + } else { + const headersList = await headers(); + const detected = detectLocaleFromAcceptLanguage(headersList.get("Accept-Language")); + locale = detected ?? DEFAULT_LOCALE; + } // Safe: `locale` is validated against SUPPORTED_LOCALES above (only en/it/nl/de/fr/es). const messages = (await import(`../messages/${locale}.json`)).default;