From f6578c5bf75abc0009395b9cc1d4bfae7ef19ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Thu, 5 Jun 2025 19:13:50 -0300 Subject: [PATCH] Remove translation handling with cookies --- hooks.server.ts | 21 ------------- src/lib/translations/index.js | 9 ++++-- src/routes/+layout.js | 54 +++++++++++++++++++++++++++------- src/routes/+layout.server.js | 55 ++++++++++++++--------------------- src/routes/+layout.svelte | 23 +++++++++++---- 5 files changed, 90 insertions(+), 72 deletions(-) delete mode 100644 hooks.server.ts diff --git a/hooks.server.ts b/hooks.server.ts deleted file mode 100644 index 6dc3214..0000000 --- a/hooks.server.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Handle } from '@sveltejs/kit'; - -export const handle: Handle = async ({ event, resolve }) => { - // Correct origin behind Nginx - const host = event.request.headers.get('x-forwarded-host') ?? event.request.headers.get('host'); - const proto = event.request.headers.get('x-forwarded-proto') ?? 'https'; - const origin = `${proto}://${host}`; - - // Example: force HTTPS (optional, Nginx should already do this) - if (proto === 'http') { - return new Response(null, { - status: 308, - headers: { - Location: origin + event.url.pathname + event.url.search - } - }); - } - - // Proceed with default behavior - return resolve(event); -}; diff --git a/src/lib/translations/index.js b/src/lib/translations/index.js index e354002..6fc26cb 100644 --- a/src/lib/translations/index.js +++ b/src/lib/translations/index.js @@ -127,7 +127,10 @@ export const { } = new i18n(config); locale.subscribe(($locale) => { - // if (typeof document !== 'undefined') { - // document.cookie = `locale=${$locale}; path=/; SameSite=None; Secure`; - // } + if (typeof localStorage !== 'undefined' && $locale) { + const existing = localStorage.getItem('locale'); + if (existing !== $locale) { + localStorage.setItem('locale', $locale); + } + } }); diff --git a/src/routes/+layout.js b/src/routes/+layout.js index 89b2c80..bf7ed0a 100644 --- a/src/routes/+layout.js +++ b/src/routes/+layout.js @@ -1,17 +1,51 @@ -import { setLocale, setRoute } from '$lib/translations'; +import { browser } from '$app/environment'; +import { loadTranslations, setLocale, setRoute } from '$lib/translations'; +import { SUPPORTED_LOCALES } from '$lib/translations'; /** - * @typedef {Object} LayoutData - * @property {string} route - * @property {string} language + * Type guard that checks if a string is a supported locale. + * @param {string | null} locale + * @returns {locale is "en-US" | "pt-BR"} */ +function isSupportedLocale(locale) { + // @ts-ignore + return locale !== null && Object.values(SUPPORTED_LOCALES).includes(locale); +} -/** @type {import('@sveltejs/kit').Load} */ -export const load = async ({ data }) => { - const { route, language } = data ?? {}; +/** + * Client-side load function to initialize translations based on localStorage or server fallback. + * + * This function runs in the browser and: + * - Prioritizes locale from localStorage (if valid) + * - Falls back to the server-provided fallbackLanguage (from Accept-Language) + * - Initializes translations and routing + * + * @type {import('@sveltejs/kit').Load} + */ +export const load = async ({ data, url }) => { + /** @type {string} */ + const route = url.pathname; - if (route) await setRoute(route); - if (language) await setLocale(language); + /** @type {"en-US" | "pt-BR"} */ + let language; - return data ?? {}; + if (browser) { + /** + * Locale stored in the browser, if any. + * @type {string | null} + */ + const stored = localStorage.getItem('locale'); + + if (isSupportedLocale(stored)) { + language = stored; // Type narrowed here + } else { + language = data?.fallbackLanguage ?? SUPPORTED_LOCALES.EN_US; + } + + await loadTranslations(language, route); + await setLocale(language); + await setRoute(route); + } + + return {}; }; diff --git a/src/routes/+layout.server.js b/src/routes/+layout.server.js index 17c341b..f6ee3e9 100644 --- a/src/routes/+layout.server.js +++ b/src/routes/+layout.server.js @@ -1,55 +1,44 @@ import { parse } from 'accept-language-parser'; -import { loadTranslations, setLocale, setRoute } from '$lib/translations'; import { SUPPORTED_LOCALES } from '$lib/translations'; /** - * A set of all supported locale codes, used to validate and match against - * user preferences from cookies or Accept-Language headers. We're using a - * Set for better performance in lookup. - * + * A Set of all supported locale codes for quick validation. * Example values: "en-US", "pt-BR" * @type {Set} */ const SUPPORTED_LOCALE_SET = new Set(Object.values(SUPPORTED_LOCALES)); /** - * Returns a valid locale from cookies, or null if not valid/found. - * @param {{ get: (cookies: string) => any; }} cookies - */ -function localeFromCookies(cookies) { - const locale = cookies.get('locale'); - return locale && SUPPORTED_LOCALE_SET.has(locale) ? locale : null; -} - -/** - * Parses the Accept-Language header and returns the best matching locale. - * @param {string | null | undefined} header + * Extracts the best matching locale from an Accept-Language HTTP header. + * + * @param {string | null} header - The Accept-Language header value. + * @returns {string | null} - A supported locale string like "en-US", or null if none matched. */ function localeFromHeader(header) { if (!header) return null; - - const parsedLanguages = parse(header); - for (const { code, region } of parsedLanguages) { + const parsed = parse(header); + for (const { code, region } of parsed) { const locale = region ? `${code}-${region}` : code; - if (SUPPORTED_LOCALE_SET.has(locale)) { - return locale; - } + if (SUPPORTED_LOCALE_SET.has(locale)) return locale; } - return null; } -/** @type {import('@sveltejs/kit').ServerLoad}*/ -export async function load({ url, request, cookies }) { - // const cookieLocale = localeFromCookies(cookies); - // const headerLocale = localeFromHeader(request.headers.get('accept-language')); - // const language = cookieLocale || headerLocale || SUPPORTED_LOCALES.EN_US; - const language = SUPPORTED_LOCALES.EN_US; +/** + * Server-side load function that returns the initial route and fallback language. + * The language is inferred from the Accept-Language header. + * `localStorage` will take precedence on the client. + * + * @type {import('@sveltejs/kit').ServerLoad} + */ +export async function load({ url, request }) { + /** @type {string} */ const route = url.pathname; - await loadTranslations(language, route); - setLocale(language); - setRoute(route); + /** @type {string} */ + const fallbackLanguage = + localeFromHeader(request.headers.get('accept-language')) || + SUPPORTED_LOCALES.EN_US; - return { language, route }; + return { fallbackLanguage, route }; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 324a7d4..244c409 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,13 +1,26 @@ -
-
- -
-