i18ntk 4.5.2 → 4.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # i18ntk v4.5.2
1
+ # i18ntk v4.5.4
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.5.2)](https://socket.dev/npm/package/i18ntk/overview/4.5.2)
12
+ [![socket](https://socket.dev/api/badge/npm/package/i18ntk/4.5.4)](https://socket.dev/npm/package/i18ntk/overview/4.5.4)
13
13
 
14
14
  [![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)
15
15
  [![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)
@@ -64,6 +64,17 @@ i18next is mainly a runtime internationalization library. i18ntk is mainly workf
64
64
  | Validation reports | Yes | Limited |
65
65
  | Auto-translation workflow | Yes | External tooling |
66
66
 
67
+ ## What's New in 4.5.4
68
+
69
+ - **CLI RELIABILITY**: Direct and manager-routed commands now propagate runtime, validation, and report failures with non-zero exit codes and never print success after failure.
70
+ - **CI READY**: Commands skip prompts under `--no-prompt`, `CI=true`, or non-TTY stdin/stdout.
71
+ - **CLEAR FLAGS**: Added `--code-dir` / `--source-code-dir`, `--locales-dir` / `--i18n-dir`, and `--source-locale` while preserving legacy aliases.
72
+ - **COMMAND FIXES**: Fixed `i18ntk-analyze` setup guidance crash, `i18ntk-complete --help`, `i18ntk-summary` `NaN` averages, validation success/failure wording, and completion summary labels.
73
+
74
+ ## What's New in 4.5.3
75
+
76
+ - **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.
77
+
67
78
  ## What's New in 4.5.2
68
79
 
69
80
  - 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.
@@ -189,20 +200,22 @@ Note: manager route `i18ntk --command=backup` is available via the interactive m
189
200
 
190
201
  Many commands support:
191
202
 
192
- - `--source-dir <path>`
193
- - `--i18n-dir <path>`
203
+ - `--code-dir <path>` or `--source-code-dir <path>` for application source files
204
+ - `--locales-dir <path>` or `--i18n-dir <path>` for locale files
194
205
  - `--output-dir <path>`
195
- - `--source-language <code>`
206
+ - `--source-locale <code>`
196
207
  - `--ui-language <code>`
197
208
  - `--no-prompt`
198
209
  - `--help`
199
210
 
211
+ Legacy `--source-dir` and `--source-language` remain supported. For scanner-style commands, `--source-dir` means source code. For locale-only commands, prefer `--locales-dir` to avoid ambiguity.
212
+
200
213
  Command-specific tools add their own flags such as `--dry-run`, `--output-report`, `--cleanup`, `--predict-expansion`, or Auto Translate provider options.
201
214
 
202
215
  Example:
203
216
 
204
217
  ```bash
205
- i18ntk --command=analyze --source-dir=./src --i18n-dir=./locales --output-dir=./i18ntk-reports
218
+ i18ntk --command=analyze --code-dir=./src --locales-dir=./locales --source-locale=en --output-dir=./i18ntk-reports
206
219
  ```
207
220
 
208
221
  ## Auto Translate
@@ -354,8 +367,8 @@ Tune warnings in `.i18ntk-config`:
354
367
  `i18ntk-sizing` reports translation file sizes, key counts, average value length, and file-set mismatches across language folders.
355
368
 
356
369
  ```bash
357
- i18ntk-sizing --source-dir ./locales --format table
358
- i18ntk-sizing --source-dir ./locales --detailed --output-dir ./i18ntk-reports
370
+ i18ntk-sizing --locales-dir ./locales --format table
371
+ i18ntk-sizing --locales-dir ./locales --detailed --output-dir ./i18ntk-reports
359
372
  ```
360
373
 
361
374
  Use `--detailed` to print per-file rows in the terminal.
@@ -365,7 +378,7 @@ Use `--detailed` to print per-file rows in the terminal.
365
378
  Predict UI layout overflow risk by analyzing per-key character-count expansion across languages:
366
379
 
367
380
  ```bash
368
- i18ntk-sizing --source-dir ./locales --predict-expansion --output-report
381
+ i18ntk-sizing --locales-dir ./locales --predict-expansion --output-report
369
382
  ```
370
383
 
371
384
  Expansion ratios are classified into risk tiers:
@@ -381,8 +394,8 @@ The report includes a built-in language-pair expansion reference table (EN→DE
381
394
  `i18ntk-scanner` now supports detecting hardcoded text in multiple source languages beyond English:
382
395
 
383
396
  ```bash
384
- i18ntk-scanner --source-dir ./src --source-language de
385
- i18ntk-scanner --source-dir ./src --source-language ja --output-report
397
+ i18ntk-scanner --code-dir ./src --source-locale de
398
+ i18ntk-scanner --code-dir ./src --source-locale ja --output-report
386
399
  ```
387
400
 
388
401
  Supported 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.
@@ -392,8 +405,8 @@ Supported language profiles (12+): English, German, French, Spanish, Japanese, C
392
405
  `i18ntk-usage` can identify translation keys that are defined but never referenced in source code:
393
406
 
394
407
  ```bash
395
- i18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup
396
- i18ntk-usage --source-dir ./src --i18n-dir ./locales --cleanup --dry-run-delete
408
+ i18ntk-usage --code-dir ./src --locales-dir ./locales --cleanup
409
+ i18ntk-usage --code-dir ./src --locales-dir ./locales --cleanup --dry-run-delete
397
410
  ```
398
411
 
399
412
  Each dead key receives a confidence score (0.0–1.0) factoring:
@@ -542,7 +555,7 @@ Example:
542
555
 
543
556
  ```json
544
557
  {
545
- "version": "4.5.2",
558
+ "version": "4.5.4",
546
559
  "sourceDir": "./locales",
547
560
  "i18nDir": "./locales",
548
561
  "outputDir": "./i18ntk-reports",
@@ -71,9 +71,16 @@ class I18nAnalyzer {
71
71
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
72
72
  loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
73
73
 
74
- this.sourceDir = this.config.sourceDir;
75
- this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
76
- this.outputDir = this.config.outputDir;
74
+ this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
75
+ this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
76
+ this.outputDir = this.config.outputDir;
77
+
78
+ const explicitLocaleDir = args.i18nDir || (!args.i18nDir && args.sourceDir ? args.sourceDir : null);
79
+ if (explicitLocaleDir && !SecurityUtils.safeExistsSync(this.sourceDir, process.cwd())) {
80
+ const err = new Error(`Locale directory not found: ${this.sourceDir}`);
81
+ err.exitCode = 2;
82
+ throw err;
83
+ }
77
84
 
78
85
  // Validate source directory exists
79
86
  const { validateSourceDir } = require('../utils/config-helper');
@@ -710,10 +717,32 @@ try {
710
717
  }
711
718
  }
712
719
 
713
- // Show help message
714
- showHelp() {
715
- console.log(t('analyze.help_message'));
716
- }
720
+ // Show help message
721
+ showHelp() {
722
+ console.log(t('analyze.help_message'));
723
+ }
724
+
725
+ provideSetupGuidance() {
726
+ return [
727
+ 'Setup guidance:',
728
+ `- Ensure the locale root exists: ${this.sourceDir}`,
729
+ `- Ensure the source locale exists: ${path.join(this.sourceDir, this.config.sourceLanguage)}`,
730
+ '- Add at least one target locale, for example de/common.json or de.json.',
731
+ '- Prefer --locales-dir for locale files. Use --code-dir for application source files.'
732
+ ].join('\n');
733
+ }
734
+
735
+ detectStructureType() {
736
+ const sourceLocaleDir = path.join(this.sourceDir, this.config.sourceLanguage);
737
+ const sourceLocaleFile = path.join(this.sourceDir, `${this.config.sourceLanguage}.json`);
738
+ if (SecurityUtils.safeExistsSync(sourceLocaleFile, this.sourceDir)) {
739
+ return { type: 'monolith' };
740
+ }
741
+ if (SecurityUtils.safeExistsSync(sourceLocaleDir, this.sourceDir)) {
742
+ return { type: 'directory' };
743
+ }
744
+ return { type: 'unknown' };
745
+ }
717
746
 
718
747
  // Main analyze method
719
748
  async analyze() {
@@ -737,9 +766,9 @@ try {
737
766
 
738
767
  const languages = this.getAvailableLanguages();
739
768
 
740
- if (languages.length === 0) {
741
- const error = t('analyze.noLanguages') || '⚠️ No target languages found.';
742
- const guidance = this.provideSetupGuidance();
769
+ if (languages.length === 0) {
770
+ const error = t('analyze.noLanguages') || '⚠️ No target languages found.';
771
+ const guidance = this.provideSetupGuidance();
743
772
 
744
773
  if (args.json) {
745
774
  jsonOutput.setStatus('error', error);
@@ -751,10 +780,10 @@ try {
751
780
  console.log(JSON.stringify(jsonOutput.data, null, args.indent || 2));
752
781
  return;
753
782
  }
754
- console.log(error);
755
- console.log('\n' + guidance);
756
- return;
757
- }
783
+ console.log(error);
784
+ console.log('\n' + guidance);
785
+ return { success: true, warnings: 1, message: error };
786
+ }
758
787
 
759
788
  if (!args.json) {
760
789
  console.log(t('analyze.foundLanguages', { count: languages.length, languages: languages.join(', ') }) || `📋 Found ${languages.length} languages to analyze: ${languages.join(', ')}`);
@@ -773,8 +802,11 @@ try {
773
802
  const report = this.generateLanguageReport(analysis);
774
803
 
775
804
  // Save report
776
- const reportPath = await this.saveReport(language, report);
777
- const processedCount = results.length + 1;
805
+ const reportPath = await this.saveReport(language, report);
806
+ if (!reportPath) {
807
+ throw new Error(`Failed to save analysis report for ${language}`);
808
+ }
809
+ const processedCount = results.length + 1;
778
810
 
779
811
  if (!args.json) {
780
812
  console.log(t('analyze.completed', { language }) || `✅ Analysis completed for ${language}`);
@@ -842,9 +874,9 @@ try {
842
874
  console.log(t('analyze.finished') || '\n✅ Analysis completed successfully!');
843
875
 
844
876
  // Only prompt for input if running standalone and not in no-prompt mode
845
- if (require.main === module && !this.noPrompt) {
846
- await this.prompt('\nPress Enter to continue...');
847
- }
877
+ if (require.main === module && !this.noPrompt && process.env.CI !== 'true' && process.stdin.isTTY && process.stdout.isTTY) {
878
+ await this.prompt('\nPress Enter to continue...');
879
+ }
848
880
  this.closeReadline();
849
881
 
850
882
  return results;
@@ -881,8 +913,8 @@ try {
881
913
  const uiLanguage = this.config.uiLanguage || 'en';
882
914
  loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
883
915
 
884
- this.sourceDir = this.config.sourceDir;
885
- this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
916
+ this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
917
+ this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
886
918
  this.outputDir = this.config.outputDir;
887
919
  }
888
920
 
@@ -921,19 +953,20 @@ try {
921
953
  loadTranslations(args.uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));}
922
954
 
923
955
  // Update config if source directory is provided
924
- if (args.sourceDir) {
925
- this.config.sourceDir = args.sourceDir;
926
- this.sourceDir = path.resolve(PROJECT_ROOT, this.config.sourceDir);
927
- this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
928
- }
956
+ const localeDirArg = args.i18nDir || (!args.i18nDir && args.sourceDir ? args.sourceDir : null);
957
+ if (localeDirArg) {
958
+ this.config.i18nDir = localeDirArg;
959
+ this.sourceDir = path.resolve(PROJECT_ROOT, localeDirArg);
960
+ this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
961
+ }
929
962
 
930
963
  if (args.outputDir) {
931
964
  this.config.outputDir = args.outputDir;
932
965
  this.outputDir = path.resolve(this.config.outputDir);
933
966
  }
934
- const execute = async () => {
935
- await this.analyze();
936
- };
967
+ const execute = async () => {
968
+ return await this.analyze();
969
+ };
937
970
 
938
971
  if (args.watch) {
939
972
  await execute();
@@ -949,19 +982,21 @@ try {
949
982
  });
950
983
  console.log('��� Watching for translation changes. Press Ctrl+C to exit.');
951
984
  } else {
952
- await execute();
953
- if (!fromMenu && require.main === module) {
954
- process.exit(0);
955
- }
956
- }
957
- } catch (error) {
958
- console.error(t('analyze.error') || '❌ Analysis failed:', error.message);
959
- this.closeReadline();
960
- if (!fromMenu && require.main === module) {
961
- process.exit(1);
962
- }
963
- }
964
- }
985
+ const result = await execute();
986
+ if (!fromMenu && require.main === module) {
987
+ process.exit(0);
988
+ }
989
+ return result;
990
+ }
991
+ } catch (error) {
992
+ console.error(t('analyze.error') || '❌ Analysis failed:', error.message);
993
+ this.closeReadline();
994
+ if (!fromMenu && require.main === module) {
995
+ process.exit(1);
996
+ }
997
+ throw error;
998
+ }
999
+ }
965
1000
 
966
1001
  async runSetupWizard() {
967
1002
  console.log('Translation Analysis Setup Wizard');
@@ -16,9 +16,10 @@ const path = require('path');
16
16
  const fs = require('fs');
17
17
  const SecurityUtils = require('../utils/security');
18
18
  const { loadTranslations, t } = require('../utils/i18n-helper');
19
- const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
20
- const SetupEnforcer = require('../utils/setup-enforcer');
21
- const { getUnifiedConfig } = require('../utils/config-helper');
19
+ const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
20
+ const SetupEnforcer = require('../utils/setup-enforcer');
21
+ const { getUnifiedConfig, displayHelp } = require('../utils/config-helper');
22
+ const { isInteractive } = require('../utils/prompt-helper');
22
23
 
23
24
  // Ensure setup is complete before running, except for help output.
24
25
  (async () => {
@@ -64,7 +65,7 @@ class I18nCompletionTool {
64
65
 
65
66
  const baseConfig = await getUnifiedConfig('complete', args);
66
67
  this.config = { ...baseConfig, ...(this.config || {}) };
67
- this.sourceDir = this.config.sourceDir;
68
+ this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
68
69
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
69
70
 
70
71
  // Validate source directory exists
@@ -99,24 +100,31 @@ class I18nCompletionTool {
99
100
  const args = process.argv.slice(2);
100
101
  const parsed = {};
101
102
 
102
- args.forEach(arg => {
103
- if (arg.startsWith('--')) {
104
- const [key, value] = arg.substring(2).split('=');
105
- if (key === 'source-dir') {
106
- parsed.sourceDir = value;
107
- } else if (key === 'source-language') {
108
- parsed.sourceLanguage = value;
109
- } else if (key === 'auto-translate') {
103
+ for (let i = 0; i < args.length; i++) {
104
+ const arg = args[i];
105
+ if (arg.startsWith('--')) {
106
+ const [key, rawValue] = arg.substring(2).split('=');
107
+ const value = rawValue !== undefined
108
+ ? rawValue
109
+ : (args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true);
110
+ if (key === 'source-dir' || key === 'locales-dir' || key === 'i18n-dir') {
111
+ parsed.sourceDir = value;
112
+ parsed.i18nDir = value;
113
+ } else if (key === 'source-language' || key === 'source-locale') {
114
+ parsed.sourceLanguage = value;
115
+ } else if (key === 'code-dir' || key === 'source-code-dir') {
116
+ parsed.codeDir = value;
117
+ } else if (key === 'auto-translate') {
110
118
  parsed.autoTranslate = true;
111
119
  } else if (key === 'dry-run') {
112
120
  parsed.dryRun = true;
113
121
  } else if (key === 'no-prompt') {
114
122
  parsed.noPrompt = true;
115
123
  } else if (key === 'help' || key === 'h') {
116
- parsed.help = true;
117
- }
118
- }
119
- });
124
+ parsed.help = true;
125
+ }
126
+ }
127
+ }
120
128
 
121
129
  return parsed;
122
130
  }
@@ -567,22 +575,24 @@ class I18nCompletionTool {
567
575
 
568
576
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
569
577
  loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
570
- this.sourceDir = this.config.sourceDir;
578
+ this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
571
579
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
572
580
  } else {
573
581
  await this.initialize();
574
582
 
575
- if (args.sourceDir) {
576
- const resolvedSourceDir = path.resolve(args.sourceDir);
577
- const validatedSourceDir = SecurityUtils.validatePath(resolvedSourceDir, process.cwd());
578
- if (!validatedSourceDir) {
579
- console.error(t("complete.invalidSourceDir", { sourceDir: args.sourceDir }));
580
- console.error(`Path validation failed source directory is outside the allowed project boundary.`);
581
- process.exit(1);
582
- }
583
- this.config.sourceDir = resolvedSourceDir;
584
- this.sourceDir = validatedSourceDir;
585
- }
583
+ const localesDirArg = args.i18nDir || args.sourceDir;
584
+ if (localesDirArg) {
585
+ const resolvedSourceDir = path.resolve(localesDirArg);
586
+ const validatedSourceDir = SecurityUtils.validatePath(resolvedSourceDir, process.cwd());
587
+ if (!validatedSourceDir) {
588
+ console.error(t("complete.invalidSourceDir", { sourceDir: localesDirArg }));
589
+ console.error(`Path validation failed — source directory is outside the allowed project boundary.`);
590
+ process.exit(1);
591
+ }
592
+ this.config.sourceDir = resolvedSourceDir;
593
+ this.config.i18nDir = resolvedSourceDir;
594
+ this.sourceDir = validatedSourceDir;
595
+ }
586
596
 
587
597
  if (args.sourceLanguage) {
588
598
  this.config.sourceLanguage = SecurityUtils.sanitizeInput(args.sourceLanguage, {
@@ -643,15 +653,24 @@ class I18nCompletionTool {
643
653
  }
644
654
 
645
655
  console.log('\n');
646
- console.log(t("complete.summaryTitle"));
647
- console.log(t("complete.separator"));
648
- console.log(t("complete.totalChanges", { totalChanges }));
649
- console.log(t("complete.languagesProcessed", { languagesProcessed: languages.length }));
650
- console.log(t("complete.missingKeysAdded", { missingKeysAdded: missingKeys.length }));
651
-
652
- if (!args.dryRun && allChanges.length > 0 && !args.noPrompt) {
653
- const rl = this.rl || this.initReadline();
654
- const answer = await this.prompt('\n' + t('complete.generateReportPrompt') + ' (Y/N): ');
656
+ console.log(t("complete.summaryTitle"));
657
+ console.log(t("complete.separator"));
658
+ const targetLocalesChanged = allChanges.length;
659
+ const filesModified = new Set(allChanges.flatMap(lang => lang.changes.map(change => `${lang.language}/${change.file}`))).size;
660
+ const filesSkipped = Math.max(0, targetLanguages.length - targetLocalesChanged);
661
+ console.log(`Locales scanned: ${languages.length}`);
662
+ console.log(`Target locales changed: ${targetLocalesChanged}`);
663
+ console.log(`Unique source keys added: ${missingKeys.length}`);
664
+ console.log(`Total key insertions: ${totalChanges}`);
665
+ console.log(`Files modified: ${args.dryRun ? 0 : filesModified}`);
666
+ console.log(`Files skipped: ${filesSkipped}`);
667
+ if (args.dryRun) {
668
+ console.log('Dry run only. No files were modified.');
669
+ }
670
+
671
+ if (!args.dryRun && allChanges.length > 0 && isInteractive(args)) {
672
+ const rl = this.rl || this.initReadline();
673
+ const answer = await this.prompt('\n' + t('complete.generateReportPrompt') + ' (Y/N): ');
655
674
 
656
675
  if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
657
676
  await this.generateReport(allChanges, languages);
@@ -22,23 +22,32 @@ function parseArgs(argv = process.argv.slice(2)) {
22
22
  help: false,
23
23
  };
24
24
 
25
- for (const raw of argv) {
25
+ for (let i = 0; i < argv.length; i++) {
26
+ const raw = argv[i];
26
27
  const arg = raw === 'report' ? '' : raw;
27
28
  if (!arg) continue;
29
+ const nextValue = () => {
30
+ if (i + 1 < argv.length && !String(argv[i + 1]).startsWith('--')) {
31
+ return argv[++i];
32
+ }
33
+ return null;
34
+ };
28
35
  if (arg === '--help' || arg === '-h') args.help = true;
29
36
  else if (arg === '--json') args.json = true;
30
37
  else if (arg === '--markdown' || arg === '--md') args.markdown = true;
31
38
  else if (arg === '--html') args.html = true;
32
39
  else if (arg.startsWith('--out=')) args.out = arg.slice('--out='.length);
33
- else if (arg === '--out') args.expectOut = true;
34
- else if (args.expectOut) {
35
- args.out = arg;
36
- args.expectOut = false;
37
- } else if (arg.startsWith('--source-dir=')) args.sourceDir = arg.slice('--source-dir='.length);
40
+ else if (arg === '--out') args.out = nextValue();
41
+ else if (arg.startsWith('--source-dir=')) args.sourceDir = arg.slice('--source-dir='.length);
42
+ else if (arg.startsWith('--code-dir=')) args.sourceDir = arg.slice('--code-dir='.length);
43
+ else if (arg.startsWith('--source-code-dir=')) args.sourceDir = arg.slice('--source-code-dir='.length);
44
+ else if (arg === '--source-dir' || arg === '--code-dir' || arg === '--source-code-dir') args.sourceDir = nextValue();
38
45
  else if (arg.startsWith('--i18n-dir=')) args.i18nDir = arg.slice('--i18n-dir='.length);
39
46
  else if (arg.startsWith('--locales-dir=')) args.i18nDir = arg.slice('--locales-dir='.length);
47
+ else if (arg === '--i18n-dir' || arg === '--locales-dir') args.i18nDir = nextValue();
40
48
  else if (arg.startsWith('--source-language=')) args.sourceLocale = arg.slice('--source-language='.length);
41
49
  else if (arg.startsWith('--source-locale=')) args.sourceLocale = arg.slice('--source-locale='.length);
50
+ else if (arg === '--source-language' || arg === '--source-locale') args.sourceLocale = nextValue();
42
51
  }
43
52
 
44
53
  if (!args.json && !args.markdown && !args.html) {
@@ -107,9 +116,13 @@ Options:
107
116
  --markdown Print/write Markdown report output
108
117
  --html Print/write HTML report output
109
118
  --out <dir> Write selected report formats into a directory
110
- --source-dir=<dir> Source code directory to scan
111
- --i18n-dir=<dir> Locale directory
112
- --source-language=<code> Source locale code (default: en)
119
+ --code-dir=<dir> Source code directory to scan
120
+ --source-code-dir=<dir> Alias for --code-dir
121
+ --source-dir=<dir> Legacy alias for --code-dir
122
+ --locales-dir=<dir> Locale directory
123
+ --i18n-dir=<dir> Alias for --locales-dir
124
+ --source-locale=<code> Source locale code (default: en)
125
+ --source-language=<code> Legacy alias for --source-locale
113
126
  `;
114
127
  }
115
128
 
@@ -133,6 +133,8 @@ class I18nTextScanner {
133
133
 
134
134
  switch (key) {
135
135
  case 'source-dir':
136
+ case 'code-dir':
137
+ case 'source-code-dir':
136
138
  parsed.sourceDir = value || '';
137
139
  if (value === args[i + 1]) i++;
138
140
  break;
@@ -167,6 +169,7 @@ class I18nTextScanner {
167
169
  parsed.includeTests = true;
168
170
  break;
169
171
  case 'source-language':
172
+ case 'source-locale':
170
173
  parsed.sourceLanguage = value || '';
171
174
  if (value === args[i + 1]) i++;
172
175
  break;
@@ -1141,7 +1141,7 @@ Generated: ${new Date().toISOString()}
1141
1141
  const key = keyValueMatch[1];
1142
1142
  const value = keyValueMatch[2];
1143
1143
 
1144
- if (key === 'source-dir' || key === 's') {
1144
+ if (key === 'source-dir' || key === 'locales-dir' || key === 'i18n-dir' || key === 's') {
1145
1145
  options['source-dir'] = value;
1146
1146
  options.s = value;
1147
1147
  options.sourceDirExplicit = true;
@@ -1162,7 +1162,7 @@ Generated: ${new Date().toISOString()}
1162
1162
  options.threshold = numValue;
1163
1163
  options.t = numValue;
1164
1164
  }
1165
- } else if (key === 'source-language') {
1165
+ } else if (key === 'source-language' || key === 'source-locale') {
1166
1166
  options['source-language'] = value;
1167
1167
  } else if (key === 'detailed' || key === 'd') {
1168
1168
  options.detailed = value.toLowerCase() !== 'false';
@@ -1184,7 +1184,7 @@ Generated: ${new Date().toISOString()}
1184
1184
  const key = match[1];
1185
1185
  const nextArg = args[i + 1];
1186
1186
 
1187
- if (key === 'source-dir' || key === 's') {
1187
+ if (key === 'source-dir' || key === 'locales-dir' || key === 'i18n-dir' || key === 's') {
1188
1188
  options['source-dir'] = nextArg || options['source-dir'];
1189
1189
  options.s = options['source-dir'];
1190
1190
  options.sourceDirExplicit = true;
@@ -1216,7 +1216,7 @@ Generated: ${new Date().toISOString()}
1216
1216
  options.t = value;
1217
1217
  }
1218
1218
  if (nextArg && !nextArg.startsWith('-') && !isNaN(parseInt(nextArg))) i++;
1219
- } else if (key === 'source-language') {
1219
+ } else if (key === 'source-language' || key === 'source-locale') {
1220
1220
  options['source-language'] = nextArg || options['source-language'];
1221
1221
  if (nextArg && !nextArg.startsWith('-')) i++;
1222
1222
  } else if (key === 'detailed' || key === 'd') {
@@ -1257,12 +1257,15 @@ I18NTK Sizing Analysis Tool
1257
1257
  Usage: i18ntk-sizing [options]
1258
1258
 
1259
1259
  Options:
1260
- -s, --source-dir <dir> Source directory containing translation files (default: ./locales)
1260
+ -s, --locales-dir <dir> Locale directory containing translation files (default: ./locales)
1261
+ --i18n-dir <dir> Alias for --locales-dir
1262
+ --source-dir <dir> Legacy alias for --locales-dir
1261
1263
  -l, --languages <langs> Comma-separated list of languages to analyze
1262
1264
  -o, --output-report Generate detailed sizing report (default: true)
1263
1265
  -f, --format <format> Output format: json, csv, table (default: table)
1264
1266
  -t, --threshold <number> Size difference threshold for warnings (%) (default: 50)
1265
- --source-language <code> Source language baseline for comparisons (default: en)
1267
+ --source-locale <code> Source locale baseline for comparisons (default: en)
1268
+ --source-language <code> Legacy alias for --source-locale
1266
1269
  -d, --detailed Generate detailed report with more information
1267
1270
  --detailed-keys Show detailed key-level analysis
1268
1271
  --predict-expansion Predict UI layout expansion risk per language