( English | 繁體中文 )
svelte-tiny-i18n is a lightweight, type-safe, and reactive i18n (internationalization) library for Svelte and SvelteKit, built entirely on Svelte Stores.
💡 Check out the Examples Directory for executable demos of all Type-Safety patterns!
svelte-tiny-i18n is for developers who value extreme lightweightness, zero dependencies, and zero build-time configuration, while still enjoying Svelte store reactivity and instant TypeScript inference.
Its key advantage: Hybrid Type Safety.
d.ts, and types work globally.extendTranslations.// /src/lib/i18n.ts
import { createI18nStore, defineI18nConfig } from 'svelte-tiny-i18n';
const i18nConfig = defineI18nConfig({
supportedLocales: ['en', 'es', 'zh-TW'],
defaultLocale: 'en',
localStorageKey: 'my-app-language',
// (Optional) Custom Error Handler
// e.g., send to Sentry in production
onError: (err) => {
console.error('i18n error:', err.type, err.key);
},
// 1. Nested JSON is fully supported
// 2. TypeScript infers these keys automatically!
initialTranslations: [
{
hello: { en: 'Hello', 'zh-TW': '你好' },
home: {
title: { en: 'Home Page' },
btn: { en: 'Click Me' }
}
}
]
});
export const i18n = createI18nStore(i18nConfig);
<script lang="ts">
import { i18n } from '$lib/i18n';
const { t, locale, setLocale } = i18n;
</script>
<h1>{$t('hello')}</h1>
<p>{$t('home.title')}</p>
<button on:click={() => setLocale('zh-TW')}> 中文 </button>
svelte-tiny-i18n offers two powerful strategies for handling types when you load translations dynamically (e.g., using extendTranslations).
Keep your $t global, but automate the types using TypeScript's typeof import.
Create src/i18n.d.ts:
import 'svelte-tiny-i18n';
declare module 'svelte-tiny-i18n' {
export interface TinyI18nTranslations {
// Simply point to your translation files!
profile: typeof import('./locales/profile.json');
dashboard: typeof import('./features/dashboard/locales').default;
}
}
Now $t('profile.name') is typed everywhere, even before you load it!
Don't want to touch d.ts? No problem. extendTranslations returns a store typed specifically for the new content.
// /src/routes/profile/+page.svelte
import { profileTranslations } from './locales';
// The returned 't' knows about 'profile.*' keys immediately
const { t } = i18n.extendTranslations([profileTranslations]);
$t('profile.title'); // ✅ Typed!
See Example 2: Global Augmentation and Example 3: Zero-Config for full code.
| Dimension | svelte-tiny-i18n (This) |
typesafe-i18n |
svelte-i18n |
|---|---|---|---|
| Bundle Size | Tiny (<1kb) | Tiny (~1kb) | Medium (~15kb+) |
| Core Mechanism | Zero-dependency Svelte Stores + Simple String Replace | Build-time Generator | Runtime ICU Parser |
| Type Safety | Hybrid (Inference + Augmentation) | Very High (Code-Gen) | Medium (Manual setup) |
| Setup Complexity | Very Low (Single config file) | Medium (Requires generator setup) | Low (Install and use) |
| Adv. Formatting | Simple {var} replacement only |
Yes (Plurals, Dates, Numbers) | Yes (Full ICU Support) |
| Key Trade-Off | Trades ICU features for extreme lightness & zero-config type safety. | Trades setup simplicity for the strongest type safety (incl. args). | Trades bundle size for the most powerful ICU features. |
svelte-i18n is a great choice.typesafe-i18n is excellent.svelte-tiny-i18n is the ideal choice if you prioritize simplicity, minimal bundle size, and effortless type safety without a build step.A: It's even simpler. You don't need the +layout.ts or the i18n.setLocale() step.
The store will automatically initialize its language in the browser by checking localStorage and navigator.language. You can change the language at any time by simply calling i18n.setLocale('new_lang') in your components.
<html> lang attribute or handle RTL (Right-to-Left) languages?A: This library is "headless," so it doesn't modify the DOM for you. You can easily manage this yourself by subscribing to the locale store in your root layout component.
<script lang="ts">
import { i18n } from '$lib/i18n';
const { locale } = i18n;
const rtlLocales: string[] = ['ar', 'he'];
$: if (typeof document !== 'undefined') {
const direction = rtlLocales.includes($locale) ? 'rtl' : 'ltr';
document.documentElement.lang = $locale;
document.documentElement.dir = direction;
}
</script>
<slot />
defineI18nConfig(config)Helper for defining config with type inference.
createI18nStore(config)Creates the instance. Returns:
t: Derived store for translation.locale: Writable store for current language.setLocale(lang): Safely changes language.extendTranslations(modules):{ t }: A new store instance typed with the union of existing + new keys.onError: (error) => voidCallback in config to handle missing keys or locales.
error.type: 'missing_key' | 'missing_locale'error.key: The key or locale that failed.