inline-i18n-multi 0.9.0 → 0.11.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
@@ -76,6 +76,12 @@ See "Hello" in your app? Just search for "Hello" in your codebase. **Done.**
76
76
  - **Context System** - Contextual translation disambiguation (`t('greeting', { _context: 'formal' })` with `key#context` dictionary keys)
77
77
  - **Translation Extraction** - Extract inline translations to JSON files (`npx inline-i18n extract`)
78
78
  - **CLI Watch Mode** - `--watch` flag for `validate` and `typegen` commands
79
+ - **Fallback Value** - Custom fallback text when translation is missing (`t('key', { _fallback: 'Default' })`)
80
+ - **Diff Command** - Compare translations between two locales (`npx inline-i18n diff en ko`)
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()`)
79
85
 
80
86
  ---
81
87
 
@@ -779,6 +785,119 @@ Provides instant feedback during development by re-running validation or type ge
779
785
 
780
786
  ---
781
787
 
788
+ ## Fallback Value
789
+
790
+ Provide custom fallback text when a translation key is missing, instead of returning the raw key:
791
+
792
+ ```typescript
793
+ import { t, loadDictionaries, setLocale } from 'inline-i18n-multi'
794
+
795
+ loadDictionaries({
796
+ en: { greeting: 'Hello' }
797
+ })
798
+
799
+ setLocale('en')
800
+
801
+ // Without _fallback: returns the raw key when missing
802
+ t('missing.key') // → "missing.key"
803
+
804
+ // With _fallback: returns custom fallback text
805
+ t('missing.key', { _fallback: 'Default text' }) // → "Default text"
806
+
807
+ // _fallback is stripped from interpolation output (not passed as a variable)
808
+ t('greeting', { _fallback: 'Fallback' }) // → "Hello" (uses real translation, ignores _fallback)
809
+
810
+ // Works with variables — _fallback is not treated as a variable
811
+ t('welcome', { name: 'John', _fallback: 'Welcome!' })
812
+ // If 'welcome' exists: uses translation with {name} interpolated
813
+ // If 'welcome' is missing: → "Welcome!"
814
+ ```
815
+
816
+ Useful for providing user-friendly defaults in UI components where raw keys would be confusing.
817
+
818
+ ---
819
+
820
+ ## Locale Display Names
821
+
822
+ Get human-readable display names for locale codes using `Intl.DisplayNames`:
823
+
824
+ ```typescript
825
+ import { getLocaleDisplayName, setLocale } from 'inline-i18n-multi'
826
+
827
+ setLocale('en')
828
+
829
+ // Get display name in a specific locale
830
+ getLocaleDisplayName('ko', 'en') // → "Korean"
831
+ getLocaleDisplayName('ja', 'en') // → "Japanese"
832
+ getLocaleDisplayName('zh', 'en') // → "Chinese"
833
+
834
+ // Display name in the target's own locale
835
+ getLocaleDisplayName('ko', 'ko') // → "한국어"
836
+ getLocaleDisplayName('en', 'ja') // → "英語"
837
+
838
+ // Omit displayLocale to use current locale
839
+ setLocale('ko')
840
+ getLocaleDisplayName('en') // → "영어"
841
+ ```
842
+
843
+ ---
844
+
845
+ ## Translation Key Listing
846
+
847
+ Get a list of all loaded translation keys:
848
+
849
+ ```typescript
850
+ import { getTranslationKeys, loadDictionaries } from 'inline-i18n-multi'
851
+
852
+ loadDictionaries({
853
+ en: { greeting: 'Hello', farewell: 'Goodbye' },
854
+ ko: { greeting: '안녕하세요' }
855
+ }, 'common')
856
+
857
+ // Get all keys for a specific locale and namespace
858
+ getTranslationKeys('en', 'common') // → ['greeting', 'farewell']
859
+ getTranslationKeys('ko', 'common') // → ['greeting']
860
+
861
+ // Omit namespace to get keys from all namespaces
862
+ getTranslationKeys('en') // → ['common:greeting', 'common:farewell']
863
+
864
+ // Omit all parameters to use current locale
865
+ getTranslationKeys() // → all keys for current locale
866
+ ```
867
+
868
+ ---
869
+
870
+ ## Missing Translation Tracker
871
+
872
+ Collect missing translation keys at runtime to identify what needs translating:
873
+
874
+ ```typescript
875
+ import { trackMissingKeys, getMissingKeys, clearMissingKeys, t, loadDictionaries } from 'inline-i18n-multi'
876
+
877
+ loadDictionaries({ en: { greeting: 'Hello' } })
878
+
879
+ // Enable missing key tracking
880
+ trackMissingKeys(true)
881
+
882
+ // Use keys that don't exist
883
+ t('missing.key')
884
+ t('another.missing')
885
+
886
+ // Get all missing keys
887
+ getMissingKeys() // → ['missing.key', 'another.missing']
888
+
889
+ // Clear the tracked list
890
+ clearMissingKeys()
891
+ getMissingKeys() // → []
892
+
893
+ // Disable tracking
894
+ trackMissingKeys(false)
895
+ ```
896
+
897
+ Useful for discovering untranslated content during development and testing.
898
+
899
+ ---
900
+
782
901
  ## Configuration
783
902
 
784
903
  Configure global settings for fallback behavior and warnings:
@@ -873,6 +992,11 @@ Available helpers:
873
992
  | `clearICUCache()` | Clear the ICU message AST cache |
874
993
  | `restoreLocale()` | Restore locale from configured persistent storage (cookie or localStorage) |
875
994
  | `createScope(namespace)` | Return a translation function scoped to the given namespace |
995
+ | `getLocaleDisplayName(locale, displayLocale?)` | Get human-readable display name for a locale using `Intl.DisplayNames` |
996
+ | `getTranslationKeys(locale?, namespace?)` | Get all loaded translation keys |
997
+ | `trackMissingKeys(enabled)` | Enable or disable missing translation key tracking |
998
+ | `getMissingKeys()` | Get all tracked missing translation keys |
999
+ | `clearMissingKeys()` | Clear the tracked missing keys list |
876
1000
 
877
1001
  ### Custom Formatters
878
1002
 
@@ -1022,7 +1146,7 @@ npm install inline-i18n-multi-next
1022
1146
 
1023
1147
  ### CLI
1024
1148
 
1025
- Command-line tools for translation management. Find translations with `inline-i18n find "text"`, validate consistency with `inline-i18n validate`, extract inline translations with `inline-i18n extract`, and generate coverage reports with `inline-i18n coverage`. Supports `--watch` mode for `validate` and `typegen`.
1149
+ Command-line tools for translation management. Find translations with `inline-i18n find "text"`, validate consistency with `inline-i18n validate`, extract inline translations with `inline-i18n extract`, and generate coverage reports with `inline-i18n coverage`. Compare locales with `inline-i18n diff en ko` and view statistics with `inline-i18n stats`. Supports `--watch` mode for `validate` and `typegen`.
1026
1150
 
1027
1151
  ```bash
1028
1152
  npm install -D @inline-i18n-multi/cli
package/dist/index.d.mts CHANGED
@@ -11,9 +11,11 @@ type Translations = Record<Locale, string>;
11
11
  * Supports string, number, Date values for ICU formatting
12
12
  * Supports string[] for list formatting
13
13
  * Use _context for contextual translation disambiguation (v0.9.0)
14
+ * Use _fallback for custom fallback text when key is missing (v0.10.0)
14
15
  */
15
16
  type TranslationVars = Record<string, string | number | Date | string[]> & {
16
17
  _context?: string;
18
+ _fallback?: string;
17
19
  };
18
20
  /**
19
21
  * Warning information for missing translations
@@ -231,6 +233,49 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
231
233
  * Get all loaded namespaces
232
234
  */
233
235
  declare function getLoadedNamespaces(): string[];
236
+ /**
237
+ * Get the display name of a locale using Intl.DisplayNames (v0.11.0)
238
+ * @param locale - Locale code to get display name for (e.g., 'ko', 'ja', 'en-US')
239
+ * @param displayLocale - Locale in which to display the name (defaults to current locale)
240
+ * @returns Display name string (e.g., "Korean", "日本語")
241
+ *
242
+ * @example
243
+ * getLocaleDisplayName('ko', 'en') // → "Korean"
244
+ * getLocaleDisplayName('ko', 'ko') // → "한국어"
245
+ * getLocaleDisplayName('en', 'ja') // → "英語"
246
+ */
247
+ declare function getLocaleDisplayName(locale: Locale, displayLocale?: Locale): string;
248
+ /**
249
+ * Get all translation keys for a locale (v0.11.0)
250
+ * @param locale - Locale to get keys for (defaults to current locale)
251
+ * @param namespace - Optional namespace (returns from all if not specified)
252
+ * @returns Array of translation keys (dot-notation paths)
253
+ *
254
+ * @example
255
+ * getTranslationKeys('en') // → ['greeting.hello', 'greeting.goodbye', 'welcome']
256
+ * getTranslationKeys('en', 'common') // → ['hello', 'goodbye']
257
+ */
258
+ declare function getTranslationKeys(locale?: Locale, namespace?: string): string[];
259
+ /**
260
+ * Enable or disable missing translation tracking (v0.11.0)
261
+ * When enabled, all missing translation keys encountered via t() are recorded.
262
+ * @param enabled - Whether to enable tracking
263
+ *
264
+ * @example
265
+ * trackMissingKeys(true)
266
+ * t('nonexistent.key')
267
+ * getMissingKeys() // → ['nonexistent.key']
268
+ */
269
+ declare function trackMissingKeys(enabled: boolean): void;
270
+ /**
271
+ * Get all missing translation keys encountered since tracking was enabled (v0.11.0)
272
+ * @returns Array of missing keys
273
+ */
274
+ declare function getMissingKeys(): string[];
275
+ /**
276
+ * Clear the list of tracked missing keys (v0.11.0)
277
+ */
278
+ declare function clearMissingKeys(): void;
234
279
 
235
280
  type FullConfig = Required<Omit<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>> & Pick<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>;
236
281
  /**
@@ -342,4 +387,4 @@ interface RichTextSegment {
342
387
  */
343
388
  declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
344
389
 
345
- 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 };
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 };
package/dist/index.d.ts CHANGED
@@ -11,9 +11,11 @@ type Translations = Record<Locale, string>;
11
11
  * Supports string, number, Date values for ICU formatting
12
12
  * Supports string[] for list formatting
13
13
  * Use _context for contextual translation disambiguation (v0.9.0)
14
+ * Use _fallback for custom fallback text when key is missing (v0.10.0)
14
15
  */
15
16
  type TranslationVars = Record<string, string | number | Date | string[]> & {
16
17
  _context?: string;
18
+ _fallback?: string;
17
19
  };
18
20
  /**
19
21
  * Warning information for missing translations
@@ -231,6 +233,49 @@ declare function getDictionary(locale: Locale, namespace?: string): Dictionary |
231
233
  * Get all loaded namespaces
232
234
  */
233
235
  declare function getLoadedNamespaces(): string[];
236
+ /**
237
+ * Get the display name of a locale using Intl.DisplayNames (v0.11.0)
238
+ * @param locale - Locale code to get display name for (e.g., 'ko', 'ja', 'en-US')
239
+ * @param displayLocale - Locale in which to display the name (defaults to current locale)
240
+ * @returns Display name string (e.g., "Korean", "日本語")
241
+ *
242
+ * @example
243
+ * getLocaleDisplayName('ko', 'en') // → "Korean"
244
+ * getLocaleDisplayName('ko', 'ko') // → "한국어"
245
+ * getLocaleDisplayName('en', 'ja') // → "英語"
246
+ */
247
+ declare function getLocaleDisplayName(locale: Locale, displayLocale?: Locale): string;
248
+ /**
249
+ * Get all translation keys for a locale (v0.11.0)
250
+ * @param locale - Locale to get keys for (defaults to current locale)
251
+ * @param namespace - Optional namespace (returns from all if not specified)
252
+ * @returns Array of translation keys (dot-notation paths)
253
+ *
254
+ * @example
255
+ * getTranslationKeys('en') // → ['greeting.hello', 'greeting.goodbye', 'welcome']
256
+ * getTranslationKeys('en', 'common') // → ['hello', 'goodbye']
257
+ */
258
+ declare function getTranslationKeys(locale?: Locale, namespace?: string): string[];
259
+ /**
260
+ * Enable or disable missing translation tracking (v0.11.0)
261
+ * When enabled, all missing translation keys encountered via t() are recorded.
262
+ * @param enabled - Whether to enable tracking
263
+ *
264
+ * @example
265
+ * trackMissingKeys(true)
266
+ * t('nonexistent.key')
267
+ * getMissingKeys() // → ['nonexistent.key']
268
+ */
269
+ declare function trackMissingKeys(enabled: boolean): void;
270
+ /**
271
+ * Get all missing translation keys encountered since tracking was enabled (v0.11.0)
272
+ * @returns Array of missing keys
273
+ */
274
+ declare function getMissingKeys(): string[];
275
+ /**
276
+ * Clear the list of tracked missing keys (v0.11.0)
277
+ */
278
+ declare function clearMissingKeys(): void;
234
279
 
235
280
  type FullConfig = Required<Omit<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>> & Pick<Config, 'loader' | 'missingVarHandler' | 'persistLocale'>;
236
281
  /**
@@ -342,4 +387,4 @@ interface RichTextSegment {
342
387
  */
343
388
  declare function parseRichText(template: string, componentNames: string[]): RichTextSegment[];
344
389
 
345
- 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 };
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 };
package/dist/index.js CHANGED
@@ -603,14 +603,14 @@ function hasICUPattern(template) {
603
603
 
604
604
  // src/interpolation.ts
605
605
  var VARIABLE_PATTERN = /\{(\w+)\}/g;
606
- function stripContext(vars) {
607
- if (!("_context" in vars)) return vars;
608
- const { _context: _, ...rest } = vars;
606
+ function stripSpecialVars(vars) {
607
+ if (!("_context" in vars) && !("_fallback" in vars)) return vars;
608
+ const { _context: _c, _fallback: _f, ...rest } = vars;
609
609
  return rest;
610
610
  }
611
611
  function interpolate(template, vars, locale) {
612
612
  const resolvedLocale = locale || "en";
613
- const cleanVars = vars ? stripContext(vars) : vars;
613
+ const cleanVars = vars ? stripSpecialVars(vars) : vars;
614
614
  if (hasICUPattern(template) || hasCustomFormatter(template) || hasPluralShorthand(template)) {
615
615
  if (!cleanVars) {
616
616
  const cfg = getConfig();
@@ -919,6 +919,7 @@ function t(key, vars, locale) {
919
919
  }
920
920
  }
921
921
  if (!template) {
922
+ recordMissingKey(key);
922
923
  emitWarning({
923
924
  type: "missing_translation",
924
925
  key,
@@ -931,6 +932,9 @@ function t(key, vars, locale) {
931
932
  requestedLocale: currentLocale2,
932
933
  key
933
934
  };
935
+ if (vars?._fallback !== void 0) {
936
+ return applyDebugFormat(vars._fallback, debugInfo2);
937
+ }
934
938
  return applyDebugFormat(key, debugInfo2);
935
939
  }
936
940
  const isFallback = usedLocale !== currentLocale2;
@@ -984,6 +988,61 @@ function getDictionary(locale, namespace) {
984
988
  function getLoadedNamespaces() {
985
989
  return Object.keys(namespacedDictionaries);
986
990
  }
991
+ function getLocaleDisplayName(locale, displayLocale) {
992
+ const dl = displayLocale ?? getLocale();
993
+ try {
994
+ const displayNames = new Intl.DisplayNames([dl], { type: "language" });
995
+ return displayNames.of(locale) ?? locale;
996
+ } catch {
997
+ return locale;
998
+ }
999
+ }
1000
+ function getTranslationKeys(locale, namespace) {
1001
+ const loc = locale ?? getLocale();
1002
+ const keys = [];
1003
+ function collectKeys(dict, prefix) {
1004
+ for (const [k, v] of Object.entries(dict)) {
1005
+ const fullKey = prefix ? `${prefix}.${k}` : k;
1006
+ if (typeof v === "string") {
1007
+ keys.push(fullKey);
1008
+ } else {
1009
+ collectKeys(v, fullKey);
1010
+ }
1011
+ }
1012
+ }
1013
+ if (namespace) {
1014
+ const dict = namespacedDictionaries[namespace]?.[loc];
1015
+ if (dict) collectKeys(dict, "");
1016
+ } else {
1017
+ for (const [ns, dicts] of Object.entries(namespacedDictionaries)) {
1018
+ const dict = dicts[loc];
1019
+ if (dict) {
1020
+ const prefix = ns === DEFAULT_NAMESPACE ? "" : "";
1021
+ collectKeys(dict, prefix);
1022
+ }
1023
+ }
1024
+ }
1025
+ return keys;
1026
+ }
1027
+ var missingKeys = /* @__PURE__ */ new Set();
1028
+ var trackMissing = false;
1029
+ function trackMissingKeys(enabled) {
1030
+ trackMissing = enabled;
1031
+ if (!enabled) {
1032
+ missingKeys = /* @__PURE__ */ new Set();
1033
+ }
1034
+ }
1035
+ function getMissingKeys() {
1036
+ return Array.from(missingKeys);
1037
+ }
1038
+ function clearMissingKeys() {
1039
+ missingKeys = /* @__PURE__ */ new Set();
1040
+ }
1041
+ function recordMissingKey(key) {
1042
+ if (trackMissing) {
1043
+ missingKeys.add(key);
1044
+ }
1045
+ }
987
1046
 
988
1047
  // src/detect.ts
989
1048
  function matchLocale(candidate, supportedLocales) {
@@ -1119,6 +1178,7 @@ exports.__i18n_lookup = __i18n_lookup;
1119
1178
  exports.clearDictionaries = clearDictionaries;
1120
1179
  exports.clearFormatters = clearFormatters;
1121
1180
  exports.clearICUCache = clearICUCache;
1181
+ exports.clearMissingKeys = clearMissingKeys;
1122
1182
  exports.configure = configure;
1123
1183
  exports.createScope = createScope;
1124
1184
  exports.detectLocale = detectLocale;
@@ -1132,6 +1192,9 @@ exports.getDictionary = getDictionary;
1132
1192
  exports.getLoadedLocales = getLoadedLocales;
1133
1193
  exports.getLoadedNamespaces = getLoadedNamespaces;
1134
1194
  exports.getLocale = getLocale;
1195
+ exports.getLocaleDisplayName = getLocaleDisplayName;
1196
+ exports.getMissingKeys = getMissingKeys;
1197
+ exports.getTranslationKeys = getTranslationKeys;
1135
1198
  exports.hasTranslation = hasTranslation;
1136
1199
  exports.isLoaded = isLoaded;
1137
1200
  exports.it = it;
@@ -1151,6 +1214,7 @@ exports.resetConfig = resetConfig;
1151
1214
  exports.restoreLocale = restoreLocale;
1152
1215
  exports.setLocale = setLocale;
1153
1216
  exports.t = t;
1217
+ exports.trackMissingKeys = trackMissingKeys;
1154
1218
  exports.zh_es = zh_es;
1155
1219
  //# sourceMappingURL=index.js.map
1156
1220
  //# sourceMappingURL=index.js.map