inline-i18n-multi 0.2.0 → 0.4.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
@@ -53,8 +53,12 @@ See "Hello" in your app? Just search for "Hello" in your codebase. **Done.**
53
53
  - **Type-safe** - Full TypeScript support with variable type checking
54
54
  - **Multiple languages** - Support for any number of locales
55
55
  - **i18n compatible** - Support for traditional key-based translations with JSON dictionaries
56
- - **ICU Message Format** - Plural and select syntax for complex translations
56
+ - **ICU Message Format** - Plural, select, date, number, time, relative time, and list formatting
57
57
  - **Variable interpolation** - `{name}` syntax for dynamic values
58
+ - **Locale Fallback Chain** - BCP 47 parent locale support (`zh-TW` → `zh` → `en`)
59
+ - **Missing Translation Warning** - Development-time diagnostics with customizable handlers
60
+ - **Namespace Support** - Organize translations for large apps (`t('common:greeting')`)
61
+ - **Debug Mode** - Visual indicators for missing/fallback translations
58
62
 
59
63
  ---
60
64
 
@@ -184,6 +188,125 @@ it({
184
188
  ko: '{name}님이 {count, plural, =0 {메시지가 없습니다} other {# 개의 메시지가 있습니다}}',
185
189
  en: '{name} has {count, plural, =0 {no messages} one {# message} other {# messages}}'
186
190
  }, { name: 'John', count: 3 }) // → "John has 3 messages"
191
+
192
+ // Date formatting
193
+ it({
194
+ en: 'Created: {date, date, long}',
195
+ ko: '생성일: {date, date, long}'
196
+ }, { date: new Date() }) // → "Created: January 15, 2024"
197
+
198
+ // Number formatting
199
+ it({
200
+ en: 'Price: {price, number}',
201
+ ko: '가격: {price, number}'
202
+ }, { price: 1234.56 }) // → "Price: 1,234.56"
203
+ ```
204
+
205
+ **Supported ICU styles:**
206
+ - `number`: `decimal`, `percent`, `integer`, `currency`
207
+ - `date`: `short`, `medium`, `long`, `full`
208
+ - `time`: `short`, `medium`, `long`, `full`
209
+
210
+ ### Relative Time Formatting
211
+
212
+ ```typescript
213
+ it({
214
+ en: 'Updated {time, relativeTime}',
215
+ ko: '{time, relativeTime} 업데이트됨'
216
+ }, { time: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000) })
217
+ // → "Updated 3 days ago"
218
+
219
+ // Styles: long (default), short, narrow
220
+ it({ en: '{time, relativeTime, short}' }, { time: pastDate })
221
+ ```
222
+
223
+ ### List Formatting
224
+
225
+ ```typescript
226
+ it({
227
+ en: 'Invited: {names, list}',
228
+ ko: '초대됨: {names, list}'
229
+ }, { names: ['Alice', 'Bob', 'Charlie'] })
230
+ // → "Invited: Alice, Bob, and Charlie"
231
+
232
+ // Types: conjunction (and), disjunction (or), unit
233
+ it({ en: '{options, list, disjunction}' }, { options: ['A', 'B'] })
234
+ // → "A or B"
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Namespace Support
240
+
241
+ Organize translations for large applications:
242
+
243
+ ```typescript
244
+ import { loadDictionaries, t, getLoadedNamespaces, clearDictionaries } from 'inline-i18n-multi'
245
+
246
+ // Load with namespace
247
+ loadDictionaries({
248
+ en: { hello: 'Hello' },
249
+ ko: { hello: '안녕하세요' }
250
+ }, 'common')
251
+
252
+ // Use with namespace prefix
253
+ t('common:hello') // → "Hello"
254
+
255
+ // Without namespace = 'default' (backward compatible)
256
+ loadDictionaries({ en: { greeting: 'Hi' } })
257
+ t('greeting') // → "Hi"
258
+
259
+ getLoadedNamespaces() // → ['common', 'default']
260
+ clearDictionaries('common') // Clear specific namespace
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Debug Mode
266
+
267
+ Visual indicators for debugging:
268
+
269
+ ```typescript
270
+ import { configure, setLocale, it, t } from 'inline-i18n-multi'
271
+
272
+ configure({ debugMode: true })
273
+
274
+ setLocale('fr')
275
+ it({ en: 'Hello', ko: '안녕하세요' }) // → "[fr -> en] Hello"
276
+ t('missing.key') // → "[MISSING: fr] missing.key"
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Configuration
282
+
283
+ Configure global settings for fallback behavior and warnings:
284
+
285
+ ```typescript
286
+ import { configure, getConfig, resetConfig } from 'inline-i18n-multi'
287
+
288
+ configure({
289
+ fallbackLocale: 'en', // Final fallback (default: 'en')
290
+ autoParentLocale: true, // BCP 47 parent (zh-TW → zh)
291
+ fallbackChain: { // Custom chains
292
+ 'pt-BR': ['pt', 'es', 'en']
293
+ },
294
+ warnOnMissing: true, // Enable warnings
295
+ onMissingTranslation: (w) => { // Custom handler
296
+ console.warn(`Missing: ${w.requestedLocale}`)
297
+ }
298
+ })
299
+ ```
300
+
301
+ ### Locale Fallback Chain
302
+
303
+ Automatic locale fallback with BCP 47 support:
304
+
305
+ ```typescript
306
+ setLocale('zh-TW')
307
+ it({ en: 'Hello', zh: '你好' }) // → '你好' (falls back to zh)
308
+
309
+ t('greeting') // Also works with dictionaries
187
310
  ```
188
311
 
189
312
  ---
@@ -228,18 +351,55 @@ Available helpers:
228
351
  | Function | Description |
229
352
  |----------|-------------|
230
353
  | `t(key, vars?, locale?)` | Key-based translation with optional locale override |
231
- | `loadDictionaries(dicts)` | Load translation dictionaries for multiple locales |
232
- | `loadDictionary(locale, dict)` | Load dictionary for a single locale |
233
- | `hasTranslation(key, locale?)` | Check if translation key exists |
354
+ | `loadDictionaries(dicts, namespace?)` | Load translation dictionaries with optional namespace |
355
+ | `loadDictionary(locale, dict, namespace?)` | Load dictionary for a single locale with optional namespace |
356
+ | `hasTranslation(key, locale?)` | Check if translation key exists (supports namespace:key) |
234
357
  | `getLoadedLocales()` | Get array of loaded locale codes |
235
- | `getDictionary(locale)` | Get dictionary for a specific locale |
358
+ | `getLoadedNamespaces()` | Get array of loaded namespace names |
359
+ | `getDictionary(locale, namespace?)` | Get dictionary for a specific locale and namespace |
360
+ | `clearDictionaries(namespace?)` | Clear dictionaries (all or specific namespace) |
361
+
362
+ ### Configuration
363
+
364
+ | Function | Description |
365
+ |----------|-------------|
366
+ | `configure(options)` | Configure global settings (fallback, warnings, debug) |
367
+ | `getConfig()` | Get current configuration |
368
+ | `resetConfig()` | Reset configuration to defaults |
236
369
 
237
370
  ### Types
238
371
 
239
372
  ```typescript
240
373
  type Locale = string
241
374
  type Translations = Record<Locale, string>
242
- type TranslationVars = Record<string, string | number>
375
+ type TranslationVars = Record<string, string | number | Date | string[]>
376
+
377
+ interface Config {
378
+ defaultLocale: Locale
379
+ fallbackLocale?: Locale
380
+ autoParentLocale?: boolean
381
+ fallbackChain?: Record<Locale, Locale[]>
382
+ warnOnMissing?: boolean
383
+ onMissingTranslation?: WarningHandler
384
+ debugMode?: boolean | DebugModeOptions
385
+ }
386
+
387
+ interface DebugModeOptions {
388
+ showMissingPrefix?: boolean
389
+ showFallbackPrefix?: boolean
390
+ missingPrefixFormat?: (locale: string, key?: string) => string
391
+ fallbackPrefixFormat?: (requestedLocale: string, usedLocale: string, key?: string) => string
392
+ }
393
+
394
+ interface TranslationWarning {
395
+ type: 'missing_translation'
396
+ key?: string
397
+ requestedLocale: string
398
+ availableLocales: string[]
399
+ fallbackUsed?: string
400
+ }
401
+
402
+ type WarningHandler = (warning: TranslationWarning) => void
243
403
  ```
244
404
 
245
405
  ---
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Locale code type (e.g., 'ko', 'en', 'ja')
2
+ * Locale code type (e.g., 'ko', 'en', 'ja', 'zh-TW')
3
3
  */
4
4
  type Locale = string;
5
5
  /**
@@ -8,14 +8,59 @@ type Locale = string;
8
8
  type Translations = Record<Locale, string>;
9
9
  /**
10
10
  * Variables for interpolation
11
+ * Supports string, number, Date values for ICU formatting
12
+ * Supports string[] for list formatting
13
+ */
14
+ type TranslationVars = Record<string, string | number | Date | string[]>;
15
+ /**
16
+ * Warning information for missing translations
17
+ */
18
+ interface TranslationWarning {
19
+ type: 'missing_translation';
20
+ /** Dictionary key (for t() function) */
21
+ key?: string;
22
+ /** The locale that was requested */
23
+ requestedLocale: string;
24
+ /** Available locales that have translations */
25
+ availableLocales: string[];
26
+ /** The locale that was used as fallback */
27
+ fallbackUsed?: string;
28
+ }
29
+ /**
30
+ * Warning handler function type
11
31
  */
12
- type TranslationVars = Record<string, string | number>;
32
+ type WarningHandler = (warning: TranslationWarning) => void;
33
+ /**
34
+ * Debug mode options for visual indicators
35
+ */
36
+ interface DebugModeOptions {
37
+ /** Show visual prefix for missing translations (default: true) */
38
+ showMissingPrefix?: boolean;
39
+ /** Show visual prefix for fallback translations (default: true) */
40
+ showFallbackPrefix?: boolean;
41
+ /** Custom prefix format for missing translations */
42
+ missingPrefixFormat?: (locale: string, key?: string) => string;
43
+ /** Custom prefix format for fallback translations */
44
+ fallbackPrefixFormat?: (requestedLocale: string, usedLocale: string, key?: string) => string;
45
+ }
13
46
  /**
14
47
  * Configuration options
15
48
  */
16
49
  interface Config {
50
+ /** Default locale to use when none is set */
17
51
  defaultLocale: Locale;
52
+ /** Fallback locale when translation is missing (default: 'en') */
18
53
  fallbackLocale?: Locale;
54
+ /** Enable automatic BCP 47 parent locale derivation (default: true) */
55
+ autoParentLocale?: boolean;
56
+ /** Custom fallback chain for specific locales */
57
+ fallbackChain?: Record<Locale, Locale[]>;
58
+ /** Enable warnings when translation is missing (default: true in dev mode) */
59
+ warnOnMissing?: boolean;
60
+ /** Custom warning handler */
61
+ onMissingTranslation?: WarningHandler;
62
+ /** Enable debug mode with visual indicators (default: false) */
63
+ debugMode?: boolean | DebugModeOptions;
19
64
  }
20
65
 
21
66
  /**
@@ -86,44 +131,84 @@ interface PluralRules {
86
131
  /**
87
132
  * Load translations from dictionary objects
88
133
  * @param dicts - Dictionary objects keyed by locale
134
+ * @param namespace - Optional namespace (defaults to 'default')
89
135
  * @example
136
+ * // Without namespace (backward compatible)
90
137
  * loadDictionaries({
91
138
  * en: { greeting: { hello: "Hello" } },
92
139
  * ko: { greeting: { hello: "안녕하세요" } }
93
140
  * })
141
+ *
142
+ * // With namespace
143
+ * loadDictionaries({
144
+ * en: { hello: "Hello" },
145
+ * ko: { hello: "안녕하세요" }
146
+ * }, 'common')
94
147
  */
95
- declare function loadDictionaries(dicts: Dictionaries): void;
148
+ declare function loadDictionaries(dicts: Dictionaries, namespace?: string): void;
96
149
  /**
97
150
  * Load a single locale's dictionary
98
151
  * @param locale - Locale code
99
152
  * @param dict - Dictionary object
153
+ * @param namespace - Optional namespace (defaults to 'default')
100
154
  */
101
- declare function loadDictionary(locale: Locale, dict: Dictionary): void;
155
+ declare function loadDictionary(locale: Locale, dict: Dictionary, namespace?: string): void;
102
156
  /**
103
- * Clear all loaded dictionaries
157
+ * Clear loaded dictionaries
158
+ * @param namespace - Optional namespace to clear (clears all if not specified)
104
159
  */
105
- declare function clearDictionaries(): void;
160
+ declare function clearDictionaries(namespace?: string): void;
106
161
  /**
107
162
  * Translate using key-based lookup (i18n compatible)
108
- * @param key - Dot-separated translation key
163
+ * @param key - Dot-separated translation key, optionally prefixed with namespace
109
164
  * @param vars - Variables for interpolation (including 'count' for plurals)
110
165
  * @param locale - Override locale (optional)
111
166
  * @example
112
- * t('greeting.hello') // "Hello"
167
+ * t('greeting.hello') // Uses default namespace
168
+ * t('common:greeting.hello') // Uses 'common' namespace
113
169
  * t('items.count', { count: 5 }) // "5 items"
114
170
  */
115
171
  declare function t(key: string, vars?: TranslationVars, locale?: Locale): string;
116
172
  /**
117
173
  * Check if a translation key exists
174
+ * @param key - Translation key (may include namespace prefix)
175
+ * @param locale - Optional locale to check
118
176
  */
119
177
  declare function hasTranslation(key: string, locale?: Locale): boolean;
120
178
  /**
121
179
  * Get all loaded locales
180
+ * @param namespace - Optional namespace (returns from all if not specified)
122
181
  */
123
- declare function getLoadedLocales(): Locale[];
182
+ declare function getLoadedLocales(namespace?: string): Locale[];
124
183
  /**
125
184
  * Get dictionary for a specific locale
185
+ * @param locale - Locale code
186
+ * @param namespace - Optional namespace (defaults to 'default')
187
+ */
188
+ declare function getDictionary(locale: Locale, namespace?: string): Dictionary | undefined;
189
+ /**
190
+ * Get all loaded namespaces
191
+ */
192
+ declare function getLoadedNamespaces(): string[];
193
+
194
+ /**
195
+ * Configure inline-i18n-multi settings
196
+ *
197
+ * @example
198
+ * configure({
199
+ * fallbackLocale: 'en',
200
+ * fallbackChain: { 'pt-BR': ['pt', 'es', 'en'] },
201
+ * warnOnMissing: true,
202
+ * })
203
+ */
204
+ declare function configure(options: Partial<Config>): void;
205
+ /**
206
+ * Get current configuration
207
+ */
208
+ declare function getConfig(): Required<Config>;
209
+ /**
210
+ * Reset configuration to defaults
126
211
  */
127
- declare function getDictionary(locale: Locale): Dictionary | undefined;
212
+ declare function resetConfig(): void;
128
213
 
129
- export { type Config, type Dictionaries, type Dictionary, type Locale, type PluralRules, type TranslationVars, type Translations, __i18n_lookup, clearDictionaries, en_de, en_es, en_fr, en_ja, en_zh, getDictionary, getLoadedLocales, getLocale, hasTranslation, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadDictionaries, loadDictionary, setLocale, t, zh_es };
214
+ export { type Config, type DebugModeOptions, type Dictionaries, type Dictionary, type Locale, type PluralRules, type TranslationVars, type TranslationWarning, type Translations, type WarningHandler, __i18n_lookup, clearDictionaries, configure, en_de, en_es, en_fr, en_ja, en_zh, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, hasTranslation, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadDictionaries, loadDictionary, resetConfig, setLocale, t, zh_es };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Locale code type (e.g., 'ko', 'en', 'ja')
2
+ * Locale code type (e.g., 'ko', 'en', 'ja', 'zh-TW')
3
3
  */
4
4
  type Locale = string;
5
5
  /**
@@ -8,14 +8,59 @@ type Locale = string;
8
8
  type Translations = Record<Locale, string>;
9
9
  /**
10
10
  * Variables for interpolation
11
+ * Supports string, number, Date values for ICU formatting
12
+ * Supports string[] for list formatting
13
+ */
14
+ type TranslationVars = Record<string, string | number | Date | string[]>;
15
+ /**
16
+ * Warning information for missing translations
17
+ */
18
+ interface TranslationWarning {
19
+ type: 'missing_translation';
20
+ /** Dictionary key (for t() function) */
21
+ key?: string;
22
+ /** The locale that was requested */
23
+ requestedLocale: string;
24
+ /** Available locales that have translations */
25
+ availableLocales: string[];
26
+ /** The locale that was used as fallback */
27
+ fallbackUsed?: string;
28
+ }
29
+ /**
30
+ * Warning handler function type
11
31
  */
12
- type TranslationVars = Record<string, string | number>;
32
+ type WarningHandler = (warning: TranslationWarning) => void;
33
+ /**
34
+ * Debug mode options for visual indicators
35
+ */
36
+ interface DebugModeOptions {
37
+ /** Show visual prefix for missing translations (default: true) */
38
+ showMissingPrefix?: boolean;
39
+ /** Show visual prefix for fallback translations (default: true) */
40
+ showFallbackPrefix?: boolean;
41
+ /** Custom prefix format for missing translations */
42
+ missingPrefixFormat?: (locale: string, key?: string) => string;
43
+ /** Custom prefix format for fallback translations */
44
+ fallbackPrefixFormat?: (requestedLocale: string, usedLocale: string, key?: string) => string;
45
+ }
13
46
  /**
14
47
  * Configuration options
15
48
  */
16
49
  interface Config {
50
+ /** Default locale to use when none is set */
17
51
  defaultLocale: Locale;
52
+ /** Fallback locale when translation is missing (default: 'en') */
18
53
  fallbackLocale?: Locale;
54
+ /** Enable automatic BCP 47 parent locale derivation (default: true) */
55
+ autoParentLocale?: boolean;
56
+ /** Custom fallback chain for specific locales */
57
+ fallbackChain?: Record<Locale, Locale[]>;
58
+ /** Enable warnings when translation is missing (default: true in dev mode) */
59
+ warnOnMissing?: boolean;
60
+ /** Custom warning handler */
61
+ onMissingTranslation?: WarningHandler;
62
+ /** Enable debug mode with visual indicators (default: false) */
63
+ debugMode?: boolean | DebugModeOptions;
19
64
  }
20
65
 
21
66
  /**
@@ -86,44 +131,84 @@ interface PluralRules {
86
131
  /**
87
132
  * Load translations from dictionary objects
88
133
  * @param dicts - Dictionary objects keyed by locale
134
+ * @param namespace - Optional namespace (defaults to 'default')
89
135
  * @example
136
+ * // Without namespace (backward compatible)
90
137
  * loadDictionaries({
91
138
  * en: { greeting: { hello: "Hello" } },
92
139
  * ko: { greeting: { hello: "안녕하세요" } }
93
140
  * })
141
+ *
142
+ * // With namespace
143
+ * loadDictionaries({
144
+ * en: { hello: "Hello" },
145
+ * ko: { hello: "안녕하세요" }
146
+ * }, 'common')
94
147
  */
95
- declare function loadDictionaries(dicts: Dictionaries): void;
148
+ declare function loadDictionaries(dicts: Dictionaries, namespace?: string): void;
96
149
  /**
97
150
  * Load a single locale's dictionary
98
151
  * @param locale - Locale code
99
152
  * @param dict - Dictionary object
153
+ * @param namespace - Optional namespace (defaults to 'default')
100
154
  */
101
- declare function loadDictionary(locale: Locale, dict: Dictionary): void;
155
+ declare function loadDictionary(locale: Locale, dict: Dictionary, namespace?: string): void;
102
156
  /**
103
- * Clear all loaded dictionaries
157
+ * Clear loaded dictionaries
158
+ * @param namespace - Optional namespace to clear (clears all if not specified)
104
159
  */
105
- declare function clearDictionaries(): void;
160
+ declare function clearDictionaries(namespace?: string): void;
106
161
  /**
107
162
  * Translate using key-based lookup (i18n compatible)
108
- * @param key - Dot-separated translation key
163
+ * @param key - Dot-separated translation key, optionally prefixed with namespace
109
164
  * @param vars - Variables for interpolation (including 'count' for plurals)
110
165
  * @param locale - Override locale (optional)
111
166
  * @example
112
- * t('greeting.hello') // "Hello"
167
+ * t('greeting.hello') // Uses default namespace
168
+ * t('common:greeting.hello') // Uses 'common' namespace
113
169
  * t('items.count', { count: 5 }) // "5 items"
114
170
  */
115
171
  declare function t(key: string, vars?: TranslationVars, locale?: Locale): string;
116
172
  /**
117
173
  * Check if a translation key exists
174
+ * @param key - Translation key (may include namespace prefix)
175
+ * @param locale - Optional locale to check
118
176
  */
119
177
  declare function hasTranslation(key: string, locale?: Locale): boolean;
120
178
  /**
121
179
  * Get all loaded locales
180
+ * @param namespace - Optional namespace (returns from all if not specified)
122
181
  */
123
- declare function getLoadedLocales(): Locale[];
182
+ declare function getLoadedLocales(namespace?: string): Locale[];
124
183
  /**
125
184
  * Get dictionary for a specific locale
185
+ * @param locale - Locale code
186
+ * @param namespace - Optional namespace (defaults to 'default')
187
+ */
188
+ declare function getDictionary(locale: Locale, namespace?: string): Dictionary | undefined;
189
+ /**
190
+ * Get all loaded namespaces
191
+ */
192
+ declare function getLoadedNamespaces(): string[];
193
+
194
+ /**
195
+ * Configure inline-i18n-multi settings
196
+ *
197
+ * @example
198
+ * configure({
199
+ * fallbackLocale: 'en',
200
+ * fallbackChain: { 'pt-BR': ['pt', 'es', 'en'] },
201
+ * warnOnMissing: true,
202
+ * })
203
+ */
204
+ declare function configure(options: Partial<Config>): void;
205
+ /**
206
+ * Get current configuration
207
+ */
208
+ declare function getConfig(): Required<Config>;
209
+ /**
210
+ * Reset configuration to defaults
126
211
  */
127
- declare function getDictionary(locale: Locale): Dictionary | undefined;
212
+ declare function resetConfig(): void;
128
213
 
129
- export { type Config, type Dictionaries, type Dictionary, type Locale, type PluralRules, type TranslationVars, type Translations, __i18n_lookup, clearDictionaries, en_de, en_es, en_fr, en_ja, en_zh, getDictionary, getLoadedLocales, getLocale, hasTranslation, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadDictionaries, loadDictionary, setLocale, t, zh_es };
214
+ export { type Config, type DebugModeOptions, type Dictionaries, type Dictionary, type Locale, type PluralRules, type TranslationVars, type TranslationWarning, type Translations, type WarningHandler, __i18n_lookup, clearDictionaries, configure, en_de, en_es, en_fr, en_ja, en_zh, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, hasTranslation, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadDictionaries, loadDictionary, resetConfig, setLocale, t, zh_es };