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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@shelchin/svelte-i18n

v0.0.37

Published

The last Svelte i18n library you'll ever need. Type-safe, AI-powered, zero-config.

Readme

@shelchin/svelte-i18n

The last Svelte i18n library you'll ever need. Type-safe, zero-config, with seamless SSR/CSR support.

npm version License: MIT Svelte 5 TypeScript

中文文档Live DemoExamples

⚠️ Warning: This library is currently in active development and is not recommended for production use yet. APIs may change in future releases. Documentation may be incomplete or contain errors.

✨ Features

🎯 Core Features

  • 🔒 Full Type Safety - Auto-generated TypeScript types for all translation keys
  • 🚀 Zero Configuration - Works out of the box with sensible defaults
  • 📦 Optimized Bundle Size - ~35KB gzipped with tree-shaking support
  • 🌐 SSR/CSR Support - Seamless server-side and client-side rendering
  • 🔄 Hot Module Replacement - Instant translation updates during development
  • 🎨 Rich Formatting - Built-in number, date, currency, and list formatting via native Intl API
  • 📱 Smart Locale Detection - From URL pathname, browser, cookies, or localStorage

🛠️ Developer Experience

  • 🤖 Powerful CLI - Extract keys, validate translations, generate types
  • 🔍 Runtime Validation - Catch translation errors during development
  • 📚 Namespace Support - Isolate translations for packages and libraries
  • 🎯 Smart Fallbacks - Graceful degradation with fallback locales
  • 💾 Persistence - Remember user's language preference across sessions
  • 🌍 150+ Languages - Built-in metadata for all major languages

🏗️ Architecture

  • 🧩 Svelte 5 Native - Built with runes from the ground up
  • 🔌 Unified API - Same API for both applications and npm packages
  • 📊 Lazy Loading - Load translations on-demand for better performance
  • 🎛️ Configuration Inheritance - Libraries automatically inherit app configuration

📦 Installation

# Install the package
pnpm add @shelchin/svelte-i18n
# or
npm install @shelchin/svelte-i18n
# or
yarn add @shelchin/svelte-i18n

🚀 Quick Start

1. Initialize i18n in your project

Run the initialization command to auto-generate configuration:

# Run init command (auto-detects project type and generates config)
pnpm exec svelte-i18n init
# or
npx svelte-i18n init

This will:

  • Create src/translations/ directory structure
  • Generate sample translation files (locales/en.json, locales/zh.json)
  • Create i18n.ts configuration file with type-safe setup
  • Generate TypeScript type definitions

The generated i18n.ts will look like:

// src/translations/i18n.ts (auto-generated)
import { createI18n } from '@shelchin/svelte-i18n';
import type { I18nPath } from './types/i18n-generated.js';

// Auto-scan and import translations from locales directory
const translationModules = import.meta.glob('./locales/*.json', {
	eager: true,
	import: 'default'
});

const translations: Record<string, unknown> = {};

// Extract language code from file path and build translations object
for (const [path, module] of Object.entries(translationModules)) {
	const match = path.match(/\/([^/]+)\.json$/);
	if (match && match[1]) {
		const langCode = match[1];
		translations[langCode] = module;
	}
}

// Create i18n instance with type safety
export const i18n = createI18n<I18nPath>({
	namespace: 'app',
	isMain: true,
	translations,
	defaultLocale: 'en',
	fallbackLocale: 'en'
});

export default i18n;

2. Setup in SvelteKit

Configure +layout.server.ts for SSR:

// src/routes/+layout.server.ts
import { loadI18nSSR } from '@shelchin/svelte-i18n';
import { i18n } from '$src/translations/i18n.js';
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ request }) => {
	const locale = await loadI18nSSR(i18n, request);
	return {
		locale
	};
};

Configure +layout.ts for Universal Loading:

// src/routes/+layout.ts
import { loadI18nUniversal } from '@shelchin/svelte-i18n';
import { i18n } from '$src/translations/i18n.js';
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = async ({ data }) => {
	await loadI18nUniversal(i18n, data?.locale);
	return {
		locale: data?.locale
	};
};

Configure +layout.svelte for Client:

<!-- src/routes/+layout.svelte -->
<script lang="ts">
	import { onMount } from 'svelte';
	import { setupI18nClient } from '@shelchin/svelte-i18n';
	import { i18n } from '$src/translations/i18n.js';

	onMount(async () => {
		await setupI18nClient(i18n);
	});
</script>

<slot />

3. Use in Components

<script lang="ts">
	import { i18n } from '$src/translations/i18n.js';
	import { LanguageSwitcher } from '@shelchin/svelte-i18n';

	let name = $state('World');

	// Type-safe translations with autocomplete
	const welcome = i18n.t('welcome');
	const hello = i18n.t('hello', { name });
</script>

<h1>{welcome}</h1>
<p>{hello}</p>

<!-- Direct usage -->
<nav>
	<a href="/">{i18n.t('navigation.home')}</a>
	<a href="/about">{i18n.t('navigation.about')}</a>
	<a href="/contact">{i18n.t('navigation.contact')}</a>
</nav>

<!-- Language Switcher Component -->
<LanguageSwitcher {i18n} />

4. Use in Libraries/Packages

For library packages, use namespace to avoid conflicts:

// In a library: src/lib/translations/i18n.ts
import { createI18n } from '@shelchin/svelte-i18n';
import type { LibI18nPath } from './types/i18n-generated.js';

// Auto-import translations
const translationModules = import.meta.glob('./locales/*.json', {
	eager: true,
	import: 'default'
});

const translations: Record<string, unknown> = {};
for (const [path, module] of Object.entries(translationModules)) {
	const match = path.match(/\/([^/]+)\.json$/);
	if (match && match[1]) {
		translations[match[1]] = module;
	}
}

export const libI18n = createI18n<LibI18nPath>({
	namespace: 'my-ui-lib', // Use your package name
	translations
});

// Usage in library component
libI18n.t('button.save');

🛠️ CLI Commands

Generate TypeScript Types

# Generate types from translation files
pnpm exec svelte-i18n generate-types
# or with custom paths
pnpm exec svelte-i18n generate-types --dir ./src/translations/locales --out ./src/lib/types/i18n-generated.ts

Validate Translations

# Check for missing translations
pnpm exec svelte-i18n validate src/translations/locales

Extract Translation Keys

# Extract keys from source code
pnpm exec svelte-i18n extract ./src ./template.json

🎯 Type Safety

The init command automatically generates TypeScript types. To regenerate after changes:

pnpm exec svelte-i18n generate-types

This creates type definitions that provide autocomplete for all translation keys:

// Auto-generated types in src/translations/types/i18n-generated.d.ts
export type I18nPath =
	| 'welcome'
	| 'hello'
	| 'navigation.home'
	| 'navigation.about'
	| 'navigation.contact';

// Already configured in your i18n.ts with type safety
import type { I18nPath } from './types/i18n-generated.js';

export const i18n = createI18n<I18nPath>({
	// ... config
});

// Now TypeScript ensures only valid keys are used
i18n.t('welcome'); // ✅ Valid
i18n.t('hello', { name: 'John' }); // ✅ Valid with params
i18n.t('invalid.key'); // ❌ TypeScript error

🌍 Formatting

Built-in formatters using native Intl API (zero dependencies):

const i18n = getI18n();

// Numbers
i18n.formatNumber(1234567.89); // "1,234,567.89" (en) / "1.234.567,89" (de)
i18n.formatNumber(0.15, 'percent'); // "15%"
i18n.formatNumber(123456789, 'compact'); // "123M"

// Currency (auto-detects based on locale)
i18n.formatCurrency(99.99); // "$99.99" (en-US) / "99,99 €" (de-DE)
i18n.formatCurrency(99.99, 'EUR'); // "€99.99"

// Dates
i18n.formatDate(new Date()); // "1/15/2024" (en-US) / "15.1.2024" (de)
i18n.formatDate(new Date(), 'full'); // "Monday, January 15, 2024"

// Time
i18n.formatTime(new Date()); // "3:30 PM" / "15:30"

// Relative Time
i18n.formatRelativeTime(-2, 'day'); // "2 days ago"
i18n.formatRelativeTime(3, 'hour'); // "in 3 hours"

// Lists
i18n.formatList(['Apple', 'Banana', 'Orange']); // "Apple, Banana, and Orange"

🎨 Components

Language Switcher

Pre-built, accessible language switcher component:

<script>
	import { LanguageSwitcher } from '@shelchin/svelte-i18n';
	import { i18n } from '../app/i18n';
</script>

<!-- Default switcher -->
<LanguageSwitcher {i18n} />

<!-- With custom styling and position -->
<LanguageSwitcher
	{i18n}
	class="my-custom-class"
	position="top-left"
	showFlags={true}
	showLabels={true}
/>

Validation Popup (Dev Only)

Shows translation errors during development:

<script>
	import { ValidationPopup } from '@shelchin/svelte-i18n';
	import { i18n } from '../app/i18n';
</script>

{#if import.meta.env.DEV}
	<ValidationPopup {i18n} />
{/if}

📚 Advanced Features

URL-based Locale Detection

Automatically detect locale from URL pathname:

// Supports patterns like:
// /zh/about -> Chinese
// /en-US/products -> American English
// /de-DE/contact -> German

export const load: LayoutLoad = async ({ data, url }) => {
	// The url parameter enables pathname locale detection
	return await loadI18nUniversal(i18n, data, url);
};

Dynamic Translation Loading

Load translations dynamically for code splitting:

// Option 1: Dynamic imports
async function loadTranslations(locale: string) {
	const translations = await import(`../translations/${locale}.json`);
	await i18n.loadLanguage(locale, translations.default);
}

// Option 2: Fetch from API
async function fetchTranslations(locale: string) {
	const response = await fetch(`/api/translations/${locale}`);
	const translations = await response.json();
	await i18n.loadLanguage(locale, translations);
}

Namespace Support for Libraries

Libraries can have isolated translations that don't conflict with the app:

// In your library (my-ui-lib)
export const libI18n = createI18n({
	namespace: 'my-ui-lib',
	translations: {
		en: { button: { save: 'Save', cancel: 'Cancel' } },
		zh: { button: { save: '保存', cancel: '取消' } }
	}
});

// Library translations are automatically namespaced
libI18n.t('button.save'); // Uses "my-ui-lib.button.save" internally

// Libraries automatically inherit app's locale
// When app switches to 'zh', library also switches to 'zh'

SSR with Cookie Persistence

Server-side rendering with locale persistence:

// +layout.server.ts
import type { LayoutServerLoad } from './$types';
import { loadI18nSSR } from '@shelchin/svelte-i18n';

export const load: LayoutServerLoad = async ({ cookies }) => {
	const locale = cookies.get('i18n-locale') || 'en';
	return loadI18nSSR(locale, ['en', 'zh', 'ja']);
};

Pluralization

Handle plural forms correctly for all languages:

// English: 0 = plural, 1 = singular, 2+ = plural
"items.count": "No items | One item | {count} items"

// Polish: Complex plural rules
"items.count": "Brak elementów | Jeden element | {count} elementy | {count} elementów"

// Usage
i18n.t('items.count', { count: 0 });  // "No items"
i18n.t('items.count', { count: 1 });  // "One item"
i18n.t('items.count', { count: 5 });  // "5 items"

Interpolation

Dynamic values in translations:

// Basic interpolation
"welcome": "Welcome {name}!"
i18n.t('welcome', { name: 'John' }); // "Welcome John!"

// Nested values
"user.greeting": "Hello {user.firstName} {user.lastName}"
i18n.t('user.greeting', {
  user: { firstName: 'John', lastName: 'Doe' }
}); // "Hello John Doe"

// Custom interpolation markers
const i18n = createI18n({
  interpolation: {
    prefix: '{{',
    suffix: '}}'
  }
});
// Now use: "welcome": "Welcome {{name}}!"

Runtime Validation

Catch translation issues during development:

const i18n = createI18n({
	translations,
	validateInDev: true, // Enable validation
	validateOptions: {
		checkInterpolation: true, // Verify {variables} match
		checkPluralization: true, // Verify plural forms
		checkHTML: false, // Allow HTML in translations
		checkMissing: true, // Report missing keys
		checkExtra: true // Report extra keys
	}
});

// Shows validation popup in development with errors

🛠️ CLI Tools

Initialize Project

Set up i18n in your project interactively:

npx svelte-i18n init

This will:

  • Create translation directories
  • Generate initial config files
  • Set up type definitions
  • Create example translations

Extract Translation Keys

Scan your code and extract all translation keys:

# Extract from source code
npx svelte-i18n extract ./src ./translations/template.json

# Specify file extensions
npx svelte-i18n extract ./src ./translations/template.json js ts svelte

Validate Translations

Check for missing or extra keys across all locales:

# Basic validation
npx svelte-i18n validate ./translations

# Strict validation (exit with error code)
npx svelte-i18n validate ./translations --strict

# Use specific base locale
npx svelte-i18n validate ./translations --base zh

Generate TypeScript Types

Generate type definitions for translation keys:

# Generate for app translations (default)
npx svelte-i18n generate-types

# Custom paths
npx svelte-i18n generate-types \
  --dir ./translations \
  --out ./src/types/i18n.ts \
  --locale en

# Skip validation of other locales
npx svelte-i18n generate-types --no-validate

📖 API Reference

Core Functions

createI18n<TPath>(config)

Creates a typed i18n instance.

const i18n = createI18n<TranslationPaths>({
	translations, // Translation data
	defaultLocale: 'en', // Default locale
	fallbackLocale: 'en', // Fallback for missing translations
	namespace: 'app', // Namespace (for libraries)
	isMain: true, // Is main app instance?
	validateInDev: true, // Enable dev validation
	interpolation: {
		// Interpolation options
		prefix: '{',
		suffix: '}'
	}
});

i18n.t(key, params?)

Get translated text with optional interpolation.

i18n.t('welcome', { name: 'John' }); // "Welcome John!"
i18n.t('items.count', { count: 5 }); // "5 items"

i18n.setLocale(locale)

Change the current locale (async).

await i18n.setLocale('zh'); // Switch to Chinese

i18n.setLocaleSync(locale)

Change locale synchronously (for SSR).

i18n.setLocaleSync('zh'); // Immediate switch

i18n.loadLanguage(locale, translations)

Dynamically load translations.

await i18n.loadLanguage('ja', japaneseTranslations);

Properties

i18n.locale; // Current locale ('en')
i18n.locales; // Available locales (['en', 'zh', 'ja'])
i18n.isLoading; // Loading state (true/false)
i18n.errors; // Validation errors (dev only)
i18n.meta; // Language metadata (direction, native name, etc.)

SvelteKit Integration

loadI18nUniversal(i18n, data, url?, options?)

Universal load function for +layout.ts.

await loadI18nUniversal(i18n, data, url, {
	storageKey: 'i18n-locale', // localStorage key
	cookieName: 'i18n-locale', // Cookie name
	defaultLocale: 'en', // Default locale
	detectFromPath: true // Detect from URL path
});

loadI18nSSR(locale, locales, options?)

Server-side load function for +layout.server.ts.

loadI18nSSR('en', ['en', 'zh'], {
	cookieName: 'i18n-locale'
});

setupI18nClient(i18n, data, options?)

Synchronous client setup for +layout.svelte.

const result = setupI18nClient(i18n, data, {
	defaultLocale: 'en',
	restoreFromStorage: true
});

initI18nOnMount(i18n, data, options?)

Async initialization in onMount.

await initI18nOnMount(i18n, data, {
	initFunction: async (i18n) => {
		// Custom initialization
	}
});

Formatting Functions

All formatters are locale-aware and reactive:

formatNumber(value, style?, options?)
formatCurrency(value, currency?, options?)
formatDate(date, style?, options?)
formatTime(date, style?, options?)
formatRelativeTime(value, unit, options?)
formatList(items, style?, options?)

Utility Functions

// Detect browser language
detectBrowserLanguage(); // 'en-US'

// Validate translation schema
validateSchema(translations, options);

// Merge translation objects
mergeTranslations(target, source);

// Get available locales from registry
getAvailableLocales(registry);

// Check if locale is available
isLocaleAvailable(registry, 'zh');

🔧 Configuration

Full Configuration Options

interface I18nConfig {
	// Basic
	defaultLocale?: string; // Default: 'en'
	fallbackLocale?: string; // Default: same as defaultLocale
	supportedLocales?: string[]; // Auto-detected if not set

	// Features
	validateInDev?: boolean; // Default: true
	loadingDelay?: number; // Default: 200ms
	namespace?: string; // Default: 'app'
	isMain?: boolean; // Default: true for 'app'

	// Formatting
	interpolation?: {
		prefix?: string; // Default: '{'
		suffix?: string; // Default: '}'
		escapeValue?: boolean; // Default: false
	};

	pluralization?: {
		separator?: string; // Default: '|'
	};

	// Validation
	validateOptions?: {
		checkInterpolation?: boolean;
		checkPluralization?: boolean;
		checkHTML?: boolean;
		checkMissing?: boolean;
		checkExtra?: boolean;
	};
}

Environment Variables

# .env
VITE_I18N_DEFAULT_LOCALE=en
VITE_I18N_FALLBACK_LOCALE=en
VITE_I18N_SUPPORTED_LOCALES=en,zh,ja,de,fr
VITE_I18N_DEBUG=true

🎯 Best Practices

1. Structure Your Translations

src/
  translations/
    en.json          # English (base)
    zh.json          # Chinese
    ja.json          # Japanese
    locales/         # Alternative structure
      en/
        common.json
        errors.json
        forms.json

2. Use Type Safety

Always generate and use types:

// Generate types after translation changes
npm run i18n:types

// Import and use
import type { I18nPath } from '$lib/types/i18n-generated';
export const i18n = createI18n<I18nPath>({ ... });

3. Handle Loading States

{#if i18n.isLoading}
	<LoadingSpinner />
{:else}
	<Content />
{/if}

4. Optimize Bundle Size

// ❌ Don't import all translations statically
import * as allTranslations from './translations';

// ✅ Import only needed or use dynamic imports
import en from './translations/en.json';
const zh = await import('./translations/zh.json');

5. Test Your Translations

// Run validation in CI/CD
npm run i18n:validate

// Test with different locales
npm run dev -- --locale=zh

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

# Clone the repository
git clone https://github.com/atshelchin/svelte-i18n.git

# Install dependencies
pnpm install

# Start development server
pnpm dev

# Run tests
pnpm test

# Build library
pnpm build

📄 License

MIT © Shelchin

🙏 Acknowledgments

Built with ❤️ using:

  • Svelte 5 - The magical disappearing framework
  • SvelteKit - The fastest way to build Svelte apps
  • TypeScript - JavaScript with syntax for types
  • Vite - Next generation frontend tooling

Special thanks to all contributors who helped make this project better!


DocumentationLive DemoExamplesReport Bug

Made with ❤️ by Shelchin