Internationalization (i18n)

Svelar integrates with paraglide-js 2.x for type-safe, URL-based internationalization. The framework provides helpers that wire paraglide into SvelteKit's hooks system and a ready-to-use language switcher component.

i18n is opt-in. New projects scaffolded with npx svelar new ship in English with no i18n overhead. When you're ready to add multi-language support, follow the steps below — Svelar's Vite aliases for @beeblock/svelar/i18n are already pre-configured.

Setup

1. Install paraglide-js

npm install @inlang/paraglide-js
npx @inlang/paraglide-js init

The init command creates the project.inlang/ configuration directory. When prompted, select your base locale (e.g., en) and any additional locales you want to support.

2. Configure the Vite Plugin

Add the paraglide plugin to your existing vite.config.ts (the file is already scaffolded by npx svelar new):

// vite.config.ts
import { paraglideVitePlugin } from '@inlang/paraglide-js';

export default defineConfig({
  plugins: [
    sveltekit(),
    paraglideVitePlugin({
      project: './project.inlang',
      outdir: './src/lib/paraglide',
      strategy: ['url', 'cookie', 'baseLocale'],
      outputStructure: 'message-modules',
    }),
    tailwindcss(),
  ],
});

The outputStructure: 'message-modules' setting is required — it generates the full runtime with getLocale, localizeHref, deLocalizeUrl, and other exports that Svelar's i18n helpers depend on.

3. Create Translation Files

messages/
├── en.json    # English (base locale)
└── pt.json    # Portuguese
// messages/en.json
{
  "app_name": "My App",
  "nav_dashboard": "Dashboard",
  "nav_login": "Sign In",
  "nav_register": "Register",
  "nav_logout": "Sign Out"
}
// messages/pt.json
{
  "app_name": "Meu App",
  "nav_dashboard": "Painel",
  "nav_login": "Entrar",
  "nav_register": "Cadastrar",
  "nav_logout": "Sair"
}

4. Wire the Server Hooks

Your scaffolded hooks.server.ts already uses createSvelarApp. Add the i18n option to enable locale detection:

// src/hooks.server.ts
import { createSvelarApp } from '@beeblock/svelar/hooks';
import { paraglideMiddleware } from '$lib/paraglide/server';
import { getTextDirection } from '$lib/paraglide/runtime';
import { auth } from './app.js';

export const { handle, handleError } = createSvelarApp({
  auth,
  i18n: { paraglideMiddleware, getTextDirection },
});

Or, if you prefer manual control, use createI18nHandle:

// src/hooks.server.ts
import { sequence } from '@sveltejs/kit/hooks';
import { createI18nHandle } from '@beeblock/svelar/i18n';
import { createSvelarHooks } from '@beeblock/svelar/hooks';
import { paraglideMiddleware } from '$lib/paraglide/server';
import { getTextDirection } from '$lib/paraglide/runtime';

const i18nHandle = createI18nHandle({ paraglideMiddleware, getTextDirection });
const svelarHandle = createSvelarHooks({ /* ... */ });

export const handle = sequence(i18nHandle, svelarHandle);

5. Wire the Client Reroute Hook

// src/hooks.ts
import { createReroute } from '@beeblock/svelar/i18n';
import { deLocalizeUrl } from '$lib/paraglide/runtime';

export const reroute = createReroute({ deLocalizeUrl });

6. Update app.html

Replace hardcoded lang and dir with paraglide placeholders:

<!doctype html>
<html lang="%lang%" dir="%dir%">
  <head>...</head>
  <body>...</body>
</html>

Using Translations in Components

Import message functions and localizeHref in every component:

<script>
  import * as m from '$lib/paraglide/messages';
  import { localizeHref } from '$lib/paraglide/runtime';
</script>

<h1>{m.app_name()}</h1>
<a href={localizeHref('/dashboard')}>{m.nav_dashboard()}</a>

Every internal <a href> must use localizeHref() to maintain the locale prefix across navigation. Without it, clicking a link drops the /pt/ prefix and resets to the base locale.

Language Switcher

Svelar includes a ready-to-use LanguageSwitcher component:

<script>
  import LanguageSwitcher from '@beeblock/svelar/i18n/LanguageSwitcher.svelte';
  import { page } from '$app/state';
  import { locales, getLocale, localizeHref } from '$lib/paraglide/runtime';
</script>

<LanguageSwitcher
  {locales}
  {getLocale}
  {localizeHref}
  pathname={page.url.pathname}
  labels={{ en: 'EN', pt: 'PT', es: 'ES' }}
/>

The component renders locale buttons that trigger a full page reload (data-sveltekit-reload) so the server middleware picks up the new locale.

Props

  • locales — Array of available locale codes (from paraglide runtime)
  • getLocale — Function returning the current locale (from paraglide runtime)
  • localizeHref — Function to localize a path for a given locale (from paraglide runtime)
  • pathname — Current page pathname (typically page.url.pathname)
  • labels — Optional display labels per locale (defaults to uppercase code)
  • class — Extra CSS class for the container

Adding a New Language

  1. Create messages/es.json with translations
  2. Update project.inlang/settings.json to add "es" to the locales list
  3. Add the label to your LanguageSwitcher: labels={{ en: 'EN', pt: 'PT', es: 'ES' }}
  4. Restart the dev server — paraglide auto-generates the new runtime

Locale-Aware Dates

Svelar ships with @beeblock/svelar/dates — a timezone-safe date utility built on date-fns. To make dates follow the active paraglide locale automatically, create a thin wrapper in your app:

// src/lib/dates.ts
import { formatDate as svelarFormat, formatRelative as svelarRelative, timeAgo as svelarTimeAgo } from '@beeblock/svelar/dates';
import { getLocale } from '$lib/paraglide/runtime';
import { enUS, pt, es } from 'date-fns/locale';

const localeMap: Record<string, Locale> = { en: enUS, pt, es };

function getDateLocale(): Locale {
  return localeMap[getLocale()] ?? enUS;
}

export function formatDate(input: DateInput, pattern = 'PP'): string {
  return svelarFormat(input, pattern, { locale: getDateLocale() });
}

export function timeAgo(input: DateInput): string {
  return svelarTimeAgo(input, { locale: getDateLocale() });
}

Then use formatDate and timeAgo from $lib/dates in your components — they automatically pick up the current locale from paraglide.

See the full Date Utilities guide for all available functions.

Next Steps


Svelar i18n Guide © 2026

Svelar © 2026 · MIT License