svelte-tiny-i18n 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -248
- package/README.zh_tw.md +99 -258
- package/dist/svelte-tiny-i18n.cjs +73 -54
- package/dist/svelte-tiny-i18n.d.cts +60 -32
- package/dist/svelte-tiny-i18n.d.ts +60 -32
- package/dist/svelte-tiny-i18n.js +73 -54
- package/package.json +68 -63
package/README.md
CHANGED
|
@@ -1,341 +1,182 @@
|
|
|
1
1
|
# svelte-tiny-i18n
|
|
2
2
|
|
|
3
|
-
[](https://
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://
|
|
3
|
+
[](https://www.npmjs.com/package/svelte-tiny-i18n)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://bundlephobia.com/package/svelte-tiny-i18n)
|
|
6
6
|
|
|
7
7
|
( English | [繁體中文](./README.zh_tw.md) )
|
|
8
8
|
|
|
9
9
|
`svelte-tiny-i18n` is a lightweight, type-safe, and reactive i18n (internationalization) library for [Svelte](https://svelte.dev/) and [SvelteKit](https://kit.svelte.dev/), built entirely on Svelte Stores.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
> **💡 Check out the [Examples Directory](./examples/README.md) for executable demos of all Type-Safety patterns!**
|
|
12
12
|
|
|
13
13
|
## TL;DR
|
|
14
14
|
|
|
15
|
-
`svelte-tiny-i18n` is for developers who value **extreme lightweightness, zero dependencies, and zero build-time configuration**, while still enjoying
|
|
15
|
+
`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**.
|
|
16
16
|
|
|
17
|
-
Its key advantage
|
|
17
|
+
Its key advantage: **Hybrid Type Safety**.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
1. **`src/lib/i18n.ts`** (Config File)
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { createI18nStore, defineI18nConfig, type inferSupportedLocale } from 'svelte-tiny-i18n';
|
|
27
|
-
|
|
28
|
-
const config = defineI18nConfig({
|
|
29
|
-
supportedLocales: ['en', 'es'],
|
|
30
|
-
defaultLocale: 'en',
|
|
31
|
-
localStorageKey: 'lang',
|
|
32
|
-
initialTranslations: [
|
|
33
|
-
{
|
|
34
|
-
hello: { en: 'Hello', es: 'Hola' }
|
|
35
|
-
}
|
|
36
|
-
]
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export const i18n = createI18nStore(config);
|
|
40
|
-
export type SupportedLocale = inferSupportedLocale<typeof i18n>;
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
2. **`src/routes/+layout.ts`** (SvelteKit Integration)
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
import { i18n } from '$lib/i18n';
|
|
47
|
-
import type { LayoutLoad } from './$types';
|
|
48
|
-
|
|
49
|
-
export const load: LayoutLoad = ({ params }) => {
|
|
50
|
-
// 'lang' comes from your route, e.g., /[[lang]]/
|
|
51
|
-
i18n.setLocale(params.lang);
|
|
52
|
-
return {};
|
|
53
|
-
};
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
3. **`src/routes/+page.svelte`** (Usage)
|
|
57
|
-
|
|
58
|
-
```svelte
|
|
59
|
-
<script lang="ts">
|
|
60
|
-
import { i18n } from '$lib/i18n';
|
|
61
|
-
const { t, locale } = i18n;
|
|
62
|
-
</script>
|
|
63
|
-
|
|
64
|
-
<h1>{$t('hello')}</h1>
|
|
65
|
-
<button on:click={() => locale.set('es')}>Español</button>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Core Concepts
|
|
69
|
-
|
|
70
|
-
1. **Type-safe by default**: Automatically infers language codes and translation keys from your config.
|
|
71
|
-
2. **Svelte Native**: Built using Svelte stores (`writable` and `derived`). Integration feels fluid and natural.
|
|
72
|
-
3. **SvelteKit Ready**: Correctly handles language detection and initialization in both SSR (Server-Side Rendering) and CSR (Client-Side Rendering).
|
|
73
|
-
4. **Dynamic & Async Loading**: Easily load translations on demand (e.g., for specific pages) using `extendTranslations`.
|
|
74
|
-
5. **Lightweight**: Minimal dependencies and a tiny bundle size.
|
|
75
|
-
|
|
76
|
-
## Installation
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
npm install svelte-tiny-i18n
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
pnpm add svelte-tiny-i18n
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
yarn add svelte-tiny-i18n
|
|
88
|
-
```
|
|
19
|
+
1. **Config Inference**: For static translations, keys are inferred automatically.
|
|
20
|
+
2. **Global Augmentation**: For dynamic loading, simply point to your files in a `d.ts`, and types work globally.
|
|
21
|
+
3. **Zero-Config Scope**: Or, just get a typed store back from `extendTranslations`.
|
|
89
22
|
|
|
90
23
|
## Quick Start
|
|
91
24
|
|
|
92
|
-
The best way to use `svelte-tiny-i18n` is to create a dedicated singleton instance.
|
|
93
|
-
|
|
94
25
|
### 1. Create the i18n Instance
|
|
95
26
|
|
|
96
|
-
Create a file at `/src/lib/i18n.ts` (or your preferred location).
|
|
97
|
-
|
|
98
27
|
```ts
|
|
99
28
|
// /src/lib/i18n.ts
|
|
100
|
-
import {
|
|
101
|
-
|
|
102
|
-
defineI18nConfig,
|
|
103
|
-
type inferSupportedLocale,
|
|
104
|
-
type inferPartialTranslationEntry,
|
|
105
|
-
type inferTranslationEntry
|
|
106
|
-
} from 'svelte-tiny-i18n';
|
|
107
|
-
|
|
108
|
-
// 1. Define the config
|
|
29
|
+
import { createI18nStore, defineI18nConfig } from 'svelte-tiny-i18n';
|
|
30
|
+
|
|
109
31
|
const i18nConfig = defineI18nConfig({
|
|
110
|
-
// Define all supported languages
|
|
111
32
|
supportedLocales: ['en', 'es', 'zh-TW'],
|
|
112
|
-
|
|
113
|
-
// Default language
|
|
114
33
|
defaultLocale: 'en',
|
|
115
|
-
|
|
116
|
-
// Key for storing the language in localStorage
|
|
117
34
|
localStorageKey: 'my-app-language',
|
|
118
35
|
|
|
119
|
-
// (Optional)
|
|
120
|
-
|
|
36
|
+
// (Optional) Custom Error Handler
|
|
37
|
+
// e.g., send to Sentry in production
|
|
38
|
+
onError: (err) => {
|
|
39
|
+
console.error('i18n error:', err.type, err.key);
|
|
40
|
+
},
|
|
121
41
|
|
|
122
|
-
//
|
|
42
|
+
// 1. Nested JSON is fully supported
|
|
43
|
+
// 2. TypeScript infers these keys automatically!
|
|
123
44
|
initialTranslations: [
|
|
124
45
|
{
|
|
125
|
-
hello: {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
goodbye: {
|
|
131
|
-
en: 'Goodbye',
|
|
132
|
-
es: 'Adiós',
|
|
133
|
-
'zh-TW': '再見'
|
|
46
|
+
hello: { en: 'Hello', 'zh-TW': '你好' },
|
|
47
|
+
home: {
|
|
48
|
+
title: { en: 'Home Page' },
|
|
49
|
+
btn: { en: 'Click Me' }
|
|
134
50
|
}
|
|
135
51
|
}
|
|
136
52
|
]
|
|
137
53
|
});
|
|
138
54
|
|
|
139
|
-
// 2. Create and export the i18n instance
|
|
140
55
|
export const i18n = createI18nStore(i18nConfig);
|
|
141
|
-
|
|
142
|
-
// 3. (Optional) Export inferred types for app-wide type safety
|
|
143
|
-
export type SupportedLocale = inferSupportedLocale<typeof i18n>;
|
|
144
|
-
export type TranslationEntry = inferTranslationEntry<typeof i18n>;
|
|
145
|
-
export type PartialTranslationEntry = inferPartialTranslationEntry<typeof i18n>;
|
|
146
56
|
```
|
|
147
57
|
|
|
148
58
|
### 2. Use in Svelte Components
|
|
149
59
|
|
|
150
|
-
Use the derived store `$t` to get translations, and the `locale` store to read or set the language.
|
|
151
|
-
|
|
152
60
|
```svelte
|
|
153
61
|
<script lang="ts">
|
|
154
62
|
import { i18n } from '$lib/i18n';
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// Destructure the stores and functions
|
|
158
|
-
const { t, locale } = i18n;
|
|
159
|
-
|
|
160
|
-
function setLang(lang: SupportedLocale) {
|
|
161
|
-
// Just set the store's value.
|
|
162
|
-
// The '$t' store will update automatically.
|
|
163
|
-
locale.set(lang);
|
|
164
|
-
}
|
|
63
|
+
const { t, locale, setLocale } = i18n;
|
|
165
64
|
</script>
|
|
166
65
|
|
|
167
|
-
<h1>{$t('hello'
|
|
168
|
-
|
|
169
|
-
<nav>
|
|
170
|
-
<p>Current language: {$locale}</p>
|
|
171
|
-
|
|
172
|
-
<button on:click={() => setLang('en')}>English</button>
|
|
173
|
-
<button on:click={() => setLang('es')}>Español</button>
|
|
174
|
-
<button on:click={() => setLang('zh-TW')}>繁體中文</button>
|
|
175
|
-
</nav>
|
|
176
|
-
|
|
177
|
-
<p>{$t('a.missing.key')}</p>
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### 3. SvelteKit Integration (Recommended)
|
|
181
|
-
|
|
182
|
-
To make the i18n state available on both the server and client, and to initialize from a URL parameter (e.g., `/es/about`), use it in your root `+layout.ts`.
|
|
66
|
+
<h1>{$t('hello')}</h1>
|
|
67
|
+
<p>{$t('home.title')}</p>
|
|
183
68
|
|
|
184
|
-
|
|
185
|
-
// /src/routes/+layout.ts
|
|
186
|
-
import { i18n } from '$lib/i18n';
|
|
187
|
-
import type { LayoutLoad } from './$types';
|
|
188
|
-
|
|
189
|
-
// This load function runs on both SSR and CSR
|
|
190
|
-
export const load: LayoutLoad = ({ params }) => {
|
|
191
|
-
// 'lang' must match your route parameter, e.g., /[[lang]]/
|
|
192
|
-
const { lang } = params;
|
|
193
|
-
|
|
194
|
-
// The setLocale function validates the lang
|
|
195
|
-
// and sets the 'locale' store.
|
|
196
|
-
i18n.setLocale(lang);
|
|
197
|
-
|
|
198
|
-
// You can optionally return lang, but the store itself is already set
|
|
199
|
-
return { lang };
|
|
200
|
-
};
|
|
69
|
+
<button on:click={() => setLocale('zh-TW')}> 中文 </button>
|
|
201
70
|
```
|
|
202
71
|
|
|
203
|
-
|
|
72
|
+
## Advanced Usage: Dynamic Loading & Types
|
|
204
73
|
|
|
205
|
-
|
|
74
|
+
`svelte-tiny-i18n` offers two powerful strategies for handling types when you load translations dynamically (e.g., using `extendTranslations`).
|
|
206
75
|
|
|
207
|
-
###
|
|
76
|
+
### Strategy A: Global Augmentation (Recommended)
|
|
208
77
|
|
|
209
|
-
|
|
78
|
+
Keep your `$t` global, but automate the types using TypeScript's `typeof import`.
|
|
210
79
|
|
|
211
|
-
1.
|
|
80
|
+
1. Create `src/i18n.d.ts`:
|
|
212
81
|
|
|
213
82
|
```ts
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
'zh-TW': '個人資料'
|
|
222
|
-
},
|
|
223
|
-
'profile.edit_button': {
|
|
224
|
-
en: 'Edit'
|
|
225
|
-
// Missing 'es' and 'zh-TW' is allowed!
|
|
83
|
+
import 'svelte-tiny-i18n';
|
|
84
|
+
|
|
85
|
+
declare module 'svelte-tiny-i18n' {
|
|
86
|
+
export interface TinyI18nTranslations {
|
|
87
|
+
// Simply point to your translation files!
|
|
88
|
+
profile: typeof import('./locales/profile.json');
|
|
89
|
+
dashboard: typeof import('./features/dashboard/locales').default;
|
|
226
90
|
}
|
|
227
|
-
}
|
|
91
|
+
}
|
|
228
92
|
```
|
|
229
93
|
|
|
230
|
-
2.
|
|
231
|
-
|
|
232
|
-
```ts
|
|
233
|
-
// /src/routes/profile/+page.ts
|
|
234
|
-
import { i18n } from '$lib/i18n';
|
|
235
|
-
import { profileTranslations } from '$locales/profile';
|
|
236
|
-
import type { PageLoad } from './$types';
|
|
94
|
+
2. Now `$t('profile.name')` is typed everywhere, even before you load it!
|
|
237
95
|
|
|
238
|
-
|
|
239
|
-
// Dynamically add the translations for this page
|
|
240
|
-
i18n.extendTranslations([profileTranslations]);
|
|
96
|
+
### Strategy B: Zero-Config (Local Scope)
|
|
241
97
|
|
|
242
|
-
|
|
243
|
-
// const { jsonTranslations } = await import('$locales/profile.json');
|
|
244
|
-
// i18n.extendTranslations([jsonTranslations]);
|
|
245
|
-
};
|
|
246
|
-
```
|
|
98
|
+
Don't want to touch `d.ts`? No problem. `extendTranslations` returns a store typed specifically for the new content.
|
|
247
99
|
|
|
248
|
-
|
|
100
|
+
```ts
|
|
101
|
+
// /src/routes/profile/+page.svelte
|
|
102
|
+
import { profileTranslations } from './locales';
|
|
249
103
|
|
|
250
|
-
|
|
104
|
+
// The returned 't' knows about 'profile.*' keys immediately
|
|
105
|
+
const { t } = i18n.extendTranslations([profileTranslations]);
|
|
251
106
|
|
|
252
|
-
|
|
107
|
+
$t('profile.title'); // ✅ Typed!
|
|
108
|
+
```
|
|
253
109
|
|
|
254
|
-
|
|
255
|
-
- **Extremely Lightweight**: This library is "tiny" (likely ~1-2kb gzipped). It has **zero dependencies** and does not bundle a heavy ICU message parser, making your app faster.
|
|
256
|
-
- **Svelte Native**: Built purely with Svelte stores (`writable` and `derived`), it integrates seamlessly into the Svelte reactivity model.
|
|
257
|
-
- **Simple but Powerful**: Provides all the essential features: SSR/CSR support for SvelteKit, dynamic/async translation loading (`extendTranslations`), and simple variable substitution.
|
|
258
|
-
- **"Headless" by Design**: It provides only the core logic, giving you full control over your UI and integration.
|
|
110
|
+
See [**Example 2: Global Augmentation**](./examples/2-global-augmentation.ts) and [**Example 3: Zero-Config**](./examples/3-zero-config.ts) for full code.
|
|
259
111
|
|
|
260
112
|
## Comparison with Other Libraries
|
|
261
113
|
|
|
262
114
|
| Dimension | `svelte-tiny-i18n` (This) | `typesafe-i18n` | `svelte-i18n` |
|
|
263
115
|
| :------------------- | :--------------------------------------------------------------------------- | :---------------------------------------------------------------------- | :--------------------------------------------------------- |
|
|
264
|
-
| **Bundle Size** | **Tiny (
|
|
116
|
+
| **Bundle Size** | **Tiny (<1kb)** | **Tiny (~1kb)** | **Medium (~15kb+)** |
|
|
265
117
|
| **Core Mechanism** | Zero-dependency Svelte Stores + Simple String Replace | **Build-time Generator** | **Runtime ICU Parser** |
|
|
266
|
-
| **Type Safety** | **
|
|
118
|
+
| **Type Safety** | **Hybrid (Inference + Augmentation)** | **Very High (Code-Gen)** | Medium (Manual setup) |
|
|
267
119
|
| **Setup Complexity** | **Very Low** (Single config file) | Medium (Requires generator setup) | Low (Install and use) |
|
|
268
120
|
| **Adv. Formatting** | Simple `{var}` replacement only | **Yes** (Plurals, Dates, Numbers) | **Yes (Full ICU Support)** |
|
|
269
121
|
| **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**. |
|
|
270
122
|
|
|
271
123
|
### Philosophy Comparison
|
|
272
124
|
|
|
273
|
-
- **If you need advanced formatting** (complex plurals, date/number localization) and don't mind a larger bundle size, **`svelte-i18n`** is a great choice.
|
|
274
|
-
- **If you need _absolute_ type safety** (including for translation arguments
|
|
275
|
-
- **`svelte-tiny-i18n`** is the ideal choice if you
|
|
276
|
-
1. **Simplicity**: Get started in 2 minutes.
|
|
277
|
-
2. **Bundle Size**: A minimal footprint is critical.
|
|
278
|
-
3. **Effortless Type Safety**: You want strong type guarantees _without_ a build step.
|
|
125
|
+
- **If you need advanced formatting** (complex plurals, date/number localization) and don't mind a larger bundle size, **`svelte-i18n`** is a great choice.
|
|
126
|
+
- **If you need _absolute_ type safety** (including for translation arguments) and are willing to set up a code generator, **`typesafe-i18n`** is excellent.
|
|
127
|
+
- **`svelte-tiny-i18n`** is the ideal choice if you prioritize **simplicity**, **minimal bundle size**, and **effortless type safety** without a build step.
|
|
279
128
|
|
|
280
|
-
|
|
129
|
+
## FAQ / Recipes
|
|
281
130
|
|
|
282
|
-
|
|
131
|
+
### Q: How do I use this in a Svelte (Vite) project _without_ SvelteKit?
|
|
283
132
|
|
|
284
|
-
|
|
133
|
+
A: It's even simpler. You don't need the `+layout.ts` or the `i18n.setLocale()` step.
|
|
134
|
+
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.
|
|
285
135
|
|
|
286
|
-
|
|
136
|
+
### Q: How do I dynamically update the `<html>` `lang` attribute or handle RTL (Right-to-Left) languages?
|
|
287
137
|
|
|
288
|
-
|
|
138
|
+
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.
|
|
289
139
|
|
|
290
|
-
|
|
140
|
+
```svelte
|
|
141
|
+
<script lang="ts">
|
|
142
|
+
import { i18n } from '$lib/i18n';
|
|
143
|
+
const { locale } = i18n;
|
|
291
144
|
|
|
292
|
-
|
|
145
|
+
const rtlLocales: string[] = ['ar', 'he'];
|
|
293
146
|
|
|
294
|
-
|
|
147
|
+
$: if (typeof document !== 'undefined') {
|
|
148
|
+
const direction = rtlLocales.includes($locale) ? 'rtl' : 'ltr';
|
|
149
|
+
document.documentElement.lang = $locale;
|
|
150
|
+
document.documentElement.dir = direction;
|
|
151
|
+
}
|
|
152
|
+
</script>
|
|
295
153
|
|
|
296
|
-
|
|
154
|
+
<slot />
|
|
155
|
+
```
|
|
297
156
|
|
|
298
|
-
|
|
299
|
-
- `$t('key')`
|
|
300
|
-
- `$t('key', { placeholder: 'value' })`
|
|
301
|
-
- `locale`: (Writable store) The currently active language code (e.g., `'en'`). You can `set` or `update` this store.
|
|
302
|
-
- `setLocale(lang: string | null | undefined)`: A function to safely set the initial language, typically called from the root `+layout.ts`.
|
|
303
|
-
- If `lang` is a supported language, it sets the `locale` store.
|
|
304
|
-
- If `lang` is invalid (or `null`/`undefined`), it's ignored, and the `locale` store **keeps its current value**.
|
|
305
|
-
- `extendTranslations(newTranslations: PartialTranslationEntry[])`: Merges new translations (an _array_) into the main store and triggers an update.
|
|
306
|
-
- `supportedLocales`: (Read-only `readonly string[]`) The array of supported languages from your config.
|
|
307
|
-
- `defaultLocale`: (Read-only `string`) The default language from your config.
|
|
308
|
-
- `localStorageKey`: (Read-only `string`) The `localStorage` key from your config.
|
|
157
|
+
## API Reference
|
|
309
158
|
|
|
310
|
-
###
|
|
159
|
+
### `defineI18nConfig(config)`
|
|
311
160
|
|
|
312
|
-
|
|
161
|
+
Helper for defining config with type inference.
|
|
313
162
|
|
|
314
|
-
|
|
315
|
-
- `inferTranslationEntry<typeof i18n>`: Infers the _full_ translation entry type (e.g., `{ en: string; es: string; 'zh-TW': string; }`).
|
|
316
|
-
- `inferPartialTranslationEntry<typeof i18n>`: Infers the type for translation files (e.g., `{ [key: string]: { en?: string; es?: string; 'zh-TW'?: string; } }`).
|
|
163
|
+
### `createI18nStore(config)`
|
|
317
164
|
|
|
318
|
-
|
|
165
|
+
Creates the instance. Returns:
|
|
319
166
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
167
|
+
- `t`: Derived store for translation.
|
|
168
|
+
- `locale`: Writable store for current language.
|
|
169
|
+
- `setLocale(lang)`: Safely changes language.
|
|
170
|
+
- `extendTranslations(modules)`:
|
|
171
|
+
- Merges new translations.
|
|
172
|
+
- Returns `{ t }`: A new store instance typed with the union of existing + new keys.
|
|
325
173
|
|
|
326
|
-
|
|
327
|
-
// /src/components/SomeComponent.svelte
|
|
328
|
-
import { i18n } from '$lib/i18n';
|
|
329
|
-
import type { SupportedLocale } from '$lib/i18n';
|
|
174
|
+
### `onError: (error) => void`
|
|
330
175
|
|
|
331
|
-
|
|
332
|
-
function setLanguage(lang: SupportedLocale) {
|
|
333
|
-
i18n.locale.set(lang);
|
|
334
|
-
}
|
|
176
|
+
Callback in `config` to handle missing keys or locales.
|
|
335
177
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
```
|
|
178
|
+
- `error.type`: `'missing_key' | 'missing_locale'`
|
|
179
|
+
- `error.key`: The key or locale that failed.
|
|
339
180
|
|
|
340
181
|
## License
|
|
341
182
|
|