inline-i18n-multi 0.11.0 → 0.13.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
@@ -82,6 +82,8 @@ See "Hello" in your app? Just search for "Hello" in your codebase. **Done.**
82
82
  - **Locale Display Names** - Get human-readable locale names using `Intl.DisplayNames` (`getLocaleDisplayName('ko', 'en')` → `"Korean"`)
83
83
  - **Translation Key Listing** - `getTranslationKeys(locale?, namespace?)` returns all loaded translation keys
84
84
  - **Missing Translation Tracker** - Runtime collection of missing translation keys (`trackMissingKeys(true/false)`, `getMissingKeys()`, `clearMissingKeys()`)
85
+ - **Locale Change Event** — Subscribe to locale changes with `onLocaleChange()` (v0.12.0)
86
+ - **Formatting Utilities** — Locale-aware `formatNumber`, `formatDate`, `formatList` powered by `Intl` APIs (v0.13.0)
85
87
 
86
88
  ---
87
89
 
@@ -898,6 +900,23 @@ Useful for discovering untranslated content during development and testing.
898
900
 
899
901
  ---
900
902
 
903
+ ## Locale Change Event
904
+
905
+ Subscribe to locale changes with `onLocaleChange()` to run custom logic when the locale switches:
906
+
907
+ ```ts
908
+ import { setLocale, onLocaleChange } from 'inline-i18n-multi'
909
+
910
+ const unsubscribe = onLocaleChange((newLocale, previousLocale) => {
911
+ console.log(`Locale changed: ${previousLocale} → ${newLocale}`)
912
+ })
913
+
914
+ setLocale('ko') // logs: "Locale changed: en → ko"
915
+ unsubscribe() // stop listening
916
+ ```
917
+
918
+ ---
919
+
901
920
  ## Configuration
902
921
 
903
922
  Configure global settings for fallback behavior and warnings:
@@ -997,6 +1016,11 @@ Available helpers:
997
1016
  | `trackMissingKeys(enabled)` | Enable or disable missing translation key tracking |
998
1017
  | `getMissingKeys()` | Get all tracked missing translation keys |
999
1018
  | `clearMissingKeys()` | Clear the tracked missing keys list |
1019
+ | `onLocaleChange(callback)` | Subscribe to locale changes, returns unsubscribe function |
1020
+ | `clearLocaleListeners()` | Remove all locale change listeners |
1021
+ | `formatNumber(value, options?, locale?)` | Format numbers (currency, percent, etc.) |
1022
+ | `formatDate(value, options?, locale?)` | Format dates and times |
1023
+ | `formatList(values, options?, locale?)` | Format lists (conjunction, disjunction) |
1000
1024
 
1001
1025
  ### Custom Formatters
1002
1026
 
package/dist/index.d.mts CHANGED
@@ -97,6 +97,14 @@ declare function it(ko: string, en: string, vars?: TranslationVars): string;
97
97
  */
98
98
  declare function it(translations: Translations, vars?: TranslationVars): string;
99
99
 
100
+ type LocaleChangeCallback = (locale: Locale, previousLocale: Locale) => void;
101
+ /**
102
+ * Subscribe to locale changes.
103
+ * Returns an unsubscribe function.
104
+ */
105
+ declare function onLocaleChange(callback: LocaleChangeCallback): () => void;
106
+ /** Remove all locale change listeners (for testing) */
107
+ declare function clearLocaleListeners(): void;
100
108
  declare function setLocale(locale: Locale): void;
101
109
  declare function getLocale(): Locale;
102
110
  /**
@@ -359,6 +367,33 @@ declare function detectLocale(options: DetectLocaleOptions): Locale;
359
367
  */
360
368
  declare function createScope(namespace: string): (key: string, vars?: TranslationVars, locale?: Locale) => string;
361
369
 
370
+ /**
371
+ * Format a number for the given locale using Intl.NumberFormat.
372
+ *
373
+ * @example
374
+ * formatNumber(1234.5) // "1,234.5" (en)
375
+ * formatNumber(1234.5, { style: 'currency', currency: 'USD' }) // "$1,234.50"
376
+ * formatNumber(0.85, { style: 'percent' }, 'ko') // "85%"
377
+ */
378
+ declare function formatNumber(value: number, options?: Intl.NumberFormatOptions, locale?: Locale): string;
379
+ /**
380
+ * Format a date for the given locale using Intl.DateTimeFormat.
381
+ *
382
+ * @example
383
+ * formatDate(new Date()) // "3/30/2026" (en)
384
+ * formatDate(new Date(), { dateStyle: 'full' }, 'ja') // "2026年3月30日月曜日"
385
+ */
386
+ declare function formatDate(value: Date | number, options?: Intl.DateTimeFormatOptions, locale?: Locale): string;
387
+ /**
388
+ * Format a list for the given locale using Intl.ListFormat.
389
+ *
390
+ * @example
391
+ * formatList(['A', 'B', 'C']) // "A, B, and C" (en)
392
+ * formatList(['A', 'B', 'C'], { type: 'disjunction' }) // "A, B, or C"
393
+ * formatList(['りんご', 'みかん'], {}, 'ja') // "りんごとみかん"
394
+ */
395
+ declare function formatList(values: string[], options?: Intl.ListFormatOptions, locale?: Locale): string;
396
+
362
397
  /**
363
398
  * Rich Text segment types
364
399
  */
@@ -387,4 +422,4 @@ interface RichTextSegment {
387
422
  */
388
423
  declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
389
424
 
390
- 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, clearICUCache, clearMissingKeys, configure, createScope, detectLocale, en_de, en_es, en_fr, en_ja, en_zh, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, getLocaleDisplayName, getMissingKeys, getTranslationKeys, hasTranslation, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, parseRichText, registerFormatter, resetConfig, restoreLocale, setLocale, t, trackMissingKeys, zh_es };
425
+ 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, clearICUCache, clearLocaleListeners, clearMissingKeys, configure, createScope, detectLocale, en_de, en_es, en_fr, en_ja, en_zh, formatDate, formatList, formatNumber, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, getLocaleDisplayName, getMissingKeys, getTranslationKeys, hasTranslation, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, onLocaleChange, parseRichText, registerFormatter, resetConfig, restoreLocale, setLocale, t, trackMissingKeys, zh_es };
package/dist/index.d.ts CHANGED
@@ -97,6 +97,14 @@ declare function it(ko: string, en: string, vars?: TranslationVars): string;
97
97
  */
98
98
  declare function it(translations: Translations, vars?: TranslationVars): string;
99
99
 
100
+ type LocaleChangeCallback = (locale: Locale, previousLocale: Locale) => void;
101
+ /**
102
+ * Subscribe to locale changes.
103
+ * Returns an unsubscribe function.
104
+ */
105
+ declare function onLocaleChange(callback: LocaleChangeCallback): () => void;
106
+ /** Remove all locale change listeners (for testing) */
107
+ declare function clearLocaleListeners(): void;
100
108
  declare function setLocale(locale: Locale): void;
101
109
  declare function getLocale(): Locale;
102
110
  /**
@@ -359,6 +367,33 @@ declare function detectLocale(options: DetectLocaleOptions): Locale;
359
367
  */
360
368
  declare function createScope(namespace: string): (key: string, vars?: TranslationVars, locale?: Locale) => string;
361
369
 
370
+ /**
371
+ * Format a number for the given locale using Intl.NumberFormat.
372
+ *
373
+ * @example
374
+ * formatNumber(1234.5) // "1,234.5" (en)
375
+ * formatNumber(1234.5, { style: 'currency', currency: 'USD' }) // "$1,234.50"
376
+ * formatNumber(0.85, { style: 'percent' }, 'ko') // "85%"
377
+ */
378
+ declare function formatNumber(value: number, options?: Intl.NumberFormatOptions, locale?: Locale): string;
379
+ /**
380
+ * Format a date for the given locale using Intl.DateTimeFormat.
381
+ *
382
+ * @example
383
+ * formatDate(new Date()) // "3/30/2026" (en)
384
+ * formatDate(new Date(), { dateStyle: 'full' }, 'ja') // "2026年3月30日月曜日"
385
+ */
386
+ declare function formatDate(value: Date | number, options?: Intl.DateTimeFormatOptions, locale?: Locale): string;
387
+ /**
388
+ * Format a list for the given locale using Intl.ListFormat.
389
+ *
390
+ * @example
391
+ * formatList(['A', 'B', 'C']) // "A, B, and C" (en)
392
+ * formatList(['A', 'B', 'C'], { type: 'disjunction' }) // "A, B, or C"
393
+ * formatList(['りんご', 'みかん'], {}, 'ja') // "りんごとみかん"
394
+ */
395
+ declare function formatList(values: string[], options?: Intl.ListFormatOptions, locale?: Locale): string;
396
+
362
397
  /**
363
398
  * Rich Text segment types
364
399
  */
@@ -387,4 +422,4 @@ interface RichTextSegment {
387
422
  */
388
423
  declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
389
424
 
390
- 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, clearICUCache, clearMissingKeys, configure, createScope, detectLocale, en_de, en_es, en_fr, en_ja, en_zh, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, getLocaleDisplayName, getMissingKeys, getTranslationKeys, hasTranslation, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, parseRichText, registerFormatter, resetConfig, restoreLocale, setLocale, t, trackMissingKeys, zh_es };
425
+ 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, clearICUCache, clearLocaleListeners, clearMissingKeys, configure, createScope, detectLocale, en_de, en_es, en_fr, en_ja, en_zh, formatDate, formatList, formatNumber, getConfig, getDictionary, getLoadedLocales, getLoadedNamespaces, getLocale, getLocaleDisplayName, getMissingKeys, getTranslationKeys, hasTranslation, isLoaded, it, it_de, it_es, it_fr, it_ja, it_zh, ja_es, ja_zh, loadAsync, loadDictionaries, loadDictionary, onLocaleChange, parseRichText, registerFormatter, resetConfig, restoreLocale, setLocale, t, trackMissingKeys, zh_es };
package/dist/index.js CHANGED
@@ -105,6 +105,21 @@ function applyDebugFormat(output, debugInfo) {
105
105
 
106
106
  // src/context.ts
107
107
  var currentLocale = "en";
108
+ var listeners = /* @__PURE__ */ new Set();
109
+ function onLocaleChange(callback) {
110
+ listeners.add(callback);
111
+ return () => {
112
+ listeners.delete(callback);
113
+ };
114
+ }
115
+ function notifyListeners(locale, previousLocale) {
116
+ for (const cb of listeners) {
117
+ cb(locale, previousLocale);
118
+ }
119
+ }
120
+ function clearLocaleListeners() {
121
+ listeners.clear();
122
+ }
108
123
  function setCookie(name, value, days) {
109
124
  if (typeof document === "undefined") return;
110
125
  const expires = new Date(Date.now() + days * 864e5).toUTCString();
@@ -131,8 +146,12 @@ function persistLocaleToStorage(locale) {
131
146
  }
132
147
  }
133
148
  function setLocale(locale) {
149
+ const previousLocale = currentLocale;
134
150
  currentLocale = locale;
135
151
  persistLocaleToStorage(locale);
152
+ if (locale !== previousLocale) {
153
+ notifyListeners(locale, previousLocale);
154
+ }
136
155
  }
137
156
  function getLocale() {
138
157
  return currentLocale;
@@ -1138,6 +1157,17 @@ function createScope(namespace) {
1138
1157
  };
1139
1158
  }
1140
1159
 
1160
+ // src/format.ts
1161
+ function formatNumber(value, options, locale) {
1162
+ return new Intl.NumberFormat(locale ?? getLocale(), options).format(value);
1163
+ }
1164
+ function formatDate(value, options, locale) {
1165
+ return new Intl.DateTimeFormat(locale ?? getLocale(), options).format(value);
1166
+ }
1167
+ function formatList(values, options, locale) {
1168
+ return new Intl.ListFormat(locale ?? getLocale(), options).format(values);
1169
+ }
1170
+
1141
1171
  // src/richtext.ts
1142
1172
  function escapeRegExp(str) {
1143
1173
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1178,6 +1208,7 @@ exports.__i18n_lookup = __i18n_lookup;
1178
1208
  exports.clearDictionaries = clearDictionaries;
1179
1209
  exports.clearFormatters = clearFormatters;
1180
1210
  exports.clearICUCache = clearICUCache;
1211
+ exports.clearLocaleListeners = clearLocaleListeners;
1181
1212
  exports.clearMissingKeys = clearMissingKeys;
1182
1213
  exports.configure = configure;
1183
1214
  exports.createScope = createScope;
@@ -1187,6 +1218,9 @@ exports.en_es = en_es;
1187
1218
  exports.en_fr = en_fr;
1188
1219
  exports.en_ja = en_ja;
1189
1220
  exports.en_zh = en_zh;
1221
+ exports.formatDate = formatDate;
1222
+ exports.formatList = formatList;
1223
+ exports.formatNumber = formatNumber;
1190
1224
  exports.getConfig = getConfig;
1191
1225
  exports.getDictionary = getDictionary;
1192
1226
  exports.getLoadedLocales = getLoadedLocales;
@@ -1208,6 +1242,7 @@ exports.ja_zh = ja_zh;
1208
1242
  exports.loadAsync = loadAsync;
1209
1243
  exports.loadDictionaries = loadDictionaries;
1210
1244
  exports.loadDictionary = loadDictionary;
1245
+ exports.onLocaleChange = onLocaleChange;
1211
1246
  exports.parseRichText = parseRichText;
1212
1247
  exports.registerFormatter = registerFormatter;
1213
1248
  exports.resetConfig = resetConfig;