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, {