From a3241516cb1e15197080fcc20eb2ded0f061bcad Mon Sep 17 00:00:00 2001 From: Restray Date: Sat, 2 Apr 2022 20:25:24 +0200 Subject: [PATCH] Change the way to load i18n (go throw cookie) --- .vscode/settings.json | 5 +- package.json | 2 +- pnpm-lock.yaml | 124 +++++++++++-------------------------- src/hooks.ts | 46 ++++++++++++-- src/lib/lang.json | 4 ++ src/lib/translations.ts | 28 +++++++++ src/routes/__error.svelte | 7 ++- src/routes/__layout.svelte | 32 +++++----- src/routes/index.svelte | 4 +- static/locales/en.json | 3 + static/locales/fr.json | 5 ++ 11 files changed, 142 insertions(+), 118 deletions(-) create mode 100644 src/lib/lang.json create mode 100644 src/lib/translations.ts create mode 100644 static/locales/fr.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 1cc7be6e1..ab2903b56 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { - "i18n-ally.localesPaths": ["locales"], + "i18n-ally.localesPaths": ["static/locales"], "i18n-ally.keystyle": "nested", "i18n-ally.extract.ignoredByFiles": { "src\\routes\\__layout.svelte": ["Coolify", "coolLabs logo"] - } + }, + "i18n-ally.sourceLanguage": "fr" } diff --git a/package.json b/package.json index 7a0a8e4bf..2ac79d547 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,9 @@ "prisma": "3.11.1", "svelte": "3.46.4", "svelte-check": "2.4.6", - "svelte-i18n": "^3.3.13", "svelte-preprocess": "4.10.4", "svelte-select": "^4.4.7", + "sveltekit-i18n": "^2.1.2", "tailwindcss": "3.0.23", "ts-node": "10.7.0", "tslib": "2.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7594b347..0a432406d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,10 +46,10 @@ specifiers: prisma: 3.11.1 svelte: 3.46.4 svelte-check: 2.4.6 - svelte-i18n: ^3.3.13 svelte-kit-cookie-session: 2.1.2 svelte-preprocess: 4.10.4 svelte-select: ^4.4.7 + sveltekit-i18n: ^2.1.2 tailwindcss: 3.0.23 tailwindcss-scrollbar: ^0.1.0 ts-node: 10.7.0 @@ -108,9 +108,9 @@ devDependencies: prisma: 3.11.1 svelte: 3.46.4 svelte-check: 2.4.6_postcss@8.4.12+svelte@3.46.4 - svelte-i18n: 3.3.13_svelte@3.46.4 svelte-preprocess: 4.10.4_296873641a0ad9f42fe92172d27bcedd svelte-select: 4.4.7 + sveltekit-i18n: 2.1.2_svelte@3.46.4 tailwindcss: 3.0.23_b89136460714832cdda11d1e9d57d1ff ts-node: 10.7.0_ee885bc7281b682b6adbed6ae09ee090 tslib: 2.3.1 @@ -184,55 +184,6 @@ packages: - supports-color dev: true - /@formatjs/ecma402-abstract/1.11.4: - resolution: - { - integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw== - } - dependencies: - '@formatjs/intl-localematcher': 0.2.25 - tslib: 2.3.1 - dev: true - - /@formatjs/fast-memoize/1.2.1: - resolution: - { - integrity: sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg== - } - dependencies: - tslib: 2.3.1 - dev: true - - /@formatjs/icu-messageformat-parser/2.0.19: - resolution: - { - integrity: sha512-8HsLm9YLyVVIDMyBJb7wmve2wGd461cUwJ470eUog5YH5ZsF4p5lgvaJ+oGKxz1mrSMNNdDHU9v/NDsS+z+ilg== - } - dependencies: - '@formatjs/ecma402-abstract': 1.11.4 - '@formatjs/icu-skeleton-parser': 1.3.6 - tslib: 2.3.1 - dev: true - - /@formatjs/icu-skeleton-parser/1.3.6: - resolution: - { - integrity: sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg== - } - dependencies: - '@formatjs/ecma402-abstract': 1.11.4 - tslib: 2.3.1 - dev: true - - /@formatjs/intl-localematcher/0.2.25: - resolution: - { - integrity: sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA== - } - dependencies: - tslib: 2.3.1 - dev: true - /@humanwhocodes/config-array/0.5.0: resolution: { @@ -495,6 +446,26 @@ packages: - supports-color dev: true + /@sveltekit-i18n/base/1.1.1_svelte@3.46.4: + resolution: + { + integrity: sha512-J/sMU0OwS3dCLOuilHMBqu8vZHuuXiNV9vFJx8Nb4/b5BlR/KCZ4bCXI8wZR02GHeCOYKZxWus07CM1scxa/jw== + } + peerDependencies: + svelte: ^3.x + dependencies: + svelte: 3.46.4 + optionalDependencies: + '@sveltekit-i18n/parser-default': 1.0.3 + dev: true + + /@sveltekit-i18n/parser-default/1.0.3: + resolution: + { + integrity: sha512-HheveklTjp3hxpYQhoHfyA6B4bQaUeSV5MQf2usIv/58UF2jY/YqhCAWj9bDBjufbuZc5pSz4BXvdX3WVT+viA== + } + dev: true + /@szmarczak/http-timer/5.0.1: resolution: { @@ -2254,14 +2225,6 @@ packages: } dev: true - /deepmerge/4.2.2: - resolution: - { - integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - } - engines: { node: '>=0.10.0' } - dev: true - /defer-to-connect/2.0.1: resolution: { @@ -3462,18 +3425,6 @@ packages: integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== } - /intl-messageformat/9.12.0: - resolution: - { - integrity: sha512-5Q9j21JreB1G27/CqMYsA+pvJ19JjHyhiTSeUuvZK9BCDJGHtOLgpUUcGM+GLHiUuoVMKVeeX1smamiVHQrSKQ== - } - dependencies: - '@formatjs/ecma402-abstract': 1.11.4 - '@formatjs/fast-memoize': 1.2.1 - '@formatjs/icu-messageformat-parser': 2.0.19 - tslib: 2.3.1 - dev: true - /invariant/2.2.4: resolution: { @@ -5243,24 +5194,6 @@ packages: svelte: 3.46.4 dev: true - /svelte-i18n/3.3.13_svelte@3.46.4: - resolution: - { - integrity: sha512-RQM+ys4+Y9ztH//tX22H1UL2cniLNmIR+N4xmYygV6QpQ6EyQvloZiENRew8XrVzfvJ8HaE8NU6/yurLkl7z3g== - } - engines: { node: '>= 11.15.0' } - hasBin: true - peerDependencies: - svelte: ^3.25.1 - dependencies: - deepmerge: 4.2.2 - estree-walker: 2.0.2 - intl-messageformat: 9.12.0 - sade: 1.7.4 - svelte: 3.46.4 - tiny-glob: 0.2.9 - dev: true - /svelte-kit-cookie-session/2.1.2: resolution: { @@ -5338,6 +5271,19 @@ packages: engines: { node: '>= 8' } dev: true + /sveltekit-i18n/2.1.2_svelte@3.46.4: + resolution: + { + integrity: sha512-s5YxcbNd2EWNZaZR1A4Drt8s53E4fpUkN4XIWd3VRpw1pihZVWssqmBW1qkjQ6AB0kiu1Qwule+vt1HkbQOjrg== + } + peerDependencies: + svelte: ^3.x + dependencies: + '@sveltekit-i18n/base': 1.1.1_svelte@3.46.4 + '@sveltekit-i18n/parser-default': 1.0.3 + svelte: 3.46.4 + dev: true + /table/6.7.2: resolution: { diff --git a/src/hooks.ts b/src/hooks.ts index d6c6eca6d..e0e4707cb 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -6,6 +6,7 @@ import { getUserDetails, sentry } from '$lib/common'; import { version } from '$lib/common'; import cookie from 'cookie'; import { dev } from '$app/env'; +import { locales } from '$lib/translations'; export const handle = handleSession( { @@ -15,6 +16,29 @@ export const handle = handleSession( }, async function ({ event, resolve }) { let response; + + const { url, request } = event; + const { pathname } = url; + + // Get defined locales + const supportedLocales = locales.get(); + let locale; + + if (event.locals.cookies) { + locale = supportedLocales.find( + (l) => `${l}`.toLowerCase() === `${event.locals.cookies['lang']}`.toLowerCase() + ); + } + + if (!locale) { + locale = `${`${request.headers.get('accept-language')}`.match( + /[a-zA-Z]+?(?=-|_|,|;)/ + )}`.toLowerCase(); + + // Set default locale if user preferred locale does not match + if (!supportedLocales.includes(locale)) locale = 'en'; + } + try { if (event.locals.cookies) { if (event.locals.cookies['kit.session']) { @@ -24,7 +48,8 @@ export const handle = handleSession( teamId, permission, isAdmin: permission === 'admin' || permission === 'owner', - expires: event.locals.session.data.expires + expires: event.locals.session.data.expires, + lang: locale }; if (JSON.stringify(event.locals.session.data) !== JSON.stringify(newSession)) { @@ -34,12 +59,14 @@ export const handle = handleSession( } response = await resolve(event, { - ssr: !event.url.pathname.startsWith('/webhooks/success') + ssr: !event.url.pathname.startsWith('/webhooks/success'), + transformPage: ({ html }) => html.replace(//, ``) }); } catch (error) { console.log(error); response = await resolve(event, { - ssr: !event.url.pathname.startsWith('/webhooks/success') + ssr: !event.url.pathname.startsWith('/webhooks/success'), + transformPage: ({ html }) => html.replace(//, ``) }); response.headers.append( 'Set-Cookie', @@ -62,9 +89,18 @@ export const handle = handleSession( expires: new Date('Thu, 01 Jan 1970 00:00:01 GMT') }) ); - } finally { - return response; } + + response.headers.append( + 'Set-Cookie', + cookie.serialize('lang', locale, { + path: '/', + sameSite: 'strict', + maxAge: 30 * 24 * 60 * 60 + }) + ); + + return response; } ); diff --git a/src/lib/lang.json b/src/lib/lang.json new file mode 100644 index 000000000..fdfc5bf80 --- /dev/null +++ b/src/lib/lang.json @@ -0,0 +1,4 @@ +{ + "fr": "Français", + "en": "English" +} diff --git a/src/lib/translations.ts b/src/lib/translations.ts new file mode 100644 index 000000000..69282b21c --- /dev/null +++ b/src/lib/translations.ts @@ -0,0 +1,28 @@ +import i18n from 'sveltekit-i18n'; +import lang from './lang.json'; + +/** @type {import('sveltekit-i18n').Config} */ +const config = { + defaultLocale: 'en', + fallbackLocale: 'en', + translations: { + en: { lang }, + fr: { lang } + }, + loaders: [ + { + locale: 'en', + key: '', + loader: async () => (await import('../../static/locales/en.json')).default + }, + { + locale: 'fr', + key: '', + loader: async () => (await import('../../static/locales/fr.json')).default + } + ] +}; + +export const { t, locale, locales, loading, loadTranslations } = new i18n(config); + +loading.subscribe(($loading) => $loading && console.log('Loading translations...')); diff --git a/src/routes/__error.svelte b/src/routes/__error.svelte index 67693e70a..edecc0f82 100644 --- a/src/routes/__error.svelte +++ b/src/routes/__error.svelte @@ -12,7 +12,8 @@ @@ -21,8 +22,8 @@
{status}
Ooops you are lost! But don't be afraid!
- {$_('error.you_can_find_your_way_back')} - {$_('error.here')} + {$t('error.you_can_find_your_way_back')} + {$t('error.here')}
 	import type { Load } from '@sveltejs/kit';
 	import { publicPaths } from '$lib/settings';
+	import { locale, loadTranslations } from '$lib/translations';
 
 	export const load: Load = async ({ fetch, url, session }) => {
 		if (!session.userId && !publicPaths.includes(url.pathname)) {
@@ -15,6 +16,12 @@
 		const endpoint = `/teams.json`;
 		const res = await fetch(endpoint);
 
+		const { pathname } = url;
+		const defaultLocale = session.lang;
+		const initLocale = locale.get() || defaultLocale;
+
+		await loadTranslations(initLocale, pathname);
+
 		if (res.ok) {
 			return {
 				props: {
@@ -23,6 +30,7 @@
 				}
 			};
 		}
+
 		return {};
 	};
 
@@ -38,18 +46,8 @@
 	import { errorNotification } from '$lib/form';
 	import { asyncSleep } from '$lib/components/common';
 	import { del, get, post } from '$lib/api';
-	import { register, init, _, getLocaleFromNavigator } from 'svelte-i18n';
-
-	async function setup() {
-		register('en', () => import('../../static/locales/en.json'));
-
-		return await Promise.allSettled([
-			// TODO: add some more stuff you want to init ...
-			init({ initialLocale: getLocaleFromNavigator(), fallbackLocale: 'en' })
-		]);
-	}
-
-	const setupResult = setup();
+	import { browser } from '$app/env';
+	import { loading, t } from '$lib/translations';
 
 	let isUpdateAvailable = false;
 
@@ -115,7 +113,7 @@
 				return window.location.reload();
 			} else {
 				await post(`/update.json`, { type: 'update', latestVersion });
-				toast.push(`${$_('layout.update_done')}

${$_('layout.wait_new_version_startup')}`); + toast.push(`${$t('layout.update_done')}

${$t('layout.wait_new_version_startup')}`); let reachable = false; let tries = 0; do { @@ -129,7 +127,7 @@ if (reachable) break; tries++; } while (!reachable || tries < 120); - toast.push($_('layout.new_version')); + toast.push($t('layout.new_version')); updateStatus.loading = false; updateStatus.success = true; await asyncSleep(3000); @@ -147,7 +145,7 @@ Coolify -{#await setupResult} +{#await loading} Please wait... {:then} @@ -336,7 +334,7 @@ {#if isUpdateAvailable}