i18ntk 4.5.3 → 4.6.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.
@@ -15,8 +15,9 @@ const SecurityUtils = require('../../../utils/security');
15
15
  const AdminCLI = require('../../../utils/admin-cli');
16
16
  const AdminAuth = require('../../../utils/admin-auth');
17
17
  const watchLocales = require('../../../utils/watch-locales');
18
- const { getGlobalReadline, closeGlobalReadline } = require('../../../utils/cli');
19
- const { getUnifiedConfig, parseCommonArgs, displayHelp, validateSourceDir, displayPaths } = require('../../../utils/config-helper');
18
+ const { getGlobalReadline, closeGlobalReadline } = require('../../../utils/cli');
19
+ const { getUnifiedConfig, parseCommonArgs, displayHelp, validateSourceDir, displayPaths } = require('../../../utils/config-helper');
20
+ const { isInteractive } = require('../../../utils/prompt-helper');
20
21
  const I18nInitializer = require('../../i18ntk-init');
21
22
  const JsonOutput = require('../../../utils/json-output');
22
23
  const ExitCodes = require('../../../utils/exit-codes');
@@ -86,14 +87,21 @@ class ValidateCommand {
86
87
  );
87
88
 
88
89
  // Use the i18n directory for language files
89
- this.sourceDir = this.config.i18nDir || this.config.sourceDir;
90
- this.i18nDir = this.config.i18nDir || this.config.sourceDir;
90
+ this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
91
+ this.i18nDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
91
92
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
92
93
 
93
- try {
94
- validateSourceDir(this.sourceDir, 'i18ntk-validate');
95
- } catch (err) {
96
- console.log(t('init.requiredTitle'));
94
+ try {
95
+ if (!SecurityUtils.safeExistsSync(this.sourceDir, process.cwd())) {
96
+ const err = new Error(`Locale directory not found: ${this.sourceDir}`);
97
+ err.exitCode = 2;
98
+ throw err;
99
+ }
100
+ } catch (err) {
101
+ if (!isInteractive(args)) {
102
+ throw err;
103
+ }
104
+ console.log(t('init.requiredTitle'));
97
105
  console.log(t('init.requiredBody'));
98
106
  const answer = await this.prompt(t('init.promptRunNow'));
99
107
  if (answer.trim().toLowerCase() === 'y') {
@@ -732,11 +740,13 @@ class ValidateCommand {
732
740
  if (args.uiLanguage) {
733
741
  loadTranslations(args.uiLanguage, path.resolve(__dirname, '../../../ui-locales'));}
734
742
 
735
- if (args.sourceDir) {
736
- this.config.sourceDir = args.sourceDir;
737
- this.sourceDir = path.resolve(this.config.sourceDir);
738
- this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
739
- }
743
+ const localeDirArg = args.i18nDir || (!args.i18nDir && args.sourceDir ? args.sourceDir : null);
744
+ if (localeDirArg) {
745
+ this.config.i18nDir = localeDirArg;
746
+ this.sourceDir = path.resolve(localeDirArg);
747
+ this.i18nDir = this.sourceDir;
748
+ this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
749
+ }
740
750
  if (args.strictMode) {
741
751
  this.config.strictMode = true;
742
752
  }
@@ -1011,14 +1021,18 @@ class ValidateCommand {
1011
1021
  { message: 'Starting validation run' }
1012
1022
  );
1013
1023
 
1014
- const result = await this.validate();
1015
-
1016
- console.log(t('validate.validationProcessCompletedSuccessfully'));
1017
- SecurityUtils.logSecurityEvent(
1018
- t('validate.runCompleted'),
1019
- 'info',
1020
- { message: 'Validation run completed successfully' }
1021
- );
1024
+ const result = await this.validate();
1025
+
1026
+ if (result.success) {
1027
+ console.log(t('validate.validationProcessCompletedSuccessfully'));
1028
+ } else {
1029
+ console.log('❌ Validation failed.');
1030
+ }
1031
+ SecurityUtils.logSecurityEvent(
1032
+ result.success ? t('validate.runCompleted') : t('validate.runError'),
1033
+ result.success ? 'info' : 'error',
1034
+ { message: result.success ? 'Validation run completed successfully' : 'Validation run failed' }
1035
+ );
1022
1036
  return result;
1023
1037
  };
1024
1038
 
@@ -1047,8 +1061,8 @@ class ValidateCommand {
1047
1061
  async execute(options = {}) {
1048
1062
  try {
1049
1063
  await this.initialize();
1050
- await this.run(options);
1051
- return { success: true, command: 'validate' };
1064
+ const result = await this.run(options);
1065
+ return result && result.success === false ? result : { success: true, command: 'validate', result };
1052
1066
  } catch (error) {
1053
1067
  console.error(`Validate command failed: ${error.message}`);
1054
1068
  throw error;
@@ -261,18 +261,22 @@ class I18nManager {
261
261
  const sanitizedValue = value !== undefined ? value.trim() : true;
262
262
 
263
263
  switch (sanitizedKey) {
264
- case 'source-dir':
265
- parsed.sourceDir = sanitizedValue;
266
- break;
267
- case 'i18n-dir':
268
- parsed.i18nDir = sanitizedValue;
269
- break;
264
+ case 'source-dir':
265
+ case 'code-dir':
266
+ case 'source-code-dir':
267
+ parsed.sourceDir = sanitizedValue;
268
+ break;
269
+ case 'locales-dir':
270
+ case 'i18n-dir':
271
+ parsed.i18nDir = sanitizedValue;
272
+ break;
270
273
  case 'output-dir':
271
274
  parsed.outputDir = sanitizedValue;
272
275
  break;
273
- case 'source-language':
274
- parsed.sourceLanguage = sanitizedValue;
275
- break;
276
+ case 'source-language':
277
+ case 'source-locale':
278
+ parsed.sourceLanguage = sanitizedValue;
279
+ break;
276
280
  case 'ui-language':
277
281
  parsed.uiLanguage = sanitizedValue;
278
282
  break;
@@ -40,6 +40,7 @@ module.exports = class FrameworkDetectionService {
40
40
  const goModPath = path.join(process.cwd(), 'go.mod');
41
41
  const pomPath = path.join(process.cwd(), 'pom.xml');
42
42
  const composerPath = path.join(process.cwd(), 'composer.json');
43
+ const cargoTomlPath = path.join(process.cwd(), 'Cargo.toml');
43
44
 
44
45
  let detectedLanguage = 'generic';
45
46
  let detectedFramework = 'generic';
@@ -89,12 +90,21 @@ module.exports = class FrameworkDetectionService {
89
90
 
90
91
  // Only check other frameworks if i18ntk-runtime wasn't detected
91
92
  if (detectedFramework !== 'i18ntk-runtime') {
92
- if (deps.react || deps['react-dom']) detectedFramework = 'react';
93
- else if (deps.vue || deps['vue-router']) detectedFramework = 'vue';
93
+ if (deps.next || deps['next-intl'] || deps['next-i18next']) detectedFramework = 'nextjs';
94
+ else if (deps.react || deps['react-dom']) detectedFramework = 'react';
94
95
  else if (deps['@angular/core']) detectedFramework = 'angular';
95
- else if (deps.next) detectedFramework = 'nextjs';
96
- else if (deps.nuxt) detectedFramework = 'nuxt';
96
+ else if (deps.vue || deps['vue-router']) detectedFramework = 'vue';
97
+ else if (deps.nuxt || deps['@nuxtjs/i18n']) detectedFramework = 'nuxt';
97
98
  else if (deps.svelte) detectedFramework = 'svelte';
99
+ else if (deps.astro || deps['astro-i18next'] || deps['@astrojs/i18n']) detectedFramework = 'astro';
100
+ else if (deps['@builder.io/qwik'] || deps['qwik-speak'] || deps['qwik-i18n']) detectedFramework = 'qwik';
101
+ else if (deps.gatsby || deps['gatsby-plugin-react-i18next'] || deps['gatsby-plugin-intl']) detectedFramework = 'gatsby';
102
+ else if (deps['@remix-run/react'] || deps['remix-i18next'] || deps['i18next-remix']) detectedFramework = 'remix';
103
+ else if (deps['solid-js'] || deps['@solid-primitives/i18n']) detectedFramework = 'solid';
104
+ else if (deps['ember-source'] || deps['ember-intl']) detectedFramework = 'ember';
105
+ else if (deps['react-native'] || deps['react-native-localize']) detectedFramework = 'react-native';
106
+ else if (deps['expo-localization'] || deps.expo) detectedFramework = 'expo';
107
+ else if (deps['@ionic/angular'] || deps['ionic-react'] || deps['@ionic/vue']) detectedFramework = 'ionic';
98
108
  else detectedFramework = 'generic';
99
109
  }
100
110
  } catch (error) {
@@ -140,7 +150,17 @@ module.exports = class FrameworkDetectionService {
140
150
  } catch (error) {
141
151
  detectedFramework = 'generic';
142
152
  }
143
- }
153
+ } else if (SecurityUtils.safeExistsSync(cargoTomlPath)) {
154
+ detectedLanguage = 'rust';
155
+ try {
156
+ const cargoContent = SecurityUtils.safeReadFileSync(cargoTomlPath, path.dirname(cargoTomlPath), 'utf8');
157
+ if (cargoContent.includes('fluent') || cargoContent.includes('fluent-rs')) detectedFramework = 'fluent';
158
+ else if (cargoContent.includes('gettext')) detectedFramework = 'gettext-rs';
159
+ else detectedFramework = 'generic';
160
+ } catch (error) {
161
+ detectedFramework = 'generic';
162
+ }
163
+ }
144
164
 
145
165
  return { detectedLanguage, detectedFramework };
146
166
  }
@@ -155,13 +175,27 @@ module.exports = class FrameworkDetectionService {
155
175
  javascript: [
156
176
  { name: 'i18next', description: 'Feature-rich i18n framework for JavaScript' },
157
177
  { name: 'react-i18next', description: 'React integration for i18next' },
178
+ { name: 'next-intl', description: 'Next.js i18n integration' },
179
+ { name: 'remix-i18next', description: 'Remix i18n integration' },
180
+ { name: 'gatsby-plugin-react-i18next', description: 'Gatsby i18n integration' },
158
181
  { name: 'vue-i18n', description: 'Vue.js i18n plugin' },
159
- { name: 'Angular i18n', description: 'Built-in Angular i18n' }
182
+ { name: 'Angular i18n', description: 'Built-in Angular i18n' },
183
+ { name: 'svelte-i18n', description: 'Svelte i18n library' },
184
+ { name: 'astro-i18next', description: 'Astro i18n integration' },
185
+ { name: 'qwik-speak', description: 'Qwik i18n library' },
186
+ { name: 'solid-i18n', description: 'SolidJS i18n library' },
187
+ { name: 'ember-intl', description: 'Ember i18n library' },
188
+ { name: 'react-native-localize', description: 'React Native localization' },
189
+ { name: 'ionic-angular', description: 'Ionic i18n support' }
160
190
  ],
161
191
  typescript: [
162
192
  { name: 'i18next', description: 'TypeScript-first i18n framework' },
163
193
  { name: 'react-i18next', description: 'React + TypeScript integration' },
164
- { name: 'vue-i18n', description: 'Vue.js i18n with TypeScript support' }
194
+ { name: 'next-intl', description: 'Next.js + TypeScript i18n' },
195
+ { name: 'vue-i18n', description: 'Vue.js i18n with TypeScript support' },
196
+ { name: 'angular i18n', description: 'Angular i18n with TypeScript' },
197
+ { name: 'astro-i18next', description: 'Astro i18n with TypeScript' },
198
+ { name: 'qwik-speak', description: 'Qwik i18n with TypeScript' }
165
199
  ],
166
200
  python: [
167
201
  { name: 'Django i18n', description: 'Built-in Django internationalization' },
@@ -177,6 +211,11 @@ module.exports = class FrameworkDetectionService {
177
211
  { name: 'go-i18n', description: 'Go i18n library with pluralization' },
178
212
  { name: 'nicksnyder/go-i18n', description: 'Feature-rich Go i18n' }
179
213
  ],
214
+ rust: [
215
+ { name: 'fluent', description: 'Project Fluent localization for Rust' },
216
+ { name: 'fluent-rs', description: 'Rust implementation of Project Fluent' },
217
+ { name: 'gettext-rs', description: 'GNU gettext bindings for Rust' }
218
+ ],
180
219
  php: [
181
220
  { name: 'Laravel i18n', description: 'Built-in Laravel localization' },
182
221
  { name: 'Symfony Translation', description: 'Symfony translation component' },
@@ -335,13 +374,32 @@ module.exports = class FrameworkDetectionService {
335
374
 
336
375
  const i18nFrameworks = [
337
376
  'react-i18next',
377
+ 'next-intl',
378
+ 'next-i18next',
379
+ 'remix-i18next',
380
+ 'i18next-remix',
381
+ 'gatsby-plugin-react-i18next',
382
+ 'gatsby-plugin-intl',
338
383
  'vue-i18n',
339
384
  'angular-i18n',
340
385
  'i18next',
341
- 'next-i18next',
342
386
  'svelte-i18n',
387
+ 'sveltekit-i18n',
388
+ 'astro-i18next',
389
+ '@astrojs/i18n',
390
+ 'qwik-speak',
391
+ 'qwik-i18n',
392
+ '@solid-primitives/i18n',
393
+ 'ember-intl',
394
+ 'react-native-localize',
395
+ 'expo-localization',
396
+ '@ngx-translate/core',
397
+ '@ionic/angular',
343
398
  '@nuxtjs/i18n',
344
- 'i18ntk-runtime'
399
+ 'formatjs',
400
+ '@lingui/core',
401
+ 'i18ntk-runtime',
402
+ 'i18ntk/runtime'
345
403
  ];
346
404
 
347
405
  const installedFrameworks = i18nFrameworks.filter(framework => dependencies[framework]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18ntk",
3
- "version": "4.5.3",
3
+ "version": "4.6.0",
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,52 +179,41 @@
179
179
  },
180
180
  "preferGlobal": true,
181
181
  "versionInfo": {
182
- "version": "4.5.3",
183
- "releaseDate": "05/06/2026",
184
- "lastUpdated": "06/19/2026",
182
+ "version": "4.6.0",
183
+ "releaseDate": "07/04/2026",
184
+ "lastUpdated": "07/04/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
- "CONFIG: Shared .i18ntk-config now accepts extension-owned sections under extensions.workbench and extensions.lens; the CLI preserves the top-level extensions object while ignoring unknown nested extension settings.",
191
- "REPORTS: Likely-untranslated detection now ignores placeholder-only and symbol/dynamic values such as {file}, {path}, and emoji labels instead of treating them as untranslated English.",
192
- "REPORTS: Dynamic values with translated surrounding copy and English placeholder tokens are no longer flagged as untranslated.",
193
- "TRANSLATE: processFile() accepts project-relative source paths, matching direct CLI resolution.",
194
- "TRANSLATE: only-missing mode keeps existing translations that intentionally preserve configured product terms.",
195
- "TRANSLATE: broken target value detection now catches mojibake, replacement characters, repeated question marks, and target-language prefix leftovers.",
196
- "TESTS: Auto Translate regression coverage expanded for relative paths, protected terms, placeholder handling, and residual checks.",
197
- "SECURITY: Backup operations now validate all paths via SecurityUtils.validatePath fixes path traversal enabling arbitrary filesystem writes.",
198
- "SECURITY: i18ntk-complete --source-dir CLI override now validated; --source-language sanitized through SecurityUtils.sanitizeInput.",
199
- "SECURITY: config-helper dual-path resolution wraps --source-dir / --i18n-dir in SecurityUtils.validatePath.",
200
- "SECURITY: JSON parsing enforces depth (1000) and size (50 MB) limits in safeParseJSON to prevent DoS.",
201
- "SECURITY: LibreTranslate custom URL now requires I18NTK_ALLOW_CUSTOM_LIBRETRANSLATE_HOST=1 flag.",
202
- "SECURITY: sanitizeInput default whitelist tightened — removed backslash and curly brace characters.",
203
- "SECURITY: VSCode Workbench now validates report paths, limits JSON input size, checks write paths within root.",
204
- "SECURITY: i18ntk Lens rejects custom wrapper names >100 chars to prevent ReDoS.",
205
- "USAGE: Dead-key detection now uses resolved dynamic key data from insights instead of crude text-overlap heuristics.",
206
- "USAGE: Locale JSON imports are detected; telemetry/event call strings classified and excluded from translation counts.",
207
- "USAGE: Confidence-split unused key reports — confirmed/likely/possibly used tiers.",
208
- "USAGE: New --strict-unused, --json, --prune / --prune-keep flags.",
209
- "USAGE: Mojibake detection, client-boundary warnings, copy-formatter detection, local wrapper resolution.",
210
- "USAGE: Object-method .tx() calls and bounded dynamic key expansions now recognized.",
211
- "VSCode: i18ntk.clearDiagnostics command, new diagnostic codes, stale diagnostics cleared at scan start.",
212
- "TRANSLATE: Auto Translate residual reports and VS Code quick-fix integration."
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."
213
200
  ],
214
- "breakingChanges": [
215
- "i18ntk/runtime module-level helpers keep the first initialized runtime configuration for compatibility instead of being overwritten by later initRuntime() calls.",
216
- "utils/watch-locales.js returns a callable watcher object with EventEmitter methods and stop(); existing bare stop-function usage remains supported."
217
- ],
218
- "nextVersion": "4.5.4",
201
+ "breakingChanges": [],
202
+ "nextVersion": "4.6.1",
219
203
  "supportedNodeVersions": ">=16.0.0",
220
204
  "supportedFrameworks": {
221
205
  "react-i18next": ">=11.0.0",
222
206
  "vue-i18n": ">=9.0.0",
223
207
  "angular-i18n": ">=12.0.0",
224
208
  "next-i18next": ">=13.0.0",
209
+ "next-intl": ">=3.0.0",
225
210
  "nuxt-i18n": ">=8.0.0",
226
211
  "svelte-i18n": ">=3.0.0",
227
212
  "sveltekit-i18n": ">=2.0.0",
213
+ "astro-i18next": ">=0.1.0",
214
+ "remix-i18next": ">=14.0.0",
215
+ "gatsby-plugin-react-i18next": ">=5.0.0",
216
+ "qwik-speak": ">=0.11.0",
228
217
  "react-native-localize": ">=2.0.0",
229
218
  "expo-localization": ">=14.0.0",
230
219
  "ionic-angular": ">=6.0.0",
@@ -235,20 +224,22 @@
235
224
  "flask-babel": ">=2.0.0",
236
225
  "fastapi": ">=0.70.0",
237
226
  "spring-boot": ">=2.5.0",
238
- "laravel": ">=8.0.0"
227
+ "laravel": ">=8.0.0",
228
+ "fluent-rs": ">=0.16.0",
229
+ "gettext-rs": ">=0.7.0"
239
230
  },
240
- "supportPolicy": "Versions earlier than 4.4.1 may be unstable or insecure. Upgrade to 4.5.3 or newer.",
231
+ "supportPolicy": "Versions earlier than 4.6.0 may be unstable or insecure in CI automation. Upgrade to 4.6.0 or newer.",
241
232
  "deprecations": [
242
233
  "4.3.0",
243
234
  "4.3.1",
244
235
  "4.3.2",
245
236
  "4.3.3"
246
237
  ],
247
- "deprecationMessage": "i18ntk 4.3.x and earlier have known security vulnerabilities (path traversal, JSON DoS). Upgrade to i18ntk@4.5.3 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.0 or newer: npm install -g i18ntk@latest",
248
239
  "securityAdvisories": [
249
240
  "GHSA-i18ntk-4.3.x-path-traversal: Backup command accepted arbitrary paths without validation (fixed in 4.4.1)",
250
241
  "GHSA-i18ntk-4.3.x-json-dos: Deeply nested JSON files could cause denial of service (fixed in 4.4.1)"
251
242
  ]
252
243
  },
253
- "readme": "# i18ntk v4.5.3\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.5.3)](https://socket.dev/npm/package/i18ntk/overview/4.5.3)\n\n[![i18ntk Workbench](https://img.shields.io/badge/VS_Code-i18ntk_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-i18ntk_Lens-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=VladNoskov.i18ntk-lens)\n\n## The i18ntk ecosystem\n\n- i18ntk — CLI and runtime toolkit\n- i18ntk Workbench — full VS Code dashboard and reports\n- i18ntk Lens — inline hovers, CodeLens, and diagnostics\n\nUse the CLI in CI, Workbench for project-level management, and Lens for day-to-day editor feedback.\n\n## Install\n\n```bash\n# global CLI use\nnpm install -g i18ntk\n\n# local project use\nnpm install --save-dev i18ntk\n\n# one-off execution\nnpx i18ntk --help\n```\n\n## i18ntk Summary\n\n**What it does**\n\n- Manages locale files from the command line.\n- Finds missing, unused, risky, and inconsistent translation keys.\n- Produces validation and summary reports.\n- Supports framework-aware i18n workflows.\n- Provides a lightweight runtime translation toolkit.\n\n**What it does not do**\n\n- It is not a translation management SaaS.\n- It does not replace human translation review.\n- It does not force you to replace i18next, react-i18next, vue-i18n, or another runtime.\n\n**Why not i18next?**\n\ni18next is mainly a runtime internationalization library. i18ntk is mainly workflow tooling around translation files. They can work together: i18next handles runtime translation, while i18ntk handles setup, scanning, validation, reporting, and maintenance.\n\n| Need | i18ntk | i18next |\n| ------------------------- | ------------- | ---------------- |\n| Runtime translation | Basic toolkit | Mature runtime |\n| Locale file scanning | Yes | No |\n| Missing key detection | Yes | No |\n| Unused key detection | Yes | No |\n| Validation reports | Yes | Limited |\n| Auto-translation workflow | Yes | External tooling |\n\n## What's New in 4.5.3\n\n- **TSX/JSX SCANNING**: `supportedExtensions` default now includes `.tsx` and `.jsx`. Previously excluded from source scanning, causing React/Next.js projects to miss 97%+ of translation keys.\n\n## What's New in 4.5.2\n\n- The `complete` command now correctly inserts missing keys at the right nesting level when target locale files have namespace wrappers (e.g., `auth.json` containing `{ \"auth\": { ... } }`). Keys inside `auth.panel.sign_in` now go inside the `auth` wrapper, not at root level.\n- Fixed `complete` command: missing keys now inserted inside namespace wrapper when file has top-level key matching filename (e.g., auth.json with `{ \"auth\": … }`).\n- Fixed `translate --output-dir`: output now placed in `<outputDir>/<targetLang>/<filename>`, preventing language overwrites.\n- Enhanced `scanner` and `report-model` to filter out JS built-in type names (e.g., Promise, Boolean) and code expressions (e.g., `&&`, `${…}`) from hardcoded text detection.\n\n## What's New in 4.5.1\n\n- **CORRECT COMPLETENESS**: Validation now shows accurate completion percentages vs source locale (e.g., 33% instead of misleading 100%).\n- **NO MORE PARENT KEYS**: `getAllKeys()` no longer reports parent namespace objects (`footer`) as missing keys alongside their leaf children (`footer.copyright`).\n- **DOCTOR SMARTER**: No longer flags unconfigured languages (`de`, `ru`) as issues. Auto-detects available languages from the i18n directory structure.\n- **SCANNER FIXED**: Scanner now correctly scans `src/` directory for hardcoded text, not `locales/`.\n- **RUNTIME ALIASES**: `initRuntime()` now supports `localeDir`/`targetLocale`/`sourceLocale` as aliases for `baseDir`/`language`/`fallbackLanguage`.\n\n## What's New in 4.5.0\n\n- **PROTOTYPE POLLUTION HARDENED**: Three layers of defense added — `readJsonSafe()` now recursively strips `__proto__`, `constructor`, and `prototype` keys from all parsed JSON; `deepMerge()` in the runtime blocks these keys during locale merging; `mergeWithDefaults()` in settings-manager filters them from user settings.\n- **BACKUP FIXED**: All backup operations (create, restore, list, verify, cleanup) now work. A duplicate `sourceDir` declaration that caused a SyntaxError at module load has been removed. Corrupt backup files are now handled gracefully with descriptive error messages.\n- **COMPLETE COMMAND FIXED**: `i18ntk-complete` no longer crashes with `getUnifiedConfig is not defined`. The missing config-helper import has been added.\n- **MALFORMED JSON HANDLING**: Report generation now gracefully skips malformed JSON files with a warning instead of aborting the entire report.\n- **NULL SAFETY**: `stripBOMAndComments()` in i18n-helper now handles null/undefined inputs without throwing.\n- **ERROR HANDLING HARDENED**: Lazy-load failures in runtime now log to console when `I18NTK_DEBUG` is set. Settings save errors are now re-thrown instead of silently swallowed. Legacy config migration has proper error handling.\n\nSee [CHANGELOG.md](./CHANGELOG.md) for more release details.\n\n## Quick Start\n\nInitialize a project:\n\n```bash\ni18ntk\n# or with explicit command\ni18ntk --command=init\n```\n\nRun common checks:\n\n```bash\ni18ntk --command=analyze\ni18ntk --command=validate\ni18ntk --command=usage\ni18ntk report --json\ni18ntk --command=sizing\ni18ntk --command=summary\n```\n\nComplete or fix translation files:\n\n```bash\ni18ntk --command=complete\ni18ntk-fixer --help\n```\n\nAuto-translate locale JSON:\n\n```bash\ni18ntk --command=translate\n# or\ni18ntk-translate locales/en/common.json de --report-stdout\n```\n\nThe full onboarding guide is in [docs/getting-started.md](./docs/getting-started.md).\n\n## Main Commands\n\nPrimary CLI:\n\n```bash\ni18ntk\ni18ntk --help\ni18ntk --command=init\ni18ntk --command=analyze\ni18ntk --command=validate\ni18ntk --command=usage\ni18ntk report --json --markdown --html --out ./i18ntk-reports\ni18ntk --command=scanner\ni18ntk --command=sizing\ni18ntk --command=complete\ni18ntk --command=translate\ni18ntk --command=summary\n```\n\nStandalone executables:\n\n```bash\ni18ntk-init\ni18ntk-analyze\ni18ntk-validate\ni18ntk-usage\ni18ntk-report\ni18ntk-scanner\ni18ntk-sizing\ni18ntk-complete\ni18ntk-summary\ni18ntk-doctor\ni18ntk-fixer\ni18ntk-backup\ni18ntk-translate\n```\n\nNote: manager route `i18ntk --command=backup` is available via the interactive menu. Use `i18ntk-backup` directly for scripted backup operations.\n\n## Command Reference\n\n| Command | What it does | Looks for | Writes or changes |\n| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `i18ntk` | Opens the interactive management menu. | Project config, setup state, available commands. | Only changes files after you choose a command that writes. |\n| `i18ntk --command=init` / `i18ntk-init` | Sets up locale folders and missing target-language files. | Source language files and selected target languages. | Locale JSON files, `.i18ntk-config`, optional reports/backups. |\n| `i18ntk --command=analyze` / `i18ntk-analyze` | Compares source and target translation coverage. | Missing keys, extra keys, untranslated markers, completion by language. | Markdown/JSON/text reports when report output is enabled. |\n| `i18ntk --command=validate` / `i18ntk-validate` | Validates structure and translation quality risks. | Placeholder mismatches, missing keys, risky URLs/emails/secrets, likely English target text. | Validation summary report. Does not edit locale files. |\n| `i18ntk --command=usage` / `i18ntk-usage` | Maps translation keys to source files and finds unused/missing keys. | Direct i18n calls, literal known-key references, bounded dynamic templates/object maps, unresolved dynamic expressions, hardcoded text candidates, namespace/file naming mismatches. | Usage report with key locations, namespace recommendations, unresolved dynamic expressions, hardcoded text suggestions, and optional dead-key report. Does not delete unless cleanup deletion is explicitly enabled. |\n| `i18ntk report` / `i18ntk-report` | Generates the stable schemaVersion 1 report used by CLI automation and i18ntk Workbench. | Locale completeness, missing keys, unused keys with confidence, placeholders, likely untranslated values, expansion risk, and hardcoded text candidates. | JSON to stdout by default, plus JSON/Markdown/HTML files when `--out` is used. Does not edit locale files. |\n| `i18ntk --command=scanner` / `i18ntk-scanner` | Scans source for i18n issues and hardcoded user-facing text. | JSX/template text, common text attributes, i18n usage patterns, source-language text profiles. | Scanner report. Does not edit files. |\n| `i18ntk --command=complete` / `i18ntk-complete` | Adds missing keys to target language files for 100% key coverage. | Source-language keys missing from targets. | Target locale JSON files, using missing translation markers/prefixes. |\n| `i18ntk --command=translate` / `i18ntk-translate` | Auto-translates locale JSON using configured provider behavior. | Missing, empty, untranslated-marker, source-copy, likely-English, or visibly corrupt target values by default. | Target locale JSON files and translation reports. Existing translated values are kept unless `--translate-all` is used. If unresolved values remain after retry, writes `i18ntk-reports/auto-translate/latest.json` for targeted follow-up. |\n| `i18ntk --command=sizing` / `i18ntk-sizing` | Estimates translated string length expansion and layout risk. | Text length, expansion ratios, placeholder-bearing strings. | Sizing report. Does not edit locale files. |\n| `i18ntk --command=summary` / `i18ntk-summary` | Shows project translation status. | Configured locales, reports, completeness status. | Console/report output only. |\n| `i18ntk-fixer` | Fixes placeholder and missing-marker issues, and can audit English source files with `--check-placeholders`. | Placeholder corruption, missing translation markers, configured language files, `[LANG] ...` leftovers in English locales. | Locale JSON files when fixes are applied. Use dry-run options where available before bulk edits. |\n| `i18ntk-backup` | Creates, verifies, restores, and cleans locale backups. | Locale JSON files and backup manifests. | Backup archives/manifests, or restored locale files when using restore. |\n\n## Common Options\n\nMany commands support:\n\n- `--source-dir <path>`\n- `--i18n-dir <path>`\n- `--output-dir <path>`\n- `--source-language <code>`\n- `--ui-language <code>`\n- `--no-prompt`\n- `--help`\n\nCommand-specific tools add their own flags such as `--dry-run`, `--output-report`, `--cleanup`, `--predict-expansion`, or Auto Translate provider options.\n\nExample:\n\n```bash\ni18ntk --command=analyze --source-dir=./src --i18n-dir=./locales --output-dir=./i18ntk-reports\n```\n\n## Auto Translate\n\nInteractive manager flow:\n\n```bash\ni18ntk\n# choose \"Auto Translate\"\n```\n\nDirect CLI examples:\n\n```bash\ni18ntk-translate locales/en/common.json de\ni18ntk-translate locales/en/common.json fr --dry-run --report-stdout\ni18ntk-translate locales/en es --source-dir locales/en --files \"*.json\" --no-confirm --preserve-placeholders\n```\n\nProvider examples:\n\n```bash\nexport DEEPL_API_KEY=\"your-deepl-api-key\"\ni18ntk-translate locales/en/common.json de --provider deepl --no-confirm --preserve-placeholders\n\nexport LIBRETRANSLATE_URL=\"https://libretranslate.com/translate\"\nexport LIBRETRANSLATE_API_KEY=\"optional-api-key\"\ni18ntk-translate locales/en/common.json es --provider libretranslate --no-confirm --preserve-placeholders\n```\n\n`google` remains the default provider. You can also set `I18NTK_TRANSLATE_PROVIDER=deepl` or `I18NTK_TRANSLATE_PROVIDER=libretranslate`.\n\nProvider requests are HTTPS-only and response-size limited, and security logs redact provider query strings and response bodies. DeepL is pinned to official DeepL hosts by default; set `I18NTK_ALLOW_CUSTOM_TRANSLATE_HOSTS=1` only for a trusted DeepL-compatible proxy. Custom LibreTranslate URLs are blocked for localhost/private IP ranges unless `I18NTK_ALLOW_PRIVATE_TRANSLATE_URLS=1` is set for trusted local testing. Keep provider API keys in environment variables or a secret manager.\n\nThe manager flow asks for:\n\n- source locale directory, either the folder with JSON files or a locale root such as `./locales`\n- source language code\n- one or more target languages, or `all`\n- one JSON file or all JSON files in the source directory\n\nIf you select a locale root such as `./locales` and choose source language `en`, the manager automatically uses `./locales/en` when that folder contains the source JSON files.\n\nBefore writing files, the manager can run a dry-run preview. After confirmation it writes translated files under sibling target-language folders, for example:\n\n```text\nlocales/en/common.json\nlocales/de/common.json\nlocales/fr/common.json\n```\n\nAuto Translate is target-aware by default. When a target file already exists, it keeps translated target values and only sends values that are missing, empty, marked as untranslated, still identical to the source, likely still English, or visibly corrupt from encoding damage such as `?????`, replacement characters, or common mojibake. Use `--translate-all` when you intentionally want to re-translate every source string.\n\n### Placeholder Handling\n\nAuto Translate detects common placeholders such as:\n\n- `{name}`\n- `{{count}}`\n- `%s`\n- `%d`\n- `:id`\n- `%{name}`\n- `${value}`\n- `{count, plural, one {# item} other {# items}}`\n- `$t(common.save)`\n- `%(total).2f`\n\nUseful flags:\n\n- `--preserve-placeholders`: translate text around placeholders and reinsert original tokens\n- `--skip-placeholders`: copy placeholder-bearing strings unchanged\n- `--send-placeholders`: send placeholder-bearing strings through translation after masking\n- `--custom-regex <regex>`: add project-specific placeholder detection\n- `--only-missing`: keep existing translated target values and translate only missing/source-copy/likely English values (default)\n- `--translate-all`: re-translate every source string\n\nProgress output is stage-aware for large files. Normal keys are reported as `Translating strings`, while preserve-mode placeholder work is reported as `Translating placeholder-safe text segments`; each progress update includes the current key path when available.\n\n### Protected Terms and Keys\n\nAuto Translate can create and use a project-local protection file:\n\n```bash\ni18ntk-translate locales/en/common.json de --create-protection-file --protection-file ./i18ntk-auto-translate.json\n```\n\nExample `i18ntk-auto-translate.json`:\n\n```json\n{\n \"version\": 1,\n \"terms\": [\n \"BrandName\",\n \"PRODUCT_CODE\",\n { \"value\": \"OK\", \"context\": \"after:Click|Press|Tap\" },\n { \"value\": \"API\", \"context\": \"standalone\" }\n ],\n \"keys\": [\"app.brandName\", \"legal.companyName\", \"product.*.symbol\"],\n \"values\": [\"BrandName Ltd\", \"support@example.com\"],\n \"patterns\": [\"[A-Z]{2,}-\\\\d+\"]\n}\n```\n\n- `terms` are masked before translation and restored exactly afterward.\n - **Plain strings**: masked everywhere (backward compatible).\n - **Context objects**: masked only in specific contexts (`after:word`, `before:word`, `standalone`, `surrounded:left,right`).\n- `keys` are exact key paths or `*` wildcard paths copied unchanged.\n- `values` are exact source values copied unchanged.\n- `patterns` are JavaScript regex strings for advanced protected substrings.\n\nUseful flags:\n\n- `--protection-file <path>`\n- `--create-protection-file`\n- `--no-protection`\n\nOpen Settings and choose `Auto Translate` to edit defaults for placeholder mode, translate-only-needed mode, concurrency, batch size, retry settings, report output, BOM output, protection file path, first-run setup prompt, and update prompt.\n\nSee [docs/auto-translate.md](./docs/auto-translate.md) for the full Auto Translate guide.\n\n## Validation\n\nValidation checks locale structure, completeness, placeholders, and content risks.\n\nValidation warning types are specific:\n\n- `Potential risky content`: URL, email address, or secret-like value\n- `Possible untranslated English content`: target-language value appears to contain too much English\n\nEnglish-content warnings include:\n\n- detected English percentage\n- configured threshold\n- matched word count\n- sample matched words\n\nTune warnings in `.i18ntk-config`:\n\n```json\n{\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"]\n}\n```\n\n## Sizing Analysis\n\n`i18ntk-sizing` reports translation file sizes, key counts, average value length, and file-set mismatches across language folders.\n\n```bash\ni18ntk-sizing --source-dir ./locales --format table\ni18ntk-sizing --source-dir ./locales --detailed --output-dir ./i18ntk-reports\n```\n\nUse `--detailed` to print per-file rows in the terminal.\n\n### Expansion Prediction (New in 4.0.0)\n\nPredict UI layout overflow risk by analyzing per-key character-count expansion across languages:\n\n```bash\ni18ntk-sizing --source-dir ./locales --predict-expansion --output-report\n```\n\nExpansion ratios are classified into risk tiers:\n\n- **Safe** (<30% expansion): no UI impact expected\n- **Warning** (30–50%): may overflow in tight layouts — test on target languages\n- **Critical** (>50%): high risk of truncation — review UI element sizing\n\nThe report includes a built-in language-pair expansion reference table (EN→DE +35%, EN→RU +50%, EN→JA −40%, etc.) and lists the top-30 most-expanded keys.\n\n## Scanner: Multi-Language Detection (New in 4.0.0)\n\n`i18ntk-scanner` now supports detecting hardcoded text in multiple source languages beyond English:\n\n```bash\ni18ntk-scanner --source-dir ./src --source-language de\ni18ntk-scanner --source-dir ./src --source-language ja --output-report\n```\n\nSupported language profiles (12+): English, German, French, Spanish, Japanese, Chinese, Russian, Korean, Arabic, Hindi, and more. Each profile includes language-specific character ranges, stopword lists for false-positive filtering, and transliteration rules for key generation.\n\n## Usage: Dead Key Detection (New in 4.0.0)\n\n`i18ntk-usage` can identify translation keys that are defined but never referenced in source code:\n\n```bash\ni18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup\ni18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup --dry-run-delete\n```\n\nEach dead key receives a confidence score (0.0–1.0) factoring:\n\n- Unresolved dynamic key patterns (e.g., ``t(`prefix.${dynamic}`)``) — lower score and listed in the usage report; simple consts, bounded arrays, object maps, and ternaries are expanded to exact keys where possible\n- Key appears in source code comments or JSDoc — medium score\n- Parent file recently modified (<30 days) — medium score\n- No references found anywhere — high score (>0.8)\n\nThe `--dry-run-delete` flag writes a `.dead-keys.json` report for review before any destructive action.\n\n## Validator: Key Naming Conventions (New in 4.0.0)\n\nEnforce consistent translation key naming across your project:\n\n```bash\ni18ntk-validate --enforce-key-style\n```\n\nConfigure the expected style in `.i18ntk-config`:\n\n```json\n{\n \"keyStyle\": \"dot.notation\"\n}\n```\n\nSupported styles: `dot.notation`, `snake_case`, `camelCase`, `kebab-case`, `flat`. Violations are reported as warnings with suggested canonical forms.\n\n## Watch: Hot Reload (New in 4.0.0)\n\n`utils/watch-locales.js` now provides debounced file watching with EventEmitter support:\n\n```js\nconst watchLocales = require('i18ntk/utils/watch-locales');\nconst watcher = watchLocales('./locales');\n\nwatcher.on('change', (filePath) => {\n console.log('Locale changed:', filePath);\n});\n\nwatcher.on('add', (filePath) => {\n console.log('Locale added:', filePath);\n});\n\n// Later:\nwatcher.stop();\n```\n\nFeatures: 300ms debounce (configurable), SHA-256 hash tracking to skip no-change saves, and a maximum of 50 watched directories.\n\n### Migration\n\nThe `watchLocales` return value gained EventEmitter methods in v4.0.0. Existing stop-function usage still works:\n\n```js\nconst stop = watchLocales('./locales', onChange);\n```\n\nCan be updated to:\n\n```js\nconst watcher = watchLocales('./locales');\nwatcher.on('change', onChange);\nwatcher.stop();\n```\n\nPassing a callback as the second argument is still supported — it auto-subscribes to `change` and `add` events.\n\n## Backup: Incremental Mode (New in 4.0.0)\n\nCreate differential backups that only include changed files:\n\n```bash\ni18ntk-backup create ./locales --incremental\n```\n\nIncremental backups store SHA-256 hashes per file and a parent-chain reference. Restoring an incremental backup automatically chains from the oldest full backup through each incremental diff in order. Chain depth is capped at 10 increments. Use `verify` to validate the hash chain.\n\n## Runtime: Lazy Loading (New in 4.0.0)\n\nReduce memory usage by deferring locale file loads until first key access:\n\n```js\nconst runtime = require('i18ntk/runtime');\n\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n lazy: true,\n});\n\nconsole.log(i18n.t('common.hello')); // loads common.json on first access\n```\n\nWhen `lazy: true`, the runtime builds a key-to-file manifest on first access and loads individual files on demand. Files are loaded once and cached. If the manifest is missing or incomplete, the runtime falls back to full eager loading for that language. Manifest size is capped at 100KB with path containment validation.\n\nProduction guidance:\n\n- Prefer the object returned from `initRuntime()` instead of module-level `runtime.t()` in apps with multiple tenants, projects, or locale roots.\n- Use `lazy: true` for large modular locale folders where lower steady-state memory matters more than a small first-key lookup cost.\n- Use `preload: true` without `lazy` for small locale sets or latency-sensitive startup paths.\n- Call `refresh(language)` after deploying or writing changed locale files so cached data and lazy manifests are rebuilt.\n- Use per-call language overrides when rendering one-off alternate-language strings: `i18n.t('common.hello', {}, { language: 'de' })`.\n- Use `translateBatch()` for small groups of labels and `clearCache()` / `getCacheInfo()` for cache maintenance and diagnostics.\n- `i18ntk/runtime/enhanced` remains available for compatibility with existing async/encryption users, but new production integrations should start with `i18ntk/runtime`.\n\n## Runtime API\n\nUse `i18ntk/runtime` when an application needs to read locale JSON files at runtime.\n\n```js\nconst runtime = require('i18ntk/runtime');\n\nconst i18n = runtime.initRuntime({\n baseDir: './locales',\n language: 'en',\n fallbackLanguage: 'en',\n keySeparator: '.',\n preload: true,\n});\n\nconsole.log(i18n.t('common.hello'));\ni18n.setLanguage('fr');\nconsole.log(i18n.getLanguage());\nconsole.log(i18n.getAvailableLanguages());\ni18n.refresh('fr');\n```\n\nUseful production helpers:\n\n```js\ni18n.t('common.hello', {}, { language: 'de' }); // per-call language override\ni18n.translateBatch(['menu.home', 'menu.settings']);\ni18n.clearCache('fr');\nconsole.log(i18n.getCacheInfo());\n```\n\nSee [docs/runtime.md](./docs/runtime.md) for runtime details.\n\n## Configuration\n\ni18ntk uses a project-local `.i18ntk-config` file.\n\nExample:\n\n```json\n{\n \"version\": \"4.5.3\",\n \"sourceDir\": \"./locales\",\n \"i18nDir\": \"./locales\",\n \"outputDir\": \"./i18ntk-reports\",\n \"sourceLanguage\": \"en\",\n \"defaultLanguages\": [\"en\", \"de\", \"es\", \"fr\", \"ru\"],\n \"reports\": {\n \"format\": \"markdown\"\n },\n \"englishContentThresholdPercent\": 10,\n \"allowedEnglishTerms\": [\"BrandName\", \"PRODUCT_CODE\"],\n \"autoTranslate\": {\n \"placeholderMode\": \"preserve\",\n \"concurrency\": 12,\n \"batchSize\": 100,\n \"progressInterval\": 25,\n \"retryCount\": 3,\n \"retryDelay\": 1000,\n \"timeout\": 15000,\n \"dryRunFirst\": true,\n \"onlyMissingOrEnglish\": true,\n \"reportStdout\": true,\n \"bom\": false,\n \"protectionEnabled\": true,\n \"protectionFile\": \"./i18ntk-auto-translate.json\",\n \"promptProtectionSetup\": true,\n \"promptProtectionUpdate\": true\n },\n \"setup\": {\n \"completed\": true\n },\n \"extensions\": {\n \"workbench\": {\n \"localeDirectory\": \"./locales\",\n \"sourceLocale\": \"en\"\n },\n \"lens\": {\n \"localeDirectory\": \"./locales\",\n \"sourceLocale\": \"en\",\n \"keyFormats\": [\"dot\", \"snake\"]\n }\n }\n}\n```\n\nSee [docs/api/CONFIGURATION.md](./docs/api/CONFIGURATION.md) for the full configuration model.\n\n## Public Package Contents\n\nThe public package intentionally ships runtime and CLI files only.\n\nThe package includes:\n\n- CLI entry points under `main/`\n- manager commands and services\n- runtime API files under `runtime/`\n- settings UI files required at runtime\n- bundled internal UI locales\n- shared utilities required by the shipped commands\n- `README.md`, `CHANGELOG.md`, `LICENSE`, and policy files\n\nThe public package manifest includes `readmeFilename: \"README.md\"`, and the release staging script fails if `README.md` is missing or empty.\n\n## Documentation\n\n- [Documentation Index](./docs/README.md)\n- [Getting Started](./docs/getting-started.md)\n- [API Reference](./docs/api/API_REFERENCE.md)\n- [Configuration Guide](./docs/api/CONFIGURATION.md)\n- [Runtime API Guide](./docs/runtime.md)\n- [Auto Translate Guide](./docs/auto-translate.md)\n- [Scanner Guide](./docs/scanner-guide.md)\n- [Environment Variables](./docs/environment-variables.md)\n- [Migration Guide v4.3.3](./docs/migration-guide-v4.3.3.md)\n\n## Security\n\n- No API key is required for the default Auto Translate flow.\n- Do not store secrets in locale files, `.i18ntk-config`, or protection files.\n- Project-specific brand/product terms should be configured by the user, not hardcoded into the package.\n- Report security issues using [SECURITY.md](./SECURITY.md).\n\n## Community\n\n- [Contributing](./CONTRIBUTING.md)\n- [Code of Conduct](./CODE_OF_CONDUCT.md)\n- [Funding](./FUNDING.md)\n\n## Related Tools\n\n| Tool | Purpose |\n| -------------------- | ------------------------------------------------------------------------------------------------- |\n| **i18ntk** | Zero-dependency i18n toolkit for scanning, validation, translation, reports, and runtime loading. |\n| **i18ntk Workbench** | Full VS Code localization health dashboard powered by i18ntk. |\n| **i18ntk Lens** | Lightweight inline translation hovers, diagnostics, and key navigation. |\n| **PublishGuard** | Pre-publish safety scanner for npm packages and VS Code extensions. |\n| **ContextKit** | AI coding context manager for AGENTS.md, Claude, Cursor, Copilot, Roo, and Codex files. |\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n"
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"
254
245
  }
@@ -115,11 +115,11 @@ async function getUnifiedConfig(scriptName, cliArgs = {}) {
115
115
  cfg.sourceDir = path.resolve(cfg.projectRoot, cfg.scriptDirectories[scriptName]);
116
116
  }
117
117
 
118
- // Auto-fix i18nDir if missing but sourceDir exists
119
- if (!SecurityUtils.safeExistsSync(cfg.i18nDir, projectRoot) && SecurityUtils.safeExistsSync(cfg.sourceDir, projectRoot)) {
120
- await configManager.updateConfig({ i18nDir: configManager.toRelative(cfg.sourceDir) });
121
- cfg.i18nDir = cfg.sourceDir;
122
- }
118
+ // Auto-fix configured i18nDir only when the user did not pass an explicit locale path.
119
+ if (!cliArgs.i18nDir && !SecurityUtils.safeExistsSync(cfg.i18nDir, projectRoot) && SecurityUtils.safeExistsSync(cfg.sourceDir, projectRoot)) {
120
+ await configManager.updateConfig({ i18nDir: configManager.toRelative(cfg.sourceDir) });
121
+ cfg.i18nDir = cfg.sourceDir;
122
+ }
123
123
 
124
124
  settingsDir = settingsManager.configDir;
125
125
  }
@@ -165,13 +165,13 @@ async function getUnifiedConfig(scriptName, cliArgs = {}) {
165
165
 
166
166
  const config = {
167
167
  ...cfg,
168
- sourceLanguage: cliArgs.sourceLanguage || cfg.sourceLanguage || 'en',
168
+ sourceLanguage: cliArgs.sourceLanguage || cfg.sourceLanguage || 'en',
169
169
  uiLanguage: cliArgs.uiLanguage || cfg.uiLanguage || 'en',
170
170
  notTranslatedMarker: markerList[0],
171
171
  notTranslatedMarkers: markerList,
172
- supportedExtensions: cfg.supportedExtensions || cfg.processing?.supportedExtensions || ['.json', '.js', '.jsx', '.ts', '.tsx'],
172
+ supportedExtensions: cfg.supportedExtensions || cfg.processing?.supportedExtensions || ['.json', '.js', '.jsx', '.ts', '.tsx', '.mjs', '.mts', '.cjs', '.cts', '.vue', '.svelte', '.astro', '.mdx', '.rs'],
173
173
  excludeFiles: cfg.excludeFiles || cfg.processing?.excludeFiles || ['.DS_Store', 'Thumbs.db'],
174
- excludeDirs: cfg.excludeDirs || cfg.processing?.excludeDirs || ['node_modules', '.next', '.git', 'dist', 'build'],
174
+ excludeDirs: cfg.excludeDirs || cfg.processing?.excludeDirs || ['node_modules', '.next', '.nuxt', '.output', '.astro', '.svelte-kit', '.cache', '__generated__', '.git', 'dist', 'build', 'target'],
175
175
  strictMode: cliArgs.strictMode || cfg.strictMode || false,
176
176
  backupDir: path.resolve(projectRoot, normalizedBackupLocation),
177
177
  tempDir: path.join(settingsDir, 'temp'),
@@ -190,11 +190,13 @@ async function getUnifiedConfig(scriptName, cliArgs = {}) {
190
190
  advanced: cfg.advanced || {},
191
191
  },
192
192
  debug: cfg.debug || {},
193
- displayPaths,
194
- };
195
-
196
- SecurityUtils.validateConfig(config);
197
- return config;
193
+ displayPaths,
194
+ };
195
+ config.codeDir = config.sourceDir;
196
+ config.localesDir = config.i18nDir;
197
+
198
+ SecurityUtils.validateConfig(config);
199
+ return config;
198
200
  } catch (error) {
199
201
  throw new Error(`Configuration error for ${scriptName}: ${error.message}`);
200
202
  }
@@ -255,22 +257,26 @@ function parseCommonArgs(args) {
255
257
  const sanitizedKey = key?.trim();
256
258
  const sanitizedValue = value !== undefined ? value.trim() : true;
257
259
 
258
- switch (sanitizedKey) {
259
- case 'source-dir':
260
- parsed.sourceDir = sanitizedValue;
261
- break;
262
- case 'i18n-dir':
263
- parsed.i18nDir = sanitizedValue;
264
- break;
260
+ switch (sanitizedKey) {
261
+ case 'code-dir':
262
+ case 'source-code-dir':
263
+ case 'source-dir':
264
+ parsed.sourceDir = sanitizedValue;
265
+ break;
266
+ case 'locales-dir':
267
+ case 'i18n-dir':
268
+ parsed.i18nDir = sanitizedValue;
269
+ break;
265
270
  case 'output-dir':
266
271
  parsed.outputDir = sanitizedValue;
267
272
  break;
268
273
  case 'config-dir':
269
274
  parsed.configDir = sanitizedValue;
270
275
  break;
271
- case 'source-language':
272
- parsed.sourceLanguage = sanitizedValue;
273
- break;
276
+ case 'source-locale':
277
+ case 'source-language':
278
+ parsed.sourceLanguage = sanitizedValue;
279
+ break;
274
280
  case 'ui-language':
275
281
  parsed.uiLanguage = sanitizedValue;
276
282
  break;
@@ -367,11 +373,15 @@ function parseCommonArgs(args) {
367
373
  * @param {object} additionalOptions - Additional script-specific options
368
374
  */
369
375
  function displayHelp(scriptName, additionalOptions = {}) {
370
- const commonOptions = {
371
- 'source-dir': 'Source directory for translation files',
372
- 'i18n-dir': 'Directory containing i18n files (can differ from source-dir)',
373
- 'output-dir': 'Output directory for reports',
374
- 'source-language': 'Source language code (e.g., en, de)',
376
+ const commonOptions = {
377
+ 'code-dir': 'Application source code directory to scan',
378
+ 'source-code-dir': 'Alias for --code-dir',
379
+ 'source-dir': 'Legacy alias; source code for scanner commands, locale root for locale-only commands',
380
+ 'locales-dir': 'Directory containing locale/i18n files',
381
+ 'i18n-dir': 'Alias for --locales-dir',
382
+ 'output-dir': 'Output directory for reports',
383
+ 'source-locale': 'Source locale code (e.g., en, de)',
384
+ 'source-language': 'Legacy alias for --source-locale',
375
385
  'ui-language': 'UI language for messages',
376
386
  'log-level': 'Logging level (error, warn, info, debug, silent)',
377
387
  'framework': 'Preferred framework (auto, react, vue, etc.)',
@@ -415,15 +425,15 @@ function displayHelp(scriptName, additionalOptions = {}) {
415
425
  console.log(` I18NTK_FRAMEWORK_PREFERENCE Preferred framework (auto, react, vue, etc.)`);
416
426
 
417
427
  console.log(`\nExamples:`);
418
- console.log(` node ${scriptName}.js --source-dir=./locales`);
419
- console.log(` node ${scriptName}.js --source-dir=./app --i18n-dir=./locales`);
428
+ console.log(` node ${scriptName}.js --locales-dir=./locales --source-locale=en`);
429
+ console.log(` node ${scriptName}.js --code-dir=./app --locales-dir=./locales`);
420
430
  console.log(` node ${scriptName}.js --output-dir=./i18ntk-reports`);
421
- console.log(` I18NTK_LOG_LEVEL=debug node ${scriptName}.js --source-dir=./locales`);
431
+ console.log(` I18NTK_LOG_LEVEL=debug node ${scriptName}.js --locales-dir=./locales`);
422
432
  console.log(` npx i18ntk ${scriptName.replace('i18ntk-', '')} --help`);
423
433
 
424
434
  console.log('\nConfiguration:');
425
435
  console.log(` Settings are loaded from ${configManager.CONFIG_PATH}`);
426
- console.log(' Use --source-dir, --i18n-dir, and --output-dir to override');
436
+ console.log(' Use --code-dir, --locales-dir, --source-locale, and --output-dir to override');
427
437
  console.log(' Environment variables can also be used for configuration');
428
438
  }
429
439
 
@@ -1,6 +1,8 @@
1
- module.exports = {
2
- SUCCESS: 0,
3
- CONFIG_ERROR: 1,
4
- VALIDATION_FAILED: 2,
5
- SECURITY_VIOLATION: 3,
6
- };
1
+ module.exports = {
2
+ SUCCESS: 0,
3
+ VALIDATION_FAILED: 1,
4
+ RUNTIME_FAILURE: 1,
5
+ CONFIG_ERROR: 2,
6
+ INVALID_ARGUMENTS: 2,
7
+ SECURITY_VIOLATION: 3,
8
+ };