svelte-tiny-i18n 1.0.1 → 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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # svelte-tiny-i18n
2
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)
3
+ [![NPM Version](https://img.shields.io/npm/v/svelte-tiny-i18n)](https://www.npmjs.com/package/svelte-tiny-i18n)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/svelte-tiny-i18n)](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 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.
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., /[[lang]]/
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, locale } = i18n;
75
+ const { t, setLocale } = i18n;
62
76
  </script>
63
77
 
64
78
  <h1>{$t('hello')}</h1>
65
- <button on:click={() => locale.set('es')}>Español</button>
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={() => setLang('en')}>English</button>
173
- <button on:click={() => setLang('es')}>Español</button>
174
- <button on:click={() => setLang('zh-TW')}>繁體中文</button>
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., /[[lang]]/
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/[[lang]]/...` for `params.lang` to be available.
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 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.
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 (~1-2kb)** | **Tiny (~1kb)** | **Medium (~15kb+)** |
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`: (Writable store) The currently active language code (e.g., `'en'`). You can `set` or `update` this store.
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.locale.set(lang);
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://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)
3
+ [![NPM Version](https://img.shields.io/npm/v/svelte-tiny-i18n)](https://www.npmjs.com/package/svelte-tiny-i18n)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/svelte-tiny-i18n)](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
- 它的核心優勢是:**您在 `i18n.ts` 設定檔中定義翻譯,TypeScript 立刻就能為您提供 `$t('...')` key 自動完成和 `locale.set('...')` 的語言代碼檢查,完全不需執行任何程式碼產生器。**
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. /[[lang]]/
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, locale } = i18n;
75
+ const { t, setLocale } = i18n;
62
76
  </script>
63
77
 
64
78
  <h1>{$t('hello')}</h1>
65
- <button on:click={() => locale.set('es')}>Español</button>
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={() => setLang('en')}>English</button>
173
- <button on:click={() => setLang('es')}>Español</button>
174
- <button on:click={() => setLang('zh-TW')}>繁體中文</button>
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' 必須匹配路由參數,例如 /[[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/[[lang]]/...` 才能使 `params.lang` 可用。
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)、程式碼產生器或複雜設定,就能立即獲得對語言代碼和翻譯條目的完整型別安全。型別安全是透過 TypeScript 對單一設定檔的自動推斷 (inference) 來實現的。
255
- - **極致輕量 (Extremely Lightweight)**: 這個函式庫非常「微小」(gzipped 後約 1-2kb)。它**沒有任何外部依賴**,也不包含沉重的 ICU 訊息解析器 (message parser),使您的應用程式啟動更快。
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
- | **檔案大小** | **極小 (~1-2kb)** | **極小 (~1kb)** | **中等 (~15kb+)** |
264
+ | **檔案大小** | **極小 (<1kb)** | **極小 (~1kb)** | **中等 (~15kb+)** |
265
265
  | **核心機制** | 零依賴的 Svelte Stores + 簡單字串替換 | **建構期 (Build-time) 產生器** | **執行期 (Runtime) ICU 解析器** |
266
266
  | **型別安全** | **高 (即時推斷)** | **極高 (程式碼產生)** | 中 (需手動定義) |
267
267
  | **設定複雜度** | **非常低** (單一設定檔) | 中 (需設定並執行產生器) | 低 (安裝即用) |
268
- | **進階格式化** | 僅支援 `{$t('k', {v: '1'})}` 變數替換 | 支援 (複數、日期、數字格式化) | **支援 (完整 ICU 語法)** |
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`: (Writable store) 當前啟用的語言代碼 (例如 `'en'`)。您可以 `set` `update` 這個 store。
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.locale.set(lang);
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 writable Svelte store holding the current language code.
189
+ * A readable Svelte store holding the current language code.
190
190
  * (e.g., 'en')
191
191
  *
192
192
  * ---
193
193
  *
194
- * 一個 Svelte Writable store,儲存當前的語言代碼。
194
+ * 一個 Svelte Readable store,儲存當前的語言代碼。
195
195
  * (例如: 'en')
196
196
  */
197
197
  locale,
@@ -1,5 +1,13 @@
1
- import * as svelte_store from 'svelte/store';
2
- import { Writable } from 'svelte/store';
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
- * locale.set(nextLang);
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
- * locale.set(nextLang);
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 writable Svelte store holding the current language code.
204
+ * A readable Svelte store holding the current language code.
199
205
  * (e.g., 'en')
200
206
  *
201
207
  * ---
202
208
  *
203
- * 一個 Svelte Writable store,儲存當前的語言代碼。
209
+ * 一個 Svelte Readable store,儲存當前的語言代碼。
204
210
  * (例如: 'en')
205
211
  */
206
- locale: Writable<Locales[number]>;
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: svelte_store.Readable<(key: string, replacements?: Record<string, string | number>) => string>;
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.locale.set(lang);
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.locale.set(lang);
428
+ * i18n.setLocale(lang);
423
429
  * }
424
430
  *
425
431
  * setLanguage('en'); // OK
@@ -1,5 +1,13 @@
1
- import * as svelte_store from 'svelte/store';
2
- import { Writable } from 'svelte/store';
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
- * locale.set(nextLang);
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
- * locale.set(nextLang);
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 writable Svelte store holding the current language code.
204
+ * A readable Svelte store holding the current language code.
199
205
  * (e.g., 'en')
200
206
  *
201
207
  * ---
202
208
  *
203
- * 一個 Svelte Writable store,儲存當前的語言代碼。
209
+ * 一個 Svelte Readable store,儲存當前的語言代碼。
204
210
  * (例如: 'en')
205
211
  */
206
- locale: Writable<Locales[number]>;
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: svelte_store.Readable<(key: string, replacements?: Record<string, string | number>) => string>;
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.locale.set(lang);
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.locale.set(lang);
428
+ * i18n.setLocale(lang);
423
429
  * }
424
430
  *
425
431
  * setLanguage('en'); // OK
@@ -161,12 +161,12 @@ function createI18nStore(config) {
161
161
  */
162
162
  localStorageKey,
163
163
  /**
164
- * A writable Svelte store holding the current language code.
164
+ * A readable Svelte store holding the current language code.
165
165
  * (e.g., 'en')
166
166
  *
167
167
  * ---
168
168
  *
169
- * 一個 Svelte Writable store,儲存當前的語言代碼。
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.1",
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",