inline-i18n-multi 0.10.0 → 0.12.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
@@ -79,6 +79,10 @@ See "Hello" in your app? Just search for "Hello" in your codebase. **Done.**
79
79
  - **Fallback Value** - Custom fallback text when translation is missing (`t('key', { _fallback: 'Default' })`)
80
80
  - **Diff Command** - Compare translations between two locales (`npx inline-i18n diff en ko`)
81
81
  - **Stats Command** - Translation statistics dashboard (`npx inline-i18n stats`)
82
+ - **Locale Display Names** - Get human-readable locale names using `Intl.DisplayNames` (`getLocaleDisplayName('ko', 'en')` → `"Korean"`)
83
+ - **Translation Key Listing** - `getTranslationKeys(locale?, namespace?)` returns all loaded translation keys
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)
82
86
 
83
87
  ---
84
88
 
@@ -814,6 +818,104 @@ Useful for providing user-friendly defaults in UI components where raw keys woul
814
818
 
815
819
  ---
816
820
 
821
+ ## Locale Display Names
822
+
823
+ Get human-readable display names for locale codes using `Intl.DisplayNames`:
824
+
825
+ ```typescript
826
+ import { getLocaleDisplayName, setLocale } from 'inline-i18n-multi'
827
+
828
+ setLocale('en')
829
+
830
+ // Get display name in a specific locale
831
+ getLocaleDisplayName('ko', 'en') // → "Korean"
832
+ getLocaleDisplayName('ja', 'en') // → "Japanese"
833
+ getLocaleDisplayName('zh', 'en') // → "Chinese"
834
+
835
+ // Display name in the target's own locale
836
+ getLocaleDisplayName('ko', 'ko') // → "한국어"
837
+ getLocaleDisplayName('en', 'ja') // → "英語"
838
+
839
+ // Omit displayLocale to use current locale
840
+ setLocale('ko')
841
+ getLocaleDisplayName('en') // → "영어"
842
+ ```
843
+
844
+ ---
845
+
846
+ ## Translation Key Listing
847
+
848
+ Get a list of all loaded translation keys:
849
+
850
+ ```typescript
851
+ import { getTranslationKeys, loadDictionaries } from 'inline-i18n-multi'
852
+
853
+ loadDictionaries({
854
+ en: { greeting: 'Hello', farewell: 'Goodbye' },
855
+ ko: { greeting: '안녕하세요' }
856
+ }, 'common')
857
+
858
+ // Get all keys for a specific locale and namespace
859
+ getTranslationKeys('en', 'common') // → ['greeting', 'farewell']
860
+ getTranslationKeys('ko', 'common') // → ['greeting']
861
+
862
+ // Omit namespace to get keys from all namespaces
863
+ getTranslationKeys('en') // → ['common:greeting', 'common:farewell']
864
+
865
+ // Omit all parameters to use current locale
866
+ getTranslationKeys() // → all keys for current locale
867
+ ```
868
+
869
+ ---
870
+
871
+ ## Missing Translation Tracker
872
+
873
+ Collect missing translation keys at runtime to identify what needs translating:
874
+
875
+ ```typescript
876
+ import { trackMissingKeys, getMissingKeys, clearMissingKeys, t, loadDictionaries } from 'inline-i18n-multi'
877
+
878
+ loadDictionaries({ en: { greeting: 'Hello' } })
879
+
880
+ // Enable missing key tracking
881
+ trackMissingKeys(true)
882
+
883
+ // Use keys that don't exist
884
+ t('missing.key')
885
+ t('another.missing')
886
+
887
+ // Get all missing keys
888
+ getMissingKeys() // → ['missing.key', 'another.missing']
889
+
890
+ // Clear the tracked list
891
+ clearMissingKeys()
892
+ getMissingKeys() // → []
893
+
894
+ // Disable tracking
895
+ trackMissingKeys(false)
896
+ ```
897
+
898
+ Useful for discovering untranslated content during development and testing.
899
+
900
+ ---
901
+
902
+ ## Locale Change Event
903
+
904
+ Subscribe to locale changes with `onLocaleChange()` to run custom logic when the locale switches:
905
+
906
+ ```ts
907
+ import { setLocale, onLocaleChange } from 'inline-i18n-multi'
908
+
909
+ const unsubscribe = onLocaleChange((newLocale, previousLocale) => {
910
+ console.log(`Locale changed: ${previousLocale} → ${newLocale}`)
911
+ })
912
+
913
+ setLocale('ko') // logs: "Locale changed: en → ko"
914
+ unsubscribe() // stop listening
915
+ ```
916
+
917
+ ---
918
+
817
919
  ## Configuration
818
920
 
819
921
  Configure global settings for fallback behavior and warnings:
@@ -908,6 +1010,13 @@ Available helpers:
908
1010
  | `clearICUCache()` | Clear the ICU message AST cache |
909
1011
  | `restoreLocale()` | Restore locale from configured persistent storage (cookie or localStorage) |
910
1012
  | `createScope(namespace)` | Return a translation function scoped to the given namespace |
1013
+ | `getLocaleDisplayName(locale, displayLocale?)` | Get human-readable display name for a locale using `Intl.DisplayNames` |
1014
+ | `getTranslationKeys(locale?, namespace?)` | Get all loaded translation keys |
1015
+ | `trackMissingKeys(enabled)` | Enable or disable missing translation key tracking |
1016
+ | `getMissingKeys()` | Get all tracked missing translation keys |
1017
+ | `clearMissingKeys()` | Clear the tracked missing keys list |
1018
+ | `onLocaleChange(callback)` | Subscribe to locale changes, returns unsubscribe function |
1019
+ | `clearLocaleListeners()` | Remove all locale change listeners |
911
1020
 
912
1021
  ### Custom Formatters
913
1022
 
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
  /**
@@ -233,6 +241,49 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
233
241
  * Get all loaded namespaces
234
242
  */
235
243
  declare function getLoadedNamespaces(): string[];
244
+ /**
245
+ * Get the display name of a locale using Intl.DisplayNames (v0.11.0)
246
+ * @param locale - Locale code to get display name for (e.g., 'ko', 'ja', 'en-US')
247
+ * @param displayLocale - Locale in which to display the name (defaults to current locale)
248
+ * @returns Display name string (e.g., "Korean", "日本語")
249
+ *
250
+ * @example
251
+ * getLocaleDisplayName('ko', 'en') // → "Korean"
252
+ * getLocaleDisplayName('ko', 'ko') // → "한국어"
253
+ * getLocaleDisplayName('en', 'ja') // → "英語"
254
+ */
255
+ declare function getLocaleDisplayName(locale: Locale, displayLocale?: Locale): string;
256
+ /**
257
+ * Get all translation keys for a locale (v0.11.0)
258
+ * @param locale - Locale to get keys for (defaults to current locale)
259
+ * @param namespace - Optional namespace (returns from all if not specified)
260
+ * @returns Array of translation keys (dot-notation paths)
261
+ *
262
+ * @example
263
+ * getTranslationKeys('en') // → ['greeting.hello', 'greeting.goodbye', 'welcome']
264
+ * getTranslationKeys('en', 'common') // → ['hello', 'goodbye']
265
+ */
266
+ declare function getTranslationKeys(locale?: Locale, namespace?: string): string[];
267
+ /**
268
+ * Enable or disable missing translation tracking (v0.11.0)
269
+ * When enabled, all missing translation keys encountered via t() are recorded.
270
+ * @param enabled - Whether to enable tracking
271
+ *
272
+ * @example
273
+ * trackMissingKeys(true)
274
+ * t('nonexistent.key')
275
+ * getMissingKeys() // → ['nonexistent.key']
276
+ */
277
+ declare function trackMissingKeys(enabled: boolean): void;
278
+ /**
279
+ * Get all missing translation keys encountered since tracking was enabled (v0.11.0)
280
+ * @returns Array of missing keys
281
+ */
282
+ declare function getMissingKeys(): string[];
283
+ /**
284
+ * Clear the list of tracked missing keys (v0.11.0)
285
+ */
286
+ declare function clearMissingKeys(): void;
236
287
 
237
288
  type FullConfig = Required<Omit<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>> & Pick<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>;
238
289
  /**
@@ -344,4 +395,4 @@ interface RichTextSegment {
344
395
  */
345
396
  declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
346
397
 
347
- 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, configure, createScope, 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, restoreLocale, setLocale, t, zh_es };
398
+ 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, 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
  /**
@@ -233,6 +241,49 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
233
241
  * Get all loaded namespaces
234
242
  */
235
243
  declare function getLoadedNamespaces(): string[];
244
+ /**
245
+ * Get the display name of a locale using Intl.DisplayNames (v0.11.0)
246
+ * @param locale - Locale code to get display name for (e.g., 'ko', 'ja', 'en-US')
247
+ * @param displayLocale - Locale in which to display the name (defaults to current locale)
248
+ * @returns Display name string (e.g., "Korean", "日本語")
249
+ *
250
+ * @example
251
+ * getLocaleDisplayName('ko', 'en') // → "Korean"
252
+ * getLocaleDisplayName('ko', 'ko') // → "한국어"
253
+ * getLocaleDisplayName('en', 'ja') // → "英語"
254
+ */
255
+ declare function getLocaleDisplayName(locale: Locale, displayLocale?: Locale): string;
256
+ /**
257
+ * Get all translation keys for a locale (v0.11.0)
258
+ * @param locale - Locale to get keys for (defaults to current locale)
259
+ * @param namespace - Optional namespace (returns from all if not specified)
260
+ * @returns Array of translation keys (dot-notation paths)
261
+ *
262
+ * @example
263
+ * getTranslationKeys('en') // → ['greeting.hello', 'greeting.goodbye', 'welcome']
264
+ * getTranslationKeys('en', 'common') // → ['hello', 'goodbye']
265
+ */
266
+ declare function getTranslationKeys(locale?: Locale, namespace?: string): string[];
267
+ /**
268
+ * Enable or disable missing translation tracking (v0.11.0)
269
+ * When enabled, all missing translation keys encountered via t() are recorded.
270
+ * @param enabled - Whether to enable tracking
271
+ *
272
+ * @example
273
+ * trackMissingKeys(true)
274
+ * t('nonexistent.key')
275
+ * getMissingKeys() // → ['nonexistent.key']
276
+ */
277
+ declare function trackMissingKeys(enabled: boolean): void;
278
+ /**
279
+ * Get all missing translation keys encountered since tracking was enabled (v0.11.0)
280
+ * @returns Array of missing keys
281
+ */
282
+ declare function getMissingKeys(): string[];
283
+ /**
284
+ * Clear the list of tracked missing keys (v0.11.0)
285
+ */
286
+ declare function clearMissingKeys(): void;
236
287
 
237
288
  type FullConfig = Required<Omit<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>> & Pick<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>;
238
289
  /**
@@ -344,4 +395,4 @@ interface RichTextSegment {
344
395
  */
345
396
  declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
346
397
 
347
- 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, configure, createScope, 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, restoreLocale, setLocale, t, zh_es };
398
+ 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, 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;
@@ -919,6 +938,7 @@ function t(key, vars, locale) {
919
938
  }
920
939
  }
921
940
  if (!template) {
941
+ recordMissingKey(key);
922
942
  emitWarning({
923
943
  type: "missing_translation",
924
944
  key,
@@ -987,6 +1007,61 @@ function getDictionary(locale, namespace) {
987
1007
  function getLoadedNamespaces() {
988
1008
  return Object.keys(namespacedDictionaries);
989
1009
  }
1010
+ function getLocaleDisplayName(locale, displayLocale) {
1011
+ const dl = displayLocale ?? getLocale();
1012
+ try {
1013
+ const displayNames = new Intl.DisplayNames([dl], { type: "language" });
1014
+ return displayNames.of(locale) ?? locale;
1015
+ } catch {
1016
+ return locale;
1017
+ }
1018
+ }
1019
+ function getTranslationKeys(locale, namespace) {
1020
+ const loc = locale ?? getLocale();
1021
+ const keys = [];
1022
+ function collectKeys(dict, prefix) {
1023
+ for (const [k, v] of Object.entries(dict)) {
1024
+ const fullKey = prefix ? `${prefix}.${k}` : k;
1025
+ if (typeof v === "string") {
1026
+ keys.push(fullKey);
1027
+ } else {
1028
+ collectKeys(v, fullKey);
1029
+ }
1030
+ }
1031
+ }
1032
+ if (namespace) {
1033
+ const dict = namespacedDictionaries[namespace]?.[loc];
1034
+ if (dict) collectKeys(dict, "");
1035
+ } else {
1036
+ for (const [ns, dicts] of Object.entries(namespacedDictionaries)) {
1037
+ const dict = dicts[loc];
1038
+ if (dict) {
1039
+ const prefix = ns === DEFAULT_NAMESPACE ? "" : "";
1040
+ collectKeys(dict, prefix);
1041
+ }
1042
+ }
1043
+ }
1044
+ return keys;
1045
+ }
1046
+ var missingKeys = /* @__PURE__ */ new Set();
1047
+ var trackMissing = false;
1048
+ function trackMissingKeys(enabled) {
1049
+ trackMissing = enabled;
1050
+ if (!enabled) {
1051
+ missingKeys = /* @__PURE__ */ new Set();
1052
+ }
1053
+ }
1054
+ function getMissingKeys() {
1055
+ return Array.from(missingKeys);
1056
+ }
1057
+ function clearMissingKeys() {
1058
+ missingKeys = /* @__PURE__ */ new Set();
1059
+ }
1060
+ function recordMissingKey(key) {
1061
+ if (trackMissing) {
1062
+ missingKeys.add(key);
1063
+ }
1064
+ }
990
1065
 
991
1066
  // src/detect.ts
992
1067
  function matchLocale(candidate, supportedLocales) {
@@ -1122,6 +1197,8 @@ exports.__i18n_lookup = __i18n_lookup;
1122
1197
  exports.clearDictionaries = clearDictionaries;
1123
1198
  exports.clearFormatters = clearFormatters;
1124
1199
  exports.clearICUCache = clearICUCache;
1200
+ exports.clearLocaleListeners = clearLocaleListeners;
1201
+ exports.clearMissingKeys = clearMissingKeys;
1125
1202
  exports.configure = configure;
1126
1203
  exports.createScope = createScope;
1127
1204
  exports.detectLocale = detectLocale;
@@ -1135,6 +1212,9 @@ exports.getDictionary = getDictionary;
1135
1212
  exports.getLoadedLocales = getLoadedLocales;
1136
1213
  exports.getLoadedNamespaces = getLoadedNamespaces;
1137
1214
  exports.getLocale = getLocale;
1215
+ exports.getLocaleDisplayName = getLocaleDisplayName;
1216
+ exports.getMissingKeys = getMissingKeys;
1217
+ exports.getTranslationKeys = getTranslationKeys;
1138
1218
  exports.hasTranslation = hasTranslation;
1139
1219
  exports.isLoaded = isLoaded;
1140
1220
  exports.it = it;
@@ -1148,12 +1228,14 @@ exports.ja_zh = ja_zh;
1148
1228
  exports.loadAsync = loadAsync;
1149
1229
  exports.loadDictionaries = loadDictionaries;
1150
1230
  exports.loadDictionary = loadDictionary;
1231
+ exports.onLocaleChange = onLocaleChange;
1151
1232
  exports.parseRichText = parseRichText;
1152
1233
  exports.registerFormatter = registerFormatter;
1153
1234
  exports.resetConfig = resetConfig;
1154
1235
  exports.restoreLocale = restoreLocale;
1155
1236
  exports.setLocale = setLocale;
1156
1237
  exports.t = t;
1238
+ exports.trackMissingKeys = trackMissingKeys;
1157
1239
  exports.zh_es = zh_es;
1158
1240
  //# sourceMappingURL=index.js.map
1159
1241
  //# sourceMappingURL=index.js.map