npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@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

Dead Simple i18n. Typesafe, Small Footprint, SEO-Friendly and, with an IDE Integration.

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 on form elements
  • The formaction attribute on button 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

  1. Links in the same Layout Component as <ParagldieJS> will not be translated. This will also log a warning in development.
  2. Messages are not reactive. Don't use them in server-side module scope.
  3. 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.

  1. Remove the reroute hook from src/hooks.js
  2. Move the routes you want to localize routes into a [[locale]] folder
  3. 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