i18ntk 4.1.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +64 -5
  2. package/README.md +73 -17
  3. package/SECURITY.md +10 -4
  4. package/main/i18ntk-analyze.js +10 -20
  5. package/main/i18ntk-backup.js +106 -44
  6. package/main/i18ntk-init.js +153 -157
  7. package/main/i18ntk-setup.js +36 -13
  8. package/main/i18ntk-sizing.js +44 -27
  9. package/main/i18ntk-translate.js +311 -41
  10. package/main/i18ntk-usage.js +272 -103
  11. package/main/i18ntk-validate.js +38 -31
  12. package/main/manage/commands/AnalyzeCommand.js +7 -17
  13. package/main/manage/commands/CommandRouter.js +6 -6
  14. package/main/manage/commands/SizingCommand.js +5 -2
  15. package/main/manage/commands/TranslateCommand.js +73 -56
  16. package/main/manage/commands/ValidateCommand.js +58 -26
  17. package/main/manage/index.js +11 -42
  18. package/main/manage/managers/InteractiveMenu.js +11 -40
  19. package/main/manage/services/InitService.js +114 -118
  20. package/main/manage/services/UsageService.js +247 -96
  21. package/package.json +19 -14
  22. package/runtime/enhanced.d.ts +5 -5
  23. package/runtime/enhanced.js +49 -25
  24. package/runtime/i18ntk.d.ts +30 -7
  25. package/runtime/index.d.ts +48 -19
  26. package/runtime/index.js +175 -90
  27. package/settings/settings-cli.js +115 -38
  28. package/settings/settings-manager.js +24 -6
  29. package/ui-locales/de.json +192 -11
  30. package/ui-locales/en.json +182 -8
  31. package/ui-locales/es.json +193 -12
  32. package/ui-locales/fr.json +189 -8
  33. package/ui-locales/ja.json +190 -8
  34. package/ui-locales/ru.json +191 -9
  35. package/ui-locales/zh.json +194 -9
  36. package/utils/cli-helper.js +8 -12
  37. package/utils/config-helper.js +1 -1
  38. package/utils/config-manager.js +8 -6
  39. package/utils/localized-confirm.js +55 -0
  40. package/utils/menu-layout.js +41 -0
  41. package/utils/report-writer.js +110 -0
  42. package/utils/security.js +15 -22
  43. package/utils/translate/api.js +31 -3
  44. package/utils/translate/placeholder.js +42 -1
  45. package/utils/translate/report.js +32 -4
  46. package/utils/translate/safe-network.js +24 -4
  47. package/utils/usage-insights.js +435 -0
  48. package/utils/usage-source.js +50 -0
  49. package/utils/watch-locales.js +1 -8
@@ -24,30 +24,33 @@ const { getFormatAdapter } = require('../utils/format-manager');
24
24
  const UIi18n = require('./i18ntk-ui');
25
25
  loadTranslations();
26
26
  const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../utils/config-helper');
27
- const { showFrameworkWarningOnce } = require('../utils/cli-helper');
28
- const { createPrompt, isInteractive } = require('../utils/prompt-helper');
27
+ const { showFrameworkWarningOnce } = require('../utils/cli-helper');
28
+ const { createPrompt, isInteractive } = require('../utils/prompt-helper');
29
+ const { parseConfirmation } = require('../utils/localized-confirm');
30
+ const { normalizeReportFormat, writeReportFile } = require('../utils/report-writer');
29
31
 
30
32
  // Language configurations with native names
31
- const LANGUAGE_CONFIG = {
32
- 'de': { name: 'German', nativeName: 'Deutsch' },
33
- 'es': { name: 'Spanish', nativeName: 'Español' },
34
- 'fr': { name: 'French', nativeName: 'Français' },
35
- 'ru': { name: 'Russian', nativeName: 'Русский' },
36
- 'it': { name: 'Italian', nativeName: 'Italiano' },
37
- 'ja': { name: 'Japanese', nativeName: '日本語' },
38
- 'ko': { name: 'Korean', nativeName: '한국어' },
39
- 'zh': { name: 'Chinese', nativeName: '中文' },
40
- 'ar': { name: 'Arabic', nativeName: 'العربية' },
41
- 'hi': { name: 'Hindi', nativeName: 'हिन्दी' },
42
- 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
43
- 'sv': { name: 'Swedish', nativeName: 'Svenska' },
44
- 'da': { name: 'Danish', nativeName: 'Dansk' },
45
- 'no': { name: 'Norwegian', nativeName: 'Norsk' },
46
- 'fi': { name: 'Finnish', nativeName: 'Suomi' },
47
- 'pl': { name: 'Polish', nativeName: 'Polski' },
48
- 'cs': { name: 'Czech', nativeName: 'Čeština' },
49
- 'hu': { name: 'Hungarian', nativeName: 'Magyar' },
50
- 'tr': { name: 'Turkish', nativeName: 'Türkçe' }
33
+ const LANGUAGE_CONFIG = {
34
+ 'en': { name: 'English', nativeName: 'English' },
35
+ 'de': { name: 'German', nativeName: 'Deutsch' },
36
+ 'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
37
+ 'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
38
+ 'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
39
+ 'it': { name: 'Italian', nativeName: 'Italiano' },
40
+ 'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
41
+ 'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
42
+ 'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
43
+ 'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' },
44
+ 'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
45
+ 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
46
+ 'sv': { name: 'Swedish', nativeName: 'Svenska' },
47
+ 'da': { name: 'Danish', nativeName: 'Dansk' },
48
+ 'no': { name: 'Norwegian', nativeName: 'Norsk' },
49
+ 'fi': { name: 'Finnish', nativeName: 'Suomi' },
50
+ 'pl': { name: 'Polish', nativeName: 'Polski' },
51
+ 'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
52
+ 'hu': { name: 'Hungarian', nativeName: 'Magyar' },
53
+ 'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' }
51
54
  };
52
55
 
53
56
  class I18nInitializer {
@@ -76,7 +79,7 @@ class I18nInitializer {
76
79
  : path.join(this.sourceDir, this.config.sourceLanguage);
77
80
 
78
81
  // Ensure defaultLanguages is properly initialized from config
79
- this.config.defaultLanguages = this.config.defaultLanguages || ['de', 'es', 'fr', 'ru'];
82
+ this.config.defaultLanguages = this.config.defaultLanguages || ['en', 'de', 'es', 'fr', 'ru'];
80
83
 
81
84
  // No longer create readline interface here - use CLI helpers
82
85
  this.rl = null;
@@ -153,7 +156,7 @@ class I18nInitializer {
153
156
  return true;
154
157
  }
155
158
  const answer = await this.prompt('\n' + t('init.continueWithoutI18nPrompt'));
156
- return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
159
+ return parseConfirmation(answer, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false });
157
160
  }
158
161
 
159
162
  // Add the missing prompt method
@@ -767,7 +770,7 @@ class I18nInitializer {
767
770
  await flushStdout();
768
771
  const enableProtection = await ask('\n' + t('adminPin.setup_prompt'));
769
772
 
770
- if (enableProtection.toLowerCase() === 'y' || enableProtection.toLowerCase() === 'yes') {
773
+ if (parseConfirmation(enableProtection, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false })) {
771
774
  try {
772
775
  const adminAuth = new AdminAuth();
773
776
  await adminAuth.initialize();
@@ -816,16 +819,16 @@ class I18nInitializer {
816
819
  }
817
820
 
818
821
  const { ask } = require('../utils/cli');
819
- console.log('\nBackup Settings');
820
- console.log('Backups are disabled by default to avoid backup recursion and repo pollution.');
821
- const enableAnswer = await ask('Enable automatic backups? (y/N): ');
822
- const enabled = ['y', 'yes'].includes(String(enableAnswer || '').trim().toLowerCase());
822
+ console.log('\n' + t('init.backup.title'));
823
+ console.log(t('init.backup.description'));
824
+ const enableAnswer = await ask(t('init.backup.enablePrompt'));
825
+ const enabled = parseConfirmation(enableAnswer, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false });
823
826
 
824
827
  if (!enabled) {
825
828
  return defaultBackupConfig;
826
829
  }
827
830
 
828
- const keepAnswer = await ask('How many backups should be kept automatically (1-3, default 1): ');
831
+ const keepAnswer = await ask(t('init.backup.keepPrompt'));
829
832
  const parsedKeep = parseInt(String(keepAnswer || '').trim(), 10);
830
833
  const maxBackups = Number.isInteger(parsedKeep) ? Math.min(Math.max(parsedKeep, 1), 3) : 1;
831
834
 
@@ -895,7 +898,7 @@ class I18nInitializer {
895
898
  let perLanguage = [];
896
899
  if (structure !== 'existing') {
897
900
  const duplicateChoice = await ask('\n' + t('init.setup.apply_all_prompt'));
898
- duplicateStructure = duplicateChoice.toLowerCase() === 'y' || duplicateChoice.toLowerCase() === 'yes';
901
+ duplicateStructure = parseConfirmation(duplicateChoice, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: true });
899
902
  if (!duplicateStructure) {
900
903
  // Prompt for languages to include/exclude
901
904
  console.log(t('init.setup.per_language_intro'));
@@ -1054,138 +1057,131 @@ class I18nInitializer {
1054
1057
  console.log(t('init.nextStep3'));
1055
1058
  }
1056
1059
 
1057
- // Generate completion summary with proper error handling
1058
- async generateCompletionSummary(results, targetLanguages) {
1059
- try {
1060
- console.log('\n' + '='.repeat(50));
1061
- console.log(t('init.initializationSummaryTitle'));
1062
- console.log(t('common.separator'));
1063
-
1064
- let totalChanges = 0;
1065
- let languagesProcessed = 0;
1066
- let missingKeysAdded = 0;
1067
-
1068
- Object.entries(results || {}).forEach(([lang, data]) => {
1069
- if (!data || typeof data !== 'object') return;
1070
-
1071
- const langName = LANGUAGE_CONFIG[lang]?.name || 'Unknown';
1072
- const stats = data.totalStats || { total: 0, translated: 0, percentage: 0, missing: 0 };
1073
-
1074
- const statusIcon = stats.percentage === 100 ? '✅' : stats.percentage >= 80 ? '🟡' : '🔴';
1075
-
1076
- console.log(
1077
- t('init.languageSummary', {
1078
- icon: statusIcon,
1079
- name: langName,
1080
- code: lang,
1081
- percentage: stats.percentage || 0,
1082
- })
1083
- );
1084
-
1085
- if (data.files && Array.isArray(data.files)) {
1086
- console.log(t('init.languageFiles', { count: data.files.length }));
1087
- }
1088
-
1089
- console.log(
1090
- t('init.languageKeys', {
1091
- translated: stats.translated || 0,
1092
- total: stats.total || 0,
1093
- })
1094
- );
1095
-
1096
- console.log(t('init.languageMissing', { count: stats.missing || 0 }));
1097
-
1098
- totalChanges += (stats.translated || 0) + (stats.missing || 0);
1099
- languagesProcessed += 1;
1100
- missingKeysAdded += stats.missing || 0;
1101
- });
1102
-
1103
- console.log('\n📊 COMPLETION SUMMARY');
1104
- console.log(t('common.separator'));
1105
- console.log(`📝 Total changes: ${totalChanges}`);
1106
- console.log(`🌍 Languages processed: ${languagesProcessed}`);
1107
- console.log(`➕ Missing keys added: ${missingKeysAdded}`);
1108
-
1109
- if (process.stdin.isTTY && !this.config?.noPrompt) {
1110
- const { ask } = require('../utils/cli');
1111
- const generateReport = await ask('\n🤖 Would you like a report generated? (Y/N): ');
1112
- if (generateReport.toLowerCase() === 'y' || generateReport.toLowerCase() === 'yes') {
1113
- await this.generateDetailedReport(results, targetLanguages);
1114
- }
1115
- }
1116
- } catch (error) {
1117
- console.error('\n❌ Error during completion:', error.message);
1118
- console.log('📊 COMPLETION SUMMARY (Basic)');
1119
- console.log(t('common.separator'));
1120
- console.log(`🌍 Languages processed: ${Object.keys(results || {}).length}`);
1121
- }
1122
- }
1060
+ // Generate completion summary with proper error handling
1061
+ async generateCompletionSummary(results, targetLanguages) {
1062
+ return await this.generateLocalizedCompletionSummary(results, targetLanguages);
1063
+ }
1123
1064
 
1124
- // Generate detailed report
1065
+ async generateLocalizedCompletionSummary(results, targetLanguages) {
1066
+ try {
1067
+ console.log('\n' + '='.repeat(50));
1068
+ console.log(t('init.initializationSummaryTitle'));
1069
+ console.log(t('common.separator'));
1070
+
1071
+ let totalChanges = 0;
1072
+ let languagesProcessed = 0;
1073
+ let missingKeysAdded = 0;
1074
+
1075
+ Object.entries(results || {}).forEach(([lang, data]) => {
1076
+ if (!data || typeof data !== 'object') return;
1077
+
1078
+ const langName = LANGUAGE_CONFIG[lang]?.name || 'Unknown';
1079
+ const stats = data.totalStats || { total: 0, translated: 0, percentage: 0, missing: 0 };
1080
+ const statusIcon = stats.percentage === 100 ? '✅' : stats.percentage >= 80 ? '🟡' : '🔴';
1081
+
1082
+ console.log(t('init.languageSummary', {
1083
+ icon: statusIcon,
1084
+ name: langName,
1085
+ code: lang,
1086
+ percentage: stats.percentage || 0,
1087
+ }));
1088
+
1089
+ if (Array.isArray(data.files)) {
1090
+ console.log(t('init.languageFiles', { count: data.files.length }));
1091
+ }
1092
+ console.log(t('init.languageKeys', { translated: stats.translated || 0, total: stats.total || 0 }));
1093
+ console.log(t('init.languageMissing', { count: stats.missing || 0 }));
1094
+
1095
+ totalChanges += (stats.translated || 0) + (stats.missing || 0);
1096
+ languagesProcessed += 1;
1097
+ missingKeysAdded += stats.missing || 0;
1098
+ });
1099
+
1100
+ console.log('\n' + t('init.completionSummaryTitle'));
1101
+ console.log(t('common.separator'));
1102
+ console.log(t('init.totalChanges', { count: totalChanges }));
1103
+ console.log(t('init.languagesProcessed', { count: languagesProcessed }));
1104
+ console.log(t('init.missingKeysAdded', { count: missingKeysAdded }));
1105
+
1106
+ if (process.stdin.isTTY && !this.config?.noPrompt) {
1107
+ const { ask } = require('../utils/cli');
1108
+ const generateReport = await ask('\n' + t('init.reportPrompt'));
1109
+ if (parseConfirmation(generateReport, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: true })) {
1110
+ await this.generateDetailedReport(results, targetLanguages);
1111
+ }
1112
+ }
1113
+ } catch (error) {
1114
+ console.error('\n' + t('init.completionError', { error: error.message }));
1115
+ console.log(t('init.completionSummaryBasicTitle'));
1116
+ console.log(t('common.separator'));
1117
+ console.log(t('init.languagesProcessed', { count: Object.keys(results || {}).length }));
1118
+ }
1119
+ }
1120
+
1121
+ // Generate detailed report
1125
1122
  async generateDetailedReport(results, targetLanguages) {
1126
1123
  try {
1127
1124
  const outputDir = this.config.outputDir || path.join(process.cwd(), 'i18ntk-reports');
1128
- if (!SecurityUtils.safeExistsSync(outputDir)) {
1129
- fs.mkdirSync(outputDir, { recursive: true });
1130
- }
1131
-
1132
- const reportPath = path.join(outputDir, 'init-report.json');
1133
- const report = {
1134
- timestamp: new Date().toISOString(),
1135
- languages: targetLanguages,
1136
- results: results,
1137
- summary: {
1138
- languagesProcessed: targetLanguages.length,
1139
- totalFiles: Object.values(results).reduce((sum, data) => sum + (data.files?.length || 0), 0),
1140
- totalKeys: Object.values(results).reduce((sum, data) => sum + (data.totalStats?.total || 0), 0),
1141
- totalMissing: Object.values(results).reduce((sum, data) => sum + (data.totalStats?.missing || 0), 0)
1142
- }
1143
- };
1144
-
1145
- await fs.promises.writeFile(reportPath, JSON.stringify(report, null, 2));
1146
- console.log(`✅ Report generated: ${reportPath}`);
1147
- } catch (error) {
1148
- console.error('❌ Failed to generate report:', error.message);
1149
- }
1150
- }
1125
+ if (!SecurityUtils.safeExistsSync(outputDir)) {
1126
+ fs.mkdirSync(outputDir, { recursive: true });
1127
+ }
1128
+
1129
+ const reportPayload = {
1130
+ timestamp: new Date().toISOString(),
1131
+ languages: targetLanguages,
1132
+ results,
1133
+ summary: {
1134
+ languagesProcessed: targetLanguages.length,
1135
+ totalFiles: Object.values(results).reduce((sum, data) => sum + (data.files?.length || 0), 0),
1136
+ totalKeys: Object.values(results).reduce((sum, data) => sum + (data.totalStats?.total || 0), 0),
1137
+ totalMissing: Object.values(results).reduce((sum, data) => sum + (data.totalStats?.missing || 0), 0)
1138
+ }
1139
+ };
1140
+ const format = normalizeReportFormat(this.config.reports?.format || this.config.reportFormat || 'markdown');
1141
+ const writtenPath = await writeReportFile(outputDir, 'init-report', reportPayload, { format, title: 'I18NTK Init Report' });
1142
+ console.log(t('init.reportGenerated', { reportPath: writtenPath }));
1143
+ } catch (error) {
1144
+ console.error(t('init.reportFailed', { error: error.message }));
1145
+ }
1146
+ }
1151
1147
 
1152
1148
  // Offer interactive locale optimization after initialization
1153
- async offerLocaleOptimization() {
1154
- try {
1155
- console.log('\n' + '='.repeat(60));
1156
- console.log('🎯 **PACKAGE SIZE OPTIMIZATION**');
1157
- console.log('='.repeat(60));
1158
-
1159
- try {
1160
- // Import locale optimizer directly
1161
- const LocaleOptimizer = require('../utils/locale-optimizer');
1162
-
1163
- // First run dry run to show current state
1164
- console.log('\n🔍 Running locale optimization preview...');
1165
- const optimizer = new LocaleOptimizer();
1166
- await optimizer.run({ dryRun: true });
1167
-
1168
- console.log('\n💡 You can reduce package size by selecting only the languages you need');
1169
-
1170
- const answer = await this.prompt('\n🤖 Would you like to run interactive optimization now? (y/n): ');
1171
-
1172
- if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
1173
- console.log('\n🚀 Starting interactive locale optimization...');
1174
- await optimizer.run({ interactive: true });
1175
- console.log('\n Package optimization completed!');
1176
- } else {
1177
- console.log('\n💡 You can run locale optimization later with:');
1178
- console.log(' node utils/locale-optimizer.js --interactive');
1179
- }
1180
- } catch (error) {
1181
- console.log('\n⚠️ Could not offer locale optimization:', error.message);
1182
- console.log('\n💡 You can run locale optimization later with:');
1183
- console.log(' node utils/locale-optimizer.js --interactive');
1184
- }
1185
- } catch (error) {
1186
- console.log('\n⚠️ Could not offer locale optimization:', error.message);
1187
- }
1188
- }
1149
+ async offerLocaleOptimization() {
1150
+ try {
1151
+ console.log('\n' + '='.repeat(60));
1152
+ console.log(t('init.optimize.title'));
1153
+ console.log('='.repeat(60));
1154
+
1155
+ try {
1156
+ // Import locale optimizer directly
1157
+ const LocaleOptimizer = require('../utils/locale-optimizer');
1158
+
1159
+ // First run dry run to show current state
1160
+ console.log('\n' + t('init.optimize.preview'));
1161
+ const optimizer = new LocaleOptimizer();
1162
+ await optimizer.run({ dryRun: true });
1163
+
1164
+ console.log('\n' + t('init.optimize.reduceTip'));
1165
+
1166
+ const answer = await this.prompt('\n' + t('init.optimize.prompt'));
1167
+
1168
+ if (parseConfirmation(answer, { language: this.config.uiLanguage || this.config.language || 'en', defaultValue: false })) {
1169
+ console.log('\n' + t('init.optimize.starting'));
1170
+ await optimizer.run({ interactive: true });
1171
+ console.log('\n' + t('init.optimize.completed'));
1172
+ } else {
1173
+ console.log('\n' + t('init.optimize.later'));
1174
+ console.log(' node utils/locale-optimizer.js --interactive');
1175
+ }
1176
+ } catch (error) {
1177
+ console.log('\n' + t('init.optimize.unavailable', { error: error.message }));
1178
+ console.log('\n' + t('init.optimize.later'));
1179
+ console.log(' node utils/locale-optimizer.js --interactive');
1180
+ }
1181
+ } catch (error) {
1182
+ console.log('\n' + t('init.optimize.unavailable', { error: error.message }));
1183
+ }
1184
+ }
1189
1185
 
1190
1186
  // Run the initialization process with admin authentication
1191
1187
  async run(options = {}) {
@@ -15,7 +15,7 @@ const SecurityUtils = require('../utils/security');
15
15
  const configManager = require('../utils/config-manager');
16
16
  const SetupService = require('./manage/services/SetupService');
17
17
 
18
- class I18nSetupManager {
18
+ class I18nSetupManager {
19
19
  constructor() {
20
20
  // Use the new SetupService for core business logic
21
21
  this.setupService = new SetupService();
@@ -26,18 +26,41 @@ class I18nSetupManager {
26
26
  return await this.setupService.setup();
27
27
  }
28
28
 
29
-
30
- }
31
-
32
- // CLI interface
33
- if (require.main === module) {
34
- const setupManager = new I18nSetupManager();
35
- setupManager.setup().catch(console.error);
36
- }
29
+
30
+ }
31
+
32
+ function printHelp() {
33
+ console.log([
34
+ '',
35
+ 'i18ntk-setup - foundational i18n toolkit setup',
36
+ '',
37
+ 'Usage:',
38
+ ' i18ntk-setup [options]',
39
+ ' node main/i18ntk-setup.js [options]',
40
+ '',
41
+ 'Options:',
42
+ ' -h, --help Show this help message',
43
+ '',
44
+ 'The setup command detects the current project, writes i18ntk settings,',
45
+ 'and generates i18ntk-setup-report.json in the current working directory.',
46
+ ].join('\n'));
47
+ }
48
+
49
+ // CLI interface
50
+ if (require.main === module) {
51
+ if (process.argv.slice(2).some(arg => arg === '--help' || arg === '-h')) {
52
+ printHelp();
53
+ process.exit(0);
54
+ }
55
+
56
+ const setupManager = new I18nSetupManager();
57
+ setupManager.setup().catch(console.error);
58
+ }
37
59
 
38
60
  // Export both the class and a run function for direct usage
39
61
  module.exports = I18nSetupManager;
40
- module.exports.run = async function() {
41
- const setupManager = new I18nSetupManager();
42
- return await setupManager.setup();
43
- };
62
+ module.exports.run = async function() {
63
+ const setupManager = new I18nSetupManager();
64
+ return await setupManager.setup();
65
+ };
66
+ module.exports.printHelp = printHelp;
@@ -31,12 +31,13 @@
31
31
  * i18ntk sizing --threshold=30 --output-report
32
32
  */
33
33
 
34
- const fs = require('fs');
35
- const path = require('path');
36
- const { loadTranslations, t } = require('../utils/i18n-helper');
37
- const configManager = require('../settings/settings-manager');
38
- const SecurityUtils = require('../utils/security');
39
- const { getUnifiedConfig } = require('../utils/config-helper');
34
+ const fs = require('fs');
35
+ const path = require('path');
36
+ const { loadTranslations, t } = require('../utils/i18n-helper');
37
+ const configManager = require('../settings/settings-manager');
38
+ const projectConfigManager = require('../utils/config-manager');
39
+ const SecurityUtils = require('../utils/security');
40
+ const { getUnifiedConfig } = require('../utils/config-helper');
40
41
  const { logger } = require('../utils/logger');
41
42
  const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
42
43
  const SetupEnforcer = require('../utils/setup-enforcer');
@@ -1120,8 +1121,10 @@ Generated: ${new Date().toISOString()}
1120
1121
  'l': '',
1121
1122
  'o': true,
1122
1123
  'f': 'table',
1123
- 't': 50,
1124
- 'd': false
1124
+ 't': 50,
1125
+ 'd': false,
1126
+ sourceDirExplicit: false,
1127
+ outputDirExplicit: false
1125
1128
  };
1126
1129
 
1127
1130
  for (let i = 0; i < args.length; i++) {
@@ -1138,9 +1141,10 @@ Generated: ${new Date().toISOString()}
1138
1141
  const key = keyValueMatch[1];
1139
1142
  const value = keyValueMatch[2];
1140
1143
 
1141
- if (key === 'source-dir' || key === 's') {
1142
- options['source-dir'] = value;
1143
- options.s = value;
1144
+ if (key === 'source-dir' || key === 's') {
1145
+ options['source-dir'] = value;
1146
+ options.s = value;
1147
+ options.sourceDirExplicit = true;
1144
1148
  } else if (key === 'languages' || key === 'l') {
1145
1149
  options.languages = value;
1146
1150
  options.l = value;
@@ -1167,9 +1171,10 @@ Generated: ${new Date().toISOString()}
1167
1171
  options['detailed-keys'] = value.toLowerCase() !== 'false';
1168
1172
  } else if (key === 'predict-expansion') {
1169
1173
  options['predict-expansion'] = value.toLowerCase() !== 'false';
1170
- } else if (key === 'output-dir') {
1171
- options['output-dir'] = value;
1172
- }
1174
+ } else if (key === 'output-dir') {
1175
+ options['output-dir'] = value;
1176
+ options.outputDirExplicit = true;
1177
+ }
1173
1178
  continue;
1174
1179
  }
1175
1180
 
@@ -1179,10 +1184,11 @@ Generated: ${new Date().toISOString()}
1179
1184
  const key = match[1];
1180
1185
  const nextArg = args[i + 1];
1181
1186
 
1182
- if (key === 'source-dir' || key === 's') {
1183
- options['source-dir'] = nextArg || options['source-dir'];
1184
- options.s = options['source-dir'];
1185
- if (nextArg && !nextArg.startsWith('-')) i++;
1187
+ if (key === 'source-dir' || key === 's') {
1188
+ options['source-dir'] = nextArg || options['source-dir'];
1189
+ options.s = options['source-dir'];
1190
+ options.sourceDirExplicit = true;
1191
+ if (nextArg && !nextArg.startsWith('-')) i++;
1186
1192
  } else if (key === 'languages' || key === 'l') {
1187
1193
  options.languages = nextArg || options.languages;
1188
1194
  options.l = options.languages;
@@ -1238,6 +1244,7 @@ Generated: ${new Date().toISOString()}
1238
1244
  }
1239
1245
  } else if (key === 'output-dir') {
1240
1246
  options['output-dir'] = nextArg || options['output-dir'];
1247
+ options.outputDirExplicit = true;
1241
1248
  if (nextArg && !nextArg.startsWith('-')) i++;
1242
1249
  }
1243
1250
  }
@@ -1268,15 +1275,25 @@ Options:
1268
1275
  return options;
1269
1276
  }
1270
1277
 
1271
- // Add run method for compatibility with manager
1272
- async run(options = {}) {
1273
- const { fromMenu = false } = options;
1274
-
1275
- const args = this.parseArgs();
1276
- const config = await getUnifiedConfig('sizing', args);
1277
-
1278
- this.sourceDir = path.resolve(config.projectRoot || '.', config.sourceDir || './locales');
1279
- this.outputDir = path.resolve(config.projectRoot || '.', config.outputDir || './i18ntk-reports');
1278
+ // Add run method for compatibility with manager
1279
+ async run(options = {}) {
1280
+ const { fromMenu = false } = options;
1281
+
1282
+ const args = this.parseArgs();
1283
+ const commonArgs = {};
1284
+ if (args.sourceDirExplicit) commonArgs.sourceDir = args['source-dir'];
1285
+ if (args.outputDirExplicit) commonArgs.outputDir = args['output-dir'];
1286
+ if (args['source-language']) commonArgs.sourceLanguage = args['source-language'];
1287
+ const persistedConfig = projectConfigManager.getConfig();
1288
+ const config = await getUnifiedConfig('sizing', commonArgs);
1289
+ const projectRoot = config.projectRoot || persistedConfig.projectRoot || process.cwd();
1290
+ const configuredSizingDir = persistedConfig.scriptDirectories?.sizing;
1291
+ const translationDir = args.sourceDirExplicit
1292
+ ? args['source-dir']
1293
+ : (configuredSizingDir || persistedConfig.i18nDir || persistedConfig.sourceDir || config.i18nDir || config.sourceDir || './locales');
1294
+
1295
+ this.sourceDir = path.resolve(projectRoot, translationDir);
1296
+ this.outputDir = path.resolve(config.projectRoot || '.', config.outputDir || './i18ntk-reports');
1280
1297
  this.threshold = args.threshold ?? config.processing?.sizingThreshold ?? 50;
1281
1298
  this.languages = args.languages ? args.languages.split(',').map(l => l.trim()) : [];
1282
1299
  this.outputReport = args['output-report'] !== undefined ? args['output-report'] : false;