@vielzeug/i18nit 1.1.3 → 1.2.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 CHANGED
@@ -1,36 +1,86 @@
1
1
  # @vielzeug/i18nit
2
2
 
3
- Type-safe, lightweight internationalization (i18n) library for TypeScript applications. Simple, powerful translation management with zero dependencies.
3
+ ## What is I18nit?
4
4
 
5
- ## Features
5
+ **I18nit** is a type-safe, lightweight internationalization library for TypeScript. Build multilingual applications with powerful pluralization, nested translations, and lazy loading—all in just 1.6 KB.
6
6
 
7
- - **Type-Safe** - Full TypeScript support with generic types
8
- - ✅ **Lightweight** - ~2.3KB gzipped with zero dependencies
9
- - ✅ **Universal Pluralization** - 100+ languages via Intl.PluralRules API
10
- - ✅ **Smart Array Handling** - Auto-join with separators, length access, and safe indexing
11
- - ✅ **Path Interpolation** - Support for nested objects and array indices
12
- - ✅ **Lazy Loading** - Async locale loading with automatic caching
13
- - ✅ **Namespaces** - Organize translations by feature or module
14
- - ✅ **Fallback Chain** - Multiple fallback locales with automatic language variants
15
- - ✅ **HTML Escaping** - Built-in XSS protection
16
- - ✅ **Number & Date Formatting** - Locale-aware formatting with Intl API
17
- - ✅ **Structured Errors** - Detailed error information with MissingVariableError
18
- - ✅ **Framework Agnostic** - Works with React, Vue, Svelte, or vanilla JS
7
+ ### The Problem
19
8
 
20
- ## Installation
9
+ Internationalization libraries are often heavy and complex:
10
+
11
+ - **i18next** is feature-rich but adds 11KB+ to your bundle
12
+ - **react-intl** is React-specific and requires setup
13
+ - **FormatJS** has a steep learning curve
14
+ - Manual translations lead to missing keys and runtime errors
15
+ - Type safety requires extra tooling
16
+
17
+ ### The Solution
18
+
19
+ I18nit provides a simple, type-safe API using native browser APIs:
20
+
21
+ ```typescript
22
+ import { createI18n } from '@vielzeug/i18nit';
23
+
24
+ const i18n = createI18n({
25
+ locale: 'en',
26
+ messages: {
27
+ en: {
28
+ welcome: 'Welcome, {name}!',
29
+ items: 'You have {count} item | You have {count} items',
30
+ },
31
+ es: {
32
+ welcome: '¡Bienvenido, {name}!',
33
+ items: 'Tienes {count} artículo | Tienes {count} artículos',
34
+ },
35
+ },
36
+ });
37
+
38
+ // Interpolation
39
+ i18n.t('welcome', { name: 'Alice' }); // "Welcome, Alice!"
40
+
41
+ // Automatic pluralization
42
+ i18n.t('items', { count: 1 }); // "You have 1 item"
43
+ i18n.t('items', { count: 5 }); // "You have 5 items"
44
+ ```
45
+
46
+ ## ✨ Features
47
+
48
+ - ✅ **Type-Safe** – Full TypeScript support with generic types
49
+ - ✅ **Lightweight** – 1.6 KB gzipped with zero dependencies
50
+ - ✅ **Universal Pluralization** – 100+ languages via Intl.PluralRules API
51
+ - ✅ **Smart Array Handling** – Auto-join with separators, length access, and safe indexing
52
+ - ✅ **Path Interpolation** – Support for nested objects and array indices
53
+ - ✅ **Lazy Loading** – Async locale loading with automatic caching
54
+ - ✅ **Namespaces** – Organize translations by feature or module
55
+ - ✅ **Fallback Chain** – Multiple fallback locales with automatic language variants
56
+ - ✅ **HTML Escaping** – Built-in XSS protection
57
+ - ✅ **Number & Date Formatting** – Locale-aware formatting with Intl API
58
+ - ✅ **Framework Agnostic** – Works with React, Vue, Svelte, or vanilla JS
59
+
60
+ ## 🆚 Comparison with Alternatives
61
+
62
+ | Feature | I18nit | i18next | react-intl | FormatJS |
63
+ | ------------------- | -------------- | ------- | ---------- | ------------ |
64
+ | Bundle Size (gzip) | **~1.6 KB** | ~11KB | ~14KB | ~14KB |
65
+ | TypeScript Support | ✅ First-class | ✅ Good | ✅ Good | ✅ Excellent |
66
+ | Pluralization | ✅ Native Intl | ✅ ICU | ✅ ICU | ✅ ICU |
67
+ | Nested Translations | ✅ Built-in | ✅ Yes | ⚠️ Limited | ✅ Yes |
68
+ | Lazy Loading | ✅ Async | ✅ Yes | ⚠️ Manual | ✅ Yes |
69
+ | Framework Agnostic | ✅ Yes | ✅ Yes | ❌ React | ❌ React |
70
+ | Dependencies | 0 | 3 | 5 | 7 |
71
+
72
+ ## 📦 Installation
21
73
 
22
74
  ```bash
23
75
  # pnpm
24
76
  pnpm add @vielzeug/i18nit
25
-
26
77
  # npm
27
78
  npm install @vielzeug/i18nit
28
-
29
79
  # yarn
30
80
  yarn add @vielzeug/i18nit
31
81
  ```
32
82
 
33
- ## Quick Start
83
+ ## 🚀 Quick Start
34
84
 
35
85
  ```typescript
36
86
  import { createI18n } from '@vielzeug/i18nit';
@@ -71,7 +121,7 @@ i18n.setLocale('es');
71
121
  i18n.t('greeting', { name: 'Mundo' }); // "¡Hola, Mundo!"
72
122
  ```
73
123
 
74
- ## Core Concepts
124
+ ## 📚 Core Concepts
75
125
 
76
126
  ### Translation Keys
77
127
 
@@ -91,6 +141,52 @@ const i18n = createI18n({
91
141
  i18n.t('user.profile.title'); // "Profile"
92
142
  ```
93
143
 
144
+ ### Nested Message Objects
145
+
146
+ You can organize messages using nested objects for better structure:
147
+
148
+ ```typescript
149
+ const i18n = createI18n({
150
+ locale: 'en',
151
+ messages: {
152
+ en: {
153
+ // Flat structure
154
+ welcome: 'Welcome!',
155
+
156
+ // Nested structure - access with dot notation
157
+ user: {
158
+ greeting: 'Hello, {name}!',
159
+ profile: {
160
+ title: 'User Profile',
161
+ settings: 'Profile Settings',
162
+ },
163
+ },
164
+
165
+ // Deep nesting
166
+ app: {
167
+ navigation: {
168
+ menu: {
169
+ home: 'Home',
170
+ about: 'About',
171
+ },
172
+ },
173
+ },
174
+ },
175
+ },
176
+ });
177
+
178
+ // Access nested messages with dot notation
179
+ i18n.t('welcome'); // "Welcome!"
180
+ i18n.t('user.greeting', { name: 'Alice' }); // "Hello, Alice!"
181
+ i18n.t('user.profile.title'); // "User Profile"
182
+ i18n.t('app.navigation.menu.home'); // "Home"
183
+
184
+ // Use with namespaces for cleaner code
185
+ const userNs = i18n.namespace('user');
186
+ userNs.t('greeting', { name: 'Bob' }); // "Hello, Bob!"
187
+ userNs.t('profile.title'); // "User Profile"
188
+ ```
189
+
94
190
  ### Variable Interpolation
95
191
 
96
192
  #### Basic Interpolation
@@ -138,7 +234,7 @@ const i18n = createI18n({
138
234
  i18n.t('shopping', { items: ['Apple', 'Banana', 'Orange'] });
139
235
  // "Shopping list: Apple, Banana, Orange"
140
236
 
141
- // Natural "and" lists (locale-aware via Intl.ListFormat - supports 100+ languages automatically)
237
+ // Natural "and" lists (locale-aware via Intl.ListFormat supports 100+ languages automatically)
142
238
  i18n.t('guests', { names: ['Alice'] });
143
239
  // "Invited: Alice"
144
240
  i18n.t('guests', { names: ['Alice', 'Bob'] });
@@ -146,7 +242,7 @@ i18n.t('guests', { names: ['Alice', 'Bob'] });
146
242
  i18n.t('guests', { names: ['Alice', 'Bob', 'Charlie'] });
147
243
  // "Invited: Alice, Bob, and Charlie" (Oxford comma in English)
148
244
 
149
- // Natural "or" lists (locale-aware via Intl.ListFormat - supports 100+ languages automatically)
245
+ // Natural "or" lists (locale-aware via Intl.ListFormat supports 100+ languages automatically)
150
246
  i18n.t('options', { choices: ['Tea', 'Coffee', 'Juice'] });
151
247
  // "Choose: Tea, Coffee, or Juice"
152
248
 
@@ -160,22 +256,25 @@ i18n.t('count', { items: ['A', 'B', 'C'] });
160
256
  ```
161
257
 
162
258
  **Array Features:**
163
- - `{items}` - Join with comma (`, `)
164
- - `{items|and}` - Natural "and" list with locale-aware conjunction (uses Intl.ListFormat - supports 100+ languages)
165
- - `{items|or}` - Natural "or" list with locale-aware conjunction (uses Intl.ListFormat - supports 100+ languages)
166
- - `{items| - }` - Custom separator (e.g., "A - B - C")
167
- - `{items.length}` - Array length
168
- - `{items[0]}` - Safe index access (returns empty if out of bounds)
259
+
260
+ - `{items}` Join with comma (`, `)
261
+ - `{items|and}` Natural "and" list with locale-aware conjunction (uses Intl.ListFormat supports 100+ languages)
262
+ - `{items|or}` – Natural "or" list with locale-aware conjunction (uses Intl.ListFormat supports 100+ languages)
263
+ - `{items| – }` Custom separator (e.g., "A – B – C")
264
+ - `{items.length}` Array length
265
+ - `{items[0]}` – Safe index access (returns empty if out of bounds)
169
266
 
170
267
  **Locale-Aware List Formatting:**
171
268
  The `and` and `or` separators use the built-in **Intl.ListFormat API** which automatically handles:
172
- - **100+ languages** - Supports all languages available in the browser/runtime
173
- - **Proper grammar** - Oxford comma, locale-specific punctuation
174
- - **Right-to-left languages** - Arabic, Hebrew, etc.
175
- - **Unicode CLDR standards** - International standard for list formatting
176
- - **No manual configuration** - Zero maintenance required
269
+
270
+ - **100+ languages** Supports all languages available in the browser/runtime
271
+ - **Proper grammar** Oxford comma, locale-specific punctuation
272
+ - **Right-to-left languages** Arabic, Hebrew, etc.
273
+ - **Unicode CLDR standards** International standard for list formatting
274
+ - **No manual configuration** – Zero maintenance required
177
275
 
178
276
  Examples across languages:
277
+
179
278
  - **English**: "A, B, and C" (with Oxford comma)
180
279
  - **Spanish**: "A, B y C" (uses "y")
181
280
  - **French**: "A, B et C" (uses "et")
@@ -186,55 +285,31 @@ Examples across languages:
186
285
 
187
286
  #### Supported Path Formats
188
287
 
189
- - `{name}` - Simple variable
190
- - `{user.name}` - Nested object property
191
- - `{items[0]}` - Array index (safe - returns empty if out of bounds)
192
- - `{items}` - Array join with default separator
193
- - `{items|and}` - Array join with "and"
194
- - `{items.length}` - Array length
195
- - `{data.items[0].value}` - Mixed notation
288
+ - `{name}` Simple variable
289
+ - `{user.name}` Nested object property
290
+ - `{items[0]}` Array index (safe returns empty if out of bounds)
291
+ - `{items}` Array join with default separator
292
+ - `{items|and}` Array join with "and"
293
+ - `{items.length}` Array length
294
+ - `{data.items[0].value}` Mixed notation
196
295
 
197
296
  **Limitations:**
297
+
198
298
  - Only numeric bracket notation `[0]`, `[123]`
199
299
  - Quoted keys not supported `["key"]`
200
300
  - Non-numeric brackets not supported `[key]`
201
301
 
202
302
  ### Missing Variable Handling
203
303
 
204
- Control how missing variables are handled:
304
+ Missing variables are automatically replaced with empty strings:
205
305
 
206
306
  ```typescript
207
- // Empty string (default)
208
- const i18n1 = createI18n({
209
- messages: { en: { msg: 'Hello, {name}!' } },
210
- missingVar: 'empty',
211
- });
212
- i18n1.t('msg'); // "Hello, !"
213
-
214
- // Preserve placeholder
215
- const i18n2 = createI18n({
216
- messages: { en: { msg: 'Hello, {name}!' } },
217
- missingVar: 'preserve',
218
- });
219
- i18n2.t('msg'); // "Hello, {name}!"
220
-
221
- // Throw error
222
- import { MissingVariableError } from '@vielzeug/i18nit';
223
-
224
- const i18n3 = createI18n({
307
+ const i18n = createI18n({
225
308
  messages: { en: { msg: 'Hello, {name}!' } },
226
- missingVar: 'error',
227
309
  });
228
310
 
229
- try {
230
- i18n3.t('msg');
231
- } catch (error) {
232
- if (error instanceof MissingVariableError) {
233
- console.log(error.key); // 'msg'
234
- console.log(error.variable); // 'name'
235
- console.log(error.locale); // 'en'
236
- }
237
- }
311
+ i18n.t('msg'); // "Hello, !"
312
+ i18n.t('msg', { name: 'Alice' }); // "Hello, Alice!"
238
313
  ```
239
314
 
240
315
  ### Pluralization
@@ -274,40 +349,7 @@ i18nit uses the browser's built-in `Intl.PluralRules` API to automatically suppo
274
349
  - **Japanese (ja)**: other
275
350
  - And 90+ more languages...
276
351
 
277
- ### Message Functions
278
-
279
- For complex dynamic content, use function-based messages:
280
-
281
- ```typescript
282
- const i18n = createI18n({
283
- locale: 'en',
284
- messages: {
285
- en: {
286
- // Simple function
287
- dynamic: (vars) => `Hello, ${vars.name}!`,
288
-
289
- // With number formatting
290
- price: (vars, helpers) =>
291
- `Price: ${helpers.number(vars.amount as number, {
292
- style: 'currency',
293
- currency: 'USD'
294
- })}`,
295
-
296
- // With date formatting
297
- event: (vars, helpers) =>
298
- `Event on ${helpers.date(vars.date as Date, {
299
- dateStyle: 'long'
300
- })}`,
301
- },
302
- },
303
- });
304
-
305
- i18n.t('dynamic', { name: 'Eve' }); // "Hello, Eve!"
306
- i18n.t('price', { amount: 99.99 }); // "Price: $99.99"
307
- i18n.t('event', { date: new Date('2024-01-15') }); // "Event on January 15, 2024"
308
- ```
309
-
310
- ## Advanced Features
352
+ ## 🔥 Advanced Features
311
353
 
312
354
  ### Fallback Locales
313
355
 
@@ -330,6 +372,7 @@ i18n.t('welcome'); // "Welcome!" (en fallback)
330
372
  ```
331
373
 
332
374
  **Fallback Chain:**
375
+
333
376
  1. Primary locale (e.g., `de-CH`)
334
377
  2. Base language (e.g., `de` from `de-CH`)
335
378
  3. First fallback locale
@@ -338,39 +381,64 @@ i18n.t('welcome'); // "Welcome!" (en fallback)
338
381
 
339
382
  ### Async Locale Loading
340
383
 
341
- Load translations on-demand for better performance:
384
+ Load translations on-demand for better performance. Loaders receive the locale as a parameter, allowing you to reuse a single function:
342
385
 
343
386
  ```typescript
387
+ // Define a reusable loader function
388
+ const loadLocale = async (locale: string) => {
389
+ const response = await fetch(`/locales/${locale}.json`);
390
+ return response.json();
391
+ };
392
+
344
393
  const i18n = createI18n({
345
394
  locale: 'en',
346
395
  loaders: {
347
- fr: async () => {
348
- const response = await fetch('/locales/fr.json');
349
- return response.json();
350
- },
351
- de: async () => import('./locales/de.json'),
396
+ fr: loadLocale, // Loader receives 'fr' as parameter
397
+ de: loadLocale, // Loader receives 'de' as parameter
398
+ es: loadLocale, // Loader receives 'es' as parameter
352
399
  },
353
400
  });
354
401
 
355
- // Lazy translation - loads locale first
356
- const text = await i18n.tl('greeting', { name: 'World' }, { locale: 'fr' });
402
+ // Load a locale before using it
403
+ await i18n.load('fr');
404
+ i18n.setLocale('fr');
405
+ i18n.t('greeting'); // Uses loaded French messages
406
+
407
+ // Or use dynamic imports
408
+ const importLoader = async (locale: string) => {
409
+ const module = await import(`./locales/${locale}.json`);
410
+ return module.default;
411
+ };
412
+
413
+ i18n.register('it', importLoader);
414
+ await i18n.load('it');
415
+ ```
416
+
417
+ // Preload at app startup
418
+ await i18n.loadAll(['en', 'fr', 'de']);
357
419
 
358
420
  // Or load explicitly
359
- await i18n.load('de');
360
- i18n.t('greeting', undefined, { locale: 'de' });
421
+ await i18n.load('fr');
422
+ i18n.setLocale('fr');
423
+ i18n.t('greeting'); // Now uses French
361
424
 
362
425
  // Register loader dynamically
363
426
  i18n.register('es', async () => {
364
427
  const module = await import('./locales/es.json');
365
428
  return module.default;
366
429
  });
430
+
431
+ // Load and use
432
+ await i18n.load('es');
433
+ i18n.t('greeting', undefined, { locale: 'es' });
367
434
  ```
368
435
 
369
436
  **Features:**
437
+
370
438
  - Concurrent requests are deduplicated
371
- - Failed loads can be retried
372
- - Errors are logged but don't break fallback
439
+ - Failed loads throw errors (can be caught)
373
440
  - Locale is cached after loading
441
+ - Use `loadAll()` to preload multiple locales at once
374
442
 
375
443
  ### Namespaces
376
444
 
@@ -404,7 +472,7 @@ Protect against XSS attacks with automatic HTML escaping:
404
472
  ```typescript
405
473
  const i18n = createI18n({
406
474
  messages: {
407
- en: {
475
+ en: {
408
476
  userContent: 'Comment: {content}',
409
477
  },
410
478
  },
@@ -416,14 +484,11 @@ const safeI18n = createI18n({
416
484
  messages: { en: { html: '<script>alert("xss")</script>' } },
417
485
  });
418
486
 
419
- safeI18n.t('html');
487
+ safeI18n.t('html');
420
488
  // "&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;"
421
489
 
422
490
  // Or per translation
423
- i18n.t('userContent',
424
- { content: '<b>Bold</b>' },
425
- { escape: true }
426
- );
491
+ i18n.t('userContent', { content: '<b>Bold</b>' }, { escape: true });
427
492
  // "Comment: &lt;b&gt;Bold&lt;/b&gt;"
428
493
  ```
429
494
 
@@ -473,27 +538,13 @@ unsubscribe();
473
538
  ```
474
539
 
475
540
  **Use Cases:**
541
+
476
542
  - Update UI when locale changes
477
543
  - Reload locale-specific data
478
544
  - Analytics/tracking
479
545
  - State management integration
480
546
 
481
- ### Custom Missing Key Handler
482
-
483
- Customize behavior for missing translations:
484
-
485
- ```typescript
486
- const i18n = createI18n({
487
- missingKey: (key, locale) => {
488
- console.warn(`Missing translation: ${key} in ${locale}`);
489
- return `[${locale}:${key}]`;
490
- },
491
- });
492
-
493
- i18n.t('nonexistent.key'); // "[en:nonexistent.key]"
494
- ```
495
-
496
- ## API Reference
547
+ ### Subscriptions
497
548
 
498
549
  ### createI18n(config?)
499
550
 
@@ -501,13 +552,11 @@ Creates a new i18n instance.
501
552
 
502
553
  ```typescript
503
554
  type I18nConfig = {
504
- locale?: string; // Default: 'en'
505
- fallback?: string | string[]; // Fallback locale(s)
555
+ locale?: string; // Default: 'en'
556
+ fallback?: string | string[]; // Fallback locale(s)
506
557
  messages?: Record<string, Messages>; // Initial translations
507
558
  loaders?: Record<string, () => Promise<Messages>>; // Async loaders
508
- escape?: boolean; // Global HTML escaping (default: false)
509
- missingKey?: (key: string, locale: string) => string; // Missing key handler
510
- missingVar?: 'preserve' | 'empty' | 'error'; // Missing variable strategy
559
+ escape?: boolean; // Global HTML escaping (default: false)
511
560
  };
512
561
  ```
513
562
 
@@ -524,26 +573,18 @@ i18n.t('greeting', { name: 'Bob' }, { locale: 'fr', escape: true }); // With opt
524
573
  ```
525
574
 
526
575
  **Options:**
527
- - `locale?: string` - Override locale for this translation
528
- - `fallback?: string` - Custom fallback text
529
- - `escape?: boolean` - Override HTML escaping
530
576
 
531
- #### `tl(key, vars?, options?)`
532
-
533
- Translate a key asynchronously (loads locale if needed).
534
-
535
- ```typescript
536
- await i18n.tl('greeting', { name: 'Alice' }, { locale: 'fr' });
537
- ```
577
+ - `locale?: string` – Override locale for this translation
578
+ - `escape?: boolean` – Override HTML escaping
538
579
 
539
580
  ### Locale Management
540
581
 
541
582
  ```typescript
542
- i18n.setLocale('fr'); // Change locale
543
- i18n.getLocale(); // Get current locale
544
- i18n.hasLocale('es'); // Check if locale exists
545
- i18n.has('key'); // Check if key exists
546
- i18n.has('key', 'fr'); // Check if key exists in locale
583
+ i18n.setLocale('fr'); // Change locale
584
+ i18n.getLocale(); // Get current locale
585
+ i18n.hasLocale('es'); // Check if locale exists
586
+ i18n.has('key'); // Check if key exists
587
+ i18n.has('key', 'fr'); // Check if key exists in locale
547
588
  await i18n.hasAsync('key', 'es'); // Check with async loading
548
589
  ```
549
590
 
@@ -582,7 +623,6 @@ i18n.date(value, options?, locale?);
582
623
  ```typescript
583
624
  const ns = i18n.namespace('auth');
584
625
  ns.t('login.title');
585
- await ns.tl('register.title', undefined, { locale: 'fr' });
586
626
  ```
587
627
 
588
628
  ### Subscriptions
@@ -611,11 +651,7 @@ export function I18nProvider({ children, config }) {
611
651
  return i18n.subscribe(setLocale);
612
652
  }, [i18n]);
613
653
 
614
- return (
615
- <I18nContext.Provider value={{ i18n, locale }}>
616
- {children}
617
- </I18nContext.Provider>
618
- );
654
+ return <I18nContext.Provider value={{ i18n, locale }}>{children}</I18nContext.Provider>;
619
655
  }
620
656
 
621
657
  export function useI18n() {
@@ -627,10 +663,9 @@ export function useI18n() {
627
663
  export function useTranslation(namespace?: string) {
628
664
  const { i18n } = useI18n();
629
665
  const ns = namespace ? i18n.namespace(namespace) : i18n;
630
-
666
+
631
667
  return {
632
668
  t: ns.t.bind(ns),
633
- tl: ns.tl.bind(ns),
634
669
  locale: i18n.getLocale(),
635
670
  setLocale: i18n.setLocale.bind(i18n),
636
671
  };
@@ -639,7 +674,7 @@ export function useTranslation(namespace?: string) {
639
674
  // Usage
640
675
  function MyComponent() {
641
676
  const { t, locale, setLocale } = useTranslation('dashboard');
642
-
677
+
643
678
  return (
644
679
  <div>
645
680
  <h1>{t('welcome')}</h1>
@@ -666,7 +701,7 @@ export const i18nPlugin: Plugin = {
666
701
  install(app) {
667
702
  app.config.globalProperties.$t = i18n.t.bind(i18n);
668
703
  app.config.globalProperties.$i18n = i18n;
669
-
704
+
670
705
  app.provide('i18n', i18n);
671
706
  app.provide('locale', locale);
672
707
  },
@@ -676,7 +711,6 @@ export const i18nPlugin: Plugin = {
676
711
  export function useI18n() {
677
712
  return {
678
713
  t: i18n.t.bind(i18n),
679
- tl: i18n.tl.bind(i18n),
680
714
  locale,
681
715
  setLocale: (newLocale: string) => i18n.setLocale(newLocale),
682
716
  };
@@ -758,40 +792,13 @@ const i18n = createI18n({
758
792
  ### 4. Type-Safe Translation Keys
759
793
 
760
794
  ```typescript
761
- type TranslationKeys =
762
- | 'auth.login.title'
763
- | 'auth.register.title'
764
- | 'dashboard.welcome';
795
+ type TranslationKeys = 'auth.login.title' | 'auth.register.title' | 'dashboard.welcome';
765
796
 
766
797
  function t(key: TranslationKeys, vars?: Record<string, unknown>) {
767
798
  return i18n.t(key, vars);
768
799
  }
769
800
  ```
770
801
 
771
- ### 5. Use Structured Error Handling
772
-
773
- ```typescript
774
- import { MissingVariableError } from '@vielzeug/i18nit';
775
-
776
- const i18n = createI18n({
777
- missingVar: 'error',
778
- messages: { en: { greeting: 'Hello, {name}!' } },
779
- });
780
-
781
- try {
782
- i18n.t('greeting');
783
- } catch (error) {
784
- if (error instanceof MissingVariableError) {
785
- // Log to error tracking service
786
- console.error('Missing variable:', {
787
- key: error.key,
788
- variable: error.variable,
789
- locale: error.locale,
790
- });
791
- }
792
- }
793
- ```
794
-
795
802
  ## TypeScript Support
796
803
 
797
804
  Full TypeScript support with type inference:
@@ -822,33 +829,28 @@ const config: I18nConfig = {
822
829
  const i18n = createI18n(config);
823
830
  ```
824
831
 
825
- ## Comparison
832
+ ## 📖 Documentation
826
833
 
827
- | Feature | i18nit | i18next | react-intl |
828
- |---------|--------|---------|------------|
829
- | Bundle Size | **~3KB** | ~12KB | ~15KB |
830
- | Dependencies | **0** | 2+ | 10+ |
831
- | TypeScript | ✅ First-class | ✅ Good | ✅ Good |
832
- | Pluralization | ✅ Built-in | ✅ Plugin | ✅ Built-in |
833
- | Async Loading | ✅ Built-in | ✅ Built-in | ⚠️ Manual |
834
- | Path Interpolation | ✅ `{user.name}` | ❌ | ❌ |
835
- | Message Functions | ✅ Built-in | ⚠️ Limited | ✅ Components |
836
- | HTML Escaping | ✅ Built-in | ⚠️ Manual | ✅ Built-in |
837
- | Structured Errors | ✅ MissingVariableError | ❌ | ❌ |
838
- | Framework Agnostic | ✅ | ✅ | ❌ React only |
834
+ - [**Full Documentation**](https://helmuthdu.github.io/vielzeug/i18nit)
835
+ - [**Usage Guide**](https://helmuthdu.github.io/vielzeug/i18nit/usage)
836
+ - [**API Reference**](https://helmuthdu.github.io/vielzeug/i18nit/api)
837
+ - [**Examples**](https://helmuthdu.github.io/vielzeug/i18nit/examples)
839
838
 
840
- ## License
839
+ ## 📄 License
841
840
 
842
841
  MIT © [Helmuth Saatkamp](https://github.com/helmuthdu)
843
842
 
844
- ## Links
843
+ ## 🤝 Contributing
844
+
845
+ Contributions are welcome! Check our [GitHub repository](https://github.com/helmuthdu/vielzeug).
846
+
847
+ ## 🔗 Links
845
848
 
846
849
  - [GitHub Repository](https://github.com/helmuthdu/vielzeug)
847
- - [Documentation](https://vielzeug.dev)
848
- - [NPM Package](https://www.npmjs.com/package/@vielzeug/i18nit)
850
+ - [Documentation](https://helmuthdu.github.io/vielzeug/deposit)
851
+ - [NPM Package](https://www.npmjs.com/package/@vielzeug/deposit)
849
852
  - [Issue Tracker](https://github.com/helmuthdu/vielzeug/issues)
850
853
 
851
854
  ---
852
855
 
853
- Part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) ecosystem - A collection of type-safe utilities for modern web development.
854
-
856
+ Part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) ecosystem A collection of type-safe utilities for modern web development.