feat(i18n): detect browser language as default locale

On first visit (no PARAGLIDE_LOCALE cookie), parse the Accept-Language
request header and set the cookie to the best matching supported locale
(de/en/es). The user's manual choice via the switcher always takes
precedence since the detection is skipped when the cookie exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-19 18:55:45 +01:00
committed by marcel
parent 20313de4e9
commit c4be2eb46e

View File

@@ -2,9 +2,34 @@ import { redirect, type Handle, type HandleFetch } from '@sveltejs/kit';
import { paraglideMiddleware } from '$lib/paraglide/server';
import { sequence } from '@sveltejs/kit/hooks';
import { env } from 'process';
import { cookieName, cookieMaxAge, locales } from '$lib/paraglide/runtime';
const PUBLIC_PATHS = ['/login', '/logout'];
function detectLocale(acceptLanguage: string): string | null {
const preferred = acceptLanguage
.split(',')
.map((part) => {
const [lang, q] = part.trim().split(';q=');
return { lang: lang.trim().split('-')[0].toLowerCase(), q: q ? parseFloat(q) : 1 };
})
.sort((a, b) => b.q - a.q);
for (const { lang } of preferred) {
if ((locales as readonly string[]).includes(lang)) return lang;
}
return null;
}
const handleLocaleDetection: Handle = ({ event, resolve }) => {
if (!event.cookies.get(cookieName)) {
const locale = detectLocale(event.request.headers.get('accept-language') ?? '');
if (locale) {
event.cookies.set(cookieName, locale, { path: '/', sameSite: 'lax', maxAge: cookieMaxAge });
}
}
return resolve(event);
};
const handleAuth: Handle = async ({ event, resolve }) => {
const isPublic = PUBLIC_PATHS.some((p) => event.url.pathname.startsWith(p));
if (!isPublic && !event.locals.user) {
@@ -74,4 +99,4 @@ export const handleFetch: HandleFetch = async ({ event, request, fetch }) => {
return fetch(request);
};
export const handle = sequence(userGroup, handleAuth, handleParaglide);
export const handle = sequence(userGroup, handleAuth, handleLocaleDetection, handleParaglide);