inline-i18n-multi 0.4.0 → 0.6.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 +325 -28
- package/dist/index.d.mts +97 -5
- package/dist/index.d.ts +97 -5
- package/dist/index.js +451 -118
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +446 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,14 @@ See "Hello" in your app? Just search for "Hello" in your codebase. **Done.**
|
|
|
59
59
|
- **Missing Translation Warning** - Development-time diagnostics with customizable handlers
|
|
60
60
|
- **Namespace Support** - Organize translations for large apps (`t('common:greeting')`)
|
|
61
61
|
- **Debug Mode** - Visual indicators for missing/fallback translations
|
|
62
|
+
- **Currency Formatting** - Locale-aware currency display (`{price, currency, USD}`)
|
|
63
|
+
- **Compact Number Formatting** - Short number display (`{count, number, compact}`)
|
|
64
|
+
- **Rich Text Interpolation** - Embed React components in translations (`<link>text</link>`)
|
|
65
|
+
- **Lazy Loading** - Async dictionary loading on demand (`loadAsync()`)
|
|
66
|
+
- **Custom Formatter Registry** - Register custom ICU-style formatters (`registerFormatter('phone', fn)`)
|
|
67
|
+
- **Interpolation Guards** - Handle missing variables gracefully (`missingVarHandler`)
|
|
68
|
+
- **Locale Detection** - Auto-detect user locale from navigator, cookie, URL, or header (`detectLocale()`)
|
|
69
|
+
- **Selectordinal** - Ordinal plural formatting (`{rank, selectordinal, one {#st} two {#nd} ...}`)
|
|
62
70
|
|
|
63
71
|
---
|
|
64
72
|
|
|
@@ -202,8 +210,11 @@ it({
|
|
|
202
210
|
}, { price: 1234.56 }) // → "Price: 1,234.56"
|
|
203
211
|
```
|
|
204
212
|
|
|
205
|
-
**Supported ICU
|
|
206
|
-
- `
|
|
213
|
+
**Supported ICU types:**
|
|
214
|
+
- `plural`: `zero`, `one`, `two`, `few`, `many`, `other` (and exact matches like `=0`, `=1`)
|
|
215
|
+
- `select`: match on string values
|
|
216
|
+
- `selectordinal`: ordinal plural categories (`one`, `two`, `few`, `other`)
|
|
217
|
+
- `number`: `decimal`, `percent`, `integer`, `currency`, `compact`, `compactLong`
|
|
207
218
|
- `date`: `short`, `medium`, `long`, `full`
|
|
208
219
|
- `time`: `short`, `medium`, `long`, `full`
|
|
209
220
|
|
|
@@ -234,6 +245,33 @@ it({ en: '{options, list, disjunction}' }, { options: ['A', 'B'] })
|
|
|
234
245
|
// → "A or B"
|
|
235
246
|
```
|
|
236
247
|
|
|
248
|
+
### Currency Formatting
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
it({
|
|
252
|
+
en: 'Total: {price, currency, USD}',
|
|
253
|
+
ko: '합계: {price, currency, KRW}'
|
|
254
|
+
}, { price: 42000 })
|
|
255
|
+
// en → "Total: $42,000.00" / ko → "합계: ₩42,000"
|
|
256
|
+
|
|
257
|
+
// Defaults to USD when currency code omitted
|
|
258
|
+
it({ en: '{price, currency}' }, { price: 100 })
|
|
259
|
+
// → "$100.00"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Compact Number Formatting
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
it({
|
|
266
|
+
en: '{count, number, compact} views',
|
|
267
|
+
ko: '{count, number, compact} 조회'
|
|
268
|
+
}, { count: 1500000 })
|
|
269
|
+
// en → "1.5M views" / ko → "150만 조회"
|
|
270
|
+
|
|
271
|
+
it({ en: '{count, number, compactLong}' }, { count: 1500000 })
|
|
272
|
+
// → "1.5 million"
|
|
273
|
+
```
|
|
274
|
+
|
|
237
275
|
---
|
|
238
276
|
|
|
239
277
|
## Namespace Support
|
|
@@ -278,6 +316,241 @@ t('missing.key') // → "[MISSING: fr] missing.key"
|
|
|
278
316
|
|
|
279
317
|
---
|
|
280
318
|
|
|
319
|
+
## Rich Text Interpolation
|
|
320
|
+
|
|
321
|
+
Embed React components within translations:
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
import { RichText, useRichText } from 'inline-i18n-multi-react'
|
|
325
|
+
|
|
326
|
+
// Component syntax
|
|
327
|
+
<RichText
|
|
328
|
+
translations={{
|
|
329
|
+
en: 'Read <link>terms</link> and <bold>agree</bold>',
|
|
330
|
+
ko: '<link>약관</link>을 읽고 <bold>동의</bold>해주세요'
|
|
331
|
+
}}
|
|
332
|
+
components={{
|
|
333
|
+
link: (text) => <a href="/terms">{text}</a>,
|
|
334
|
+
bold: (text) => <strong>{text}</strong>
|
|
335
|
+
}}
|
|
336
|
+
/>
|
|
337
|
+
|
|
338
|
+
// Hook syntax
|
|
339
|
+
const richT = useRichText({
|
|
340
|
+
link: (text) => <a href="/terms">{text}</a>,
|
|
341
|
+
bold: (text) => <strong>{text}</strong>
|
|
342
|
+
})
|
|
343
|
+
richT({ en: 'Click <link>here</link>', ko: '<link>여기</link> 클릭' })
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Lazy Loading
|
|
349
|
+
|
|
350
|
+
Load dictionaries asynchronously on demand:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { configure, loadAsync, isLoaded, t } from 'inline-i18n-multi'
|
|
354
|
+
|
|
355
|
+
configure({
|
|
356
|
+
loader: (locale, namespace) => import(`./locales/${locale}/${namespace}.json`)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
await loadAsync('ko', 'dashboard')
|
|
360
|
+
t('dashboard:title')
|
|
361
|
+
|
|
362
|
+
isLoaded('ko', 'dashboard') // → true
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### React Hook
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
import { useLoadDictionaries } from 'inline-i18n-multi-react'
|
|
369
|
+
|
|
370
|
+
function Dashboard() {
|
|
371
|
+
const { isLoading, error } = useLoadDictionaries('ko', 'dashboard')
|
|
372
|
+
if (isLoading) return <Spinner />
|
|
373
|
+
if (error) return <Error message={error.message} />
|
|
374
|
+
return <Content />
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Custom Formatter Registry
|
|
381
|
+
|
|
382
|
+
Register custom ICU-style formatters for domain-specific formatting:
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { registerFormatter, clearFormatters, it, setLocale } from 'inline-i18n-multi'
|
|
386
|
+
|
|
387
|
+
setLocale('en')
|
|
388
|
+
|
|
389
|
+
// Register a phone number formatter
|
|
390
|
+
registerFormatter('phone', (value, locale, style?) => {
|
|
391
|
+
const s = String(value)
|
|
392
|
+
if (locale === 'ko') return `${s.slice(0, 3)}-${s.slice(3, 7)}-${s.slice(7)}`
|
|
393
|
+
return `(${s.slice(0, 3)}) ${s.slice(3, 6)}-${s.slice(6)}`
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
// Use in translations
|
|
397
|
+
it({
|
|
398
|
+
en: 'Call {num, phone}',
|
|
399
|
+
ko: '전화: {num, phone}'
|
|
400
|
+
}, { num: '2125551234' })
|
|
401
|
+
// → "Call (212) 555-1234"
|
|
402
|
+
|
|
403
|
+
// Register a formatter with style support
|
|
404
|
+
registerFormatter('mask', (value, locale, style?) => {
|
|
405
|
+
const s = String(value)
|
|
406
|
+
if (style === 'email') {
|
|
407
|
+
const [user, domain] = s.split('@')
|
|
408
|
+
return `${user[0]}***@${domain}`
|
|
409
|
+
}
|
|
410
|
+
return s.slice(0, 2) + '***' + s.slice(-2)
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
it({ en: 'Email: {email, mask, email}' }, { email: 'john@example.com' })
|
|
414
|
+
// → "Email: j***@example.com"
|
|
415
|
+
|
|
416
|
+
// Clear all custom formatters
|
|
417
|
+
clearFormatters()
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Reserved names (`plural`, `select`, `selectordinal`, `number`, `date`, `time`, `relativeTime`, `list`, `currency`) cannot be used as custom formatter names and will throw an error.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Interpolation Guards
|
|
425
|
+
|
|
426
|
+
Handle missing interpolation variables gracefully with a custom handler:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { configure, it, setLocale } from 'inline-i18n-multi'
|
|
430
|
+
|
|
431
|
+
setLocale('en')
|
|
432
|
+
|
|
433
|
+
// Configure a missing variable handler
|
|
434
|
+
configure({
|
|
435
|
+
missingVarHandler: (varName, locale) => {
|
|
436
|
+
console.warn(`Missing variable "${varName}" for locale "${locale}"`)
|
|
437
|
+
return `[${varName}]`
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
// When a variable is missing, the handler is called instead of leaving {varName}
|
|
442
|
+
it({ en: 'Hello, {name}!' })
|
|
443
|
+
// logs: Missing variable "name" for locale "en"
|
|
444
|
+
// → "Hello, [name]!"
|
|
445
|
+
|
|
446
|
+
// Works with ICU patterns too
|
|
447
|
+
it({ en: '{count, plural, one {# item} other {# items}}' })
|
|
448
|
+
// logs: Missing variable "count" for locale "en"
|
|
449
|
+
// → "{count}"
|
|
450
|
+
|
|
451
|
+
// Without a handler, missing variables are left as-is: {varName}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Locale Detection
|
|
457
|
+
|
|
458
|
+
Auto-detect the user's locale from multiple sources:
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { detectLocale, setLocale } from 'inline-i18n-multi'
|
|
462
|
+
|
|
463
|
+
// Basic detection from browser navigator
|
|
464
|
+
const locale = detectLocale({
|
|
465
|
+
supportedLocales: ['en', 'ko', 'ja'],
|
|
466
|
+
defaultLocale: 'en',
|
|
467
|
+
})
|
|
468
|
+
setLocale(locale)
|
|
469
|
+
|
|
470
|
+
// Multiple sources in priority order
|
|
471
|
+
const detected = detectLocale({
|
|
472
|
+
supportedLocales: ['en', 'ko', 'ja'],
|
|
473
|
+
defaultLocale: 'en',
|
|
474
|
+
sources: ['cookie', 'url', 'navigator'],
|
|
475
|
+
cookieName: 'NEXT_LOCALE', // default
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
// Server-side detection from Accept-Language header
|
|
479
|
+
const ssrLocale = detectLocale({
|
|
480
|
+
supportedLocales: ['en', 'ko', 'ja'],
|
|
481
|
+
defaultLocale: 'en',
|
|
482
|
+
sources: ['header'],
|
|
483
|
+
headerValue: 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
484
|
+
})
|
|
485
|
+
// → "ko"
|
|
486
|
+
|
|
487
|
+
// BCP 47 parent matching (en-US matches en)
|
|
488
|
+
const matched = detectLocale({
|
|
489
|
+
supportedLocales: ['en', 'ko'],
|
|
490
|
+
defaultLocale: 'en',
|
|
491
|
+
sources: ['navigator'],
|
|
492
|
+
})
|
|
493
|
+
// Browser reports "en-US" → matches "en"
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Detection sources:**
|
|
497
|
+
|
|
498
|
+
| Source | Description |
|
|
499
|
+
|--------|-------------|
|
|
500
|
+
| `navigator` | Browser `navigator.languages` / `navigator.language` |
|
|
501
|
+
| `cookie` | Reads locale from `document.cookie` (configurable name) |
|
|
502
|
+
| `url` | First path segment (e.g., `/ko/about` matches `ko`) |
|
|
503
|
+
| `header` | Parses `Accept-Language` header value (for SSR) |
|
|
504
|
+
|
|
505
|
+
Sources are tried in order; the first match wins. If no source matches, `defaultLocale` is returned.
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Selectordinal
|
|
510
|
+
|
|
511
|
+
Ordinal plural formatting for ranking and ordering (e.g., 1st, 2nd, 3rd):
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
import { it, setLocale } from 'inline-i18n-multi'
|
|
515
|
+
|
|
516
|
+
setLocale('en')
|
|
517
|
+
|
|
518
|
+
// Ordinal suffixes
|
|
519
|
+
it({
|
|
520
|
+
en: '{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
|
|
521
|
+
}, { rank: 1 }) // → "1st"
|
|
522
|
+
|
|
523
|
+
it({
|
|
524
|
+
en: '{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
|
|
525
|
+
}, { rank: 2 }) // → "2nd"
|
|
526
|
+
|
|
527
|
+
it({
|
|
528
|
+
en: '{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
|
|
529
|
+
}, { rank: 3 }) // → "3rd"
|
|
530
|
+
|
|
531
|
+
it({
|
|
532
|
+
en: '{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
|
|
533
|
+
}, { rank: 4 }) // → "4th"
|
|
534
|
+
|
|
535
|
+
// Handles English irregulars correctly
|
|
536
|
+
it({
|
|
537
|
+
en: '{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
|
|
538
|
+
}, { rank: 11 }) // → "11th" (not "11st")
|
|
539
|
+
|
|
540
|
+
it({
|
|
541
|
+
en: '{rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}'
|
|
542
|
+
}, { rank: 21 }) // → "21st"
|
|
543
|
+
|
|
544
|
+
// Combined with text
|
|
545
|
+
it({
|
|
546
|
+
en: 'You finished {rank, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} place!'
|
|
547
|
+
}, { rank: 3 }) // → "You finished 3rd place!"
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Uses `Intl.PluralRules` with `{ type: 'ordinal' }` for locale-aware ordinal categories.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
281
554
|
## Configuration
|
|
282
555
|
|
|
283
556
|
Configure global settings for fallback behavior and warnings:
|
|
@@ -363,9 +636,33 @@ Available helpers:
|
|
|
363
636
|
|
|
364
637
|
| Function | Description |
|
|
365
638
|
|----------|-------------|
|
|
366
|
-
| `configure(options)` | Configure global settings (fallback, warnings, debug) |
|
|
639
|
+
| `configure(options)` | Configure global settings (fallback, warnings, debug, missingVarHandler) |
|
|
367
640
|
| `getConfig()` | Get current configuration |
|
|
368
641
|
| `resetConfig()` | Reset configuration to defaults |
|
|
642
|
+
| `loadAsync(locale, namespace?)` | Asynchronously load dictionary using configured loader |
|
|
643
|
+
| `isLoaded(locale, namespace?)` | Check if dictionary has been loaded |
|
|
644
|
+
| `parseRichText(template, names)` | Parse rich text template into segments |
|
|
645
|
+
|
|
646
|
+
### Custom Formatters
|
|
647
|
+
|
|
648
|
+
| Function | Description |
|
|
649
|
+
|----------|-------------|
|
|
650
|
+
| `registerFormatter(name, formatter)` | Register a custom ICU-style formatter |
|
|
651
|
+
| `clearFormatters()` | Clear all custom formatters |
|
|
652
|
+
|
|
653
|
+
### Locale Detection
|
|
654
|
+
|
|
655
|
+
| Function | Description |
|
|
656
|
+
|----------|-------------|
|
|
657
|
+
| `detectLocale(options)` | Auto-detect user's locale from multiple sources |
|
|
658
|
+
|
|
659
|
+
### React Hooks & Components
|
|
660
|
+
|
|
661
|
+
| Export | Description |
|
|
662
|
+
|--------|-------------|
|
|
663
|
+
| `RichText` | Rich text translation component with embedded components |
|
|
664
|
+
| `useRichText(components)` | Hook returning function for rich text translations |
|
|
665
|
+
| `useLoadDictionaries(locale, ns?)` | Hook for lazy loading dictionaries with loading state |
|
|
369
666
|
|
|
370
667
|
### Types
|
|
371
668
|
|
|
@@ -382,6 +679,8 @@ interface Config {
|
|
|
382
679
|
warnOnMissing?: boolean
|
|
383
680
|
onMissingTranslation?: WarningHandler
|
|
384
681
|
debugMode?: boolean | DebugModeOptions
|
|
682
|
+
loader?: (locale: Locale, namespace: string) => Promise<Record<string, unknown>>
|
|
683
|
+
missingVarHandler?: (varName: string, locale: string) => string
|
|
385
684
|
}
|
|
386
685
|
|
|
387
686
|
interface DebugModeOptions {
|
|
@@ -400,6 +699,29 @@ interface TranslationWarning {
|
|
|
400
699
|
}
|
|
401
700
|
|
|
402
701
|
type WarningHandler = (warning: TranslationWarning) => void
|
|
702
|
+
|
|
703
|
+
type CustomFormatter = (value: unknown, locale: string, style?: string) => string
|
|
704
|
+
|
|
705
|
+
type DetectSource = 'navigator' | 'cookie' | 'url' | 'header'
|
|
706
|
+
|
|
707
|
+
interface DetectLocaleOptions {
|
|
708
|
+
/** Locales your app supports */
|
|
709
|
+
supportedLocales: Locale[]
|
|
710
|
+
/** Fallback when no source matches */
|
|
711
|
+
defaultLocale: Locale
|
|
712
|
+
/** Detection sources in priority order (default: ['navigator']) */
|
|
713
|
+
sources?: DetectSource[]
|
|
714
|
+
/** Cookie name to read (default: 'NEXT_LOCALE') */
|
|
715
|
+
cookieName?: string
|
|
716
|
+
/** Accept-Language header value (for SSR) */
|
|
717
|
+
headerValue?: string
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
interface RichTextSegment {
|
|
721
|
+
type: 'text' | 'component'
|
|
722
|
+
content: string
|
|
723
|
+
componentName?: string
|
|
724
|
+
}
|
|
403
725
|
```
|
|
404
726
|
|
|
405
727
|
---
|
|
@@ -454,30 +776,6 @@ npm install inline-i18n-multi-next
|
|
|
454
776
|
|
|
455
777
|
---
|
|
456
778
|
|
|
457
|
-
## Build-Time Optimization
|
|
458
|
-
|
|
459
|
-
### Babel Plugin
|
|
460
|
-
|
|
461
|
-
Transform `it()` calls at build time for better performance. Extracts translations for static analysis and enables dead code elimination for unused locales.
|
|
462
|
-
|
|
463
|
-
```bash
|
|
464
|
-
npm install -D @inline-i18n-multi/babel-plugin
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
[View Babel plugin →](https://www.npmjs.com/package/@inline-i18n-multi/babel-plugin)
|
|
468
|
-
|
|
469
|
-
### SWC Plugin
|
|
470
|
-
|
|
471
|
-
SWC plugin for Next.js 13+ projects. Faster than Babel with the same optimization benefits. Configure in `next.config.js` under `experimental.swcPlugins`.
|
|
472
|
-
|
|
473
|
-
```bash
|
|
474
|
-
npm install -D @inline-i18n-multi/swc-plugin
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
[View SWC plugin →](https://www.npmjs.com/package/@inline-i18n-multi/swc-plugin)
|
|
478
|
-
|
|
479
|
-
---
|
|
480
|
-
|
|
481
779
|
## Developer Tools
|
|
482
780
|
|
|
483
781
|
### CLI
|
|
@@ -504,7 +802,6 @@ npm install -D @inline-i18n-multi/cli
|
|
|
504
802
|
**Please read the [full documentation on GitHub](https://github.com/exiivy98/inline-i18n-multi)** for:
|
|
505
803
|
- Complete API reference
|
|
506
804
|
- Framework integrations (React, Next.js)
|
|
507
|
-
- Build-time optimization
|
|
508
805
|
- CLI tools
|
|
509
806
|
- Best practices and examples
|
|
510
807
|
|
package/dist/index.d.mts
CHANGED
|
@@ -61,6 +61,10 @@ interface Config {
|
|
|
61
61
|
onMissingTranslation?: WarningHandler;
|
|
62
62
|
/** Enable debug mode with visual indicators (default: false) */
|
|
63
63
|
debugMode?: boolean | DebugModeOptions;
|
|
64
|
+
/** Async loader function for lazy loading dictionaries */
|
|
65
|
+
loader?: (locale: Locale, namespace: string) => Promise<Record<string, unknown>>;
|
|
66
|
+
/** Custom handler for missing interpolation variables (v0.6.0) */
|
|
67
|
+
missingVarHandler?: (varName: string, locale: string) => string;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
/**
|
|
@@ -81,10 +85,9 @@ declare function setLocale(locale: Locale): void;
|
|
|
81
85
|
declare function getLocale(): Locale;
|
|
82
86
|
|
|
83
87
|
/**
|
|
84
|
-
* Runtime lookup function for
|
|
85
|
-
* This is called by code that has been processed by @inline-i18n-multi/babel-plugin
|
|
86
|
-
* or @inline-i18n-multi/swc-plugin.
|
|
88
|
+
* Runtime lookup function for build-tool-transformed code.
|
|
87
89
|
*
|
|
90
|
+
* @deprecated Will be removed in v1.0.0
|
|
88
91
|
* @param _hash - Content hash (for caching/debugging, unused at runtime)
|
|
89
92
|
* @param translations - Translation map with locale keys
|
|
90
93
|
* @param vars - Variables for interpolation
|
|
@@ -158,6 +161,24 @@ declare function loadDictionary(locale: Locale, dict: Dictionary, namespace?: st
|
|
|
158
161
|
* @param namespace - Optional namespace to clear (clears all if not specified)
|
|
159
162
|
*/
|
|
160
163
|
declare function clearDictionaries(namespace?: string): void;
|
|
164
|
+
/**
|
|
165
|
+
* Asynchronously load a dictionary using the configured loader
|
|
166
|
+
* @param locale - Locale to load
|
|
167
|
+
* @param namespace - Optional namespace (defaults to 'default')
|
|
168
|
+
* @throws Error if no loader is configured
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* configure({ loader: (locale, ns) => import(`./locales/${locale}/${ns}.json`) })
|
|
172
|
+
* await loadAsync('ko', 'dashboard')
|
|
173
|
+
* t('dashboard:title')
|
|
174
|
+
*/
|
|
175
|
+
declare function loadAsync(locale: Locale, namespace?: string): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Check if a dictionary has been loaded for a locale/namespace
|
|
178
|
+
* @param locale - Locale to check
|
|
179
|
+
* @param namespace - Optional namespace (defaults to 'default')
|
|
180
|
+
*/
|
|
181
|
+
declare function isLoaded(locale: Locale, namespace?: string): boolean;
|
|
161
182
|
/**
|
|
162
183
|
* Translate using key-based lookup (i18n compatible)
|
|
163
184
|
* @param key - Dot-separated translation key, optionally prefixed with namespace
|
|
@@ -191,6 +212,7 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
|
|
|
191
212
|
*/
|
|
192
213
|
declare function getLoadedNamespaces(): string[];
|
|
193
214
|
|
|
215
|
+
type FullConfig = Required<Omit<Config, 'loader' | 'missingVarHandler'>> & Pick<Config, 'loader' | 'missingVarHandler'>;
|
|
194
216
|
/**
|
|
195
217
|
* Configure inline-i18n-multi settings
|
|
196
218
|
*
|
|
@@ -205,10 +227,80 @@ declare function configure(options: Partial<Config>): void;
|
|
|
205
227
|
/**
|
|
206
228
|
* Get current configuration
|
|
207
229
|
*/
|
|
208
|
-
declare function getConfig():
|
|
230
|
+
declare function getConfig(): FullConfig;
|
|
209
231
|
/**
|
|
210
232
|
* Reset configuration to defaults
|
|
211
233
|
*/
|
|
212
234
|
declare function resetConfig(): void;
|
|
213
235
|
|
|
214
|
-
|
|
236
|
+
type CustomFormatter = (value: unknown, locale: string, style?: string) => string;
|
|
237
|
+
/**
|
|
238
|
+
* Register a custom formatter
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* registerFormatter('phone', (value, locale, style?) => {
|
|
242
|
+
* const s = String(value)
|
|
243
|
+
* return `(${s.slice(0,3)}) ${s.slice(3,6)}-${s.slice(6)}`
|
|
244
|
+
* })
|
|
245
|
+
*/
|
|
246
|
+
declare function registerFormatter(name: string, formatter: CustomFormatter): void;
|
|
247
|
+
/**
|
|
248
|
+
* Clear all custom formatters
|
|
249
|
+
*/
|
|
250
|
+
declare function clearFormatters(): void;
|
|
251
|
+
|
|
252
|
+
type DetectSource = 'navigator' | 'cookie' | 'url' | 'header';
|
|
253
|
+
interface DetectLocaleOptions {
|
|
254
|
+
/** Locales your app supports */
|
|
255
|
+
supportedLocales: Locale[];
|
|
256
|
+
/** Fallback when no source matches */
|
|
257
|
+
defaultLocale: Locale;
|
|
258
|
+
/** Detection sources in priority order (default: ['navigator']) */
|
|
259
|
+
sources?: DetectSource[];
|
|
260
|
+
/** Cookie name to read (default: 'NEXT_LOCALE') */
|
|
261
|
+
cookieName?: string;
|
|
262
|
+
/** Accept-Language header value (for SSR) */
|
|
263
|
+
headerValue?: string;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Auto-detect the user's locale from multiple sources
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* const locale = detectLocale({
|
|
270
|
+
* supportedLocales: ['en', 'ko', 'ja'],
|
|
271
|
+
* defaultLocale: 'en',
|
|
272
|
+
* sources: ['cookie', 'navigator'],
|
|
273
|
+
* cookieName: 'NEXT_LOCALE',
|
|
274
|
+
* })
|
|
275
|
+
*/
|
|
276
|
+
declare function detectLocale(options: DetectLocaleOptions): Locale;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Rich Text segment types
|
|
280
|
+
*/
|
|
281
|
+
interface RichTextSegment {
|
|
282
|
+
type: 'text' | 'component';
|
|
283
|
+
content: string;
|
|
284
|
+
/** Component name (only for type === 'component') */
|
|
285
|
+
componentName?: string;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Parse a template string into rich text segments.
|
|
289
|
+
* Matches patterns like <name>content</name> for each component name.
|
|
290
|
+
*
|
|
291
|
+
* @param template - The template string with component tags
|
|
292
|
+
* @param componentNames - Array of valid component names to match
|
|
293
|
+
* @returns Array of segments (text or component)
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* parseRichText('Read <link>terms</link> and <bold>agree</bold>', ['link', 'bold'])
|
|
297
|
+
* // [
|
|
298
|
+
* // { type: 'text', content: 'Read ' },
|
|
299
|
+
* // { type: 'component', content: 'terms', componentName: 'link' },
|
|
300
|
+
* // { type: 'text', content: ' and ' },
|
|
301
|
+
* // { type: 'component', content: 'agree', componentName: 'bold' },
|
|
302
|
+
* // ]
|
|
303
|
+
*/
|
|
304
|
+
declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
|
|
305
|
+
|
|
306
|
+
export { type Config, type CustomFormatter, type DebugModeOptions, type DetectLocaleOptions, type DetectSource, type Dictionaries, type Dictionary, type Locale, type PluralRules, type RichTextSegment, type TranslationVars, type TranslationWarning, type Translations, type WarningHandler, __i18n_lookup, clearDictionaries, clearFormatters, configure, detectLocale, en_de, en_es, en_fr, en_ja, en_zh, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, hasTranslation, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, parseRichText, registerFormatter, resetConfig, setLocale, t, zh_es };
|
package/dist/index.d.ts
CHANGED
|
@@ -61,6 +61,10 @@ interface Config {
|
|
|
61
61
|
onMissingTranslation?: WarningHandler;
|
|
62
62
|
/** Enable debug mode with visual indicators (default: false) */
|
|
63
63
|
debugMode?: boolean | DebugModeOptions;
|
|
64
|
+
/** Async loader function for lazy loading dictionaries */
|
|
65
|
+
loader?: (locale: Locale, namespace: string) => Promise<Record<string, unknown>>;
|
|
66
|
+
/** Custom handler for missing interpolation variables (v0.6.0) */
|
|
67
|
+
missingVarHandler?: (varName: string, locale: string) => string;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
/**
|
|
@@ -81,10 +85,9 @@ declare function setLocale(locale: Locale): void;
|
|
|
81
85
|
declare function getLocale(): Locale;
|
|
82
86
|
|
|
83
87
|
/**
|
|
84
|
-
* Runtime lookup function for
|
|
85
|
-
* This is called by code that has been processed by @inline-i18n-multi/babel-plugin
|
|
86
|
-
* or @inline-i18n-multi/swc-plugin.
|
|
88
|
+
* Runtime lookup function for build-tool-transformed code.
|
|
87
89
|
*
|
|
90
|
+
* @deprecated Will be removed in v1.0.0
|
|
88
91
|
* @param _hash - Content hash (for caching/debugging, unused at runtime)
|
|
89
92
|
* @param translations - Translation map with locale keys
|
|
90
93
|
* @param vars - Variables for interpolation
|
|
@@ -158,6 +161,24 @@ declare function loadDictionary(locale: Locale, dict: Dictionary, namespace?: st
|
|
|
158
161
|
* @param namespace - Optional namespace to clear (clears all if not specified)
|
|
159
162
|
*/
|
|
160
163
|
declare function clearDictionaries(namespace?: string): void;
|
|
164
|
+
/**
|
|
165
|
+
* Asynchronously load a dictionary using the configured loader
|
|
166
|
+
* @param locale - Locale to load
|
|
167
|
+
* @param namespace - Optional namespace (defaults to 'default')
|
|
168
|
+
* @throws Error if no loader is configured
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* configure({ loader: (locale, ns) => import(`./locales/${locale}/${ns}.json`) })
|
|
172
|
+
* await loadAsync('ko', 'dashboard')
|
|
173
|
+
* t('dashboard:title')
|
|
174
|
+
*/
|
|
175
|
+
declare function loadAsync(locale: Locale, namespace?: string): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Check if a dictionary has been loaded for a locale/namespace
|
|
178
|
+
* @param locale - Locale to check
|
|
179
|
+
* @param namespace - Optional namespace (defaults to 'default')
|
|
180
|
+
*/
|
|
181
|
+
declare function isLoaded(locale: Locale, namespace?: string): boolean;
|
|
161
182
|
/**
|
|
162
183
|
* Translate using key-based lookup (i18n compatible)
|
|
163
184
|
* @param key - Dot-separated translation key, optionally prefixed with namespace
|
|
@@ -191,6 +212,7 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
|
|
|
191
212
|
*/
|
|
192
213
|
declare function getLoadedNamespaces(): string[];
|
|
193
214
|
|
|
215
|
+
type FullConfig = Required<Omit<Config, 'loader' | 'missingVarHandler'>> & Pick<Config, 'loader' | 'missingVarHandler'>;
|
|
194
216
|
/**
|
|
195
217
|
* Configure inline-i18n-multi settings
|
|
196
218
|
*
|
|
@@ -205,10 +227,80 @@ declare function configure(options: Partial<Config>): void;
|
|
|
205
227
|
/**
|
|
206
228
|
* Get current configuration
|
|
207
229
|
*/
|
|
208
|
-
declare function getConfig():
|
|
230
|
+
declare function getConfig(): FullConfig;
|
|
209
231
|
/**
|
|
210
232
|
* Reset configuration to defaults
|
|
211
233
|
*/
|
|
212
234
|
declare function resetConfig(): void;
|
|
213
235
|
|
|
214
|
-
|
|
236
|
+
type CustomFormatter = (value: unknown, locale: string, style?: string) => string;
|
|
237
|
+
/**
|
|
238
|
+
* Register a custom formatter
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* registerFormatter('phone', (value, locale, style?) => {
|
|
242
|
+
* const s = String(value)
|
|
243
|
+
* return `(${s.slice(0,3)}) ${s.slice(3,6)}-${s.slice(6)}`
|
|
244
|
+
* })
|
|
245
|
+
*/
|
|
246
|
+
declare function registerFormatter(name: string, formatter: CustomFormatter): void;
|
|
247
|
+
/**
|
|
248
|
+
* Clear all custom formatters
|
|
249
|
+
*/
|
|
250
|
+
declare function clearFormatters(): void;
|
|
251
|
+
|
|
252
|
+
type DetectSource = 'navigator' | 'cookie' | 'url' | 'header';
|
|
253
|
+
interface DetectLocaleOptions {
|
|
254
|
+
/** Locales your app supports */
|
|
255
|
+
supportedLocales: Locale[];
|
|
256
|
+
/** Fallback when no source matches */
|
|
257
|
+
defaultLocale: Locale;
|
|
258
|
+
/** Detection sources in priority order (default: ['navigator']) */
|
|
259
|
+
sources?: DetectSource[];
|
|
260
|
+
/** Cookie name to read (default: 'NEXT_LOCALE') */
|
|
261
|
+
cookieName?: string;
|
|
262
|
+
/** Accept-Language header value (for SSR) */
|
|
263
|
+
headerValue?: string;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Auto-detect the user's locale from multiple sources
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* const locale = detectLocale({
|
|
270
|
+
* supportedLocales: ['en', 'ko', 'ja'],
|
|
271
|
+
* defaultLocale: 'en',
|
|
272
|
+
* sources: ['cookie', 'navigator'],
|
|
273
|
+
* cookieName: 'NEXT_LOCALE',
|
|
274
|
+
* })
|
|
275
|
+
*/
|
|
276
|
+
declare function detectLocale(options: DetectLocaleOptions): Locale;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Rich Text segment types
|
|
280
|
+
*/
|
|
281
|
+
interface RichTextSegment {
|
|
282
|
+
type: 'text' | 'component';
|
|
283
|
+
content: string;
|
|
284
|
+
/** Component name (only for type === 'component') */
|
|
285
|
+
componentName?: string;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Parse a template string into rich text segments.
|
|
289
|
+
* Matches patterns like <name>content</name> for each component name.
|
|
290
|
+
*
|
|
291
|
+
* @param template - The template string with component tags
|
|
292
|
+
* @param componentNames - Array of valid component names to match
|
|
293
|
+
* @returns Array of segments (text or component)
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* parseRichText('Read <link>terms</link> and <bold>agree</bold>', ['link', 'bold'])
|
|
297
|
+
* // [
|
|
298
|
+
* // { type: 'text', content: 'Read ' },
|
|
299
|
+
* // { type: 'component', content: 'terms', componentName: 'link' },
|
|
300
|
+
* // { type: 'text', content: ' and ' },
|
|
301
|
+
* // { type: 'component', content: 'agree', componentName: 'bold' },
|
|
302
|
+
* // ]
|
|
303
|
+
*/
|
|
304
|
+
declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
|
|
305
|
+
|
|
306
|
+
export { type Config, type CustomFormatter, type DebugModeOptions, type DetectLocaleOptions, type DetectSource, type Dictionaries, type Dictionary, type Locale, type PluralRules, type RichTextSegment, type TranslationVars, type TranslationWarning, type Translations, type WarningHandler, __i18n_lookup, clearDictionaries, clearFormatters, configure, detectLocale, en_de, en_es, en_fr, en_ja, en_zh, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, hasTranslation, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, parseRichText, registerFormatter, resetConfig, setLocale, t, zh_es };
|