From fd8990e258d6d5aeec178623d1fe0feffd398dc4 Mon Sep 17 00:00:00 2001 From: Karl Ludwig Weise Date: Fri, 12 Sep 2025 16:28:58 +0200 Subject: [PATCH] Properly detect locale (#80) * Properly detect locale * Fix test --------- Co-authored-by: Karl Ludwig Weise --- src/app.d.ts | 2 ++ src/routes/(pages)/+layout.server.ts | 7 +++++++ src/routes/(pages)/+layout.svelte | 9 +++++++-- src/routes/(pages)/page.svelte.test.ts | 2 ++ src/server-hooks/i18nHandle.ts | 18 +++++++++++++++++- 5 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/routes/(pages)/+layout.server.ts diff --git a/src/app.d.ts b/src/app.d.ts index 11a7753..d7e42ae 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,5 +1,6 @@ // See https://svelte.dev/docs/kit/types#app.d.ts +import type { Locale } from "$i18n/runtime"; import type { UserRole } from "$lib/server/auth/authorization-service"; import type { JWTPayload } from "jose"; @@ -16,6 +17,7 @@ declare global { role: UserRole; tenantId?: string | null; }; + locale?: Locale; } // interface PageData {} // interface PageState {} diff --git a/src/routes/(pages)/+layout.server.ts b/src/routes/(pages)/+layout.server.ts new file mode 100644 index 0000000..0f9c939 --- /dev/null +++ b/src/routes/(pages)/+layout.server.ts @@ -0,0 +1,7 @@ +import type { LayoutServerLoad } from "./$types"; + +export const load: LayoutServerLoad = async ({ locals }) => { + return { + locale: locals.locale, + }; +}; diff --git a/src/routes/(pages)/+layout.svelte b/src/routes/(pages)/+layout.svelte index 29709d6..33391c3 100644 --- a/src/routes/(pages)/+layout.svelte +++ b/src/routes/(pages)/+layout.svelte @@ -3,10 +3,17 @@ import { Toaster } from "$lib/components/ui/sonner/index.js"; import { ModeWatcher, setMode } from "mode-watcher"; import "../../app.css"; + import { setLocale } from "$i18n/runtime"; + + let { data, children } = $props(); $effect(() => { if (!browser) return; + if (data.locale) { + setLocale(data.locale); + } + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const handler = () => { const newMode = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; @@ -16,8 +23,6 @@ mediaQuery.addEventListener("change", handler); return () => mediaQuery.removeEventListener("change", handler); }); - - let { children } = $props(); diff --git a/src/routes/(pages)/page.svelte.test.ts b/src/routes/(pages)/page.svelte.test.ts index f47f961..ae95885 100644 --- a/src/routes/(pages)/page.svelte.test.ts +++ b/src/routes/(pages)/page.svelte.test.ts @@ -2,6 +2,7 @@ import { describe, test, expect, vi, beforeEach } from "vitest"; import "@testing-library/jest-dom/vitest"; import { render, screen } from "@testing-library/svelte"; import Page from "./+page.svelte"; +import type { Locale } from "$i18n/runtime"; describe("/+page.svelte", () => { beforeEach(() => { @@ -15,6 +16,7 @@ describe("/+page.svelte", () => { test("should render h1", async () => { const mockData = { + locale: "en" as Locale, streamed: { isEnvOk: Promise.resolve(true), }, diff --git a/src/server-hooks/i18nHandle.ts b/src/server-hooks/i18nHandle.ts index 8368a14..472a137 100644 --- a/src/server-hooks/i18nHandle.ts +++ b/src/server-hooks/i18nHandle.ts @@ -1,5 +1,11 @@ -import type { Handle } from "@sveltejs/kit"; +import { + extractLocaleFromCookie, + extractLocaleFromHeader, + setLocale, + type Locale, +} from "$i18n/runtime"; import { paraglideMiddleware } from "$i18n/server"; +import type { Handle } from "@sveltejs/kit"; /** * SvelteKit server hook that handles i18n localization @@ -10,6 +16,16 @@ import { paraglideMiddleware } from "$i18n/server"; * @returns {Promise} The response with applied headers and rate limiting */ export const i18nHandle: Handle = ({ event, resolve }) => { + // Determine browser locale and use it + const cookieLocale = extractLocaleFromCookie(); + let locale: Locale | undefined = cookieLocale as Locale; + + if (!locale) { + locale = extractLocaleFromHeader(event.request); + setLocale(locale); + event.locals.locale = locale; + } + return paraglideMiddleware(event.request, ({ request: localizedRequest, locale }) => { event.request = localizedRequest; return resolve(event, {