@inlang/paraglide-js-adapter-sveltekit
v0.6.7
Published
![Dead Simple i18n. Typesafe, Small Footprint, SEO-Friendly and, with an IDE Integration.](https://cdn.jsdelivr.net/gh/opral/monorepo@latest/inlang/source-code/paraglide/paraglide-js-adapter-sveltekit/assets/header.png)
Downloads
4,179
Readme
Getting Started
1. Install dependencies
Install ParaglideJS and the ParaglideJS SvelteKit Adapter.
npx @inlang/paraglide-js init
npm i -D @inlang/paraglide-js-adapter-sveltekit
This will generate a messages/{lang}.json
file for each of your languages. This is where your translation files live.
2. Add the Vite Plugin
Add the adapter-plugin to your vite.config.js
file. This will make sure to rerun the paraglide compiler when needed and add the link preprocessor.
import { paraglide } from "@inlang/paraglide-js-adapter-sveltekit/vite"
export default defineConfig({
plugins: [
paraglide({
//recommended
project: "./project.inlang",
outdir: "./src/lib/paraglide",
}),
sveltekit(),
],
})
3. Initialise the Adapter
Create a src/lib/i18n.js
file:
// src/lib/i18n.js
import { createI18n } from "@inlang/paraglide-js-adapter-sveltekit"
import * as runtime from "$lib/paraglide/runtime.js"
export const i18n = createI18n(runtime);
createI18n
will be your one-stop shop for configuring the adapter.
4. Add the Language Provider to your Layout
Add the ParaglideJS
component to your layout and pass it the i18n
instance.
<!-- src/routes/+layout.svelte -->
<script>
import { ParaglideJS } from '@inlang/paraglide-js-adapter-sveltekit'
import { i18n } from '$lib/i18n.js'
</script>
<ParaglideJS {i18n}>
<slot />
</ParaglideJS>
5. Add the Hooks
In your src/hooks.js
file, use the i18n
instance to add the reroute
hook:
import { i18n } from '$lib/i18n.js'
export const reroute = i18n.reroute()
The reroute hook was added in SvelteKit 2.3.0
In src/hooks.server.js
add the handle hook.
// src/hooks.server.js
import { i18n } from '$lib/i18n.js'
export const handle = i18n.handle()
This will make the language and text-direction on event.locals.paraglide
.
To set the lang
and dir
attributes on your <html>
tag add placeholders in src/app.html
. These placeholders will be replaced by the handle
hook.
<!-- src/app.html -->
<html lang="%paraglide.lang%" dir="%paraglide.textDirection%">
Go try it out!
Visit /
to see your default language, and /{lang}
to see other languages. All links should be translated automatically.
Advanced Setup
Excluding certain routes
Exclude routes from being translated with the exclude
option.
import { createI18n } from "@inlang/paraglide-js-adapter-sveltekit"
import * as runtime from "$lib/paraglide/runtime.js"
export const i18n = createI18n(runtime, {
// don't include the language or base path
exclude: ["/admin", "/login", /^\/user\/\d+$/],
})
Excluded routes will:
- Not have any
rel="alternate"
links added to them - Not have their Paths translated
- Not have links pointing to them translated
Make sure excluded pages are still wrapped in the <ParaglideJS>
so that outgoing links are still translated.
Translated Paths
/en/about
for English/de/uber-uns
for German/fr/a-propos
for French
You can have different paths for each language with the pathnames
option.
Don't include the language or the base path.
import { createI18n } from "@inlang/paraglide-js-adapter-sveltekit"
import * as runtime from "$lib/paraglide/runtime.js"
import * as m from "$lib/paraglide/messages.js"
import { match as int } from "../params/int.js"
export const i18n = createI18n(runtime, {
pathnames: {
"/about" : {
en: "/about",
de: "/uber-uns",
fr: "/a-propos",
},
// You can use parameters
// All translations must use identical parameters and names
"/user/[id=int]/[...rest]" : {
en: "/user/[id=int]/[...rest]",
de: "/benutzer/[id=int]/[...rest]",
fr: "/utilisateur/[id=int]/[...rest]",
},
// Instead of a map, you can also pass a message-function
"/admin" : m.admin_path
}
// If you're using matchers in the pathnames, you need to pass them
matchers: { int }
})
Customizing Link Translation
Links are translated automatically using a preprocessor. This means that you can use the normal a
-tag and the adapter will translate it for you.
<a href="/about">{m.about()}</a>
<!-- will become on of -->
<a href="/en/about">{m.about()}</a>
<a href="/de/uber-uns">{m.about()}</a>
<a href="/fr/a-propos">{m.about()}</a>
If you want a link to be translated into a specific language set the hreflang
attribute.
<a href="/about" hreflang="de">{m.about()}</a>
<!-- Will always be german, regardless of the current language -->
<a href="/de/uber-uns" hreflang="de">{m.about()}</a>
Opt-out of translation by adding a data-no-translate
attribute.
<!-- this will never be translated -->
<a href="/about" data-no-translate>{m.about()}</a>
Other attributes that are also translated are:
- The
action
attribute onform
elements - The
formaction
attribute onbutton
elements
If you have other attributes that you want to be translated open an issue.
Programmatic Navigation with Translated Paths
SvelteKit's goto
and redirect
cannot be translated automatically. Localize the URLs you pass to them with i18n.resolveRoute()
.
import { i18n } from '$lib/i18n.js'
import { redirect } from '@sveltejs/kit'
import { goto } from '$app/navigation'
redirect(i18n.resolveRoute("/about", "en"))
//Omitting the language argument uses the current languageTag()
goto(i18n.resolveRoute("/about"))
Language Switchers
Language switchers are tricky because we need to dynamically translate the current URL path, which is itself translated. We need to get the untranslated version of the current path & translate it into the target language.
You can get the untranslated path using i18n.route()
// $page.url.pathname = "/base/de/uber-uns"
const route = i18n.route($page.url.pathname)
// route = "/base/about"
Use this to create a language switcher.
<script>
import { availableLanguageTags, languageTag } from "$lib/paraglide/runtime.js"
import { i18n } from '$lib/i18n.js'
import { page } from '$app/stores'
</script>
{#each availableLanguageTags as lang}
<!-- the hreflang attribute decides which language the link points to -->
<a
href={i18n.route($page.url.pathname)}
hreflang={lang}
aria-current={lang === languageTag() ? "page" : undefined}
>
{lang}
</a>
{/each}
Overriding Text direction
Paraglide guesses the text direction using the Intl.Locale
API. This is not supported in all runtimes. Use the textDirection
option to provide the text direction yourself.
import { createI18n } from "@inlang/paraglide-js-adapter-sveltekit"
import * as runtime from "$lib/paraglide/runtime.js"
export const i18n = createI18n(runtime, {
textDirection: {
en: "ltr",
ar: "rtl",
},
})
Accessing lang
and textDirection
On the server you can access the current language and text direction on event.locals.paraglide
.
On the client, you can call languageTag()
exported ./paraglide/runtime.js
.
### Using the Language on the Server
#### Avoiding Cross-Talk
SvelteKit does two kinds of work on the server: Loading and Rendering.
- Loading includes running your
load
functions,actions
or server-hooks. - Rendering is anything that happens in or is called from a
.svelte
file.
Loading is asynchronous & rendering is synchronous.
During the asynchronous loading, there is danger of crosstalk. If you aren't careful it's possible for one request to override the language of another request. You can avoid this by explicitly specifying which language a message should be in.
import * as m from "$lib/paraglide/messages.js"
export async function load({ locals }) {
const translatedText = m.some_message({ ...message_params }, { languageTag: locals.paraglide.lang })
return { translatedText }
}
During rendering there is no danger of crosstalk. You can safely use messages and the langaugeTag()
function.
Re-Loading Language-Dependent data
You can tell a load function to re-run on language changes by calling depends("paraglide:lang")
.
export async function load({ depends }) {
// The Adapter automatically calls `invalidate("paraglide:lang")` whenever the langauge changes
// This tells SvelteKit to re-run this function whenever that happens
depends("paraglide:lang")
return await myLanguageSpecificData();
}
## Caveats
- Links in the same Layout Component as
<ParagldieJS>
will not be translated. This will also log a warning in development. - Messages are not reactive. Don't use them in server-side module scope.
- Side effects triggered by
data
will run on language changes even if the data didn't change. If the data is language-dependent the side effect will run twice.
Using messages in +layout.svelte
The language state get's set when the <ParaglideJS>
component is mounted. Since you usually place it inside +layout.svelte
using messages in the layout's <script>
can cause issues.
<script>
import { ParaglideJS } from '@inlang/paraglide-js-adapter-sveltekit'
import { i18n } from '$lib/i18n.js'
//using messages here can cause hydration issues
</script>
<ParaglideJS {i18n}>
<!-- Using messages here is fine -->
<slot />
</ParaglideJS>
Issues on Vercel
SvelteKit's reroute
hook currently doens't play well with Vercel (see sveltejs/kit#11879), which means that we need to slightly adapt the adapter setup to make it work when deployed to Vercel.
- Remove the
reroute
hook fromsrc/hooks.js
- Move the routes you want to localize
routes
into a[[locale]]
folder - Don't use translated
pathnames
We are working on contributing a fix for sveltejs/kit#11879, so this workaround will hopefully not be needed much longer.
FAQ
Roadmap to 1.0
- Translate parameters themselves
- More routing flexibility
- Domain Based routing
- Language Detection & Redirects
- Fix the Caveats
- Allow links in the same component as
- Run seamlessly when deployed to Vercel
- Fix side-effects that are triggered by page-
data
changing double triggering on language changes if they depend on the language.
Playground
Play around with it on StackBlitz