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 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelte-locale

v2.1.1

Published

A SvelteKit-native i18n library for Svelte 5. Zero dependencies.

Readme

svelte-locale

A SvelteKit-native i18n library for Svelte 5. Zero dependencies.

Features

  • i18n.t() — string translations with {variable} interpolation
  • i18n.plural() — count-based pluralisation via Intl.PluralRules
  • i18n.fn() — logic-based translations returning arbitrary values
  • <I18n> — rich component translations with server-side rendering (uses JavaScript for locale switching)
  • <LocaleLink> — locale-aware anchor tags
  • <LocaleSwitcher> — accessible language switcher with custom snippet support
  • <HreflangLinks> — SEO <link rel="alternate"> tags
  • Intl formatters for numbers, currencies, dates, and relative time
  • ISO 8601 date formatters (formatDateISO, formatDateTimeISO)
  • BCP 47 / ISO 639 regional locale support (en-US, pt-BR, zh-CN, etc.)
  • Server locale detection: URL prefix → cookie → Accept-Language (full BCP 47) → default
  • RTL support via <html dir> (auto-detected from locale registry)
  • Zero flicker — locale is resolved server-side before any HTML is sent

Comparison

| Feature | svelte-locale | Paraglide | typesafe-i18n | svelte-i18n | |---------|---------------|-----------|---------------|-------------| | Zero direct dependencies | ✓ | ✗ | ✓ | ✗ | | Server-first / zero flicker | ✓ | ✓ | △ | △ | | Svelte 5 runes-friendly | ✓ | ✓ | △ | △ | | SvelteKit-native scaffolding | ✓ | ✓ | △ | ✗ | | Plural rules via Intl | ✓ | ✓ | ✓ | ✓ | | Logic-based fn() translations | ✓ | △ | △ | ✗ | | Rich component <I18n> | ✓ | △ | ✗ | ✗ | | Rich text / component interpolation | ✓ | ✓ | ✗ | △ | | Intl formatter helpers | ✓ | ✓ | ✓ | ✗ | | ISO 8601 formatters | ✓ | ✗ | ✗ | ✗ | | BCP 47 / regional locale support | ✓ | ✓ | ✗ | ✗ | | RTL via <html dir> | ✓ | ✓ | ✗ | ✗ | | <LocaleSwitcher> component | ✓ | ✗ | ✗ | ✗ | | <HreflangLinks> SEO component | ✓ | △ | ✗ | ✗ | | URL prefix routing | ✓ | ✓ | ✗ | ✗ | | Type-safe keys and params | ✓ | ✓ | ✓ | ✗ |

Legend: ✓ Built in / first-class | △ Partial / manual / different API | ✗ Not built in

Installation

npx svelte-locale init

That's it. The init command installs the package and writes minimal boilerplate into your project:

  • src/app.html — patches %lang% and %dir% placeholders onto <html>
  • src/app.d.ts — types App.Locals.locale
  • src/hooks.server.ts — wires up handleI18n()
  • src/lib/i18n.ts — configuration file with your locales
  • vite.config.ts — patches in the richI18n() Vite plugin

Existing files are never overwritten — the command skips them and tells you.

Then edit src/lib/i18n.ts to configure your locales and create language data files. You'll need to set up your layout files manually to pass the locale to the client and initialize it.


Manual Setup

If you prefer to wire things up yourself, first install the package:

npm install svelte-locale

Then create these files:

src/lib/i18n.ts

import { defineConfig } from 'svelte-locale';

export default defineConfig({
  locales: ['en-US', 'sv-SE'],
  defaultLocale: 'en-US',
  fallbackLocale: 'en-US',
  cookieName: 'locale',
  routing: {
    strategy: 'none',
    prefixDefaultLocale: false
  },
  detection: ['url', 'cookie', 'accept-language', 'default']
});

vite.config.ts

Add richI18n before sveltekit(). The plugin auto-discovers your i18n.ts config.

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { richI18n } from 'svelte-locale/vite';

export default defineConfig({
  plugins: [
    richI18n(),
    sveltekit()
  ]
});

src/app.html

<html lang="%lang%" dir="%dir%">

src/app.d.ts

declare global {
  namespace App {
    interface Locals {
      locale: import('svelte-locale').Locale;
    }
  }
}
export {};

src/hooks.server.ts

import type { Handle } from '@sveltejs/kit';
import { handleI18n } from 'svelte-locale/server';

export const handle: Handle = handleI18n();

Defining Language Data

Create language data files anywhere in your project and register them via the i18n namespace:

// src/lib/i18n/messages.ts
import i18n from 'svelte-locale';

i18n.defineMessages({
  'en-US': {
    'common.save': 'Save',
    'common.cancel': 'Cancel'
  },
  'sv-SE': {
    'common.save': 'Spara',
    'common.cancel': 'Avbryt'
  }
});
// src/lib/i18n/plurals.ts
import i18n from 'svelte-locale';

i18n.definePlurals({
  'en-US': {
    'items.count': { one: '{count} item', other: '{count} items' }
  },
  'sv-SE': {
    'items.count': { one: '{count} sak', other: '{count} saker' }
  }
});
// src/lib/i18n/functions.ts
import i18n from 'svelte-locale';

i18n.defineFunctions({
  'en-US': {
    'format.currency': (amount: number) => `$${amount.toFixed(2)}`
  },
  'sv-SE': {
    'format.currency': (amount: number) => `${amount.toFixed(2)} kr`
  }
});

Note: defineMessages, definePlurals, and defineFunctions can be called anywhere and as many times as you want — they merge into the registry.


The library ships with a default config.ts baked into the library itself. To customise it, create your own src/lib/i18n.ts with defineConfig(). The current defaults are:

export const i18nConfig = {
  locales: ['en-US', 'sv-SE'] as const,
  defaultLocale: 'en-US',
  fallbackLocale: 'en-US',
  cookieName: 'locale',
  routing: {
    strategy: 'none', // 'prefix' | 'prefix-non-default' | 'none'
    prefixDefaultLocale: false
  },
  detection: ['url', 'cookie', 'accept-language', 'default']
} as const;

locales — all supported locale codes.

defaultLocale — used when no locale can be detected.

fallbackLocale — used when a translation key is missing for the active locale.

cookieName — the cookie used to persist locale preference across requests.

routing.strategy

  • 'none' — no URL prefix, locale is cookie/header only.
  • 'prefix-non-default' — non-default locales get a prefix (e.g. /sv/settings), default locale uses clean paths.
  • 'prefix' — all locales get a prefix including the default.

detection — order of locale detection sources. 'url' checks the path prefix; 'cookie' reads the cookie; 'accept-language' parses the request header; 'default' falls back to defaultLocale.


Translation Files

Create these anywhere in your project (e.g. src/lib/i18n/). They register data into the shared runtime registry via side-effects.

Messages — t()

import { defineMessages } from 'svelte-locale';

defineMessages({
  'en-US': {
    'common.save': 'Save',
    'common.cancel': 'Cancel',
    'auth.welcome': 'Welcome back, {name}'
  },
  'sv-SE': {
    'common.save': 'Spara',
    'common.cancel': 'Avbryt',
    'auth.welcome': 'Välkommen tillbaka, {name}'
  }
});

Use {variableName} syntax for interpolation. Missing variables produce a warning in dev and are left as-is in the output.

Plurals — plural()

import { definePlurals } from 'svelte-locale';

definePlurals({
  'en-US': {
    'tickets.count': {
      one: '{count} ticket',
      other: '{count} tickets'
    }
  },
  'sv-SE': {
    'tickets.count': {
      one: '{count} ärende',
      other: '{count} ärenden'
    }
  }
});

Each entry can have keys: one, other, zero, two, few, many. Which categories apply depends on the locale — they match the categories returned by Intl.PluralRules. other is always required as a fallback.

Functions — fn()

For translations that require logic — formatting, conditionals, composing strings from data.

import { defineFunctions, createFn } from 'svelte-locale';

export type AppFunctions = {
  'billing.status': (input: { status: 'paid' | 'pending' | 'overdue'; days?: number }) => string;
  'audit.event': (input: { actor: string; action: string; target: string }) => string;
};

defineFunctions({
  'en-US': {
    'billing.status': ({ status, days }) => {
      if (status === 'paid') return 'Invoice is paid.';
      if (status === 'overdue') return `Invoice is overdue by ${days ?? 0} days.`;
      return 'Invoice is pending.';
    },
    'audit.event': ({ actor, action, target }) => `${actor} ${action} ${target}.`
  },
  'sv-SE': {
    'billing.status': ({ status, days }) => {
      if (status === 'paid') return 'Fakturan är betald.';
      if (status === 'overdue') return `Fakturan är ${days ?? 0} dagar sen.`;
      return 'Fakturan väntar på betalning.';
    },
    'audit.event': ({ actor, action, target }) => `${actor} ${action} ${target}.`
  }
});

// Export a typed fn() bound to your function map
export const fn = createFn<AppFunctions>();

Import and use fn from your own file — not from svelte-locale directly — to get full type inference on keys and arguments.


API

t(key, values?)

Translate a string key for the current locale. Falls back to fallbackLocale if missing, then returns the key itself.

import { t } from 'svelte-locale';

t('common.save')                         // → 'Save'
t('auth.welcome', { name: 'Vincent' })   // → 'Welcome back, Vincent'

tForLocale(locale, key, values?)

Same as t() but for a specific locale. Useful in server-side load functions.

import { tForLocale } from 'svelte-locale';

tForLocale('sv', 'common.save') // → 'Spara'

plural(key, count, values?)

Resolve a plural string using Intl.PluralRules for the current locale. {count} is automatically available as an interpolation variable.

import { plural } from 'svelte-locale';

plural('tickets.count', 1)  // → '1 ticket'
plural('tickets.count', 5)  // → '5 tickets'

pluralForLocale(locale, key, count, values?)

Same as plural() but for a specific locale.

fn(key, input) / createFn<M>()

fn from svelte-locale is untyped. Use createFn<YourFunctionMap>() to get a typed version bound to your own function definitions.

// src/lib/i18n/functions.ts
export const fn = createFn<AppFunctions>();

// In a component
import { fn } from '$lib/i18n/functions';
fn('billing.status', { status: 'overdue', days: 3 }) // fully typed

fnForLocale(locale, key, ...args)

Untyped fn call for a specific locale. Useful server-side.

getLocale()

Returns the currently active locale as a reactive value (Svelte 5 $state under the hood). Safe to call in $derived and templates.

import { getLocale } from 'svelte-locale';

const locale = getLocale(); // 'en' | 'sv'

setLocale(locale, options?)

Change the active locale at runtime.

  • Updates the reactive locale state
  • Sets document.documentElement.lang and dir
  • Writes the locale cookie (1 year, SameSite=Lax)
  • If navigate: true and routing strategy is 'none', calls invalidateAll() to re-run load functions
  • If navigate: true and a prefix strategy is used, calls goto() with the locale-swapped path (noScroll, keepFocus)
import { setLocale } from 'svelte-locale';

setLocale('sv', { navigate: true });

initLocale(locale)

Set the locale from a server-resolved value. Call this in your root layout from $effect. No navigation side-effects.

defineLocale(code, definition)

Register a custom locale not in the built-in registry, or override an existing entry.

import { defineLocale } from 'svelte-locale';

defineLocale('tok', { name: 'Toki Pona', dir: 'ltr' });

getLocaleName(locale)

Get the human-readable name of a locale from the built-in registry.

getLocaleName('sv') // → 'Svenska'
getLocaleName('ar') // → 'العربية'

getLocaleDir(locale)

Get the text direction for a locale. Returns 'ltr' or 'rtl'.

getLocaleDir('ar') // → 'rtl'
getLocaleDir('en') // → 'ltr'

getLocaleRegistry()

Returns a read-only snapshot of the full locale registry (all built-in + custom locales).

import { getLocaleRegistry } from 'svelte-locale';

const registry = getLocaleRegistry();
// { en: { name: 'English', dir: 'ltr' }, sv: { name: 'Svenska', dir: 'ltr' }, ... }

Intl Formatters

All formatters read the current locale automatically. Formatters are cached per locale+options combination.

formatNumber(value, options?)

import { formatNumber } from 'svelte-locale';

formatNumber(1234567.89)                        // → '1,234,567.89' (en)
formatNumber(0.42, { style: 'percent' })        // → '42%'

formatCurrency(value, currency?, options?)

import { formatCurrency } from 'svelte-locale';

formatCurrency(299, 'SEK')   // → 'SEK 299.00' (en) / '299,00 kr' (sv)
formatCurrency(9.99, 'USD')  // → '$9.99'

formatDate(value, options?)

Accepts a Date, ISO string, or timestamp.

import { formatDate } from 'svelte-locale';

formatDate(new Date(), { dateStyle: 'long' })   // → 'July 3, 2026' (en)
formatDate('2026-01-01', { month: 'short', year: 'numeric' })

formatRelativeTime(value, unit, options?)

import { formatRelativeTime } from 'svelte-locale';

formatRelativeTime(-2, 'day')   // → '2 days ago' (en)
formatRelativeTime(1, 'hour')   // → 'in 1 hour'

formatDateISO(value)

Returns a date-only ISO 8601 string. Locale-independent.

import { formatDateISO } from 'svelte-locale';

formatDateISO(new Date())          // → '2026-07-03'
formatDateISO('2026-01-15T10:30Z') // → '2026-01-15'

formatDateTimeISO(value)

Returns a full UTC ISO 8601 timestamp. Useful for <time datetime> attributes.

import { formatDateTimeISO } from 'svelte-locale';

formatDateTimeISO(new Date()) // → '2026-07-03T10:00:00.000Z'

Routing Helpers

withLocale(path, locale?)

Prepend the locale prefix to a path based on the configured routing strategy. If locale is omitted, uses the current active locale.

import { withLocale } from 'svelte-locale';

// strategy: 'prefix-non-default', default: 'en'
withLocale('/settings')        // → '/settings' (en, no prefix)
withLocale('/settings', 'sv')  // → '/sv/settings'

switchLocaleInPath(pathname, locale)

Swap the locale prefix in a path.

switchLocaleInPath('/sv/settings', 'en') // → '/settings'
switchLocaleInPath('/settings', 'sv')    // → '/sv/settings'

getLocaleFromPath(pathname)

Extract the locale from the first path segment. Returns defaultLocale if no locale prefix is found.

getLocaleFromPath('/sv/settings') // → 'sv'
getLocipeFromPath('/settings')    // → 'en'

stripLocalePrefix(pathname)

Remove the locale prefix from a path.

stripLocalePrefix('/sv/settings') // → '/settings'

ISO & BCP 47 Locale Support

Regional locale codes

The library supports full BCP 47 locale tags. Use them anywhere you use short codes:

// vite.config.ts
richI18n({ locales: ['en-US', 'en-GB', 'pt-BR', 'zh-CN', 'zh-TW'] })
// src/lib/i18n/messages.ts
defineMessages({
  'en-US': { 'currency.symbol': '$' },
  'en-GB': { 'currency.symbol': '£' },
  'pt-BR': { 'greeting': 'Olá' }
});

The registry ships with entries for all common regional variants. For any unregistered subtag the library automatically falls back to the base language for RTL detection — so ar-DZ correctly resolves as rtl via the ar base entry.

Built-in regional variants

| Tag | Name | |-----|------| | en-US | English (US) | | en-GB | English (UK) | | en-AU | English (Australia) | | en-CA | English (Canada) | | fr-FR / fr-CA / fr-BE / fr-CH | French variants | | de-DE / de-AT / de-CH | German variants | | es-ES / es-MX / es-AR / es-CO | Spanish variants | | pt-PT / pt-BR | Portuguese variants | | zh-CN / zh-TW / zh-HK | Chinese variants | | ar-SA / ar-EG | Arabic variants (RTL) | | nl-NL / nl-BE | Dutch variants | | it-IT / it-CH | Italian variants | | sv-SE / nb-NO / nn-NO / fi-FI / da-DK | Nordic | | ru-RU / uk-UA / pl-PL | Eastern European | | ja-JP / ko-KR / hi-IN | Asian |

Add any extra variant with defineLocale:

import { defineLocale } from 'svelte-locale';
defineLocale('es-CL', { name: 'Español (Chile)', dir: 'ltr' });

Locale utilities

import { getBaseLocale, normalizeLocale, getLocaleDir, getLocaleName } from 'svelte-locale';

getBaseLocale('en-US')      // → 'en'
normalizeLocale('pt-br')    // → 'pt-BR'
getLocaleDir('ar-SA')       // → 'rtl'
getLocaleDir('ar-DZ')       // → 'rtl'  (falls back to 'ar')
getLocaleName('pt-BR')      // → 'Português (Brasil)'

Accept-Language detection

The server-side locale detector now matches full BCP 47 tags before falling back to the base language:

  • Accept-Language: pt-BR, pt;q=0.9, en;q=0.8 → resolves to pt-BR if configured, then pt, then en
  • Works automatically — no extra configuration needed.

API Usage

All i18n functions can be accessed in two ways:

Namespace Object

Import the namespace and access all functions through it:

import i18n from 'svelte-locale';

i18n.t('key', { name: 'World' });
i18n.plural('items', 5);
i18n.formatNumber(1234.56);
i18n.setLocale('en-US');

Direct Imports

Import individual functions directly:

import { t, plural, formatNumber, setLocale } from 'svelte-locale';

t('key', { name: 'World' });
plural('items', 5);
formatNumber(1234.56);
setLocale('en-US');

Both approaches work identically. Choose whichever style you prefer.


Components

<I18n>

Renders only the content block matching the active locale. Use lang attributes on child elements to specify locale-specific content.

<I18n key="home.hero">
  <div lang="en">
    <h1>Support made simple</h1>
  </div>
  <div lang="sv">
    <h1>Support gjort enkelt</h1>
  </div>
</I18n>

Props:

  • key — identifier used in dev warnings (does not look up registry)
  • fallback? — locale to render if the active locale block is missing (defaults to fallbackLocale)
  • as? — wrap the output in an HTML element ('section', 'div', etc.)
  • class? / style? — passed to the wrapper element (requires as)

Each direct child must have a lang attribute matching a configured locale. Any valid Svelte content is allowed inside — components, {#if}, {#each}, slots, etc.

The Vite plugin (richI18n) transforms the lang-attributed children into Svelte snippets at compile time. Only the active locale's snippet is rendered, ensuring proper server-side rendering without flash. Missing or duplicate lang values produce warnings during development.

<LocaleSwitcher>

Renders a button per configured locale. The active locale button has aria-pressed="true". Clicking switches the locale and navigates if needed.

<LocaleSwitcher />

Default styling props:

  • class? / style? — applied to the wrapping <div role="group">
  • buttonClass? / buttonStyle? — applied to each default button

Custom button via snippet:

Replace the default button entirely. The snippet receives { locale, active, label, select }.

<LocaleSwitcher>
  {#snippet button({ locale, active, label, select })}
    <button
      onclick={select}
      aria-pressed={active}
      style="font-weight: {active ? 'bold' : 'normal'}"
    >
      {locale.toUpperCase()} — {label}
    </button>
  {/snippet}
</LocaleSwitcher>

<LocaleLink>

A locale-aware anchor. Automatically prepends the correct locale prefix based on the routing strategy. Accepts all standard <a> attributes.

<LocaleLink href="/settings">Settings</LocaleLink>

<!-- Force a specific locale -->
<LocaleLink href="/settings" locale="sv">Svenska inställningar</LocaleLink>

<HreflangLinks>

Injects <link rel="alternate" hreflang="..."> tags into <head> for SEO. Place it in your root layout.

<script lang="ts">
  import { page } from '$app/stores';
  import { HreflangLinks } from 'svelte-locale/svelte';
</script>

<HreflangLinks pathname={$page.url.pathname} origin={$page.url.origin} />

Props:

  • pathname — current page path (required)
  • origin? — base URL for absolute hrefs (e.g. https://example.com)
  • xDefault? — include hreflang="x-default" pointing to the default locale (default: true)
  • extraLocales? — additional { hreflang, locale } entries for region variants (e.g. en-GB)

Server API

handleI18n()

Returns a SvelteKit Handle function. Does the following on every request:

  1. Detects locale (URL → cookie → Accept-Language → default)
  2. Sets event.locals.locale
  3. Handles routing redirects for prefix strategies
  4. Writes the locale cookie (1 year)
  5. Replaces %lang% and %dir% in the HTML via transformPageChunk
  6. Sets Content-Language response header
import { handleI18n } from 'svelte-locale/server';

export const handle: Handle = handleI18n();

detectLocale(event)

Runs just the detection logic without the full handle middleware. Useful if you need to compose handle manually.

import { detectLocale } from 'svelte-locale/server';

const locale = detectLocale(event); // 'en' | 'sv' | ...

Routing Strategies

| Strategy | /settings | /sv/settings | /en/settings | |---|---|---|---| | none | ✓ (en) | — | — | | prefix-non-default | ✓ (en) | ✓ (sv) | redirects → /settings | | prefix | redirects → /en/settings | ✓ (sv) | ✓ (en) |


Built-in Locale Registry

The library includes display names and text directions for 80+ languages out of the box (af, ar, bg, cs, da, de, el, en, es, et, fa, fi, fr, he, hi, hr, hu, id, it, ja, ko, lt, lv, mk, ms, nl, nb, nn, pl, pt, ro, ru, sk, sl, sq, sr, sv, th, tr, uk, ur, vi, zh, and more).

RTL locales include ar, he, fa, ku, ur, yi.

Use defineLocale() to add or override any entry.


Vite Plugin

richI18n({ locales? }) — processes .svelte files at compile time.

  • Finds <I18n> components and transforms their lang-attributed children into Svelte {#snippet} blocks
  • Generates proper source maps so stack traces point to the original source
  • In development, warns about: invalid lang values, duplicate lang values, and missing locale blocks
  • locales — array of valid locale codes for validation (should match your i18nConfig.locales)

Publishing a New Version

# Build and validate
npm run prepack

# Bump version (patch / minor / major), auto-commits + tags
npm version patch

# Push commits and tag
git push && git push --tags

# Publish to npm
npm publish --access public