Skip to content

Commit

Permalink
Add locale-router-advanced example
Browse files Browse the repository at this point in the history
  • Loading branch information
jarda-svoboda committed Jul 10, 2023
1 parent ad8dbd5 commit 7c8cb30
Show file tree
Hide file tree
Showing 33 changed files with 430 additions and 0 deletions.
8 changes: 8 additions & 0 deletions examples/locale-router-advanced/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
node_modules
build
.svelte-kit
package
.env
.env.*
!.env.example
1 change: 1 addition & 0 deletions examples/locale-router-advanced/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
7 changes: 7 additions & 0 deletions examples/locale-router-advanced/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Locale-loader
This app shows how to integrate locale routing using dynamic adapters (e.g. `@sveltejs/adapter-node`). It includes two pages and three language mutations (`en`, `de`, `cs`). Error pages are included as well.

## Setup

### `./src/hooks.server.js`
Takes care about redirects to appropriate language mutation.
3 changes: 3 additions & 0 deletions examples/locale-router-advanced/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./.svelte-kit/tsconfig.json"
}
21 changes: 21 additions & 0 deletions examples/locale-router-advanced/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "locale-router-advanced",
"version": "0.0.1",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"package": "svelte-kit package",
"preview": "vite preview",
"prepare": "svelte-kit sync"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.1.0",
"@sveltejs/kit": "^1.22.1",
"svelte": "^4.0.5",
"vite": "^4.4.2"
},
"type": "module",
"dependencies": {
"sveltekit-i18n": "file:../../"
}
}
10 changes: 10 additions & 0 deletions examples/locale-router-advanced/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// <reference types="@sveltejs/kit" />

// See https://kit.svelte.dev/docs/types#the-app-namespace
// for information about these interfaces
declare namespace App {
// interface Locals {}
// interface Platform {}
// interface Session {}
// interface Stuff {}
}
16 changes: 16 additions & 0 deletions examples/locale-router-advanced/src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="description" content="" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>

<body>
<div>%sveltekit.body%</div>
</body>

</html>
81 changes: 81 additions & 0 deletions examples/locale-router-advanced/src/hooks.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { defaultLocale, loadTranslations, locales } from '$lib/translations';

const routeRegex = new RegExp(/^\/[^.]*([?#].*)?$/);

/** @type {import('@sveltejs/kit').Handle} */
export const handle = async ({ event, resolve }) => {
const { url, request, isDataRequest } = event;
const { pathname, origin } = url;

// If this request is a route request
if (routeRegex.test(pathname)) {

// Get defined locales
const supportedLocales = locales.get();

// Try to get locale from `pathname`.
let locale = supportedLocales.find((l) => l === `${pathname.match(/[^/]+?(?=\/|$)/)}`.toLowerCase());
// We want to redirect the default locale to "no-locale" path
if (locale === defaultLocale && !request.headers.get('no-redirect')) {
const localeRegex = new RegExp(`^/${locale}`);
const location = `${pathname}`.replace(localeRegex, '') || '/';

return new Response(undefined, { headers: new Headers({ location }), status: 301 });

// If route locale is not supported
} else if (!locale) {
// Get user preferred 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 = defaultLocale;

if (locale === defaultLocale) {

const path = `${pathname}`.replace(/\/$/, '');
const redirectTo = `${origin}/${locale}${path}${isDataRequest ? '/__data.json?x-sveltekit-invalidated=100' : ''}`;

request.headers.set('no-redirect', '1');

// Fetch the redirected route
const response = await fetch(redirectTo, request);

// Get response body and set html headers
const data = await response.text();

// Serve the redirected route.
// In this case we don't have to set the html 'lang' attribute
// as the default locale is already included in our app.html.
return new Response(data, {
...response,
headers: {
...response.headers,
'Content-Type': isDataRequest ? 'application/json' : 'text/html',
},
});

}

// 301 redirect
return new Response(undefined, { headers: { 'location': `/${locale}${pathname}` }, status: 302 });
}

// Add html `lang` attribute
return resolve({ ...event, locals: { lang: locale } }, {
transformPageChunk: ({ html }) => html.replace(/<html.*>/, `<html lang="${locale}">`),
});
}

return resolve(event);
};


/** @type {import('@sveltejs/kit').HandleServerError} */
export const handleError = async ({ event }) => {
const { locals } = event;
const { lang } = locals;

await loadTranslations(lang, 'error');

return locals;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "O této aplikaci",
"text": "<p>Toto je <a href=\"https://kit.svelte.dev\">SvelteKit</a> aplikace. Můžete si vytvořit svou vlastní vložením následujícího příkazu do příkazové řádky:</p><pre>npm init svelte@next</pre> <p> Tato stránka je čistě statické HTML, bez nutnosti klientské interakce. Díky tomu není potřeba načítat žádný JavaScript. Zkuste zobrazit drojový kód stránky, nebo otevřete vývojářské nástroje a znovu načtěte stránku.</p>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"shit.happens": "No toto...",
"404": "Stránka nenalezena.",
"500": "Interní chyba serveru.",
"default": "Něco se pokazilo."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Vítejte ve SvelteKit",
"text": "Dokumentace je k přečtení na <a href=\"{{link}}\">kit.svelte.dev</a>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"home": "Domů",
"about": "O nás",
"notification": "{{count:gt; 0:Máte {{count}} {{count:gte; 1:novou zprávu; 2:nové zprávy; 5:nových zpráv}}!; default:Nemáte žádné zprávy...}}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Über diese App",
"text": "<p>Dies ist eine <a href=\"https://kit.svelte.dev\">SvelteKit</a>-App. Sie können Ihre eigene erstellen, indem Sie Folgendes in Ihre Befehlszeile eingeben und den Eingabeaufforderungen folgen:</p><pre>npm init svelte@next</pre> <p> Die Seite, die Sie sich ansehen, ist rein statisches HTML mit keine clientseitige Interaktivität erforderlich. Aus diesem Grund müssen wir kein JavaScript laden. Versuchen Sie, die Quelle der Seite anzuzeigen oder das devtools-Netzwerkfenster zu öffnen und neu zu laden.</p>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"shit.happens": "Auweh...",
"404": "Seite nicht gefunden.",
"500": "Server interner Fehler.",
"default": "Ein Fehler ist aufgetreten."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Willkommen bei SvelteKit",
"text": "Besuchen Sie <a href=\"{{link}}\">kit.svelte.dev</a>, um die Dokumentation zu lesen"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"home": "Startseite",
"about": "Über uns",
"notification": "Sie haben {{count:gt; 0:{{count}} neue {{count; 1:Nachricht; default:Nachrichten}}!; default:keine Nachrichten...}}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "About this app",
"text": "<p>This is a <a href=\"https://kit.svelte.dev\">SvelteKit</a> app. You can make your own by typing the following into your command line and following the prompts:</p><pre>npm init svelte@next</pre> <p> The page you're looking at is purely static HTML, with no client-side interactivity needed. Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening the devtools network panel and reloading. </p>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"shit.happens": "Oh, dear...",
"404": "Page not found.",
"500": "Server internal error.",
"default": "Some error occurred."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Welcome to SvelteKit",
"text": "Visit <a href=\"{{link}}\">kit.svelte.dev</a> to read the documentation"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"home": "Home",
"about": "About",
"notification": "You have {{count:gt; 0:{{count}} new {{count; 1:message; default:messages}}!; default:no messages...}}"
}
96 changes: 96 additions & 0 deletions examples/locale-router-advanced/src/lib/translations/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import i18n from 'sveltekit-i18n';
import lang from './lang.json';

/** @type {import('sveltekit-i18n').Config} */
const config = {
translations: {
en: { lang },
de: { lang },
cs: { lang },
},
loaders: [
{
locale: 'en',
key: 'menu',
loader: async () => (await import('./en/menu.json')).default,
},
{
locale: 'en',
key: 'home',
routes: ['', '/'],
loader: async () => (await import('./en/home.json')).default,
},
{
locale: 'en',
key: 'about',
routes: ['/about'],
loader: async () => (await import('./en/about.json')).default,
},
{
locale: 'de',
key: 'menu',
loader: async () => (await import('./de/menu.json')).default,
},
{
locale: 'en',
key: 'error',
routes: ['error'],
loader: async () => (await import('./en/error.json')).default,
},
{
locale: 'de',
key: 'home',
routes: ['', '/'],
loader: async () => (await import('./de/home.json')).default,
},
{
locale: 'de',
key: 'about',
routes: ['/about'],
loader: async () => (await import('./de/about.json')).default,
},
{
locale: 'cs',
key: 'menu',
loader: async () => (await import('./cs/menu.json')).default,
},
{
locale: 'de',
key: 'error',
routes: ['error'],
loader: async () => (await import('./de/error.json')).default,
},
{
locale: 'cs',
key: 'home',
routes: ['', '/'],
loader: async () => (await import('./cs/home.json')).default,
},
{
locale: 'cs',
key: 'about',
routes: ['/about'],
loader: async () => (await import('./cs/about.json')).default,
},
{
locale: 'cs',
key: 'error',
routes: ['error'],
loader: async () => (await import('./cs/error.json')).default,
},
],
};

export const defaultLocale = 'cs';

export const { t, locale, locales, loading, addTranslations, loadTranslations, translations, setRoute, setLocale } = new i18n(config);

// Translations logs
loading.subscribe(async ($loading) => {
if ($loading) {
console.log('Loading translations...');

await loading.toPromise();
console.log('Updated translations', translations.get());
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"en": "English",
"de": "Deutsch",
"cs": "Česky"
}
6 changes: 6 additions & 0 deletions examples/locale-router-advanced/src/params/locale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { locales } from '$lib/translations';

/** @type {import('@sveltejs/kit').ParamMatcher} */
export function match(param) {
return locales.get().includes(param);
}
14 changes: 14 additions & 0 deletions examples/locale-router-advanced/src/routes/+error.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script>
import { page } from '$app/stores';
import { t, locale } from '$lib/translations';
const { status } = $page;
</script>

<div class="content">
<h1>{$t('error.shit.happens')} ({status})</h1>
<p>{$t(`error.${status}`, { default: $t('error.default') })}</p>
<br>
<br>
{$locale} – {$t(`lang.${$locale}`)}
</div>
14 changes: 14 additions & 0 deletions examples/locale-router-advanced/src/routes/+layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { addTranslations, setLocale, setRoute } from '$lib/translations';

/** @type {import('@sveltejs/kit').LayoutLoad} */
export const load = async ({ data }) => {
const { i18n, translations } = data;
const { lang, route } = i18n;

addTranslations(translations);

await setRoute(route);
await setLocale(lang);

return i18n;
};
13 changes: 13 additions & 0 deletions examples/locale-router-advanced/src/routes/+layout.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { loadTranslations, translations } from '$lib/translations';

/** @type {import('@sveltejs/kit').ServerLoad} */
export const load = async ({ url, locals }) => {
const { pathname } = url;
const { lang } = locals;

const route = pathname.replace(new RegExp(`^/${lang}`), '');

await loadTranslations(lang, route);

return { i18n: { route, lang }, translations: translations.get() };
};
Loading

0 comments on commit 7c8cb30

Please sign in to comment.