A powerful, type-safe internationalization (i18n) library for Svelte applications. Built with Svelte 5 runes and TypeScript for a modern, reactive localization experience.
Svelte Phrase Chain uses a dedicated CLI for project initialization, ensuring a streamlined setup process.
Note: You can skip installing the package, as the CLI will effectively just copy paste all necessary files to your project.
# Using bun
bunx svelte-phrase-chain init
# Using npm
npx svelte-phrase-chain init
# Using pnpm
pnpx svelte-phrase-chain init
The CLI accepts several options to customize your setup:
# Full example with all options
bunx svelte-phrase-chain init \
--locales en,fr,es,de \
--fallbackLocale en \
--persistLocale \
--localStorageKey app_locale \
--translationsDir src/lib/i18n/translations \
--generateTranslations \
--debug
Option | Description | Default |
---|---|---|
--locales |
Supported locales (comma-separated) | en,es,fr |
--fallbackLocale |
Fallback locale | en |
--persistLocale |
Persist locale in localStorage | true |
--localStorageKey |
LocalStorage key for locale | app_locale |
--translationsDir |
Translations folder path | src/lib/translations |
--generateTranslations |
Generate initial translation JSON files | true |
--debug |
Enable debug logging | true |
The CLI will generate:
src/
โโโ lib/
โโโ i18n/
โ โโโ i18n.ts # Main i18n configuration & exports
โ โโโ core/ # Core implementation files
โ โโโ index.svelte.ts # Main i18n functionality
โ โโโ types.ts # TypeScript types
โโโ translations/ # Translation JSON files
โโโ en.json
โโโ es.json
โโโ fr.json
<script lang="ts">
import { t, setLocale, locale, initLocale } from '$lib/i18n/i18n';
import type { Locale } from '$lib/i18n/i18n';
// Initialize with browser detection
initLocale({
preferBrowser: true,
preferStorage: true,
defaultLocale: 'en'
});
// Example usage
let name = $state("User");
let messageCount = $state(1);
function changeLanguage(lang: Locale) {
setLocale(lang);
}
</script>
<h1>{t('common.welcome')}</h1>
<p>{t('common.greeting', { name })}</p>
<p>{t('user.messageCount', { count: messageCount }, messageCount)}</p>
<!-- Language switcher -->
<div>
<p>Current locale: {locale()}</p>
<button onclick={() => changeLanguage('en')}>English</button>
<button onclick={() => changeLanguage('fr')}>Franรงais</button>
<button onclick={() => changeLanguage('es')}>Espaรฑol</button>
</div>
Svelte Phrase Chain follows the "code is yours, do what you want with it" approach, similar to shadcn and other projects. It is designed with sensible defaults, but all you need to add is up to you:
When you run the CLI:
While there are several i18n solutions for Svelte like svelte-i18n and typesafe-i18n, Svelte Phrase Chain offers distinct advantages:
Hello, {name}!
)The library expects JSON files with translations for each locale:
// src/lib/translations/en.json
{
"common": {
"welcome": "Welcome to our app",
"greeting": "Hello, {name}!",
"footer": "ยฉ 2025 Awesome App. All rights reserved."
},
"user": {
"messageCount": {
"zero": "No messages",
"one": "You have {count} unread message",
"other": "You have {count} unread messages"
}
}
}
<script>
import { t } from '$lib/i18n/i18n';
const joinDate = new Date('2023-01-15');
const lastLoginDate = new Date(Date.now() - 3600000); // 1 hour ago
</script>
<!-- In your translation file: "user.joinDate": "Member since {date:date}" -->
<p>{t('user.joinDate', { date: joinDate })}</p>
<!-- In your translation file: "user.lastLogin": "Last login: {date:relative}" -->
<p>{t('user.lastLogin', { date: lastLoginDate })}</p>
<script>
import { t } from '$lib/i18n/i18n';
let count = $state(1);
function increment() {
count++;
}
</script>
<!--
Translation structure in user.messageCount:
{
"zero": "No messages",
"one": "{count} message",
"other": "{count} messages"
}
-->
<p>{t('user.messageCount', { count }, count)}</p>
<button on:click={increment}>Add Message</button>
Use the provided schema tools to validate your translation files:
// scripts/validate-translations.ts
import { createI18nSchema } from 'svelte-phrase-chain/schema';
import en from '../src/lib/translations/en.json';
import es from '../src/lib/translations/es.json';
import fr from '../src/lib/translations/fr.json';
const mySchema = createI18nSchema({
pluralKeyIdentifier: (key) => key.endsWith('Count'),
requiredPluralKeys: ['one', 'other'],
optionalPluralKeys: ['zero', 'few', 'many'],
allowedDateFormats: ['date', 'relative', 'fullDate']
});
// Validate all translation files
try {
const enValid = mySchema.parse(en);
const esValid = mySchema.parse(es);
const frValid = mySchema.parse(fr);
console.log("โ
All translation files are valid!");
} catch (error) {
console.error("โ Validation failed:", error);
process.exit(1);
}
Supports all modern browsers with ECMAScript 2015 (ES6) support, with features relying on:
Intl
API for formattingContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.