svelte-tiny-i18n 1.0.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/LICENSE +21 -0
- package/README.md +342 -0
- package/README.zh_tw.md +342 -0
- package/dist/svelte-tiny-i18n.cjs +355 -0
- package/dist/svelte-tiny-i18n.d.cts +526 -0
- package/dist/svelte-tiny-i18n.d.ts +526 -0
- package/dist/svelte-tiny-i18n.js +329 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tzu-Ching Yang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# svelte-tiny-i18n
|
|
2
|
+
|
|
3
|
+
[](https://www.google.com/search?q=https://www.npmjs.com/package/svelte-tiny-i18n)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.google.com/search?q=https://bundlephobia.com/package/svelte-tiny-i18n)
|
|
6
|
+
|
|
7
|
+
( English | [繁體中文](./README.zh_tw.md) )
|
|
8
|
+
|
|
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
|
+
|
|
11
|
+
This library is "headless," meaning it only provides the core logic and Svelte stores, leaving the UI and component integration entirely up to the developer.
|
|
12
|
+
|
|
13
|
+
## TL;DR
|
|
14
|
+
|
|
15
|
+
`svelte-tiny-i18n` is for developers who value **extreme lightweightness, zero dependencies, and zero build-time configuration**, while still enjoying a **native Svelte store experience and instant TypeScript inference**.
|
|
16
|
+
|
|
17
|
+
Its key advantage is: **You define your translations in your `i18n.ts` config file, and TypeScript _immediately_ gives you type-safety and autocompletion** for both `$t('...')` keys and `locale.set('...')` language codes, all without running a code generator.
|
|
18
|
+
|
|
19
|
+
This makes it the ideal choice for small-to-medium projects and Svelte purists.
|
|
20
|
+
|
|
21
|
+
### Example: Minimal SvelteKit Integration
|
|
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
|
+
```
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
The best way to use `svelte-tiny-i18n` is to create a dedicated singleton instance.
|
|
93
|
+
|
|
94
|
+
### 1. Create the i18n Instance
|
|
95
|
+
|
|
96
|
+
Create a file at `/src/lib/i18n.ts` (or your preferred location).
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// /src/lib/i18n.ts
|
|
100
|
+
import {
|
|
101
|
+
createI18nStore,
|
|
102
|
+
defineI18nConfig,
|
|
103
|
+
type inferSupportedLocale,
|
|
104
|
+
type inferPartialTranslationEntry,
|
|
105
|
+
type inferTranslationEntry
|
|
106
|
+
} from 'svelte-tiny-i18n';
|
|
107
|
+
|
|
108
|
+
// 1. Define the config
|
|
109
|
+
const i18nConfig = defineI18nConfig({
|
|
110
|
+
// Define all supported languages
|
|
111
|
+
supportedLocales: ['en', 'es', 'zh-TW'],
|
|
112
|
+
|
|
113
|
+
// Default language
|
|
114
|
+
defaultLocale: 'en',
|
|
115
|
+
|
|
116
|
+
// Key for storing the language in localStorage
|
|
117
|
+
localStorageKey: 'my-app-language',
|
|
118
|
+
|
|
119
|
+
// (Optional) Log warnings to the console if a key is not found
|
|
120
|
+
devLogs: true,
|
|
121
|
+
|
|
122
|
+
// Define initial, global translations
|
|
123
|
+
initialTranslations: [
|
|
124
|
+
{
|
|
125
|
+
hello: {
|
|
126
|
+
en: 'Hello, {name}!',
|
|
127
|
+
es: '¡Hola, {name}!',
|
|
128
|
+
'zh-TW': '你好, {name}!'
|
|
129
|
+
},
|
|
130
|
+
goodbye: {
|
|
131
|
+
en: 'Goodbye',
|
|
132
|
+
es: 'Adiós',
|
|
133
|
+
'zh-TW': '再見'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// 2. Create and export the i18n instance
|
|
140
|
+
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
|
+
```
|
|
147
|
+
|
|
148
|
+
### 2. Use in Svelte Components
|
|
149
|
+
|
|
150
|
+
Use the derived store `$t` to get translations, and the `locale` store to read or set the language.
|
|
151
|
+
|
|
152
|
+
```svelte
|
|
153
|
+
<script lang="ts">
|
|
154
|
+
import { i18n } from '$lib/i18n';
|
|
155
|
+
import type { SupportedLocale } from '$lib/i18n';
|
|
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
|
+
}
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<h1>{$t('hello', { name: 'World' })}</h1>
|
|
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`.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
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
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Note:** Your SvelteKit route structure must be similar to `/src/routes/[[lang]]/...` for `params.lang` to be available.
|
|
204
|
+
|
|
205
|
+
## Advanced Usage
|
|
206
|
+
|
|
207
|
+
### Dynamic (Async) Translation Loading
|
|
208
|
+
|
|
209
|
+
You don't need to load all translations at startup. You can load them on demand in a page's `+page.ts` or `+layout.ts` using `extendTranslations`.
|
|
210
|
+
|
|
211
|
+
1. Define page-specific translations:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
// /src/locales/profile.ts
|
|
215
|
+
import type { PartialTranslationEntry } from '$lib/i18n';
|
|
216
|
+
|
|
217
|
+
export const profileTranslations: PartialTranslationEntry = {
|
|
218
|
+
'profile.title': {
|
|
219
|
+
en: 'My Profile',
|
|
220
|
+
es: 'Mi Perfil',
|
|
221
|
+
'zh-TW': '個人資料'
|
|
222
|
+
},
|
|
223
|
+
'profile.edit_button': {
|
|
224
|
+
en: 'Edit'
|
|
225
|
+
// Missing 'es' and 'zh-TW' is allowed!
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
2. Load them in the page's loader:
|
|
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';
|
|
237
|
+
|
|
238
|
+
export const load: PageLoad = () => {
|
|
239
|
+
// Dynamically add the translations for this page
|
|
240
|
+
i18n.extendTranslations([profileTranslations]);
|
|
241
|
+
|
|
242
|
+
// You can also await an async import
|
|
243
|
+
// const { jsonTranslations } = await import('$locales/profile.json');
|
|
244
|
+
// i18n.extendTranslations([jsonTranslations]);
|
|
245
|
+
};
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The new translations are now merged into the store and available via the `$t` function.
|
|
249
|
+
|
|
250
|
+
## Advantages
|
|
251
|
+
|
|
252
|
+
`svelte-tiny-i18n` is designed to be the "sweet spot" for Svelte developers who need a simple, fast, and type-safe solution without the overhead of larger libraries.
|
|
253
|
+
|
|
254
|
+
- **Zero-Config Type Safety**: Get full type-safety for your language codes and translation entries _without_ a build step, code generator, or complex setup. Type-safety is achieved instantly via TypeScript inference from your single configuration object.
|
|
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.
|
|
259
|
+
|
|
260
|
+
## Comparison with Other Libraries
|
|
261
|
+
|
|
262
|
+
| Dimension | `svelte-tiny-i18n` (This) | `typesafe-i18n` | `svelte-i18n` |
|
|
263
|
+
| :------------------- | :--------------------------------------------------------------------------- | :---------------------------------------------------------------------- | :--------------------------------------------------------- |
|
|
264
|
+
| **Bundle Size** | **Tiny (~1-2kb)** | **Tiny (~1kb)** | **Medium (~15kb+)** |
|
|
265
|
+
| **Core Mechanism** | Zero-dependency Svelte Stores + Simple String Replace | **Build-time Generator** | **Runtime ICU Parser** |
|
|
266
|
+
| **Type Safety** | **High (Instant Inference)** | **Very High (Code-Gen)** | Medium (Manual setup) |
|
|
267
|
+
| **Setup Complexity** | **Very Low** (Single config file) | Medium (Requires generator setup) | Low (Install and use) |
|
|
268
|
+
| **Adv. Formatting** | Simple `{var}` replacement only | **Yes** (Plurals, Dates, Numbers) | **Yes (Full ICU Support)** |
|
|
269
|
+
| **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
|
+
|
|
271
|
+
### Philosophy Comparison
|
|
272
|
+
|
|
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. It uses the industry-standard `formatjs` and ICU syntax.
|
|
274
|
+
- **If you need _absolute_ type safety** (including for translation arguments, e.g., `$t('key', { arg: 'val' })`) and are willing to set up a code generator, **`typesafe-i18n`** is excellent.
|
|
275
|
+
- **`svelte-tiny-i18n`** is the ideal choice if you value:
|
|
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.
|
|
279
|
+
|
|
280
|
+
This library intentionally trades complex ICU formatting (which `svelte-i18n` provides) and argument-level type safety (which `typesafe-i18n` provides) for **extreme simplicity, minimal size, and zero-config type safety.**
|
|
281
|
+
|
|
282
|
+
## API Reference
|
|
283
|
+
|
|
284
|
+
### Factory Functions
|
|
285
|
+
|
|
286
|
+
#### `createI18nStore(config)`
|
|
287
|
+
|
|
288
|
+
Creates the core i18n instance. Returns an object containing stores and functions.
|
|
289
|
+
|
|
290
|
+
#### `defineI18nConfig(config)`
|
|
291
|
+
|
|
292
|
+
A helper function for defining your `I18nConfig` that provides full type safety and inference.
|
|
293
|
+
|
|
294
|
+
### The Returned Instance (`i18n`)
|
|
295
|
+
|
|
296
|
+
When you call `createI18nStore`, you get an object with:
|
|
297
|
+
|
|
298
|
+
- `t`: (Read-only derived store) The translation function.
|
|
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.
|
|
309
|
+
|
|
310
|
+
### Type Helpers
|
|
311
|
+
|
|
312
|
+
For robust type safety in your app, you can import type helpers directly from `svelte-tiny-i18n`.
|
|
313
|
+
|
|
314
|
+
- `inferSupportedLocale<typeof i18n>`: Infers the union of supported language codes (e.g., `'en' | 'es' | 'zh-TW'`).
|
|
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; } }`).
|
|
317
|
+
|
|
318
|
+
**Example:**
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
// /src/lib/i18n.ts
|
|
322
|
+
// ... (as shown in "Quick Start")
|
|
323
|
+
export type SupportedLocale = inferSupportedLocale<typeof i18n>;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
// /src/components/SomeComponent.svelte
|
|
328
|
+
import { i18n } from '$lib/i18n';
|
|
329
|
+
import type { SupportedLocale } from '$lib/i18n';
|
|
330
|
+
|
|
331
|
+
// The 'lang' variable is now type-checked
|
|
332
|
+
function setLanguage(lang: SupportedLocale) {
|
|
333
|
+
i18n.locale.set(lang);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
setLanguage('en'); // OK
|
|
337
|
+
setLanguage('fr'); // TypeScript Error
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
[MIT](https://opensource.org/licenses/MIT)
|