svelte-tiny-i18n 1.0.0 → 1.0.2
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 +71 -37
- package/README.zh_tw.md +73 -38
- package/dist/svelte-tiny-i18n.cjs +2 -2
- package/dist/svelte-tiny-i18n.d.cts +20 -14
- package/dist/svelte-tiny-i18n.d.ts +20 -14
- package/dist/svelte-tiny-i18n.js +2 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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
|
|
|
@@ -14,12 +14,26 @@ This library is "headless," meaning it only provides the core logic and Svelte s
|
|
|
14
14
|
|
|
15
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
16
|
|
|
17
|
-
Its key advantage
|
|
17
|
+
Its key advantage: **Zero-config type safety**. You define your supported locales (e.g., `['en', 'es']`) in a single config file, and TypeScript immediately provides type-checking and autocompletion for your `setLocale` function (e.g., `setLocale('es')` is valid, `setLocale('fr')` is an error) — all without running a code generator.
|
|
18
18
|
|
|
19
19
|
This makes it the ideal choice for small-to-medium projects and Svelte purists.
|
|
20
20
|
|
|
21
21
|
### Example: Minimal SvelteKit Integration
|
|
22
22
|
|
|
23
|
+
Assuming a project structure like this:
|
|
24
|
+
|
|
25
|
+
```tree
|
|
26
|
+
/src
|
|
27
|
+
├── /lib
|
|
28
|
+
│ └── i18n.ts <- 1. Config File
|
|
29
|
+
└── /routes
|
|
30
|
+
└── /[lang]
|
|
31
|
+
├── +layout.ts <- 2. SvelteKit Integration
|
|
32
|
+
├── +page.svelte <- 3. Usage
|
|
33
|
+
└── /about
|
|
34
|
+
└── +page.svelte
|
|
35
|
+
```
|
|
36
|
+
|
|
23
37
|
1. **`src/lib/i18n.ts`** (Config File)
|
|
24
38
|
|
|
25
39
|
```ts
|
|
@@ -40,39 +54,31 @@ This makes it the ideal choice for small-to-medium projects and Svelte purists.
|
|
|
40
54
|
export type SupportedLocale = inferSupportedLocale<typeof i18n>;
|
|
41
55
|
```
|
|
42
56
|
|
|
43
|
-
2. **`src/routes/+layout.ts`** (SvelteKit Integration)
|
|
57
|
+
2. **`src/routes/[lang]/+layout.ts`** (SvelteKit Integration)
|
|
44
58
|
|
|
45
59
|
```ts
|
|
46
60
|
import { i18n } from '$lib/i18n';
|
|
47
61
|
import type { LayoutLoad } from './$types';
|
|
48
62
|
|
|
49
63
|
export const load: LayoutLoad = ({ params }) => {
|
|
50
|
-
// 'lang' comes from your route, e.g., /[
|
|
64
|
+
// 'lang' comes from your route, e.g., /[lang]/
|
|
51
65
|
i18n.setLocale(params.lang);
|
|
52
66
|
return {};
|
|
53
67
|
};
|
|
54
68
|
```
|
|
55
69
|
|
|
56
|
-
3. **`src/routes/+page.svelte`** (Usage)
|
|
70
|
+
3. **`src/routes/[lang]/+page.svelte`** (Usage)
|
|
57
71
|
|
|
58
72
|
```svelte
|
|
59
73
|
<script lang="ts">
|
|
60
74
|
import { i18n } from '$lib/i18n';
|
|
61
|
-
const { t,
|
|
75
|
+
const { t, setLocale } = i18n;
|
|
62
76
|
</script>
|
|
63
77
|
|
|
64
78
|
<h1>{$t('hello')}</h1>
|
|
65
|
-
<button on:click={() =>
|
|
79
|
+
<button on:click={() => setLocale('es')}>Español</button>
|
|
66
80
|
```
|
|
67
81
|
|
|
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
82
|
## Installation
|
|
77
83
|
|
|
78
84
|
```bash
|
|
@@ -147,21 +153,14 @@ export type PartialTranslationEntry = inferPartialTranslationEntry<typeof i18n>;
|
|
|
147
153
|
|
|
148
154
|
### 2. Use in Svelte Components
|
|
149
155
|
|
|
150
|
-
Use the derived store `$t` to get translations, and the `locale` store to read or set the language.
|
|
156
|
+
Use the derived store `$t` to get translations, and the `locale` store to read or `setLocale` to set the language.
|
|
151
157
|
|
|
152
158
|
```svelte
|
|
153
159
|
<script lang="ts">
|
|
154
160
|
import { i18n } from '$lib/i18n';
|
|
155
|
-
import type { SupportedLocale } from '$lib/i18n';
|
|
156
161
|
|
|
157
162
|
// 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
|
-
}
|
|
163
|
+
const { t, locale, setLocale } = i18n;
|
|
165
164
|
</script>
|
|
166
165
|
|
|
167
166
|
<h1>{$t('hello', { name: 'World' })}</h1>
|
|
@@ -169,9 +168,9 @@ Use the derived store `$t` to get translations, and the `locale` store to read o
|
|
|
169
168
|
<nav>
|
|
170
169
|
<p>Current language: {$locale}</p>
|
|
171
170
|
|
|
172
|
-
<button on:click={() =>
|
|
173
|
-
<button on:click={() =>
|
|
174
|
-
<button on:click={() =>
|
|
171
|
+
<button on:click={() => setLocale('en')}>English</button>
|
|
172
|
+
<button on:click={() => setLocale('es')}>Español</button>
|
|
173
|
+
<button on:click={() => setLocale('zh-TW')}>繁體中文</button>
|
|
175
174
|
</nav>
|
|
176
175
|
|
|
177
176
|
<p>{$t('a.missing.key')}</p>
|
|
@@ -188,7 +187,7 @@ import type { LayoutLoad } from './$types';
|
|
|
188
187
|
|
|
189
188
|
// This load function runs on both SSR and CSR
|
|
190
189
|
export const load: LayoutLoad = ({ params }) => {
|
|
191
|
-
// 'lang' must match your route parameter, e.g., /[
|
|
190
|
+
// 'lang' must match your route parameter, e.g., /[lang]/
|
|
192
191
|
const { lang } = params;
|
|
193
192
|
|
|
194
193
|
// The setLocale function validates the lang
|
|
@@ -200,7 +199,7 @@ export const load: LayoutLoad = ({ params }) => {
|
|
|
200
199
|
};
|
|
201
200
|
```
|
|
202
201
|
|
|
203
|
-
**Note:** Your SvelteKit route structure must be similar to `/src/routes/[
|
|
202
|
+
**Note:** Your SvelteKit route structure must be similar to `/src/routes/[lang]/...` for `params.lang` to be available.
|
|
204
203
|
|
|
205
204
|
## Advanced Usage
|
|
206
205
|
|
|
@@ -251,17 +250,17 @@ The new translations are now merged into the store and available via the `$t` fu
|
|
|
251
250
|
|
|
252
251
|
`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
252
|
|
|
254
|
-
- **Zero-Config Type Safety**: Get full type-safety for your language codes
|
|
255
|
-
- **Extremely Lightweight**: This library is "tiny" (likely
|
|
253
|
+
- **Zero-Config Type Safety**: Get full type-safety for your language codes _without_ a build step, code generator, or complex setup. Type-safety is achieved instantly via TypeScript inference from your single configuration object.
|
|
254
|
+
- **Extremely Lightweight**: This library is "tiny" (likely <1kb gzipped). It has **zero dependencies** and does not bundle a heavy ICU message parser, making your app faster.
|
|
256
255
|
- **Svelte Native**: Built purely with Svelte stores (`writable` and `derived`), it integrates seamlessly into the Svelte reactivity model.
|
|
257
256
|
- **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.
|
|
257
|
+
- **"Headless" by Design**: It provides only the core logic, giving you full control over your UI and integration. (This also means you are responsible for updating the `<html>` `lang` attribute; see the [FAQ for a recipe](#q-how-do-i-dynamically-update-the-html-lang-attribute-or-handle-rtl-right-to-left-languages).)
|
|
259
258
|
|
|
260
259
|
## Comparison with Other Libraries
|
|
261
260
|
|
|
262
261
|
| Dimension | `svelte-tiny-i18n` (This) | `typesafe-i18n` | `svelte-i18n` |
|
|
263
262
|
| :------------------- | :--------------------------------------------------------------------------- | :---------------------------------------------------------------------- | :--------------------------------------------------------- |
|
|
264
|
-
| **Bundle Size** | **Tiny (
|
|
263
|
+
| **Bundle Size** | **Tiny (<1kb)** | **Tiny (~1kb)** | **Medium (~15kb+)** |
|
|
265
264
|
| **Core Mechanism** | Zero-dependency Svelte Stores + Simple String Replace | **Build-time Generator** | **Runtime ICU Parser** |
|
|
266
265
|
| **Type Safety** | **High (Instant Inference)** | **Very High (Code-Gen)** | Medium (Manual setup) |
|
|
267
266
|
| **Setup Complexity** | **Very Low** (Single config file) | Medium (Requires generator setup) | Low (Install and use) |
|
|
@@ -298,7 +297,7 @@ When you call `createI18nStore`, you get an object with:
|
|
|
298
297
|
- `t`: (Read-only derived store) The translation function.
|
|
299
298
|
- `$t('key')`
|
|
300
299
|
- `$t('key', { placeholder: 'value' })`
|
|
301
|
-
- `locale`: (
|
|
300
|
+
- `locale`: (Readable store) The currently active language code (e.g., `en`). This store is readable; to update it, use the `setLocale()` function.
|
|
302
301
|
- `setLocale(lang: string | null | undefined)`: A function to safely set the initial language, typically called from the root `+layout.ts`.
|
|
303
302
|
- If `lang` is a supported language, it sets the `locale` store.
|
|
304
303
|
- If `lang` is invalid (or `null`/`undefined`), it's ignored, and the `locale` store **keeps its current value**.
|
|
@@ -330,13 +329,48 @@ import type { SupportedLocale } from '$lib/i18n';
|
|
|
330
329
|
|
|
331
330
|
// The 'lang' variable is now type-checked
|
|
332
331
|
function setLanguage(lang: SupportedLocale) {
|
|
333
|
-
i18n.
|
|
332
|
+
i18n.setLocale(lang);
|
|
334
333
|
}
|
|
335
334
|
|
|
336
335
|
setLanguage('en'); // OK
|
|
337
336
|
setLanguage('fr'); // TypeScript Error
|
|
338
337
|
```
|
|
339
338
|
|
|
339
|
+
## FAQ / Recipes
|
|
340
|
+
|
|
341
|
+
### Q: How do I use this in a Svelte (Vite) project _without_ SvelteKit?
|
|
342
|
+
|
|
343
|
+
A: It's even simpler. You don't need the `+layout.ts` file or the `i18n.setLocale()` step.
|
|
344
|
+
|
|
345
|
+
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.
|
|
346
|
+
|
|
347
|
+
### Q: How do I dynamically update the `<html>` `lang` attribute or handle RTL (Right-to-Left) languages?
|
|
348
|
+
|
|
349
|
+
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 (e.g., `+layout.svelte` for SvelteKit or `App.svelte` for Svelte/Vite).
|
|
350
|
+
|
|
351
|
+
Here is an example for a SvelteKit project that sets both the `lang` and `dir` attributes:
|
|
352
|
+
|
|
353
|
+
```svelte
|
|
354
|
+
<script lang="ts">
|
|
355
|
+
import { i18n } from '$lib/i18n';
|
|
356
|
+
const { locale } = i18n;
|
|
357
|
+
|
|
358
|
+
// Define which of your supported locales are RTL
|
|
359
|
+
// (e.g., Arabic 'ar', Hebrew 'he')
|
|
360
|
+
const rtlLocales: string[] = ['ar', 'he'];
|
|
361
|
+
|
|
362
|
+
$: if (typeof document !== 'undefined') {
|
|
363
|
+
const direction = rtlLocales.includes($locale) ? 'rtl' : 'ltr';
|
|
364
|
+
|
|
365
|
+
// Dynamically set attributes on <html>
|
|
366
|
+
document.documentElement.lang = $locale;
|
|
367
|
+
document.documentElement.dir = direction;
|
|
368
|
+
}
|
|
369
|
+
</script>
|
|
370
|
+
|
|
371
|
+
<slot />
|
|
372
|
+
```
|
|
373
|
+
|
|
340
374
|
## License
|
|
341
375
|
|
|
342
376
|
[MIT](https://opensource.org/licenses/MIT)
|
package/README.zh_tw.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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.md) | 繁體中文 )
|
|
8
8
|
|
|
@@ -14,12 +14,26 @@
|
|
|
14
14
|
|
|
15
15
|
`svelte-tiny-i18n` 是為那些**追求極致輕量、零依賴、零建構設定**,同時又**享受 Svelte 原生 Store 體驗和 TypeScript 即時型別推斷**的開發者設計的。
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
它的核心優勢是:**零設定的型別安全**。您只需在 `i18n.ts` 設定檔中定義支援的語言(例如 `['en', 'es']`),TypeScript 立刻就能為您的 `setLocale` 函式提供即時的型別檢查與自動補全(例如 `setLocale('es')` 合法,`setLocale('fr')` 會報錯)—— 完全不需執行任何程式碼產生器。
|
|
18
18
|
|
|
19
19
|
這使它成為中小型專案、或Svelte生態愛好者的理想選擇。
|
|
20
20
|
|
|
21
21
|
### 範例:最精簡的 SvelteKit 整合
|
|
22
22
|
|
|
23
|
+
假設專案的目錄結構如下:
|
|
24
|
+
|
|
25
|
+
```tree
|
|
26
|
+
/src
|
|
27
|
+
├── /lib
|
|
28
|
+
│ └── i18n.ts <- 1. 設定檔
|
|
29
|
+
└── /routes
|
|
30
|
+
└── /[lang]
|
|
31
|
+
├── +layout.ts <- 2. SvelteKit 整合
|
|
32
|
+
├── +page.svelte <- 3. 使用
|
|
33
|
+
└── /about
|
|
34
|
+
└── +page.svelte
|
|
35
|
+
```
|
|
36
|
+
|
|
23
37
|
1. **`src/lib/i18n.ts`** (設定檔)
|
|
24
38
|
|
|
25
39
|
```ts
|
|
@@ -40,39 +54,31 @@
|
|
|
40
54
|
export type SupportedLocale = inferSupportedLocale<typeof i18n>;
|
|
41
55
|
```
|
|
42
56
|
|
|
43
|
-
2. **`src/routes/+layout.ts`** (SvelteKit 整合)
|
|
57
|
+
2. **`src/routes/[lang]/+layout.ts`** (SvelteKit 整合)
|
|
44
58
|
|
|
45
59
|
```ts
|
|
46
60
|
import { i18n } from '$lib/i18n';
|
|
47
61
|
import type { LayoutLoad } from './$types';
|
|
48
62
|
|
|
49
63
|
export const load: LayoutLoad = ({ params }) => {
|
|
50
|
-
// 'lang' 來自您的路由 e.g. /[
|
|
64
|
+
// 'lang' 來自您的路由 e.g. /[lang]/
|
|
51
65
|
i18n.setLocale(params.lang);
|
|
52
66
|
return {};
|
|
53
67
|
};
|
|
54
68
|
```
|
|
55
69
|
|
|
56
|
-
3. **`src/routes/+page.svelte`** (使用)
|
|
70
|
+
3. **`src/routes/[lang]/+page.svelte`** (使用)
|
|
57
71
|
|
|
58
72
|
```svelte
|
|
59
73
|
<script lang="ts">
|
|
60
74
|
import { i18n } from '$lib/i18n';
|
|
61
|
-
const { t,
|
|
75
|
+
const { t, setLocale } = i18n;
|
|
62
76
|
</script>
|
|
63
77
|
|
|
64
78
|
<h1>{$t('hello')}</h1>
|
|
65
|
-
<button on:click={() =>
|
|
79
|
+
<button on:click={() => setLocale('es')}>Español</button>
|
|
66
80
|
```
|
|
67
81
|
|
|
68
|
-
## 核心理念
|
|
69
|
-
|
|
70
|
-
1. **預設型別安全**: 從設定檔中自動推斷語言代碼和翻譯鍵。
|
|
71
|
-
2. **Svelte 原生**: 使用 Svelte stores (`writable` 和 `derived`) 構建。與 Svelte 組件的整合感覺流暢自然。
|
|
72
|
-
3. **SvelteKit 就緒**: 正確處理 SSR (伺服器端渲染) 和 CSR (客戶端渲染) 中的語言偵測和初始化。
|
|
73
|
-
4. **動態與非同步載入**: 使用 `extendTranslations` 輕鬆地按需載入翻譯 (例如:載入特定頁面的翻譯)。
|
|
74
|
-
5. **輕量級**: 最少的依賴和極小的檔案體積。
|
|
75
|
-
|
|
76
82
|
## 安裝
|
|
77
83
|
|
|
78
84
|
```bash
|
|
@@ -117,6 +123,7 @@ const i18nConfig = defineI18nConfig({
|
|
|
117
123
|
localStorageKey: 'my-app-language',
|
|
118
124
|
|
|
119
125
|
// (可選) 如果找不到翻譯鍵,在控制台顯示警告
|
|
126
|
+
// 預設為 true
|
|
120
127
|
devLogs: true,
|
|
121
128
|
|
|
122
129
|
// 定義初始、全域翻譯
|
|
@@ -147,21 +154,14 @@ export type PartialTranslationEntry = inferPartialTranslationEntry<typeof i18n>;
|
|
|
147
154
|
|
|
148
155
|
### 2. 在 Svelte 組件中使用
|
|
149
156
|
|
|
150
|
-
使用衍生的 store `$t` 來獲取翻譯,並使用 `locale` store
|
|
157
|
+
使用衍生的 store `$t` 來獲取翻譯,並使用 `locale` 來讀取、`setLocale` store 來設定語言。
|
|
151
158
|
|
|
152
159
|
```svelte
|
|
153
160
|
<script lang="ts">
|
|
154
161
|
import { i18n } from '$lib/i18n';
|
|
155
|
-
import type { SupportedLocale } from '$lib/i18n';
|
|
156
162
|
|
|
157
163
|
// 解構 stores 和函式
|
|
158
|
-
const { t, locale } = i18n;
|
|
159
|
-
|
|
160
|
-
function setLang(lang: SupportedLocale) {
|
|
161
|
-
// 只需設定 store 的值。
|
|
162
|
-
// '$t' store 將會自動更新。
|
|
163
|
-
locale.set(lang);
|
|
164
|
-
}
|
|
164
|
+
const { t, locale, setLocale } = i18n;
|
|
165
165
|
</script>
|
|
166
166
|
|
|
167
167
|
<h1>{$t('hello', { name: 'World' })}</h1>
|
|
@@ -169,9 +169,9 @@ export type PartialTranslationEntry = inferPartialTranslationEntry<typeof i18n>;
|
|
|
169
169
|
<nav>
|
|
170
170
|
<p>目前語言: {$locale}</p>
|
|
171
171
|
|
|
172
|
-
<button on:click={() =>
|
|
173
|
-
<button on:click={() =>
|
|
174
|
-
<button on:click={() =>
|
|
172
|
+
<button on:click={() => setLocale('en')}>English</button>
|
|
173
|
+
<button on:click={() => setLocale('es')}>Español</button>
|
|
174
|
+
<button on:click={() => setLocale('zh-TW')}>繁體中文</button>
|
|
175
175
|
</nav>
|
|
176
176
|
|
|
177
177
|
<p>{$t('a.missing.key')}</p>
|
|
@@ -188,7 +188,7 @@ import type { LayoutLoad } from './$types';
|
|
|
188
188
|
|
|
189
189
|
// 這個 load 函式會在 SSR 和 CSR 上都運行
|
|
190
190
|
export const load: LayoutLoad = ({ params }) => {
|
|
191
|
-
// 'lang' 必須匹配路由參數,例如 /[
|
|
191
|
+
// 'lang' 必須匹配路由參數,例如 /[lang]/
|
|
192
192
|
const { lang } = params;
|
|
193
193
|
|
|
194
194
|
// setLocale 函式會驗證語言
|
|
@@ -200,7 +200,7 @@ export const load: LayoutLoad = ({ params }) => {
|
|
|
200
200
|
};
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
**注意:** 您的 SvelteKit 路由結構必須類似 `/src/routes/[
|
|
203
|
+
**注意:** 您的 SvelteKit 路由結構必須類似 `/src/routes/[lang]/...` 才能使 `params.lang` 可用。
|
|
204
204
|
|
|
205
205
|
## 進階用法
|
|
206
206
|
|
|
@@ -251,21 +251,21 @@ export const load: LayoutLoad = ({ params }) => {
|
|
|
251
251
|
|
|
252
252
|
`svelte-tiny-i18n` 旨在為 Svelte 開發者提供一個「最佳平衡點」—— 一個簡單、快速且型別安全的解決方案,而無需大型函式庫的額外開銷。
|
|
253
253
|
|
|
254
|
-
- **零設定的型別安全 (Zero-Config Type Safety)**: 您**不需**任何建構步驟 (build step)
|
|
255
|
-
- **極致輕量 (Extremely Lightweight)**: 這個函式庫非常「微小」(gzipped 後約
|
|
254
|
+
- **零設定的型別安全 (Zero-Config Type Safety)**: 您**不需**任何建構步驟 (build step)、程式碼產生器或複雜設定,就能立即獲得對語言代碼的完整型別安全。型別安全是透過 TypeScript 對單一設定檔的自動推斷 (inference) 來實現的。
|
|
255
|
+
- **極致輕量 (Extremely Lightweight)**: 這個函式庫非常「微小」(gzipped 後約 <1kb)。它**沒有任何外部依賴**,也不包含沉重的 ICU 訊息解析器 (message parser),使您的應用程式啟動更快。
|
|
256
256
|
- **Svelte 原生 (Svelte Native)**: 完全基於 Svelte stores (`writable` 和 `derived`) 構建,使其無縫融入 Svelte 的響應式模型。
|
|
257
257
|
- **簡單而強大 (Simple but Powerful)**: 提供了所有基本功能:支援 SvelteKit 的 SSR/CSR、動態/非同步翻譯載入 (`extendTranslations`),以及簡單的變數替換。
|
|
258
|
-
- **Headless 設計**: 函式庫只提供核心邏輯,讓您對 UI 整合擁有完全的控制權。
|
|
258
|
+
- **Headless 設計**: 函式庫只提供核心邏輯,讓您對 UI 整合擁有完全的控制權。(這也代表您需要自行更新 `<html>` 上的 `lang` 屬性。請參閱 [FAQ 中的範例](#q-如何動態更新-html-上的-lang-屬性或處理-rtl-由右至左-語言)。)
|
|
259
259
|
|
|
260
260
|
## 與其他函式庫比較
|
|
261
261
|
|
|
262
262
|
| 維度 | `svelte-tiny-i18n` (本專案) | `typesafe-i18n` | `svelte-i18n` |
|
|
263
263
|
| :------------- | :------------------------------------------------------ | :------------------------------------------------ | :----------------------------------------------- |
|
|
264
|
-
| **檔案大小** | **極小 (
|
|
264
|
+
| **檔案大小** | **極小 (<1kb)** | **極小 (~1kb)** | **中等 (~15kb+)** |
|
|
265
265
|
| **核心機制** | 零依賴的 Svelte Stores + 簡單字串替換 | **建構期 (Build-time) 產生器** | **執行期 (Runtime) ICU 解析器** |
|
|
266
266
|
| **型別安全** | **高 (即時推斷)** | **極高 (程式碼產生)** | 中 (需手動定義) |
|
|
267
267
|
| **設定複雜度** | **非常低** (單一設定檔) | 中 (需設定並執行產生器) | 低 (安裝即用) |
|
|
268
|
-
| **進階格式化** | 僅支援 `{
|
|
268
|
+
| **進階格式化** | 僅支援 `{var}` 變數替換 | 支援 (複數、日期、數字格式化) | **支援 (完整 ICU 語法)** |
|
|
269
269
|
| **主要取捨** | 捨棄 ICU 功能,換取**極致輕量**與**零設定的型別安全**。 | 需額外建構步驟,換取**最強的型別安全** (含參數)。 | 需載入 runtime 解析器,換取**最強的 ICU 功能**。 |
|
|
270
270
|
|
|
271
271
|
### 設計理念比較
|
|
@@ -298,7 +298,7 @@ export const load: LayoutLoad = ({ params }) => {
|
|
|
298
298
|
- `t`: (唯讀的 derived store) 翻譯函式。
|
|
299
299
|
- `$t('key')`
|
|
300
300
|
- `$t('key', { placeholder: 'value' })`
|
|
301
|
-
- `locale`: (
|
|
301
|
+
- `locale`: (Readable store) 當前啟用的語言代碼 (例如 `en`)。此 store 是唯讀的;若要更新它,請使用 `setLocale()` 函式。
|
|
302
302
|
- `setLocale(lang: string | null | undefined)`: 一個用於安全設定初始語言的函式,通常在根 `+layout.ts` 中呼叫。
|
|
303
303
|
- 如果 `lang` 是支援的語言,它將設定 `locale` store。
|
|
304
304
|
- 如果 `lang` 是無效的 (或 `null`/`undefined`),它將被忽略,`locale` store 會**保持其目前的值**。
|
|
@@ -330,13 +330,48 @@ import type { SupportedLocale } from '$lib/i18n';
|
|
|
330
330
|
|
|
331
331
|
// 'lang' 變數現在受到型別檢查
|
|
332
332
|
function setLanguage(lang: SupportedLocale) {
|
|
333
|
-
i18n.
|
|
333
|
+
i18n.setLocale(lang);
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
setLanguage('en'); // OK
|
|
337
337
|
setLanguage('fr'); // TypeScript 錯誤
|
|
338
338
|
```
|
|
339
339
|
|
|
340
|
+
## FAQ
|
|
341
|
+
|
|
342
|
+
### Q: 如何在**不使用 SvelteKit** 的 Svelte (Vite) 專案中使用?
|
|
343
|
+
|
|
344
|
+
A: 這樣更簡單。您不需要 `+layout.ts` 和 `i18n.setLocale()` 步驟。
|
|
345
|
+
|
|
346
|
+
Store 在瀏覽器環境中會自動透過 `localStorage` 或 `navigator.language` 來偵測並初始化語言。您可以在組件中隨時呼叫 `i18n.setLocale('new_lang')` 來切換語言。
|
|
347
|
+
|
|
348
|
+
### Q: 如何動態更新 `<html>` 上的 `lang` 屬性,或處理 RTL (由右至左) 語言?
|
|
349
|
+
|
|
350
|
+
A: 本函式庫是「Headless」設計,代表它不會主動操作 DOM。您可以輕鬆地在根佈局組件 (SvelteKit 的 `+layout.svelte` 或 Svelte/Vite 的 `App.svelte`) 中訂閱 `locale` store 來自行管理。
|
|
351
|
+
|
|
352
|
+
這是一個 SvelteKit 專案的範例,它會同時設定 `lang` 和 `dir` 屬性:
|
|
353
|
+
|
|
354
|
+
```svelte
|
|
355
|
+
<script lang="ts">
|
|
356
|
+
import { i18n } from '$lib/i18n';
|
|
357
|
+
const { locale } = i18n;
|
|
358
|
+
|
|
359
|
+
// 定義您支援的語言中有哪些是 RTL
|
|
360
|
+
// (例如阿拉伯語 'ar', 希伯來語 'he')
|
|
361
|
+
const rtlLocales: string[] = ['ar', 'he'];
|
|
362
|
+
|
|
363
|
+
$: if (typeof document !== 'undefined') {
|
|
364
|
+
const direction = rtlLocales.includes($locale) ? 'rtl' : 'ltr';
|
|
365
|
+
|
|
366
|
+
// 動態設定 <html> 上的屬性
|
|
367
|
+
document.documentElement.lang = $locale;
|
|
368
|
+
document.documentElement.dir = direction;
|
|
369
|
+
}
|
|
370
|
+
</script>
|
|
371
|
+
|
|
372
|
+
<slot />
|
|
373
|
+
```
|
|
374
|
+
|
|
340
375
|
## 授權 (License)
|
|
341
376
|
|
|
342
377
|
[MIT](https://opensource.org/licenses/MIT)
|
|
@@ -186,12 +186,12 @@ function createI18nStore(config) {
|
|
|
186
186
|
*/
|
|
187
187
|
localStorageKey,
|
|
188
188
|
/**
|
|
189
|
-
* A
|
|
189
|
+
* A readable Svelte store holding the current language code.
|
|
190
190
|
* (e.g., 'en')
|
|
191
191
|
*
|
|
192
192
|
* ---
|
|
193
193
|
*
|
|
194
|
-
* 一個 Svelte
|
|
194
|
+
* 一個 Svelte Readable store,儲存當前的語言代碼。
|
|
195
195
|
* (例如: 'en')
|
|
196
196
|
*/
|
|
197
197
|
locale,
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { Readable } from 'svelte/store';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license MIT
|
|
5
|
+
* svelte-tiny-i18n
|
|
6
|
+
* Copyright (c) 2025 Shiritai (Yang Tzu-Ching)
|
|
7
|
+
*
|
|
8
|
+
* A tiny, type-safe i18n library for Svelte and SvelteKit.
|
|
9
|
+
* https://github.com/Shiritai/svelte-tiny-i18n
|
|
10
|
+
*/
|
|
3
11
|
|
|
4
12
|
/**
|
|
5
13
|
* Interface for the i18n configuration object.
|
|
@@ -98,12 +106,11 @@ type PartialTranslationEntryMap<Locales extends string> = {
|
|
|
98
106
|
* import { i18n } from '$lib/i18n'; // Import the instance from step 1
|
|
99
107
|
*
|
|
100
108
|
* // Destructure the stores and functions
|
|
101
|
-
* const { t, locale } = i18n;
|
|
109
|
+
* const { t, locale, setLocale } = i18n;
|
|
102
110
|
*
|
|
103
111
|
* function changeLang() {
|
|
104
|
-
* // You can 'set' the store value directly
|
|
105
112
|
* const nextLang = $locale === 'en' ? 'es' : 'en';
|
|
106
|
-
*
|
|
113
|
+
* setLocale(nextLang);
|
|
107
114
|
* }
|
|
108
115
|
* </script>
|
|
109
116
|
*
|
|
@@ -151,12 +158,11 @@ type PartialTranslationEntryMap<Locales extends string> = {
|
|
|
151
158
|
* import { i18n } from '$lib/i18n'; // 導入步驟 1 建立的實例
|
|
152
159
|
*
|
|
153
160
|
* // 解構 Svelte stores 和函式
|
|
154
|
-
* const { t, locale } = i18n;
|
|
161
|
+
* const { t, locale, setLocale } = i18n;
|
|
155
162
|
*
|
|
156
163
|
* function changeLang() {
|
|
157
|
-
* // 你可以直接 'set' store 的值
|
|
158
164
|
* const nextLang = $locale === 'en' ? 'es' : 'en';
|
|
159
|
-
*
|
|
165
|
+
* setLocale(nextLang);
|
|
160
166
|
* }
|
|
161
167
|
* </script>
|
|
162
168
|
*
|
|
@@ -195,15 +201,15 @@ declare function createI18nStore<const Locales extends readonly string[]>(config
|
|
|
195
201
|
*/
|
|
196
202
|
localStorageKey: string;
|
|
197
203
|
/**
|
|
198
|
-
* A
|
|
204
|
+
* A readable Svelte store holding the current language code.
|
|
199
205
|
* (e.g., 'en')
|
|
200
206
|
*
|
|
201
207
|
* ---
|
|
202
208
|
*
|
|
203
|
-
* 一個 Svelte
|
|
209
|
+
* 一個 Svelte Readable store,儲存當前的語言代碼。
|
|
204
210
|
* (例如: 'en')
|
|
205
211
|
*/
|
|
206
|
-
locale:
|
|
212
|
+
locale: Readable<Locales[number]>;
|
|
207
213
|
/**
|
|
208
214
|
* The translation function.
|
|
209
215
|
*
|
|
@@ -225,7 +231,7 @@ declare function createI18nStore<const Locales extends readonly string[]>(config
|
|
|
225
231
|
* ## 返回值
|
|
226
232
|
* 翻譯後的字串。如果找不到,將回傳 key 本身以方便除錯。
|
|
227
233
|
*/
|
|
228
|
-
t:
|
|
234
|
+
t: Readable<(key: string, replacements?: Record<string, string | number>) => string>;
|
|
229
235
|
/**
|
|
230
236
|
* Initializes or updates the language, typically called from a root layout.
|
|
231
237
|
*
|
|
@@ -397,7 +403,7 @@ type AnyI18nStore = {
|
|
|
397
403
|
* function setLanguage(lang: SupportedLocale) {
|
|
398
404
|
* // The 'lang' variable is now type-checked
|
|
399
405
|
* // (e.g., only 'en' | 'es' are allowed)
|
|
400
|
-
* i18n.
|
|
406
|
+
* i18n.setLocale(lang);
|
|
401
407
|
* }
|
|
402
408
|
*
|
|
403
409
|
* setLanguage('en'); // OK
|
|
@@ -419,7 +425,7 @@ type AnyI18nStore = {
|
|
|
419
425
|
* function setLanguage(lang: SupportedLocale) {
|
|
420
426
|
* // lang 變數現在會被 TypeScript 檢查
|
|
421
427
|
* // (例如: 只接受 'en' | 'es')
|
|
422
|
-
* i18n.
|
|
428
|
+
* i18n.setLocale(lang);
|
|
423
429
|
* }
|
|
424
430
|
*
|
|
425
431
|
* setLanguage('en'); // OK
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { Readable } from 'svelte/store';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license MIT
|
|
5
|
+
* svelte-tiny-i18n
|
|
6
|
+
* Copyright (c) 2025 Shiritai (Yang Tzu-Ching)
|
|
7
|
+
*
|
|
8
|
+
* A tiny, type-safe i18n library for Svelte and SvelteKit.
|
|
9
|
+
* https://github.com/Shiritai/svelte-tiny-i18n
|
|
10
|
+
*/
|
|
3
11
|
|
|
4
12
|
/**
|
|
5
13
|
* Interface for the i18n configuration object.
|
|
@@ -98,12 +106,11 @@ type PartialTranslationEntryMap<Locales extends string> = {
|
|
|
98
106
|
* import { i18n } from '$lib/i18n'; // Import the instance from step 1
|
|
99
107
|
*
|
|
100
108
|
* // Destructure the stores and functions
|
|
101
|
-
* const { t, locale } = i18n;
|
|
109
|
+
* const { t, locale, setLocale } = i18n;
|
|
102
110
|
*
|
|
103
111
|
* function changeLang() {
|
|
104
|
-
* // You can 'set' the store value directly
|
|
105
112
|
* const nextLang = $locale === 'en' ? 'es' : 'en';
|
|
106
|
-
*
|
|
113
|
+
* setLocale(nextLang);
|
|
107
114
|
* }
|
|
108
115
|
* </script>
|
|
109
116
|
*
|
|
@@ -151,12 +158,11 @@ type PartialTranslationEntryMap<Locales extends string> = {
|
|
|
151
158
|
* import { i18n } from '$lib/i18n'; // 導入步驟 1 建立的實例
|
|
152
159
|
*
|
|
153
160
|
* // 解構 Svelte stores 和函式
|
|
154
|
-
* const { t, locale } = i18n;
|
|
161
|
+
* const { t, locale, setLocale } = i18n;
|
|
155
162
|
*
|
|
156
163
|
* function changeLang() {
|
|
157
|
-
* // 你可以直接 'set' store 的值
|
|
158
164
|
* const nextLang = $locale === 'en' ? 'es' : 'en';
|
|
159
|
-
*
|
|
165
|
+
* setLocale(nextLang);
|
|
160
166
|
* }
|
|
161
167
|
* </script>
|
|
162
168
|
*
|
|
@@ -195,15 +201,15 @@ declare function createI18nStore<const Locales extends readonly string[]>(config
|
|
|
195
201
|
*/
|
|
196
202
|
localStorageKey: string;
|
|
197
203
|
/**
|
|
198
|
-
* A
|
|
204
|
+
* A readable Svelte store holding the current language code.
|
|
199
205
|
* (e.g., 'en')
|
|
200
206
|
*
|
|
201
207
|
* ---
|
|
202
208
|
*
|
|
203
|
-
* 一個 Svelte
|
|
209
|
+
* 一個 Svelte Readable store,儲存當前的語言代碼。
|
|
204
210
|
* (例如: 'en')
|
|
205
211
|
*/
|
|
206
|
-
locale:
|
|
212
|
+
locale: Readable<Locales[number]>;
|
|
207
213
|
/**
|
|
208
214
|
* The translation function.
|
|
209
215
|
*
|
|
@@ -225,7 +231,7 @@ declare function createI18nStore<const Locales extends readonly string[]>(config
|
|
|
225
231
|
* ## 返回值
|
|
226
232
|
* 翻譯後的字串。如果找不到,將回傳 key 本身以方便除錯。
|
|
227
233
|
*/
|
|
228
|
-
t:
|
|
234
|
+
t: Readable<(key: string, replacements?: Record<string, string | number>) => string>;
|
|
229
235
|
/**
|
|
230
236
|
* Initializes or updates the language, typically called from a root layout.
|
|
231
237
|
*
|
|
@@ -397,7 +403,7 @@ type AnyI18nStore = {
|
|
|
397
403
|
* function setLanguage(lang: SupportedLocale) {
|
|
398
404
|
* // The 'lang' variable is now type-checked
|
|
399
405
|
* // (e.g., only 'en' | 'es' are allowed)
|
|
400
|
-
* i18n.
|
|
406
|
+
* i18n.setLocale(lang);
|
|
401
407
|
* }
|
|
402
408
|
*
|
|
403
409
|
* setLanguage('en'); // OK
|
|
@@ -419,7 +425,7 @@ type AnyI18nStore = {
|
|
|
419
425
|
* function setLanguage(lang: SupportedLocale) {
|
|
420
426
|
* // lang 變數現在會被 TypeScript 檢查
|
|
421
427
|
* // (例如: 只接受 'en' | 'es')
|
|
422
|
-
* i18n.
|
|
428
|
+
* i18n.setLocale(lang);
|
|
423
429
|
* }
|
|
424
430
|
*
|
|
425
431
|
* setLanguage('en'); // OK
|
package/dist/svelte-tiny-i18n.js
CHANGED
|
@@ -161,12 +161,12 @@ function createI18nStore(config) {
|
|
|
161
161
|
*/
|
|
162
162
|
localStorageKey,
|
|
163
163
|
/**
|
|
164
|
-
* A
|
|
164
|
+
* A readable Svelte store holding the current language code.
|
|
165
165
|
* (e.g., 'en')
|
|
166
166
|
*
|
|
167
167
|
* ---
|
|
168
168
|
*
|
|
169
|
-
* 一個 Svelte
|
|
169
|
+
* 一個 Svelte Readable store,儲存當前的語言代碼。
|
|
170
170
|
* (例如: 'en')
|
|
171
171
|
*/
|
|
172
172
|
locale,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-tiny-i18n",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A lightweight, type-safe, and reactive i18n library for Svelte and SvelteKit.",
|
|
5
5
|
"author": "Shiritai",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"svelte": "^3.30.0"
|
|
42
|
+
"svelte": "^3.30.0 || ^4.0.0 || ^5.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@eslint/compat": "^1.2.5",
|
|
46
|
-
"@types/node": "^
|
|
46
|
+
"@types/node": "^24",
|
|
47
47
|
"eslint": "^9.22.0",
|
|
48
48
|
"eslint-config-prettier": "^10.0.1",
|
|
49
49
|
"jiti": "^2.6.1",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"tsup": "^8.0.0",
|
|
54
54
|
"typescript": "^5.0.0",
|
|
55
55
|
"typescript-eslint": "^8.20.0",
|
|
56
|
-
"vitest": "^
|
|
56
|
+
"vitest": "^4.0.0"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"test": "vitest run",
|