-
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #122 from sveltekit-i18n/example/locale-router-adv…
…anced Add SEO friendly `locale-router` example
- Loading branch information
Showing
35 changed files
with
860 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[![Netlify Status](https://api.netlify.com/api/v1/badges/bcefda87-9dad-4c73-bf5f-d9b4c03cac9c/deploy-status)](https://locale-router-advanced.netlify.app) | ||
|
||
# Locale-router-advanced | ||
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. The default language (`en`) has no lang prefix in URL path. | ||
|
||
## Preview | ||
You can view this demo live on [Netlify](https://locale-router-advanced.netlify.app). | ||
|
||
## Noticeable files | ||
|
||
### `./src/hooks.server.js` | ||
Takes care about redirects to appropriate language mutation. It fetches pages with the default language mutation on background, and serves it to the client with no lang prefix in the URL path. | ||
|
||
### `./src/params/locale.js` (`./src/routes/[...lang=locale]`) | ||
Allows to pass only pages starting with locale or having no `path` (dafault lang index). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "./.svelte-kit/tsconfig.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[build] | ||
command = "cd ../../ && npm run build && npm run build --workspace locale-router-advanced" | ||
publish = "build" | ||
[functions] | ||
node_bundler = "esbuild" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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-netlify": "^2.0.7", | ||
"@sveltejs/kit": "^1.22.1", | ||
"svelte": "^4.0.5", | ||
"vite": "^4.4.2" | ||
}, | ||
"type": "module", | ||
"dependencies": { | ||
"sveltekit-i18n": "file:../../" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<!DOCTYPE html> | ||
<html lang="%lang%"> | ||
|
||
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
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('prevent-redirect')) { | ||
const localeRegex = new RegExp(`^/${locale}`); | ||
const location = `${pathname}`.replace(localeRegex, '') || '/'; | ||
|
||
return new Response(undefined, { headers: { location }, status: 301 }); | ||
|
||
// If route locale is not supported | ||
} else if (!locale) { | ||
// Get user preferred locale if it's a direct navigation | ||
if (!isDataRequest) { | ||
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' : ''}`; | ||
|
||
// We want to prevent redirect to fetch data for the default locale | ||
request.headers.set('prevent-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: 301 }); | ||
} | ||
|
||
// Add html `lang` attribute | ||
return resolve({ ...event, locals: { lang: locale } }, { | ||
transformPageChunk: ({ html }) => html.replace('%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; | ||
}; |
4 changes: 4 additions & 0 deletions
4
examples/locale-router-advanced/src/lib/translations/cs/about.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>" | ||
} |
6 changes: 6 additions & 0 deletions
6
examples/locale-router-advanced/src/lib/translations/cs/error.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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." | ||
} |
4 changes: 4 additions & 0 deletions
4
examples/locale-router-advanced/src/lib/translations/cs/home.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>" | ||
} |
5 changes: 5 additions & 0 deletions
5
examples/locale-router-advanced/src/lib/translations/cs/menu.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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...}}" | ||
} |
4 changes: 4 additions & 0 deletions
4
examples/locale-router-advanced/src/lib/translations/de/about.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>" | ||
} |
6 changes: 6 additions & 0 deletions
6
examples/locale-router-advanced/src/lib/translations/de/error.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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." | ||
} |
4 changes: 4 additions & 0 deletions
4
examples/locale-router-advanced/src/lib/translations/de/home.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
5 changes: 5 additions & 0 deletions
5
examples/locale-router-advanced/src/lib/translations/de/menu.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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...}}" | ||
} |
4 changes: 4 additions & 0 deletions
4
examples/locale-router-advanced/src/lib/translations/en/about.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>" | ||
} |
6 changes: 6 additions & 0 deletions
6
examples/locale-router-advanced/src/lib/translations/en/error.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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." | ||
} |
4 changes: 4 additions & 0 deletions
4
examples/locale-router-advanced/src/lib/translations/en/home.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
5 changes: 5 additions & 0 deletions
5
examples/locale-router-advanced/src/lib/translations/en/menu.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
96
examples/locale-router-advanced/src/lib/translations/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = 'en'; | ||
|
||
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()); | ||
} | ||
}); |
5 changes: 5 additions & 0 deletions
5
examples/locale-router-advanced/src/lib/translations/lang.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"en": "English", | ||
"de": "Deutsch", | ||
"cs": "Česky" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { locales } from '$lib/translations'; | ||
|
||
/** @type {import('@sveltejs/kit').ParamMatcher} */ | ||
export function match(param) { | ||
const definedLocales = locales.get(); | ||
const paths = [...definedLocales, '']; | ||
const slashPaths = paths.map((l) => `${l}/`); | ||
|
||
return [...paths, ...slashPaths].includes(param); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
Oops, something went wrong.