diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..054d599c
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "printWidth": 120
+}
diff --git a/src/components/LocaleSwitcher.svelte b/src/components/LocaleSwitcher.svelte
index 2415008c..1940dae6 100644
--- a/src/components/LocaleSwitcher.svelte
+++ b/src/components/LocaleSwitcher.svelte
@@ -1,70 +1,53 @@
-
- {#each supportedLocales as { code, label }}
-
- {/each}
+
+
+
-
-
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index 452cf0b1..d5df0857 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -1,10 +1,32 @@
-import { locale } from "svelte-i18n";
+import { sequence } from "@sveltejs/kit/hooks";
+import { init, locale } from "svelte-i18n";
import type { Handle } from "@sveltejs/kit";
+import { config, hasTranslations, isValidLocale } from "$lib/i18n";
-export const handle: Handle = async ({ event, resolve }) => {
- const lang = event.request.headers.get("accept-language")?.split(",")[0];
- if (lang) {
- locale.set(lang);
+const i18n: Handle = async ({ event, resolve }) => {
+ const urlLocale = event.params.locale;
+ const acceptLang = event.request.headers.get("accept-language")?.split(",")[0]?.split("-")[0];
+
+ let resolvedLocale = config.defaultLocale;
+
+ if (urlLocale && isValidLocale(urlLocale) && hasTranslations(urlLocale)) {
+ resolvedLocale = urlLocale;
+ } else if (acceptLang && isValidLocale(acceptLang) && hasTranslations(acceptLang)) {
+ resolvedLocale = acceptLang;
}
+
+ // Initialize i18n for SSR if not already initialized
+ try {
+ locale.set(resolvedLocale);
+ } catch {
+ init({
+ fallbackLocale: config.defaultLocale,
+ initialLocale: resolvedLocale,
+ });
+ }
+
+ event.locals.locale = resolvedLocale;
return resolve(event);
};
+
+export const handle = sequence(i18n);
diff --git a/src/lib/i18n/config.ts b/src/lib/i18n/config.ts
new file mode 100644
index 00000000..3dd00ac8
--- /dev/null
+++ b/src/lib/i18n/config.ts
@@ -0,0 +1,15 @@
+import type { LocaleConfig, SupportedLocale } from "./types";
+
+export const config: LocaleConfig = {
+ supportedLocales: ["en", "es", "ca", "fr", "de"],
+ defaultLocale: "en",
+ availableLocales: ["en", "es"], // locales with actual translations
+};
+
+export function isValidLocale(locale: string): locale is SupportedLocale {
+ return config.supportedLocales.includes(locale as SupportedLocale);
+}
+
+export function hasTranslations(locale: SupportedLocale): boolean {
+ return config.availableLocales.includes(locale);
+}
diff --git a/src/lib/i18n/index.ts b/src/lib/i18n/index.ts
index 72d5a30d..cd41f14a 100644
--- a/src/lib/i18n/index.ts
+++ b/src/lib/i18n/index.ts
@@ -1,42 +1,37 @@
import { browser } from "$app/environment";
-import { init, register } from "svelte-i18n";
+import { init, register, getLocaleFromNavigator } from "svelte-i18n";
+import { config, hasTranslations } from "./config";
import type { SupportedLocale } from "./types";
-const defaultLocale: SupportedLocale = "es";
-const supportedLocales: SupportedLocale[] = ["en", "es"];
+let initializationPromise: Promise
| null = null;
-register("en", () => import("./locales/en.json").catch(() => ({})));
-register("es", () => import("./locales/es.json").catch(() => ({})));
-
-/**
- * Determines the initial locale based on the following priority:
- * 1. Previously saved locale in localStorage
- * 2. Browser's language
- * 3. Default locale
- */
-const getInitialLocale = (): SupportedLocale => {
- if (!browser) return defaultLocale;
-
- try {
- const savedLocale = localStorage.getItem("locale") as SupportedLocale;
- if (savedLocale && supportedLocales.includes(savedLocale)) {
- return savedLocale;
- }
-
- const browserLocale = window.navigator.language.split(
- "-",
- )[0] as SupportedLocale;
- return supportedLocales.includes(browserLocale)
- ? browserLocale
- : defaultLocale;
- } catch {
- return defaultLocale;
- }
-};
-
-init({
- fallbackLocale: defaultLocale,
- initialLocale: getInitialLocale(),
+// Pre-register all available locales
+config.availableLocales.forEach((locale) => {
+ register(locale, () => import(`./locales/${locale}.json`).then((module) => module.default));
});
-export { supportedLocales, defaultLocale };
+export function getBrowserLocale(): SupportedLocale | null {
+ if (!browser) return null;
+ const navigatorLocale = getLocaleFromNavigator()?.split("-")[0] as SupportedLocale;
+ return hasTranslations(navigatorLocale) ? navigatorLocale : null;
+}
+
+export async function initI18n() {
+ if (initializationPromise) return initializationPromise;
+
+ initializationPromise = (async () => {
+ const initLocale = browser ? getBrowserLocale() || config.defaultLocale : config.defaultLocale;
+
+ init({
+ fallbackLocale: config.defaultLocale,
+ initialLocale: initLocale,
+ });
+ })();
+
+ return initializationPromise;
+}
+
+// Remove loadLocaleData as it's no longer needed
+export * from "./config";
+export { config, isValidLocale, hasTranslations } from "./config";
+export type { LocaleConfig, SupportedLocale } from "./types";
diff --git a/src/lib/i18n/store.ts b/src/lib/i18n/store.ts
new file mode 100644
index 00000000..0e83a23b
--- /dev/null
+++ b/src/lib/i18n/store.ts
@@ -0,0 +1,3 @@
+import { writable } from "svelte/store";
+
+export const locale = writable("en");
diff --git a/src/lib/i18n/types.ts b/src/lib/i18n/types.ts
index c2c715fb..805c278f 100644
--- a/src/lib/i18n/types.ts
+++ b/src/lib/i18n/types.ts
@@ -1,6 +1,7 @@
-export type SupportedLocale = "en" | "es";
+export type SupportedLocale = "en" | "es" | "ca" | "fr" | "de";
export interface LocaleConfig {
- code: SupportedLocale;
- label: string;
+ supportedLocales: SupportedLocale[];
+ defaultLocale: SupportedLocale;
+ availableLocales: SupportedLocale[];
}
diff --git a/src/params/locale.ts b/src/params/locale.ts
new file mode 100644
index 00000000..ce43d099
--- /dev/null
+++ b/src/params/locale.ts
@@ -0,0 +1,5 @@
+import { isValidLocale } from "$lib/i18n";
+
+export const match = (param: string) => {
+ return isValidLocale(param);
+};
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
deleted file mode 100644
index c0e99765..00000000
--- a/src/routes/+layout.svelte
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-{#if $isLoading}
- Loading...
-{:else}
-
-{/if}
-
-
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
index 2b76143f..0ea7f440 100644
--- a/src/routes/+layout.ts
+++ b/src/routes/+layout.ts
@@ -1,20 +1,7 @@
-import { locale, waitLocale } from "svelte-i18n";
-import { browser } from "$app/environment";
-import "$lib/i18n";
import type { LayoutLoad } from "./$types";
-import { defaultLocale } from "$lib/i18n";
+import { initI18n } from "$lib/i18n";
export const load: LayoutLoad = async () => {
- try {
- if (browser) {
- await locale.set(window.navigator.language);
- }
- await waitLocale();
- } catch (error) {
- console.error("Failed to load locale:", error);
- if (browser) {
- await locale.set(defaultLocale);
- }
- }
+ await initI18n();
return {};
};
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
deleted file mode 100644
index ec8182cb..00000000
--- a/src/routes/+page.svelte
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-{$_("greeting")}
-{$_("farewell")}
-
-
-
diff --git a/src/routes/[[locale]]/+layout.svelte b/src/routes/[[locale]]/+layout.svelte
new file mode 100644
index 00000000..57f227d8
--- /dev/null
+++ b/src/routes/[[locale]]/+layout.svelte
@@ -0,0 +1,18 @@
+
+
+{#if $isLoading}
+ Loading...
+{:else}
+
+{/if}
diff --git a/src/routes/[[locale]]/+layout.ts b/src/routes/[[locale]]/+layout.ts
new file mode 100644
index 00000000..0f653e58
--- /dev/null
+++ b/src/routes/[[locale]]/+layout.ts
@@ -0,0 +1,22 @@
+import { redirect } from "@sveltejs/kit";
+import type { LayoutLoad } from "./$types";
+import { getBrowserLocale, hasTranslations, isValidLocale } from "$lib/i18n";
+import { config } from "$lib/i18n/config";
+
+export const load: LayoutLoad = async ({ params, url, parent }) => {
+ const parentData = await parent();
+ const urlLocale = params.locale;
+
+ if (!urlLocale) {
+ const browserLocale = getBrowserLocale();
+ const locale = browserLocale || config.defaultLocale;
+ return { locale, ...parentData };
+ }
+
+ if (!isValidLocale(urlLocale) || !hasTranslations(urlLocale)) {
+ const redirectUrl = url.pathname.replace(`/${urlLocale}`, "");
+ throw redirect(301, redirectUrl);
+ }
+
+ return { locale: urlLocale, ...parentData };
+};
diff --git a/src/routes/[[locale]]/+page.svelte b/src/routes/[[locale]]/+page.svelte
new file mode 100644
index 00000000..af9c9881
--- /dev/null
+++ b/src/routes/[[locale]]/+page.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
{$_("greeting")}
+
{$_("farewell")}
+
Current locale: {data.locale}
+
+
+