inline-i18n-multi 0.4.0 → 0.5.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
@@ -59,6 +59,10 @@ 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()`)
62
66
 
63
67
  ---
64
68
 
@@ -203,7 +207,7 @@ it({
203
207
  ```
204
208
 
205
209
  **Supported ICU styles:**
206
- - `number`: `decimal`, `percent`, `integer`, `currency`
210
+ - `number`: `decimal`, `percent`, `integer`, `currency`, `compact`, `compactLong`
207
211
  - `date`: `short`, `medium`, `long`, `full`
208
212
  - `time`: `short`, `medium`, `long`, `full`
209
213
 
@@ -234,6 +238,33 @@ it({ en: '{options, list, disjunction}' }, { options: ['A', 'B'] })
234
238
  // → "A or B"
235
239
  ```
236
240
 
241
+ ### Currency Formatting
242
+
243
+ ```typescript
244
+ it({
245
+ en: 'Total: {price, currency, USD}',
246
+ ko: '합계: {price, currency, KRW}'
247
+ }, { price: 42000 })
248
+ // en → "Total: $42,000.00" / ko → "합계: ₩42,000"
249
+
250
+ // Defaults to USD when currency code omitted
251
+ it({ en: '{price, currency}' }, { price: 100 })
252
+ // → "$100.00"
253
+ ```
254
+
255
+ ### Compact Number Formatting
256
+
257
+ ```typescript
258
+ it({
259
+ en: '{count, number, compact} views',
260
+ ko: '{count, number, compact} 조회'
261
+ }, { count: 1500000 })
262
+ // en → "1.5M views" / ko → "150만 조회"
263
+
264
+ it({ en: '{count, number, compactLong}' }, { count: 1500000 })
265
+ // → "1.5 million"
266
+ ```
267
+
237
268
  ---
238
269
 
239
270
  ## Namespace Support
@@ -278,6 +309,67 @@ t('missing.key') // → "[MISSING: fr] missing.key"
278
309
 
279
310
  ---
280
311
 
312
+ ## Rich Text Interpolation
313
+
314
+ Embed React components within translations:
315
+
316
+ ```tsx
317
+ import { RichText, useRichText } from 'inline-i18n-multi-react'
318
+
319
+ // Component syntax
320
+ <RichText
321
+ translations={{
322
+ en: 'Read <link>terms</link> and <bold>agree</bold>',
323
+ ko: '<link>약관</link>을 읽고 <bold>동의</bold>해주세요'
324
+ }}
325
+ components={{
326
+ link: (text) => <a href="/terms">{text}</a>,
327
+ bold: (text) => <strong>{text}</strong>
328
+ }}
329
+ />
330
+
331
+ // Hook syntax
332
+ const richT = useRichText({
333
+ link: (text) => <a href="/terms">{text}</a>,
334
+ bold: (text) => <strong>{text}</strong>
335
+ })
336
+ richT({ en: 'Click <link>here</link>', ko: '<link>여기</link> 클릭' })
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Lazy Loading
342
+
343
+ Load dictionaries asynchronously on demand:
344
+
345
+ ```typescript
346
+ import { configure, loadAsync, isLoaded, t } from 'inline-i18n-multi'
347
+
348
+ configure({
349
+ loader: (locale, namespace) => import(`./locales/${locale}/${namespace}.json`)
350
+ })
351
+
352
+ await loadAsync('ko', 'dashboard')
353
+ t('dashboard:title')
354
+
355
+ isLoaded('ko', 'dashboard') // → true
356
+ ```
357
+
358
+ ### React Hook
359
+
360
+ ```tsx
361
+ import { useLoadDictionaries } from 'inline-i18n-multi-react'
362
+
363
+ function Dashboard() {
364
+ const { isLoading, error } = useLoadDictionaries('ko', 'dashboard')
365
+ if (isLoading) return <Spinner />
366
+ if (error) return <Error message={error.message} />
367
+ return <Content />
368
+ }
369
+ ```
370
+
371
+ ---
372
+
281
373
  ## Configuration
282
374
 
283
375
  Configure global settings for fallback behavior and warnings:
@@ -366,6 +458,17 @@ Available helpers:
366
458
  | `configure(options)` | Configure global settings (fallback, warnings, debug) |
367
459
  | `getConfig()` | Get current configuration |
368
460
  | `resetConfig()` | Reset configuration to defaults |
461
+ | `loadAsync(locale, namespace?)` | Asynchronously load dictionary using configured loader |
462
+ | `isLoaded(locale, namespace?)` | Check if dictionary has been loaded |
463
+ | `parseRichText(template, names)` | Parse rich text template into segments |
464
+
465
+ ### React Hooks & Components
466
+
467
+ | Export | Description |
468
+ |--------|-------------|
469
+ | `RichText` | Rich text translation component with embedded components |
470
+ | `useRichText(components)` | Hook returning function for rich text translations |
471
+ | `useLoadDictionaries(locale, ns?)` | Hook for lazy loading dictionaries with loading state |
369
472
 
370
473
  ### Types
371
474
 
@@ -382,6 +485,7 @@ interface Config {
382
485
  warnOnMissing?: boolean
383
486
  onMissingTranslation?: WarningHandler
384
487
  debugMode?: boolean | DebugModeOptions
488
+ loader?: (locale: Locale, namespace: string) => Promise<Record<string, unknown>>
385
489
  }
386
490
 
387
491
  interface DebugModeOptions {
@@ -400,6 +504,12 @@ interface TranslationWarning {
400
504
  }
401
505
 
402
506
  type WarningHandler = (warning: TranslationWarning) => void
507
+
508
+ interface RichTextSegment {
509
+ type: 'text' | 'component'
510
+ content: string
511
+ componentName?: string
512
+ }
403
513
  ```
404
514
 
405
515
  ---
package/dist/index.d.mts CHANGED
@@ -61,6 +61,8 @@ 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>>;
64
66
  }
65
67
 
66
68
  /**
@@ -158,6 +160,24 @@ declare function loadDictionary(locale: Locale, dict: Dictionary, namespace?: st
158
160
  * @param namespace - Optional namespace to clear (clears all if not specified)
159
161
  */
160
162
  declare function clearDictionaries(namespace?: string): void;
163
+ /**
164
+ * Asynchronously load a dictionary using the configured loader
165
+ * @param locale - Locale to load
166
+ * @param namespace - Optional namespace (defaults to 'default')
167
+ * @throws Error if no loader is configured
168
+ *
169
+ * @example
170
+ * configure({ loader: (locale, ns) => import(`./locales/${locale}/${ns}.json`) })
171
+ * await loadAsync('ko', 'dashboard')
172
+ * t('dashboard:title')
173
+ */
174
+ declare function loadAsync(locale: Locale, namespace?: string): Promise<void>;
175
+ /**
176
+ * Check if a dictionary has been loaded for a locale/namespace
177
+ * @param locale - Locale to check
178
+ * @param namespace - Optional namespace (defaults to 'default')
179
+ */
180
+ declare function isLoaded(locale: Locale, namespace?: string): boolean;
161
181
  /**
162
182
  * Translate using key-based lookup (i18n compatible)
163
183
  * @param key - Dot-separated translation key, optionally prefixed with namespace
@@ -191,6 +211,7 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
191
211
  */
192
212
  declare function getLoadedNamespaces(): string[];
193
213
 
214
+ type FullConfig = Required<Omit<Config, 'loader'>> & Pick<Config, 'loader'>;
194
215
  /**
195
216
  * Configure inline-i18n-multi settings
196
217
  *
@@ -205,10 +226,38 @@ declare function configure(options: Partial<Config>): void;
205
226
  /**
206
227
  * Get current configuration
207
228
  */
208
- declare function getConfig(): Required<Config>;
229
+ declare function getConfig(): FullConfig;
209
230
  /**
210
231
  * Reset configuration to defaults
211
232
  */
212
233
  declare function resetConfig(): void;
213
234
 
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 };
235
+ /**
236
+ * Rich Text segment types
237
+ */
238
+ interface RichTextSegment {
239
+ type: 'text' | 'component';
240
+ content: string;
241
+ /** Component name (only for type === 'component') */
242
+ componentName?: string;
243
+ }
244
+ /**
245
+ * Parse a template string into rich text segments.
246
+ * Matches patterns like <name>content</name> for each component name.
247
+ *
248
+ * @param template - The template string with component tags
249
+ * @param componentNames - Array of valid component names to match
250
+ * @returns Array of segments (text or component)
251
+ *
252
+ * @example
253
+ * parseRichText('Read <link>terms</link> and <bold>agree</bold>', ['link', 'bold'])
254
+ * // [
255
+ * // { type: 'text', content: 'Read ' },
256
+ * // { type: 'component', content: 'terms', componentName: 'link' },
257
+ * // { type: 'text', content: ' and ' },
258
+ * // { type: 'component', content: 'agree', componentName: 'bold' },
259
+ * // ]
260
+ */
261
+ declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
262
+
263
+ export { type Config, type DebugModeOptions, type Dictionaries, type Dictionary, type Locale, type PluralRules, type RichTextSegment, 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, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, parseRichText, resetConfig, setLocale, t, zh_es };
package/dist/index.d.ts CHANGED
@@ -61,6 +61,8 @@ 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>>;
64
66
  }
65
67
 
66
68
  /**
@@ -158,6 +160,24 @@ declare function loadDictionary(locale: Locale, dict: Dictionary, namespace?: st
158
160
  * @param namespace - Optional namespace to clear (clears all if not specified)
159
161
  */
160
162
  declare function clearDictionaries(namespace?: string): void;
163
+ /**
164
+ * Asynchronously load a dictionary using the configured loader
165
+ * @param locale - Locale to load
166
+ * @param namespace - Optional namespace (defaults to 'default')
167
+ * @throws Error if no loader is configured
168
+ *
169
+ * @example
170
+ * configure({ loader: (locale, ns) => import(`./locales/${locale}/${ns}.json`) })
171
+ * await loadAsync('ko', 'dashboard')
172
+ * t('dashboard:title')
173
+ */
174
+ declare function loadAsync(locale: Locale, namespace?: string): Promise<void>;
175
+ /**
176
+ * Check if a dictionary has been loaded for a locale/namespace
177
+ * @param locale - Locale to check
178
+ * @param namespace - Optional namespace (defaults to 'default')
179
+ */
180
+ declare function isLoaded(locale: Locale, namespace?: string): boolean;
161
181
  /**
162
182
  * Translate using key-based lookup (i18n compatible)
163
183
  * @param key - Dot-separated translation key, optionally prefixed with namespace
@@ -191,6 +211,7 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
191
211
  */
192
212
  declare function getLoadedNamespaces(): string[];
193
213
 
214
+ type FullConfig = Required<Omit<Config, 'loader'>> & Pick<Config, 'loader'>;
194
215
  /**
195
216
  * Configure inline-i18n-multi settings
196
217
  *
@@ -205,10 +226,38 @@ declare function configure(options: Partial<Config>): void;
205
226
  /**
206
227
  * Get current configuration
207
228
  */
208
- declare function getConfig(): Required<Config>;
229
+ declare function getConfig(): FullConfig;
209
230
  /**
210
231
  * Reset configuration to defaults
211
232
  */
212
233
  declare function resetConfig(): void;
213
234
 
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 };
235
+ /**
236
+ * Rich Text segment types
237
+ */
238
+ interface RichTextSegment {
239
+ type: 'text' | 'component';
240
+ content: string;
241
+ /** Component name (only for type === 'component') */
242
+ componentName?: string;
243
+ }
244
+ /**
245
+ * Parse a template string into rich text segments.
246
+ * Matches patterns like <name>content</name> for each component name.
247
+ *
248
+ * @param template - The template string with component tags
249
+ * @param componentNames - Array of valid component names to match
250
+ * @returns Array of segments (text or component)
251
+ *
252
+ * @example
253
+ * parseRichText('Read <link>terms</link> and <bold>agree</bold>', ['link', 'bold'])
254
+ * // [
255
+ * // { type: 'text', content: 'Read ' },
256
+ * // { type: 'component', content: 'terms', componentName: 'link' },
257
+ * // { type: 'text', content: ' and ' },
258
+ * // { type: 'component', content: 'agree', componentName: 'bold' },
259
+ * // ]
260
+ */
261
+ declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
262
+
263
+ export { type Config, type DebugModeOptions, type Dictionaries, type Dictionary, type Locale, type PluralRules, type RichTextSegment, 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, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, parseRichText, resetConfig, setLocale, t, zh_es };
package/dist/index.js CHANGED
@@ -115,6 +115,67 @@ function formatTimeElement(el, vars, locale) {
115
115
  return `{${el.value}}`;
116
116
  }
117
117
  }
118
+ var CURRENCY_PATTERN = /\{(\w+),\s*currency(?:,\s*(\w+))?\}/g;
119
+ function preprocessCurrency(template) {
120
+ const replacements = /* @__PURE__ */ new Map();
121
+ let counter = 0;
122
+ const processed = template.replace(CURRENCY_PATTERN, (_, variable, currencyCode) => {
123
+ const placeholder = `__CURRENCY_${counter++}__`;
124
+ replacements.set(placeholder, { variable, currencyCode: currencyCode || "USD" });
125
+ return `{${placeholder}}`;
126
+ });
127
+ return { processed, replacements };
128
+ }
129
+ function formatCurrencyValue(variableName, currencyCode, vars, locale) {
130
+ const value = vars[variableName];
131
+ if (value === void 0) {
132
+ return `{${variableName}}`;
133
+ }
134
+ const num = typeof value === "number" ? value : Number(value);
135
+ if (isNaN(num)) {
136
+ return `{${variableName}}`;
137
+ }
138
+ try {
139
+ return new Intl.NumberFormat(locale, {
140
+ style: "currency",
141
+ currency: currencyCode
142
+ }).format(num);
143
+ } catch {
144
+ return String(num);
145
+ }
146
+ }
147
+ var COMPACT_NUMBER_PATTERN = /\{(\w+),\s*number,\s*(compact|compactLong)\}/g;
148
+ function preprocessCompactNumber(template) {
149
+ const replacements = /* @__PURE__ */ new Map();
150
+ let counter = 0;
151
+ const processed = template.replace(COMPACT_NUMBER_PATTERN, (_, variable, style) => {
152
+ const placeholder = `__COMPACT_${counter++}__`;
153
+ replacements.set(placeholder, {
154
+ variable,
155
+ display: style === "compactLong" ? "long" : "short"
156
+ });
157
+ return `{${placeholder}}`;
158
+ });
159
+ return { processed, replacements };
160
+ }
161
+ function formatCompactNumber(variableName, display, vars, locale) {
162
+ const value = vars[variableName];
163
+ if (value === void 0) {
164
+ return `{${variableName}}`;
165
+ }
166
+ const num = typeof value === "number" ? value : Number(value);
167
+ if (isNaN(num)) {
168
+ return `{${variableName}}`;
169
+ }
170
+ try {
171
+ return new Intl.NumberFormat(locale, {
172
+ notation: "compact",
173
+ compactDisplay: display
174
+ }).format(num);
175
+ } catch {
176
+ return String(num);
177
+ }
178
+ }
118
179
  function getRelativeTimeUnit(date, now = /* @__PURE__ */ new Date()) {
119
180
  const diffMs = date.getTime() - now.getTime();
120
181
  const diffSeconds = Math.round(diffMs / 1e3);
@@ -194,10 +255,20 @@ function formatListValue(variableName, type, style, vars, locale) {
194
255
  }
195
256
  }
196
257
  function interpolateICU(template, vars, locale) {
197
- const { processed: afterRelTime, replacements: relTimeReplacements } = preprocessRelativeTime(template);
258
+ const { processed: afterCurrency, replacements: currencyReplacements } = preprocessCurrency(template);
259
+ const { processed: afterCompact, replacements: compactReplacements } = preprocessCompactNumber(afterCurrency);
260
+ const { processed: afterRelTime, replacements: relTimeReplacements } = preprocessRelativeTime(afterCompact);
198
261
  const { processed: afterList, replacements: listReplacements } = preprocessList(afterRelTime);
199
262
  const ast = icuMessageformatParser.parse(afterList);
200
263
  let result = formatElements(ast, vars, locale, null);
264
+ for (const [placeholder, { variable, currencyCode }] of currencyReplacements) {
265
+ const formatted = formatCurrencyValue(variable, currencyCode, vars, locale);
266
+ result = result.replace(`{${placeholder}}`, formatted);
267
+ }
268
+ for (const [placeholder, { variable, display }] of compactReplacements) {
269
+ const formatted = formatCompactNumber(variable, display, vars, locale);
270
+ result = result.replace(`{${placeholder}}`, formatted);
271
+ }
201
272
  for (const [placeholder, { variable, style }] of relTimeReplacements) {
202
273
  const formatted = formatRelativeTimeValue(variable, style, vars, locale);
203
274
  result = result.replace(`{${placeholder}}`, formatted);
@@ -270,7 +341,7 @@ function formatSelect(el, vars, locale) {
270
341
  }
271
342
  return `{${el.value}}`;
272
343
  }
273
- var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal|number|date|time|relativeTime|list)\s*[,}]/;
344
+ var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal|number|date|time|relativeTime|list|currency)\s*[,}]/;
274
345
  function hasICUPattern(template) {
275
346
  return ICU_PATTERN.test(template);
276
347
  }
@@ -318,7 +389,8 @@ var defaultConfig = {
318
389
  fallbackChain: {},
319
390
  warnOnMissing: isDevMode(),
320
391
  onMissingTranslation: defaultWarningHandler,
321
- debugMode: false
392
+ debugMode: false,
393
+ loader: void 0
322
394
  };
323
395
  var config = { ...defaultConfig };
324
396
  function configure(options) {
@@ -521,6 +593,11 @@ var zh_es = createPair("zh", "es");
521
593
  var DEFAULT_NAMESPACE = "default";
522
594
  var NAMESPACE_SEPARATOR = ":";
523
595
  var namespacedDictionaries = {};
596
+ var loadingState = {};
597
+ var loadingPromises = /* @__PURE__ */ new Map();
598
+ function getLoadingKey(locale, namespace) {
599
+ return `${namespace}:${locale}`;
600
+ }
524
601
  function parseKey(fullKey) {
525
602
  const separatorIndex = fullKey.indexOf(NAMESPACE_SEPARATOR);
526
603
  if (separatorIndex > 0) {
@@ -557,10 +634,50 @@ function loadDictionary(locale, dict, namespace) {
557
634
  function clearDictionaries(namespace) {
558
635
  if (namespace) {
559
636
  delete namespacedDictionaries[namespace];
637
+ for (const key of Object.keys(loadingState)) {
638
+ if (key.startsWith(`${namespace}:`)) {
639
+ delete loadingState[key];
640
+ loadingPromises.delete(key);
641
+ }
642
+ }
560
643
  } else {
561
644
  namespacedDictionaries = {};
645
+ loadingState = {};
646
+ loadingPromises.clear();
562
647
  }
563
648
  }
649
+ async function loadAsync(locale, namespace) {
650
+ const ns = namespace || DEFAULT_NAMESPACE;
651
+ const cfg = getConfig();
652
+ if (!cfg.loader) {
653
+ throw new Error("No loader configured. Call configure({ loader: ... }) first.");
654
+ }
655
+ const key = getLoadingKey(locale, ns);
656
+ if (loadingState[key] === "loaded") return;
657
+ if (loadingPromises.has(key)) {
658
+ return loadingPromises.get(key);
659
+ }
660
+ const promise = (async () => {
661
+ loadingState[key] = "loading";
662
+ try {
663
+ const dict = await cfg.loader(locale, ns);
664
+ loadDictionary(locale, dict, ns);
665
+ loadingState[key] = "loaded";
666
+ } catch (error) {
667
+ loadingState[key] = "error";
668
+ throw error;
669
+ } finally {
670
+ loadingPromises.delete(key);
671
+ }
672
+ })();
673
+ loadingPromises.set(key, promise);
674
+ return promise;
675
+ }
676
+ function isLoaded(locale, namespace) {
677
+ const ns = namespace || DEFAULT_NAMESPACE;
678
+ const key = getLoadingKey(locale, ns);
679
+ return loadingState[key] === "loaded";
680
+ }
564
681
  function getNestedValue(dict, key) {
565
682
  const parts = key.split(".");
566
683
  let current = dict;
@@ -670,6 +787,42 @@ function getLoadedNamespaces() {
670
787
  return Object.keys(namespacedDictionaries);
671
788
  }
672
789
 
790
+ // src/richtext.ts
791
+ function escapeRegExp(str) {
792
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
793
+ }
794
+ function parseRichText(template, componentNames) {
795
+ if (componentNames.length === 0) {
796
+ return [{ type: "text", content: template }];
797
+ }
798
+ const segments = [];
799
+ const namesPattern = componentNames.map(escapeRegExp).join("|");
800
+ const regex = new RegExp(`<(${namesPattern})>(.*?)</\\1>`, "gs");
801
+ let lastIndex = 0;
802
+ let match;
803
+ while ((match = regex.exec(template)) !== null) {
804
+ if (match.index > lastIndex) {
805
+ segments.push({
806
+ type: "text",
807
+ content: template.slice(lastIndex, match.index)
808
+ });
809
+ }
810
+ segments.push({
811
+ type: "component",
812
+ content: match[2] ?? "",
813
+ componentName: match[1]
814
+ });
815
+ lastIndex = match.index + match[0].length;
816
+ }
817
+ if (lastIndex < template.length) {
818
+ segments.push({
819
+ type: "text",
820
+ content: template.slice(lastIndex)
821
+ });
822
+ }
823
+ return segments;
824
+ }
825
+
673
826
  exports.__i18n_lookup = __i18n_lookup;
674
827
  exports.clearDictionaries = clearDictionaries;
675
828
  exports.configure = configure;
@@ -684,6 +837,7 @@ exports.getLoadedLocales = getLoadedLocales;
684
837
  exports.getLoadedNamespaces = getLoadedNamespaces;
685
838
  exports.getLocale = getLocale;
686
839
  exports.hasTranslation = hasTranslation;
840
+ exports.isLoaded = isLoaded;
687
841
  exports.it = it;
688
842
  exports.it_de = it_de;
689
843
  exports.it_es = it_es;
@@ -692,8 +846,10 @@ exports.it_ja = it_ja;
692
846
  exports.it_zh = it_zh;
693
847
  exports.ja_es = ja_es;
694
848
  exports.ja_zh = ja_zh;
849
+ exports.loadAsync = loadAsync;
695
850
  exports.loadDictionaries = loadDictionaries;
696
851
  exports.loadDictionary = loadDictionary;
852
+ exports.parseRichText = parseRichText;
697
853
  exports.resetConfig = resetConfig;
698
854
  exports.setLocale = setLocale;
699
855
  exports.t = t;