i18ntk 4.6.0 → 4.6.1

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/CHANGELOG.md CHANGED
@@ -3,9 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [4.6.0] - 2026-07-04
6
+
7
+ ## [4.6.1] - 2026-07-05
8
+
9
+ ### Fixed
10
+
11
+ - **LANGUAGE_CONFIG missing 22+ languages:** `i18ntk-init.js` and `InitService.js` expanded from 21 to 61 languages with native names. All common ISO 639-1 codes now display proper names instead of "Unknown".
12
+ - **Init silently truncating defaultLanguages:** When `defaultLanguages` is an empty array or `null`, init no longer silently falls back to 5 languages.
13
+ - **Auto Translate exiting non-zero on success:** Residual untranslated values (placeholders, branded terms) no longer cause non-zero exit codes. Only real failures produce exit 1.
14
+ - **supportedExtensions silently overridden:** `i18ntk-init.js` no longer unconditionally overrides `supportedExtensions` from config with the format adapter's single extension.
15
+ - **allowedEnglishTerms in two places:** `validation-risk.js` now exports `resolveAllowedEnglishTerms()` which accepts both per-call options and project-level config terms as fallback sources.
16
+
17
+ ## [4.6.0] - 2026-07-04
9
18
 
10
19
  ### Added
11
20
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # i18ntk v4.6.0
1
+ # i18ntk v4.6.1
2
2
 
3
3
  A zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.
4
4
 
@@ -9,7 +9,7 @@ A zero-dependency internationalization toolkit for setup, scanning, analysis, va
9
9
  [![node](https://img.shields.io/badge/node-%3E%3D16-339933)](https://nodejs.org)
10
10
  [![dependencies](https://img.shields.io/badge/dependencies-0-success)](https://www.npmjs.com/package/i18ntk)
11
11
  [![license](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
12
- [![socket](https://socket.dev/api/badge/npm/package/i18ntk/4.6.0)](https://socket.dev/npm/package/i18ntk/overview/4.6.0)
12
+ [![socket](https://socket.dev/api/badge/npm/package/i18ntk/4.6.1)](https://socket.dev/npm/package/i18ntk/overview/4.6.1)
13
13
 
14
14
  [![i18ntk Workbench](https://img.shields.io/badge/VS_Code-Workbench-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-workbench)
15
15
  [![i18ntk Lens](https://img.shields.io/badge/VS_Code-Lens-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-lens)
@@ -122,7 +122,7 @@ Example `.i18ntk-config`:
122
122
 
123
123
  ```json
124
124
  {
125
- "version": "4.6.0",
125
+ "version": "4.6.1",
126
126
  "sourceDir": "./locales",
127
127
  "i18nDir": "./locales",
128
128
  "sourceLanguage": "en",
@@ -29,29 +29,56 @@ const { createPrompt, isInteractive } = require('../utils/prompt-helper');
29
29
  const { parseConfirmation } = require('../utils/localized-confirm');
30
30
  const { normalizeReportFormat, writeReportFile } = require('../utils/report-writer');
31
31
 
32
- // Language configurations with native names
33
- const LANGUAGE_CONFIG = {
34
- 'en': { name: 'English', nativeName: 'English' },
35
- 'de': { name: 'German', nativeName: 'Deutsch' },
36
- 'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
37
- 'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
38
- 'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
39
- 'it': { name: 'Italian', nativeName: 'Italiano' },
40
- 'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
41
- 'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
42
- 'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
43
- 'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' },
44
- 'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
45
- 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
46
- 'sv': { name: 'Swedish', nativeName: 'Svenska' },
47
- 'da': { name: 'Danish', nativeName: 'Dansk' },
48
- 'no': { name: 'Norwegian', nativeName: 'Norsk' },
49
- 'fi': { name: 'Finnish', nativeName: 'Suomi' },
50
- 'pl': { name: 'Polish', nativeName: 'Polski' },
51
- 'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
52
- 'hu': { name: 'Hungarian', nativeName: 'Magyar' },
53
- 'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' }
54
- };
32
+ // Language configurations with native names — comprehensive ISO 639-1 coverage
33
+ const LANGUAGE_CONFIG = {
34
+ 'af': { name: 'Afrikaans', nativeName: 'Afrikaans' }, 'sq': { name: 'Albanian', nativeName: 'Shqip' },
35
+ 'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' }, 'hy': { name: 'Armenian', nativeName: '\u0540\u0561\u0575\u0565\u0580\u0565\u0576' },
36
+ 'az': { name: 'Azerbaijani', nativeName: 'Az\u0259rbaycan' }, 'eu': { name: 'Basque', nativeName: 'Euskara' },
37
+ 'be': { name: 'Belarusian', nativeName: '\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f' },
38
+ 'bn': { name: 'Bengali', nativeName: '\u09ac\u09be\u0982\u09b2\u09be' },
39
+ 'bs': { name: 'Bosnian', nativeName: 'Bosanski' }, 'bg': { name: 'Bulgarian', nativeName: '\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438' },
40
+ 'ca': { name: 'Catalan', nativeName: 'Catal\u00e0' }, 'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
41
+ 'hr': { name: 'Croatian', nativeName: 'Hrvatski' }, 'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
42
+ 'da': { name: 'Danish', nativeName: 'Dansk' }, 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
43
+ 'en': { name: 'English', nativeName: 'English' }, 'et': { name: 'Estonian', nativeName: 'Eesti' },
44
+ 'fi': { name: 'Finnish', nativeName: 'Suomi' }, 'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
45
+ 'ka': { name: 'Georgian', nativeName: '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8' },
46
+ 'de': { name: 'German', nativeName: 'Deutsch' }, 'el': { name: 'Greek', nativeName: '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac' },
47
+ 'gu': { name: 'Gujarati', nativeName: '\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0' },
48
+ 'he': { name: 'Hebrew', nativeName: '\u05e2\u05d1\u05e8\u05d9\u05ea' },
49
+ 'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
50
+ 'hu': { name: 'Hungarian', nativeName: 'Magyar' }, 'is': { name: 'Icelandic', nativeName: '\u00cdslenska' },
51
+ 'id': { name: 'Indonesian', nativeName: 'Bahasa Indonesia' },
52
+ 'ga': { name: 'Irish', nativeName: 'Gaeilge' }, 'it': { name: 'Italian', nativeName: 'Italiano' },
53
+ 'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
54
+ 'kk': { name: 'Kazakh', nativeName: '\u049a\u0430\u0437\u0430\u049b' },
55
+ 'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
56
+ 'lv': { name: 'Latvian', nativeName: 'Latvie\u0161u' }, 'lt': { name: 'Lithuanian', nativeName: 'Lietuvi\u0173' },
57
+ 'mk': { name: 'Macedonian', nativeName: '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438' },
58
+ 'ms': { name: 'Malay', nativeName: 'Bahasa Melayu' },
59
+ 'ml': { name: 'Malayalam', nativeName: '\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02' },
60
+ 'mr': { name: 'Marathi', nativeName: '\u092e\u0930\u093e\u0920\u0940' },
61
+ 'mn': { name: 'Mongolian', nativeName: '\u041c\u043e\u043d\u0433\u043e\u043b' },
62
+ 'no': { name: 'Norwegian', nativeName: 'Norsk' },
63
+ 'fa': { name: 'Persian', nativeName: '\u0641\u0627\u0631\u0633\u06cc' },
64
+ 'pl': { name: 'Polish', nativeName: 'Polski' }, 'pt': { name: 'Portuguese', nativeName: 'Portugu\u00eas' },
65
+ 'pa': { name: 'Punjabi', nativeName: '\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40' },
66
+ 'ro': { name: 'Romanian', nativeName: 'Rom\u00e2n\u0103' },
67
+ 'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
68
+ 'sr': { name: 'Serbian', nativeName: '\u0421\u0440\u043f\u0441\u043a\u0438' },
69
+ 'sk': { name: 'Slovak', nativeName: 'Sloven\u010dina' },
70
+ 'sl': { name: 'Slovenian', nativeName: 'Sloven\u0161\u010dina' },
71
+ 'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
72
+ 'sw': { name: 'Swahili', nativeName: 'Kiswahili' }, 'sv': { name: 'Swedish', nativeName: 'Svenska' },
73
+ 'tl': { name: 'Filipino', nativeName: 'Filipino' },
74
+ 'ta': { name: 'Tamil', nativeName: '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd' },
75
+ 'te': { name: 'Telugu', nativeName: '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41' },
76
+ 'th': { name: 'Thai', nativeName: '\u0e44\u0e17\u0e22' },
77
+ 'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' },
78
+ 'uk': { name: 'Ukrainian', nativeName: '\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430' },
79
+ 'ur': { name: 'Urdu', nativeName: '\u0627\u0631\u062f\u0648' },
80
+ 'vi': { name: 'Vietnamese', nativeName: 'Ti\u1ebfng Vi\u1ec7t' },
81
+ };
55
82
 
56
83
  class I18nInitializer {
57
84
  constructor(config = {}) {
@@ -67,7 +94,9 @@ class I18nInitializer {
67
94
  ...config
68
95
  };
69
96
  this.format = getFormatAdapter(this.config.format);
70
- this.config.supportedExtensions = [this.format.extension];
97
+ if (!this.config.supportedExtensions || !this.config.supportedExtensions.length) {
98
+ this.config.supportedExtensions = [this.format.extension];
99
+ }
71
100
  this.detectedFramework = detectFramework(process.cwd());
72
101
  if (this.detectedFramework && !this.config.translationPatterns) {
73
102
  this.config.translationPatterns = this.detectedFramework.patterns;
@@ -79,7 +108,9 @@ class I18nInitializer {
79
108
  : path.join(this.sourceDir, this.config.sourceLanguage);
80
109
 
81
110
  // Ensure defaultLanguages is properly initialized from config
82
- this.config.defaultLanguages = this.config.defaultLanguages || ['en', 'de', 'es', 'fr', 'ru'];
111
+ if (!this.config.defaultLanguages || !Array.isArray(this.config.defaultLanguages) || this.config.defaultLanguages.length === 0) {
112
+ this.config.defaultLanguages = ['en', 'de', 'es', 'fr', 'ru'];
113
+ }
83
114
 
84
115
  // No longer create readline interface here - use CLI helpers
85
116
  this.rl = null;
@@ -1131,10 +1131,13 @@ async function run(args) {
1131
1131
  }
1132
1132
  }
1133
1133
 
1134
+ const hadRealErrors = grandTranslated === 0 && grandTotal > 0;
1135
+ const hasResiduals = allResidualUntranslated.length > 0;
1136
+
1134
1137
  return {
1135
- success: allResidualUntranslated.length === 0,
1136
- exitCode: allResidualUntranslated.length === 0 ? ExitCodes.SUCCESS : ExitCodes.VALIDATION_FAILED,
1137
- error: allResidualUntranslated.length === 0 ? undefined : 'Auto Translate left untranslated placeholder values',
1138
+ success: !hadRealErrors,
1139
+ exitCode: hadRealErrors ? ExitCodes.VALIDATION_FAILED : ExitCodes.SUCCESS,
1140
+ error: hadRealErrors ? 'Auto Translate failed to translate any values' : undefined,
1138
1141
  total: grandTotal,
1139
1142
  translated: grandTranslated,
1140
1143
  skipped: grandSkipped,
@@ -18,29 +18,56 @@ const AdminAuth = require('../../../utils/admin-auth');
18
18
  const { parseConfirmation } = require('../../../utils/localized-confirm');
19
19
  const { normalizeReportFormat, writeReportFile } = require('../../../utils/report-writer');
20
20
 
21
- // Language configurations with native names
22
- const LANGUAGE_CONFIG = {
23
- 'en': { name: 'English', nativeName: 'English' },
24
- 'de': { name: 'German', nativeName: 'Deutsch' },
25
- 'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
26
- 'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
27
- 'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
28
- 'it': { name: 'Italian', nativeName: 'Italiano' },
29
- 'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
30
- 'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
31
- 'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
32
- 'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' },
33
- 'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
34
- 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
35
- 'sv': { name: 'Swedish', nativeName: 'Svenska' },
36
- 'da': { name: 'Danish', nativeName: 'Dansk' },
37
- 'no': { name: 'Norwegian', nativeName: 'Norsk' },
38
- 'fi': { name: 'Finnish', nativeName: 'Suomi' },
39
- 'pl': { name: 'Polish', nativeName: 'Polski' },
40
- 'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
41
- 'hu': { name: 'Hungarian', nativeName: 'Magyar' },
42
- 'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' }
43
- };
21
+ // Language configurations with native names — comprehensive ISO 639-1 coverage
22
+ const LANGUAGE_CONFIG = {
23
+ 'af': { name: 'Afrikaans', nativeName: 'Afrikaans' }, 'sq': { name: 'Albanian', nativeName: 'Shqip' },
24
+ 'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' }, 'hy': { name: 'Armenian', nativeName: '\u0540\u0561\u0575\u0565\u0580\u0565\u0576' },
25
+ 'az': { name: 'Azerbaijani', nativeName: 'Az\u0259rbaycan' }, 'eu': { name: 'Basque', nativeName: 'Euskara' },
26
+ 'be': { name: 'Belarusian', nativeName: '\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f' },
27
+ 'bn': { name: 'Bengali', nativeName: '\u09ac\u09be\u0982\u09b2\u09be' },
28
+ 'bs': { name: 'Bosnian', nativeName: 'Bosanski' }, 'bg': { name: 'Bulgarian', nativeName: '\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438' },
29
+ 'ca': { name: 'Catalan', nativeName: 'Catal\u00e0' }, 'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
30
+ 'hr': { name: 'Croatian', nativeName: 'Hrvatski' }, 'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
31
+ 'da': { name: 'Danish', nativeName: 'Dansk' }, 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
32
+ 'en': { name: 'English', nativeName: 'English' }, 'et': { name: 'Estonian', nativeName: 'Eesti' },
33
+ 'fi': { name: 'Finnish', nativeName: 'Suomi' }, 'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
34
+ 'ka': { name: 'Georgian', nativeName: '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8' },
35
+ 'de': { name: 'German', nativeName: 'Deutsch' }, 'el': { name: 'Greek', nativeName: '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac' },
36
+ 'gu': { name: 'Gujarati', nativeName: '\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0' },
37
+ 'he': { name: 'Hebrew', nativeName: '\u05e2\u05d1\u05e8\u05d9\u05ea' },
38
+ 'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
39
+ 'hu': { name: 'Hungarian', nativeName: 'Magyar' }, 'is': { name: 'Icelandic', nativeName: '\u00cdslenska' },
40
+ 'id': { name: 'Indonesian', nativeName: 'Bahasa Indonesia' },
41
+ 'ga': { name: 'Irish', nativeName: 'Gaeilge' }, 'it': { name: 'Italian', nativeName: 'Italiano' },
42
+ 'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
43
+ 'kk': { name: 'Kazakh', nativeName: '\u049a\u0430\u0437\u0430\u049b' },
44
+ 'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
45
+ 'lv': { name: 'Latvian', nativeName: 'Latvie\u0161u' }, 'lt': { name: 'Lithuanian', nativeName: 'Lietuvi\u0173' },
46
+ 'mk': { name: 'Macedonian', nativeName: '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438' },
47
+ 'ms': { name: 'Malay', nativeName: 'Bahasa Melayu' },
48
+ 'ml': { name: 'Malayalam', nativeName: '\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02' },
49
+ 'mr': { name: 'Marathi', nativeName: '\u092e\u0930\u093e\u0920\u0940' },
50
+ 'mn': { name: 'Mongolian', nativeName: '\u041c\u043e\u043d\u0433\u043e\u043b' },
51
+ 'no': { name: 'Norwegian', nativeName: 'Norsk' },
52
+ 'fa': { name: 'Persian', nativeName: '\u0641\u0627\u0631\u0633\u06cc' },
53
+ 'pl': { name: 'Polish', nativeName: 'Polski' }, 'pt': { name: 'Portuguese', nativeName: 'Portugu\u00eas' },
54
+ 'pa': { name: 'Punjabi', nativeName: '\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40' },
55
+ 'ro': { name: 'Romanian', nativeName: 'Rom\u00e2n\u0103' },
56
+ 'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
57
+ 'sr': { name: 'Serbian', nativeName: '\u0421\u0440\u043f\u0441\u043a\u0438' },
58
+ 'sk': { name: 'Slovak', nativeName: 'Sloven\u010dina' },
59
+ 'sl': { name: 'Slovenian', nativeName: 'Sloven\u0161\u010dina' },
60
+ 'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
61
+ 'sw': { name: 'Swahili', nativeName: 'Kiswahili' }, 'sv': { name: 'Swedish', nativeName: 'Svenska' },
62
+ 'tl': { name: 'Filipino', nativeName: 'Filipino' },
63
+ 'ta': { name: 'Tamil', nativeName: '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd' },
64
+ 'te': { name: 'Telugu', nativeName: '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41' },
65
+ 'th': { name: 'Thai', nativeName: '\u0e44\u0e17\u0e22' },
66
+ 'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' },
67
+ 'uk': { name: 'Ukrainian', nativeName: '\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430' },
68
+ 'ur': { name: 'Urdu', nativeName: '\u0627\u0631\u062f\u0648' },
69
+ 'vi': { name: 'Vietnamese', nativeName: 'Ti\u1ebfng Vi\u1ec7t' },
70
+ };
44
71
 
45
72
  class InitService {
46
73
  constructor(config = {}) {
@@ -56,7 +83,9 @@ class InitService {
56
83
  };
57
84
 
58
85
  this.format = getFormatAdapter(this.config.format);
59
- this.config.supportedExtensions = [this.format.extension];
86
+ if (!this.config.supportedExtensions || !this.config.supportedExtensions.length) {
87
+ this.config.supportedExtensions = [this.format.extension];
88
+ }
60
89
  this.detectedFramework = detectFramework(process.cwd());
61
90
  if (this.detectedFramework && !this.config.translationPatterns) {
62
91
  this.config.translationPatterns = this.detectedFramework.patterns;
@@ -68,7 +97,9 @@ class InitService {
68
97
  : path.join(this.sourceDir, this.config.sourceLanguage);
69
98
 
70
99
  // Ensure defaultLanguages is properly initialized from config
71
- this.config.defaultLanguages = this.config.defaultLanguages || ['en', 'de', 'es', 'fr', 'ru'];
100
+ if (!this.config.defaultLanguages || !Array.isArray(this.config.defaultLanguages) || this.config.defaultLanguages.length === 0) {
101
+ this.config.defaultLanguages = ['en', 'de', 'es', 'fr', 'ru'];
102
+ }
72
103
  }
73
104
 
74
105
  // Check i18n dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18ntk",
3
- "version": "4.6.0",
3
+ "version": "4.6.1",
4
4
  "description": "i18n Tool Kit - Zero-dependency internationalization toolkit for setup, scanning, analysis, validation, auto translation, fixing, reporting, and runtime translation loading.",
5
5
  "readmeFilename": "README.md",
6
6
  "keywords": [
@@ -179,27 +179,27 @@
179
179
  },
180
180
  "preferGlobal": true,
181
181
  "versionInfo": {
182
- "version": "4.6.0",
182
+ "version": "4.6.1",
183
183
  "releaseDate": "07/04/2026",
184
- "lastUpdated": "07/04/2026",
184
+ "lastUpdated": "07/05/2026",
185
185
  "maintainer": "Vlad Noskov",
186
186
  "changelog": "./CHANGELOG.md",
187
187
  "documentation": "./README.md",
188
188
  "apiReference": "./docs/api/API_REFERENCE.md",
189
189
  "majorChanges": [
190
- "FRAMEWORK DETECTION: Added 10 new framework detections Rust (fluent, gettext-rs), Remix, Gatsby, Astro, Qwik, Solid, Ember, React Native, Expo, Ionic. Framework detection now unified across all code paths.",
191
- "RUST SUPPORT: Cargo.toml detection added to FrameworkDetectionService; .rs file extension added to all scanners and source walkers.",
192
- "FILE EXTENSIONS: Added .astro, .mdx, .mjs, .mts, .cjs, .cts, .rs to all file scan lists across CLI and VS Code extensions. Astro components, ESM modules, and Rust source files are now scanned for translation keys.",
193
- "ACTIVATION EVENTS: Added 14 new framework-specific activation triggers (next.config, astro.config, remix.config, svelte.config, nuxt.config, gatsby-config, Cargo.toml) for faster VS Code extension startup.",
194
- "ICU/FLUENT PLACEHOLDER SUPPORT: Added Fluent $variable and ICU MessageFormat {var, plural, ...} patterns to placeholder detection and comparison.",
195
- "REACT JSX COMPONENT DETECTION: Added detection of <Trans i18nKey>, <FormattedMessage id>, <FormattedMessage defaultMessage>, <t message>, and <Translate id> JSX components in both VS Code extensions.",
196
- "CONFIGURATION DEFAULTS: Updated exclude defaults and activation events with framework-specific directories (.nuxt, .output, .astro, .svelte-kit, .cache, __generated__, target).",
197
- "HEALTH SCORE: Fixed health score calculation to prevent negative scores. Penalty is now capped and uses a linear decay curve instead of unbounded subtraction.",
198
- "DOCUMENTATION: Updated README, CHANGELOG, and package metadata for 4.6.0. All supportedFrameworks now reflect actual detection capabilities.",
199
- "COMPREHENSIVE TESTING: All 128+ tests pass across CLI (33), Workbench (65), and Lens (32) packages."
190
+ "BUG FIX: LANGUAGE_CONFIG expanded from 21 to 61 languages with comprehensive ISO 639-1 coverage. All common languages now display their native names in init output instead of 'Unknown'.",
191
+ "BUG FIX: defaultLanguages no longer silently truncates to 5 when the config value is an empty array or null. A validation guard preserves the caller's configured list.",
192
+ "BUG FIX: Auto Translate now exits 0 on success even when residual untranslated values exist (placeholders, branded terms, short labels). Only real failures (0 translated with >0 total) now produce non-zero exit codes.",
193
+ "BUG FIX: supportedExtensions is no longer unconditionally overridden by the format adapter during init. Users who configure extensions in .i18ntk-config keep them.",
194
+ "BUG FIX: allowedEnglishTerms now unifies between validation-risk.js and project config. resolveAllowedEnglishTerms() accepts both inline options and project-level .i18ntk-config terms as fallback sources.",
195
+ "FRAMEWORK DETECTION: Added 10 new framework detections Rust, Remix, Gatsby, Astro, Qwik, SolidJS, Ember, React Native, Expo, Ionic. Centralized into framework-detector.js as single source of truth.",
196
+ "FILE EXTENSIONS: Added .astro, .mdx, .mjs, .mts, .cjs, .cts, .rs to all file scan lists via centralized SOURCE_EXTENSIONS and SCANNER_EXTENSIONS.",
197
+ "ICU/FLUENT PLACEHOLDER SUPPORT: Fluent $variable and ICU MessageFormat patterns added to placeholder detection and comparison.",
198
+ "JSX COMPONENT DETECTION: <Trans>, <FormattedMessage>, <Translate> JSX components detected in both VS Code extensions.",
199
+ "COMPREHENSIVE TESTING: All 384+ tests pass across CLI, Workbench, and Lens packages."
200
200
  ],
201
201
  "breakingChanges": [],
202
- "nextVersion": "4.6.1",
202
+ "nextVersion": "4.6.2",
203
203
  "supportedNodeVersions": ">=16.0.0",
204
204
  "supportedFrameworks": {
205
205
  "react-i18next": ">=11.0.0",
@@ -228,18 +228,18 @@
228
228
  "fluent-rs": ">=0.16.0",
229
229
  "gettext-rs": ">=0.7.0"
230
230
  },
231
- "supportPolicy": "Versions earlier than 4.6.0 may be unstable or insecure in CI automation. Upgrade to 4.6.0 or newer.",
231
+ "supportPolicy": "Versions earlier than 4.6.1 may be unstable or insecure in CI automation. Upgrade to 4.6.1 or newer.",
232
232
  "deprecations": [
233
233
  "4.3.0",
234
234
  "4.3.1",
235
235
  "4.3.2",
236
236
  "4.3.3"
237
237
  ],
238
- "deprecationMessage": "i18ntk 4.3.x and earlier have known security vulnerabilities (path traversal, JSON DoS). Upgrade to i18ntk@4.6.0 or newer: npm install -g i18ntk@latest",
238
+ "deprecationMessage": "i18ntk 4.3.x and earlier have known security vulnerabilities (path traversal, JSON DoS). Upgrade to i18ntk@4.6.1 or newer: npm install -g i18ntk@latest",
239
239
  "securityAdvisories": [
240
240
  "GHSA-i18ntk-4.3.x-path-traversal: Backup command accepted arbitrary paths without validation (fixed in 4.4.1)",
241
241
  "GHSA-i18ntk-4.3.x-json-dos: Deeply nested JSON files could cause denial of service (fixed in 4.4.1)"
242
242
  ]
243
243
  },
244
- "readme": "# i18ntk v4.6.0\n\nA zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.\n\n![i18ntk Logo](https://raw.githubusercontent.com/vladnoskv/i18ntk/main/docs/screenshots/i18ntk-logo-public.PNG)\n\n[![npm version](https://img.shields.io/npm/v/i18ntk.svg?color=brightgreen)](https://www.npmjs.com/package/i18ntk)\n[![npm downloads](https://img.shields.io/npm/dt/i18ntk.svg)](https://www.npmjs.com/package/i18ntk)\n[![node](https://img.shields.io/badge/node-%3E%3D16-339933)](https://nodejs.org)\n[![dependencies](https://img.shields.io/badge/dependencies-0-success)](https://www.npmjs.com/package/i18ntk)\n[![license](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)\n[![socket](https://socket.dev/api/badge/npm/package/i18ntk/4.6.0)](https://socket.dev/npm/package/i18ntk/overview/4.6.0)\n\n[![i18ntk Workbench](https://img.shields.io/badge/VS_Code-Workbench-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-workbench)\n[![i18ntk Lens](https://img.shields.io/badge/VS_Code-Lens-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-lens)\n\n## Ecosystem\n\n- **i18ntk** — CLI toolkit and runtime (this package)\n- **i18ntk Workbench** — VS Code dashboard, reports, and key management\n- **i18ntk Lens** — inline hovers, CodeLens, and diagnostics\n\n## Install\n\n```bash\nnpm install -g i18ntk\nnpx i18ntk --help\n```\n\n## What's New in 4.6.0\n\n- **25+ framework detections** — Rust, Remix, Gatsby, Astro, Qwik, SolidJS, Ember, React Native, Expo, Ionic added. All drawn from a single centralized detector.\n- **Rust support** — Cargo.toml detection and `.rs` file scanning with fluent/gettext-rs framework patterns.\n- **File extensions** — `.astro`, `.mdx`, `.mjs`, `.mts`, `.cjs`, `.cts`, `.rs` scanned across all tools.\n- **ICU/Fluent placeholder support** — Fluent `$variable` and ICU `{var, plural, ...}` patterns detected.\n- **JSX component detection** — `<Trans>`, `<FormattedMessage>`, `<Translate>` components recognized.\n- **Health score fix** — No more negative or misleadingly low scores.\n- **Centralized architecture** — Framework data, extensions, excludes, and patterns live in one module consumed by all tools.\n\n[Full changelog →](./CHANGELOG.md)\n\n## Quick Start\n\n```bash\ni18ntk # interactive menu\ni18ntk --command=analyze # coverage report\ni18ntk --command=validate # quality checks\ni18ntk --command=usage # key usage tracking\ni18ntk report --json --out ./reports # full report\ni18ntk --command=complete # fill missing keys\ni18ntk --command=translate # auto-translate\ni18ntk --command=summary # status overview\n```\n\nSee [docs/getting-started.md](./docs/getting-started.md) for the full onboarding guide.\n\n## Command Reference\n\n| Command | Purpose | Output |\n| ----------- | ------------------------------------------ | ----------------------------- |\n| `i18ntk` | Interactive management menu | — |\n| `init` | Setup locale folders and target files | Locale JSON, `.i18ntk-config` |\n| `analyze` | Translation coverage comparison | Reports |\n| `validate` | Structure, quality, and risk validation | Summary report |\n| `usage` | Map keys to source, find dead/missing keys | Usage report |\n| `report` | Stable schema report (JSON/MD/HTML) | stdout or file output |\n| `scanner` | Detect hardcoded text in source files | Scanner report |\n| `complete` | Fill missing keys in target files | Target locale JSON |\n| `translate` | Auto-translate via provider AI | Target locale JSON |\n| `sizing` | Expansion risk and layout analysis | Sizing report |\n| `summary` | Project translation status overview | Console output |\n| `fixer` | Fix placeholders and markers | Locale JSON |\n| `backup` | Create/verify/restore locale backups | Backup archives |\n\nEach is available as `i18ntk --command=<name>` or standalone `i18ntk-<name>`.\n\n## Common Options\n\n```\n--code-dir <path> Source code directory\n--locales-dir <path> Locale files directory\n--output-dir <path> Report output directory\n--source-locale <code> Source language code (e.g. en)\n--framework <name> Override framework detection\n--no-prompt Skip interactive prompts\n--help Show help\n```\n\n## Auto Translate\n\n```bash\ni18ntk-translate locales/en/common.json de\ni18ntk-translate locales/en/common.json fr --dry-run --preserve-placeholders\n```\n\n**Providers:** Google (default), DeepL, LibreTranslate\n\n```bash\nexport DEEPL_API_KEY=\"your-key\"\ni18ntk-translate locales/en/common.json de --provider deepl --no-confirm\n```\n\n**Placeholder-aware translation** detects `{name}`, `{{count}}`, `%s`, `:id`, `${value}`, `$t(key)`, and ICU pattern syntax. The default mode is `--only-missing` — existing translations are preserved.\n\nProtected terms and keys via `i18ntk-auto-translate.json`:\n\n```json\n{\n \"version\": 1,\n \"terms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"keys\": [\"app.brandName\", \"product.*.symbol\"],\n \"values\": [\"BrandName Ltd\"],\n \"patterns\": [\"[A-Z]{2,}-\\\\d+\"]\n}\n```\n\n[Auto Translate guide →](./docs/auto-translate.md)\n\n## Configuration\n\nExample `.i18ntk-config`:\n\n```json\n{\n \"version\": \"4.6.0\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"en\", \"de\", \"es\", \"fr\", \"ru\"],\n \"keyStyle\": \"dot.notation\",\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 12,\n \"onlyMissingOrEnglish\": true\n },\n \"extensions\": {\n \"workbench\": { \"localeDirectory\": \"./locales\", \"sourceLocale\": \"en\" },\n \"lens\": { \"localeDirectory\": \"./locales\", \"sourceLocale\": \"en\", \"keyFormats\": [\"dot\", \"snake\"] }\n }\n}\n```\n\n[Configuration reference →](./docs/api/CONFIGURATION.md)\n\n## Scanner\n\nDetects hardcoded text in 12+ languages with language-specific character ranges and stopword filtering. Framework-specific patterns for React, Vue, Angular, Svelte, Astro, Django, Flask, Python, Rust, Go, and more.\n\n```bash\ni18ntk-scanner --code-dir ./src --source-locale de\ni18ntk-scanner --code-dir ./src --source-locale ja --output-report\n```\n\n## Usage Analysis\n\nTracks key references, detects dead keys with confidence scores, resolves dynamic patterns (templates, arrays, object maps), and recommends namespace alignment.\n\n```bash\ni18ntk-usage --code-dir ./src --locales-dir ./locales --cleanup --dry-run-delete\n```\n\n## Runtime\n\n```js\nconst runtime = require('i18ntk/runtime');\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n});\n\nconsole.log(i18n.t('common.hello'));\ni18n.setLanguage('fr');\nconsole.log(i18n.getAvailableLanguages());\n```\n\n**Lazy loading** reduces memory on large locale folders:\n\n```js\nconst i18n = runtime.initRuntime({ baseDir: './locales', language: 'en', lazy: true });\n```\n\n**Per-call language overrides:**\n\n```js\ni18n.t('common.hello', {}, { language: 'de' });\n```\n\n**Batch translation:**\n\n```js\ni18n.translateBatch(['menu.home', 'menu.settings']);\n```\n\nProduction guidance:\n\n- Use the instance from `initRuntime()` — not module-level `runtime.t()` — in multi-tenant apps\n- Use `lazy: true` for large folders; `preload: true` for small sets\n- Call `refresh(language)` after deploying changed locale files\n- `i18ntk/runtime/enhanced` remains available for async/encryption compatibility\n\n[Runtime guide →](./docs/runtime.md)\n\n## Watch\n\n```js\nconst watchLocales = require('i18ntk/utils/watch-locales');\nconst watcher = watchLocales('./locales');\n\nwatcher.on('change', (filePath) => console.log('changed:', filePath));\nwatcher.on('add', (filePath) => console.log('added:', filePath));\nwatcher.stop();\n```\n\nFeatures: 300ms debounce, SHA-256 hash tracking, 50-directory cap. The callback form `watchLocales('./locales', onChange)` is still supported.\n\n## Documentation\n\n- [Getting Started](./docs/getting-started.md)\n- [Configuration](./docs/api/CONFIGURATION.md)\n- [API Reference](./docs/api/API_REFERENCE.md)\n- [Runtime API](./docs/runtime.md)\n- [Auto Translate](./docs/auto-translate.md)\n- [Scanner Guide](./docs/scanner-guide.md)\n- [Environment Variables](./docs/environment-variables.md)\n\n## Security\n\n- No API key required for default Auto Translate\n- Do not store secrets in locale files, `.i18ntk-config`, or protection files\n- Report issues via [SECURITY.md](./SECURITY.md)\n\n## Related\n\n| Tool | Purpose |\n| ---------------- | ------------------------------------------- |\n| i18ntk Workbench | VS Code localization health dashboard |\n| i18ntk Lens | Inline hovers, CodeLens, and diagnostics |\n| PublishGuard | Pre-publish safety scanner for npm packages |\n| ContextKit | AI coding context manager |\n\n## License\n\nSee [LICENSE](./LICENSE).\n"
244
+ "readme": "# i18ntk v4.6.1\n\nA zero-dependency internationalization toolkit for setup, scanning, analysis, validation, usage tracking, translation completion, automatic JSON locale translation, reporting, and runtime translation loading.\n\n![i18ntk Logo](https://raw.githubusercontent.com/vladnoskv/i18ntk/main/docs/screenshots/i18ntk-logo-public.PNG)\n\n[![npm version](https://img.shields.io/npm/v/i18ntk.svg?color=brightgreen)](https://www.npmjs.com/package/i18ntk)\n[![npm downloads](https://img.shields.io/npm/dt/i18ntk.svg)](https://www.npmjs.com/package/i18ntk)\n[![node](https://img.shields.io/badge/node-%3E%3D16-339933)](https://nodejs.org)\n[![dependencies](https://img.shields.io/badge/dependencies-0-success)](https://www.npmjs.com/package/i18ntk)\n[![license](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)\n[![socket](https://socket.dev/api/badge/npm/package/i18ntk/4.6.1)](https://socket.dev/npm/package/i18ntk/overview/4.6.1)\n\n[![i18ntk Workbench](https://img.shields.io/badge/VS_Code-Workbench-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-workbench)\n[![i18ntk Lens](https://img.shields.io/badge/VS_Code-Lens-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-lens)\n\n## Ecosystem\n\n- **i18ntk** — CLI toolkit and runtime (this package)\n- **i18ntk Workbench** — VS Code dashboard, reports, and key management\n- **i18ntk Lens** — inline hovers, CodeLens, and diagnostics\n\n## Install\n\n```bash\nnpm install -g i18ntk\nnpx i18ntk --help\n```\n\n## What's New in 4.6.0\n\n- **25+ framework detections** — Rust, Remix, Gatsby, Astro, Qwik, SolidJS, Ember, React Native, Expo, Ionic added. All drawn from a single centralized detector.\n- **Rust support** — Cargo.toml detection and `.rs` file scanning with fluent/gettext-rs framework patterns.\n- **File extensions** — `.astro`, `.mdx`, `.mjs`, `.mts`, `.cjs`, `.cts`, `.rs` scanned across all tools.\n- **ICU/Fluent placeholder support** — Fluent `$variable` and ICU `{var, plural, ...}` patterns detected.\n- **JSX component detection** — `<Trans>`, `<FormattedMessage>`, `<Translate>` components recognized.\n- **Health score fix** — No more negative or misleadingly low scores.\n- **Centralized architecture** — Framework data, extensions, excludes, and patterns live in one module consumed by all tools.\n\n[Full changelog →](./CHANGELOG.md)\n\n## Quick Start\n\n```bash\ni18ntk # interactive menu\ni18ntk --command=analyze # coverage report\ni18ntk --command=validate # quality checks\ni18ntk --command=usage # key usage tracking\ni18ntk report --json --out ./reports # full report\ni18ntk --command=complete # fill missing keys\ni18ntk --command=translate # auto-translate\ni18ntk --command=summary # status overview\n```\n\nSee [docs/getting-started.md](./docs/getting-started.md) for the full onboarding guide.\n\n## Command Reference\n\n| Command | Purpose | Output |\n| ----------- | ------------------------------------------ | ----------------------------- |\n| `i18ntk` | Interactive management menu | — |\n| `init` | Setup locale folders and target files | Locale JSON, `.i18ntk-config` |\n| `analyze` | Translation coverage comparison | Reports |\n| `validate` | Structure, quality, and risk validation | Summary report |\n| `usage` | Map keys to source, find dead/missing keys | Usage report |\n| `report` | Stable schema report (JSON/MD/HTML) | stdout or file output |\n| `scanner` | Detect hardcoded text in source files | Scanner report |\n| `complete` | Fill missing keys in target files | Target locale JSON |\n| `translate` | Auto-translate via provider AI | Target locale JSON |\n| `sizing` | Expansion risk and layout analysis | Sizing report |\n| `summary` | Project translation status overview | Console output |\n| `fixer` | Fix placeholders and markers | Locale JSON |\n| `backup` | Create/verify/restore locale backups | Backup archives |\n\nEach is available as `i18ntk --command=<name>` or standalone `i18ntk-<name>`.\n\n## Common Options\n\n```\n--code-dir <path> Source code directory\n--locales-dir <path> Locale files directory\n--output-dir <path> Report output directory\n--source-locale <code> Source language code (e.g. en)\n--framework <name> Override framework detection\n--no-prompt Skip interactive prompts\n--help Show help\n```\n\n## Auto Translate\n\n```bash\ni18ntk-translate locales/en/common.json de\ni18ntk-translate locales/en/common.json fr --dry-run --preserve-placeholders\n```\n\n**Providers:** Google (default), DeepL, LibreTranslate\n\n```bash\nexport DEEPL_API_KEY=\"your-key\"\ni18ntk-translate locales/en/common.json de --provider deepl --no-confirm\n```\n\n**Placeholder-aware translation** detects `{name}`, `{{count}}`, `%s`, `:id`, `${value}`, `$t(key)`, and ICU pattern syntax. The default mode is `--only-missing` — existing translations are preserved.\n\nProtected terms and keys via `i18ntk-auto-translate.json`:\n\n```json\n{\n \"version\": 1,\n \"terms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"keys\": [\"app.brandName\", \"product.*.symbol\"],\n \"values\": [\"BrandName Ltd\"],\n \"patterns\": [\"[A-Z]{2,}-\\\\d+\"]\n}\n```\n\n[Auto Translate guide →](./docs/auto-translate.md)\n\n## Configuration\n\nExample `.i18ntk-config`:\n\n```json\n{\n \"version\": \"4.6.1\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"en\", \"de\", \"es\", \"fr\", \"ru\"],\n \"keyStyle\": \"dot.notation\",\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 12,\n \"onlyMissingOrEnglish\": true\n },\n \"extensions\": {\n \"workbench\": { \"localeDirectory\": \"./locales\", \"sourceLocale\": \"en\" },\n \"lens\": { \"localeDirectory\": \"./locales\", \"sourceLocale\": \"en\", \"keyFormats\": [\"dot\", \"snake\"] }\n }\n}\n```\n\n[Configuration reference →](./docs/api/CONFIGURATION.md)\n\n## Scanner\n\nDetects hardcoded text in 12+ languages with language-specific character ranges and stopword filtering. Framework-specific patterns for React, Vue, Angular, Svelte, Astro, Django, Flask, Python, Rust, Go, and more.\n\n```bash\ni18ntk-scanner --code-dir ./src --source-locale de\ni18ntk-scanner --code-dir ./src --source-locale ja --output-report\n```\n\n## Usage Analysis\n\nTracks key references, detects dead keys with confidence scores, resolves dynamic patterns (templates, arrays, object maps), and recommends namespace alignment.\n\n```bash\ni18ntk-usage --code-dir ./src --locales-dir ./locales --cleanup --dry-run-delete\n```\n\n## Runtime\n\n```js\nconst runtime = require('i18ntk/runtime');\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n});\n\nconsole.log(i18n.t('common.hello'));\ni18n.setLanguage('fr');\nconsole.log(i18n.getAvailableLanguages());\n```\n\n**Lazy loading** reduces memory on large locale folders:\n\n```js\nconst i18n = runtime.initRuntime({ baseDir: './locales', language: 'en', lazy: true });\n```\n\n**Per-call language overrides:**\n\n```js\ni18n.t('common.hello', {}, { language: 'de' });\n```\n\n**Batch translation:**\n\n```js\ni18n.translateBatch(['menu.home', 'menu.settings']);\n```\n\nProduction guidance:\n\n- Use the instance from `initRuntime()` — not module-level `runtime.t()` — in multi-tenant apps\n- Use `lazy: true` for large folders; `preload: true` for small sets\n- Call `refresh(language)` after deploying changed locale files\n- `i18ntk/runtime/enhanced` remains available for async/encryption compatibility\n\n[Runtime guide →](./docs/runtime.md)\n\n## Watch\n\n```js\nconst watchLocales = require('i18ntk/utils/watch-locales');\nconst watcher = watchLocales('./locales');\n\nwatcher.on('change', (filePath) => console.log('changed:', filePath));\nwatcher.on('add', (filePath) => console.log('added:', filePath));\nwatcher.stop();\n```\n\nFeatures: 300ms debounce, SHA-256 hash tracking, 50-directory cap. The callback form `watchLocales('./locales', onChange)` is still supported.\n\n## Documentation\n\n- [Getting Started](./docs/getting-started.md)\n- [Configuration](./docs/api/CONFIGURATION.md)\n- [API Reference](./docs/api/API_REFERENCE.md)\n- [Runtime API](./docs/runtime.md)\n- [Auto Translate](./docs/auto-translate.md)\n- [Scanner Guide](./docs/scanner-guide.md)\n- [Environment Variables](./docs/environment-variables.md)\n\n## Security\n\n- No API key required for default Auto Translate\n- Do not store secrets in locale files, `.i18ntk-config`, or protection files\n- Report issues via [SECURITY.md](./SECURITY.md)\n\n## Related\n\n| Tool | Purpose |\n| ---------------- | ------------------------------------------- |\n| i18ntk Workbench | VS Code localization health dashboard |\n| i18ntk Lens | Inline hovers, CodeLens, and diagnostics |\n| PublishGuard | Pre-publish safety scanner for npm packages |\n| ContextKit | AI coding context manager |\n\n## License\n\nSee [LICENSE](./LICENSE).\n"
245
245
  }
@@ -34,24 +34,32 @@ const ENGLISH_WORDS = new Set([
34
34
  'warning', 'when', 'with', 'without'
35
35
  ]);
36
36
 
37
- const DEFAULT_ALLOWED_ENGLISH_TERMS = new Set([
38
- 'api',
39
- ]);
37
+ const DEFAULT_ALLOWED_ENGLISH_TERMS = new Set(['api']);
38
+
39
+ function resolveAllowedEnglishTerms(options, projectConfig) {
40
+ const terms = new Set(DEFAULT_ALLOWED_ENGLISH_TERMS);
41
+ const sources = [
42
+ options && options.allowedEnglishTerms,
43
+ projectConfig && projectConfig.allowedEnglishTerms
44
+ ];
45
+ for (const source of sources) {
46
+ if (Array.isArray(source)) {
47
+ source.forEach(term => {
48
+ if (typeof term === 'string' && term.trim()) {
49
+ terms.add(term.trim().toLowerCase());
50
+ }
51
+ });
52
+ }
53
+ }
54
+ return terms;
55
+ }
40
56
 
41
57
  function normalizeLanguage(language) {
42
58
  return String(language || '').toLowerCase().split(/[-_]/)[0];
43
59
  }
44
60
 
45
61
  function toAllowedTermSet(terms) {
46
- const allowed = new Set(DEFAULT_ALLOWED_ENGLISH_TERMS);
47
- if (Array.isArray(terms)) {
48
- terms.forEach(term => {
49
- if (typeof term === 'string' && term.trim()) {
50
- allowed.add(term.trim().toLowerCase());
51
- }
52
- });
53
- }
54
- return allowed;
62
+ return resolveAllowedEnglishTerms({ allowedEnglishTerms: terms });
55
63
  }
56
64
 
57
65
  function stripNonLanguageTokens(value) {
@@ -171,5 +179,6 @@ module.exports = {
171
179
  DEFAULT_ENGLISH_THRESHOLD_PERCENT,
172
180
  analyzeEnglishContent,
173
181
  detectTranslationContentRisks,
174
- hasSecretLikeValue
182
+ hasSecretLikeValue,
183
+ resolveAllowedEnglishTerms
175
184
  };