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/CHANGELOG.md +182 -37
- package/README.md +27 -14
- package/main/i18ntk-analyze.js +77 -42
- package/main/i18ntk-complete.js +56 -37
- package/main/i18ntk-report.js +22 -9
- package/main/i18ntk-scanner.js +3 -0
- package/main/i18ntk-sizing.js +9 -6
- package/main/i18ntk-summary.js +61 -39
- package/main/i18ntk-usage.js +30 -17
- package/main/i18ntk-validate.js +38 -24
- package/main/manage/commands/AnalyzeCommand.js +79 -44
- package/main/manage/commands/ScannerCommand.js +13 -7
- package/main/manage/commands/ValidateCommand.js +37 -23
- package/main/manage/index.js +13 -9
- package/package.json +13 -6
- package/utils/config-helper.js +41 -31
- package/utils/exit-codes.js +8 -6
- package/utils/prompt-helper.js +1 -1
package/main/i18ntk-summary.js
CHANGED
|
@@ -56,11 +56,14 @@ class I18nSummaryReporter {
|
|
|
56
56
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
57
57
|
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
58
58
|
|
|
59
|
-
this.sourceDir = this.config.sourceDir;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
|
|
60
|
+
this.config.sourceDir = this.sourceDir;
|
|
61
|
+
|
|
62
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir, process.cwd())) {
|
|
63
|
+
const err = new Error(`Locale directory not found: ${this.sourceDir}`);
|
|
64
|
+
err.exitCode = 2;
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
64
67
|
} catch (error) {
|
|
65
68
|
console.error(`Error initializing summary reporter: ${error.message}`);
|
|
66
69
|
throw error;
|
|
@@ -523,7 +526,10 @@ class I18nSummaryReporter {
|
|
|
523
526
|
report.push(t('summary.languagesCount', {count: this.stats.languages.length}));
|
|
524
527
|
report.push(t('summary.totalFiles', {count: this.stats.totalFiles}));
|
|
525
528
|
report.push(t('summary.totalKeys', {count: this.stats.totalKeys}));
|
|
526
|
-
|
|
529
|
+
const averageKeysPerLanguage = this.stats.languages.length > 0
|
|
530
|
+
? Math.round(this.stats.totalKeys / this.stats.languages.length)
|
|
531
|
+
: 0;
|
|
532
|
+
report.push(t('summary.avgKeysPerLanguage', {count: averageKeysPerLanguage}));
|
|
527
533
|
|
|
528
534
|
// Calculate total size
|
|
529
535
|
const totalSize = Object.values(this.stats.folderSizes).reduce((sum, size) => sum + size, 0);
|
|
@@ -666,8 +672,13 @@ class I18nSummaryReporter {
|
|
|
666
672
|
}
|
|
667
673
|
report.push('');
|
|
668
674
|
}
|
|
669
|
-
} else {
|
|
670
|
-
report.push(
|
|
675
|
+
} else if (this.stats.languages.length === 0) {
|
|
676
|
+
report.push('⚠️ NO LOCALE LANGUAGES FOUND');
|
|
677
|
+
report.push('='.repeat(30));
|
|
678
|
+
report.push('No locale language files or directories were found in the selected locale root.');
|
|
679
|
+
report.push('');
|
|
680
|
+
} else {
|
|
681
|
+
report.push(t('summary.noIssuesFound'));
|
|
671
682
|
report.push('='.repeat(30));
|
|
672
683
|
report.push(t('summary.allFilesConsistent'));
|
|
673
684
|
report.push('');
|
|
@@ -795,13 +806,20 @@ class I18nSummaryReporter {
|
|
|
795
806
|
const args = process.argv.slice(2);
|
|
796
807
|
const parsed = {};
|
|
797
808
|
|
|
798
|
-
args.
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
809
|
+
for (let i = 0; i < args.length; i++) {
|
|
810
|
+
const arg = args[i];
|
|
811
|
+
if (arg.startsWith('--')) {
|
|
812
|
+
const [key, rawValue] = arg.substring(2).split('=');
|
|
813
|
+
const value = rawValue !== undefined
|
|
814
|
+
? rawValue
|
|
815
|
+
: (args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true);
|
|
816
|
+
if (key === 'source-dir' || key === 'locales-dir' || key === 'i18n-dir') {
|
|
817
|
+
parsed.sourceDir = value;
|
|
818
|
+
parsed.i18nDir = value;
|
|
819
|
+
} else if (key === 'source-language' || key === 'source-locale') {
|
|
820
|
+
parsed.sourceLanguage = value;
|
|
821
|
+
} else if (key === 'code-dir' || key === 'source-code-dir') {
|
|
822
|
+
parsed.codeDir = value;
|
|
805
823
|
} else if (key === 'output-file') {
|
|
806
824
|
parsed.outputFile = value;
|
|
807
825
|
} else if (key === 'verbose') {
|
|
@@ -813,10 +831,10 @@ class I18nSummaryReporter {
|
|
|
813
831
|
} else if (key === 'no-prompt') {
|
|
814
832
|
parsed.noPrompt = true;
|
|
815
833
|
} else if (key === 'help') {
|
|
816
|
-
parsed.help = true;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
834
|
+
parsed.help = true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
820
838
|
|
|
821
839
|
return parsed;
|
|
822
840
|
}
|
|
@@ -827,14 +845,16 @@ class I18nSummaryReporter {
|
|
|
827
845
|
const { fromMenu = false, noPrompt = false } = options;
|
|
828
846
|
|
|
829
847
|
// Always use unified configuration to respect settings
|
|
830
|
-
const baseConfig = await getUnifiedConfig('summary', args);
|
|
831
|
-
this.config = { ...this.config, ...baseConfig };
|
|
848
|
+
const baseConfig = await getUnifiedConfig('summary', args);
|
|
849
|
+
this.config = { ...this.config, ...baseConfig };
|
|
832
850
|
|
|
833
851
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
834
852
|
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
835
|
-
if (!this.config.sourceDir) {
|
|
836
|
-
this.config.sourceDir = this.detectI18nDirectory();
|
|
837
|
-
}
|
|
853
|
+
if (!this.config.sourceDir) {
|
|
854
|
+
this.config.sourceDir = this.detectI18nDirectory();
|
|
855
|
+
}
|
|
856
|
+
this.config.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
|
|
857
|
+
this.sourceDir = this.config.sourceDir;
|
|
838
858
|
|
|
839
859
|
if (!fromMenu) {
|
|
840
860
|
// Check admin authentication for sensitive operations (only when called directly)
|
|
@@ -866,10 +886,10 @@ class I18nSummaryReporter {
|
|
|
866
886
|
}
|
|
867
887
|
|
|
868
888
|
// Validate source directory exists
|
|
869
|
-
if (!SecurityUtils.safeExistsSync(this.config.sourceDir,
|
|
870
|
-
console.error(t('summary.sourceDirectoryDoesNotExist', { sourceDir: this.config.sourceDir }));
|
|
871
|
-
process.exit(
|
|
872
|
-
}
|
|
889
|
+
if (!SecurityUtils.safeExistsSync(this.config.sourceDir, process.cwd())) {
|
|
890
|
+
console.error(t('summary.sourceDirectoryDoesNotExist', { sourceDir: this.config.sourceDir }));
|
|
891
|
+
process.exit(2);
|
|
892
|
+
}
|
|
873
893
|
}
|
|
874
894
|
|
|
875
895
|
console.log(t('summary.i18nSummaryReportGenerator'));
|
|
@@ -992,17 +1012,19 @@ class I18nSummaryReporter {
|
|
|
992
1012
|
|
|
993
1013
|
if (totalIssues > 0) {
|
|
994
1014
|
console.log(t('summary.foundIssues', {count: totalIssues}));
|
|
995
|
-
} else {
|
|
996
|
-
console.log(
|
|
997
|
-
}
|
|
1015
|
+
} else if (this.stats.languages.length === 0) {
|
|
1016
|
+
console.log('⚠️ No locale languages found in the selected locale root.');
|
|
1017
|
+
} else {
|
|
1018
|
+
console.log(t('summary.noIssuesConsole'));
|
|
1019
|
+
}
|
|
998
1020
|
|
|
999
1021
|
} catch (error) {
|
|
1000
1022
|
console.error(t('summary.errorDuringAnalysis', { error: error.message }));
|
|
1001
1023
|
if (args.verbose) {
|
|
1002
1024
|
console.error(error.stack);
|
|
1003
1025
|
}
|
|
1004
|
-
process.exit(1);
|
|
1005
|
-
}
|
|
1026
|
+
process.exit(error.exitCode || 1);
|
|
1027
|
+
}
|
|
1006
1028
|
process.exit(0);
|
|
1007
1029
|
}
|
|
1008
1030
|
}
|
|
@@ -1014,10 +1036,10 @@ if (require.main === module) {
|
|
|
1014
1036
|
await reporter.initialize();
|
|
1015
1037
|
await reporter.run();
|
|
1016
1038
|
}
|
|
1017
|
-
main().catch(error => {
|
|
1018
|
-
console.error('Error in i18ntk-summary:', error.message);
|
|
1019
|
-
process.exit(1);
|
|
1020
|
-
});
|
|
1021
|
-
}
|
|
1039
|
+
main().catch(error => {
|
|
1040
|
+
console.error('Error in i18ntk-summary:', error.message);
|
|
1041
|
+
process.exit(error.exitCode || 1);
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1022
1044
|
|
|
1023
|
-
module.exports = I18nSummaryReporter;
|
|
1045
|
+
module.exports = I18nSummaryReporter;
|
package/main/i18ntk-usage.js
CHANGED
|
@@ -46,6 +46,7 @@ const JsonOutput = require('../utils/json-output');
|
|
|
46
46
|
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
47
47
|
const { resolveUsageSourceDir } = require('../utils/usage-source');
|
|
48
48
|
const { analyzeSourceForUsageInsights } = require('../utils/usage-insights');
|
|
49
|
+
const { isInteractive } = require('../utils/prompt-helper');
|
|
49
50
|
|
|
50
51
|
// Ensure setup is complete before running
|
|
51
52
|
(async () => {
|
|
@@ -195,7 +196,7 @@ class I18nUsageAnalyzer {
|
|
|
195
196
|
this.config.excludeDirs = ['node_modules', '.git'];
|
|
196
197
|
}
|
|
197
198
|
if (!Array.isArray(this.config.includeExtensions) && !Array.isArray(this.config.supportedExtensions)) {
|
|
198
|
-
this.config.includeExtensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.pyx', '.pyi'];
|
|
199
|
+
this.config.includeExtensions = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.py', '.pyx', '.pyi'];
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
await SecurityUtils.logSecurityEvent(t('usage.analyzerInitialized'), 'info', { component: 'i18ntk-usage' });
|
|
@@ -207,9 +208,10 @@ class I18nUsageAnalyzer {
|
|
|
207
208
|
|
|
208
209
|
// Normalize CLI arguments to handle both camelCase and hyphenated flags
|
|
209
210
|
normalizeArgs(a) {
|
|
210
|
-
return {
|
|
211
|
-
sourceDir: a.sourceDir ?? a['source-dir'],
|
|
212
|
-
i18nDir: a.i18nDir ?? a['i18n-dir'],
|
|
211
|
+
return {
|
|
212
|
+
sourceDir: a.sourceDir ?? a['source-dir'] ?? a['code-dir'] ?? a['source-code-dir'],
|
|
213
|
+
i18nDir: a.i18nDir ?? a['i18n-dir'] ?? a['locales-dir'],
|
|
214
|
+
sourceLanguage: a.sourceLanguage ?? a['source-language'] ?? a['source-locale'],
|
|
213
215
|
outputReport: a.outputReport ?? a['output-report'],
|
|
214
216
|
outputDir: a.outputDir ?? a['output-dir'],
|
|
215
217
|
uiLanguage: a.uiLanguage ?? a['ui-language'],
|
|
@@ -231,11 +233,18 @@ class I18nUsageAnalyzer {
|
|
|
231
233
|
const args = process.argv.slice(2);
|
|
232
234
|
const parsed = {};
|
|
233
235
|
|
|
234
|
-
for (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
for (let i = 0; i < args.length; i++) {
|
|
237
|
+
const arg = args[i];
|
|
238
|
+
if (arg.startsWith('--')) {
|
|
239
|
+
let [key, value] = arg.substring(2).split('=');
|
|
240
|
+
if (value === undefined && args[i + 1] && !args[i + 1].startsWith('-')) {
|
|
241
|
+
value = args[++i];
|
|
242
|
+
}
|
|
243
|
+
if (key === 'code-dir' || key === 'source-code-dir') key = 'source-dir';
|
|
244
|
+
if (key === 'locales-dir') key = 'i18n-dir';
|
|
245
|
+
if (key === 'source-locale') key = 'source-language';
|
|
246
|
+
if (value !== undefined) {
|
|
247
|
+
parsed[key] = value;
|
|
239
248
|
} else {
|
|
240
249
|
parsed[key] = true;
|
|
241
250
|
}
|
|
@@ -513,11 +522,15 @@ class I18nUsageAnalyzer {
|
|
|
513
522
|
this.sourceDir = path.resolve(args.sourceDir);
|
|
514
523
|
}
|
|
515
524
|
|
|
516
|
-
if (args.i18nDir) {
|
|
517
|
-
this.config.i18nDir = args.i18nDir;
|
|
518
|
-
this.i18nDir = path.resolve(args.i18nDir);
|
|
519
|
-
this.sourceLanguageDir = path.join(this.i18nDir, this.config.sourceLanguage);
|
|
520
|
-
}
|
|
525
|
+
if (args.i18nDir) {
|
|
526
|
+
this.config.i18nDir = args.i18nDir;
|
|
527
|
+
this.i18nDir = path.resolve(args.i18nDir);
|
|
528
|
+
this.sourceLanguageDir = path.join(this.i18nDir, this.config.sourceLanguage);
|
|
529
|
+
}
|
|
530
|
+
if (args.sourceLanguage) {
|
|
531
|
+
this.config.sourceLanguage = args.sourceLanguage;
|
|
532
|
+
this.sourceLanguageDir = path.join(this.i18nDir, this.config.sourceLanguage);
|
|
533
|
+
}
|
|
521
534
|
|
|
522
535
|
if (this.sourceDir || this.i18nDir) {
|
|
523
536
|
await configManager.updateConfig({
|
|
@@ -722,9 +735,9 @@ class I18nUsageAnalyzer {
|
|
|
722
735
|
|
|
723
736
|
console.log('\n' + t('usage.analysisCompletedSuccessfully'));
|
|
724
737
|
|
|
725
|
-
if (require.main === module &&
|
|
726
|
-
await this.prompt('\nPress Enter to continue...');
|
|
727
|
-
}
|
|
738
|
+
if (require.main === module && isInteractive(args)) {
|
|
739
|
+
await this.prompt('\nPress Enter to continue...');
|
|
740
|
+
}
|
|
728
741
|
this.closeReadline();
|
|
729
742
|
|
|
730
743
|
} catch (error) {
|
package/main/i18ntk-validate.js
CHANGED
|
@@ -43,8 +43,9 @@ const configManager = require('../utils/config-manager');
|
|
|
43
43
|
const SecurityUtils = require('../utils/security');
|
|
44
44
|
const AdminCLI = require('../utils/admin-cli');
|
|
45
45
|
const watchLocales = require('../utils/watch-locales');
|
|
46
|
-
const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
|
|
47
|
-
const { getUnifiedConfig, parseCommonArgs, displayHelp, validateSourceDir, displayPaths } = require('../utils/config-helper');
|
|
46
|
+
const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
|
|
47
|
+
const { getUnifiedConfig, parseCommonArgs, displayHelp, validateSourceDir, displayPaths } = require('../utils/config-helper');
|
|
48
|
+
const { isInteractive } = require('../utils/prompt-helper');
|
|
48
49
|
const I18nInitializer = require('./i18ntk-init');
|
|
49
50
|
const JsonOutput = require('../utils/json-output');
|
|
50
51
|
const ExitCodes = require('../utils/exit-codes');
|
|
@@ -105,14 +106,21 @@ class I18nValidator {
|
|
|
105
106
|
);
|
|
106
107
|
|
|
107
108
|
// Use the i18n directory for language files
|
|
108
|
-
this.sourceDir = this.config.i18nDir || this.config.sourceDir;
|
|
109
|
-
this.i18nDir = this.config.i18nDir || this.config.sourceDir;
|
|
109
|
+
this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
|
|
110
|
+
this.i18nDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
|
|
110
111
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
111
112
|
|
|
112
|
-
try {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
try {
|
|
114
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir, process.cwd())) {
|
|
115
|
+
const err = new Error(`Locale directory not found: ${this.sourceDir}`);
|
|
116
|
+
err.exitCode = 2;
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (!isInteractive(args)) {
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
console.log(t('init.requiredTitle'));
|
|
116
124
|
console.log(t('init.requiredBody'));
|
|
117
125
|
const answer = await this.prompt(t('init.promptRunNow'));
|
|
118
126
|
if (answer.trim().toLowerCase() === 'y') {
|
|
@@ -827,11 +835,13 @@ class I18nValidator {
|
|
|
827
835
|
if (args.uiLanguage) {
|
|
828
836
|
loadTranslations(args.uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));}
|
|
829
837
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
this.
|
|
833
|
-
this.
|
|
834
|
-
|
|
838
|
+
const localeDirArg = args.i18nDir || (!args.i18nDir && args.sourceDir ? args.sourceDir : null);
|
|
839
|
+
if (localeDirArg) {
|
|
840
|
+
this.config.i18nDir = localeDirArg;
|
|
841
|
+
this.sourceDir = path.resolve(localeDirArg);
|
|
842
|
+
this.i18nDir = this.sourceDir;
|
|
843
|
+
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
844
|
+
}
|
|
835
845
|
if (args.strictMode) {
|
|
836
846
|
this.config.strictMode = true;
|
|
837
847
|
}
|
|
@@ -1131,14 +1141,18 @@ class I18nValidator {
|
|
|
1131
1141
|
{ message: 'Starting validation run' }
|
|
1132
1142
|
);
|
|
1133
1143
|
|
|
1134
|
-
const result = await this.validate();
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
'
|
|
1140
|
-
|
|
1141
|
-
|
|
1144
|
+
const result = await this.validate();
|
|
1145
|
+
|
|
1146
|
+
if (result.success) {
|
|
1147
|
+
console.log(t('validate.validationProcessCompletedSuccessfully'));
|
|
1148
|
+
} else {
|
|
1149
|
+
console.log('❌ Validation failed.');
|
|
1150
|
+
}
|
|
1151
|
+
SecurityUtils.logSecurityEvent(
|
|
1152
|
+
result.success ? t('validate.runCompleted') : t('validate.runError'),
|
|
1153
|
+
result.success ? 'info' : 'error',
|
|
1154
|
+
{ message: result.success ? 'Validation run completed successfully' : 'Validation run failed' }
|
|
1155
|
+
);
|
|
1142
1156
|
return result;
|
|
1143
1157
|
};
|
|
1144
1158
|
|
|
@@ -1234,8 +1248,8 @@ if (require.main === module) {
|
|
|
1234
1248
|
timestamp: new Date().toISOString()
|
|
1235
1249
|
});
|
|
1236
1250
|
|
|
1237
|
-
process.exit(result.success ? ExitCodes.SUCCESS : ExitCodes.VALIDATION_FAILED);
|
|
1238
|
-
}
|
|
1251
|
+
process.exit(result.success ? ExitCodes.SUCCESS : ExitCodes.VALIDATION_FAILED);
|
|
1252
|
+
}
|
|
1239
1253
|
} catch (error) {
|
|
1240
1254
|
console.error(t('validate.fatalValidationError', { error: error.message }));
|
|
1241
1255
|
console.error(t('validate.stackTrace', { stack: error.stack }));
|
|
@@ -1245,7 +1259,7 @@ if (require.main === module) {
|
|
|
1245
1259
|
timestamp: new Date().toISOString()
|
|
1246
1260
|
});
|
|
1247
1261
|
|
|
1248
|
-
process.exit(ExitCodes.CONFIG_ERROR);
|
|
1262
|
+
process.exit(error.exitCode || ExitCodes.CONFIG_ERROR);
|
|
1249
1263
|
}
|
|
1250
1264
|
})();
|
|
1251
1265
|
}
|
|
@@ -80,10 +80,17 @@ class AnalyzeCommand {
|
|
|
80
80
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
81
81
|
loadTranslations(uiLanguage, path.resolve(__dirname, '../../../ui-locales'));
|
|
82
82
|
|
|
83
|
-
this.sourceDir = this.config.sourceDir;
|
|
83
|
+
this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
|
|
84
84
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
85
85
|
this.outputDir = this.config.outputDir;
|
|
86
86
|
|
|
87
|
+
const explicitLocaleDir = args.i18nDir || (!args.i18nDir && args.sourceDir ? args.sourceDir : null);
|
|
88
|
+
if (explicitLocaleDir && !SecurityUtils.safeExistsSync(this.sourceDir, process.cwd())) {
|
|
89
|
+
const err = new Error(`Locale directory not found: ${this.sourceDir}`);
|
|
90
|
+
err.exitCode = 2;
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
// Validate source directory exists and is readable/writable
|
|
88
95
|
this.validateSourceDirWithFallback(this.sourceDir);
|
|
89
96
|
|
|
@@ -766,12 +773,34 @@ class AnalyzeCommand {
|
|
|
766
773
|
}
|
|
767
774
|
}
|
|
768
775
|
|
|
769
|
-
// Show help message
|
|
770
|
-
showHelp() {
|
|
771
|
-
console.log(t('analyze.help_message'));
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
|
|
776
|
+
// Show help message
|
|
777
|
+
showHelp() {
|
|
778
|
+
console.log(t('analyze.help_message'));
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
provideSetupGuidance() {
|
|
782
|
+
return [
|
|
783
|
+
'Setup guidance:',
|
|
784
|
+
`- Ensure the locale root exists: ${this.sourceDir}`,
|
|
785
|
+
`- Ensure the source locale exists: ${path.join(this.sourceDir, this.config.sourceLanguage)}`,
|
|
786
|
+
'- Add at least one target locale, for example de/common.json or de.json.',
|
|
787
|
+
'- Prefer --locales-dir for locale files. Use --code-dir for application source files.'
|
|
788
|
+
].join('\n');
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
detectStructureType() {
|
|
792
|
+
const sourceLocaleDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
793
|
+
const sourceLocaleFile = path.join(this.sourceDir, `${this.config.sourceLanguage}.json`);
|
|
794
|
+
if (SecurityUtils.safeExistsSync(sourceLocaleFile, this.sourceDir)) {
|
|
795
|
+
return { type: 'monolith' };
|
|
796
|
+
}
|
|
797
|
+
if (SecurityUtils.safeExistsSync(sourceLocaleDir, this.sourceDir)) {
|
|
798
|
+
return { type: 'directory' };
|
|
799
|
+
}
|
|
800
|
+
return { type: 'unknown' };
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Main analyze method
|
|
775
804
|
async analyze() {
|
|
776
805
|
try {
|
|
777
806
|
const results = [];
|
|
@@ -806,11 +835,11 @@ class AnalyzeCommand {
|
|
|
806
835
|
});
|
|
807
836
|
console.log(JSON.stringify(jsonOutput.data, null, args.indent || 2));
|
|
808
837
|
return;
|
|
809
|
-
}
|
|
810
|
-
console.log(error);
|
|
811
|
-
console.log('\n' + guidance);
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
838
|
+
}
|
|
839
|
+
console.log(error);
|
|
840
|
+
console.log('\n' + guidance);
|
|
841
|
+
return { success: true, warnings: 1, message: error };
|
|
842
|
+
}
|
|
814
843
|
|
|
815
844
|
if (!args.json) {
|
|
816
845
|
console.log(t('analyze.foundLanguages', { count: languages.length, languages: languages.join(', ') }) || `📋 Found ${languages.length} languages to analyze: ${languages.join(', ')}`);
|
|
@@ -829,8 +858,11 @@ class AnalyzeCommand {
|
|
|
829
858
|
const report = this.generateLanguageReport(analysis);
|
|
830
859
|
|
|
831
860
|
// Save report
|
|
832
|
-
const reportPath = await this.saveReport(language, report);
|
|
833
|
-
|
|
861
|
+
const reportPath = await this.saveReport(language, report);
|
|
862
|
+
if (!reportPath) {
|
|
863
|
+
throw new Error(`Failed to save analysis report for ${language}`);
|
|
864
|
+
}
|
|
865
|
+
const processedCount = results.length + 1;
|
|
834
866
|
|
|
835
867
|
if (!args.json) {
|
|
836
868
|
console.log(t('analyze.completed', { language }) || `✅ Analysis completed for ${language}`);
|
|
@@ -898,9 +930,9 @@ class AnalyzeCommand {
|
|
|
898
930
|
console.log(t('analyze.finished') || '\n✅ Analysis completed successfully!');
|
|
899
931
|
|
|
900
932
|
// Only prompt for input if running standalone and not in no-prompt mode
|
|
901
|
-
if (require.main === module && !this.noPrompt) {
|
|
902
|
-
await this.prompt('\nPress Enter to continue...');
|
|
903
|
-
}
|
|
933
|
+
if (require.main === module && !this.noPrompt && process.env.CI !== 'true' && process.stdin.isTTY && process.stdout.isTTY) {
|
|
934
|
+
await this.prompt('\nPress Enter to continue...');
|
|
935
|
+
}
|
|
904
936
|
this.closeReadline();
|
|
905
937
|
|
|
906
938
|
return results;
|
|
@@ -937,7 +969,7 @@ class AnalyzeCommand {
|
|
|
937
969
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
938
970
|
loadTranslations(uiLanguage, path.resolve(__dirname, '../../../ui-locales'));
|
|
939
971
|
|
|
940
|
-
this.sourceDir = this.config.sourceDir;
|
|
972
|
+
this.sourceDir = this.config.localesDir || this.config.i18nDir || this.config.sourceDir;
|
|
941
973
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
942
974
|
this.outputDir = this.config.outputDir;
|
|
943
975
|
}
|
|
@@ -975,19 +1007,20 @@ class AnalyzeCommand {
|
|
|
975
1007
|
loadTranslations(args.uiLanguage, path.resolve(__dirname, '../../../ui-locales'));}
|
|
976
1008
|
|
|
977
1009
|
// Update config if source directory is provided
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
this.
|
|
981
|
-
this.
|
|
982
|
-
|
|
1010
|
+
const localeDirArg = args.i18nDir || (!args.i18nDir && args.sourceDir ? args.sourceDir : null);
|
|
1011
|
+
if (localeDirArg) {
|
|
1012
|
+
this.config.i18nDir = localeDirArg;
|
|
1013
|
+
this.sourceDir = path.resolve(PROJECT_ROOT, localeDirArg);
|
|
1014
|
+
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
1015
|
+
}
|
|
983
1016
|
|
|
984
1017
|
if (args.outputDir) {
|
|
985
1018
|
this.config.outputDir = args.outputDir;
|
|
986
1019
|
this.outputDir = path.resolve(this.config.outputDir);
|
|
987
1020
|
}
|
|
988
|
-
const execute = async () => {
|
|
989
|
-
await this.analyze();
|
|
990
|
-
};
|
|
1021
|
+
const execute = async () => {
|
|
1022
|
+
return await this.analyze();
|
|
1023
|
+
};
|
|
991
1024
|
|
|
992
1025
|
if (args.watch) {
|
|
993
1026
|
await execute();
|
|
@@ -1003,19 +1036,21 @@ class AnalyzeCommand {
|
|
|
1003
1036
|
});
|
|
1004
1037
|
console.log('��� Watching for translation changes. Press Ctrl+C to exit.');
|
|
1005
1038
|
} else {
|
|
1006
|
-
await execute();
|
|
1007
|
-
if (!fromMenu && require.main === module) {
|
|
1008
|
-
process.exit(0);
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1039
|
+
const result = await execute();
|
|
1040
|
+
if (!fromMenu && require.main === module) {
|
|
1041
|
+
process.exit(0);
|
|
1042
|
+
}
|
|
1043
|
+
return result;
|
|
1044
|
+
}
|
|
1011
1045
|
} catch (error) {
|
|
1012
1046
|
console.error(t('analyze.error') || '❌ Analysis failed:', error.message);
|
|
1013
1047
|
this.closeReadline();
|
|
1014
|
-
if (!fromMenu && require.main === module) {
|
|
1015
|
-
process.exit(1);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1048
|
+
if (!fromMenu && require.main === module) {
|
|
1049
|
+
process.exit(1);
|
|
1050
|
+
}
|
|
1051
|
+
throw error;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1019
1054
|
|
|
1020
1055
|
async runSetupWizard() {
|
|
1021
1056
|
console.log('Translation Analysis Setup Wizard');
|
|
@@ -1132,14 +1167,14 @@ class AnalyzeCommand {
|
|
|
1132
1167
|
/**
|
|
1133
1168
|
* Execute the analyze command
|
|
1134
1169
|
*/
|
|
1135
|
-
async execute(options = {}) {
|
|
1136
|
-
try {
|
|
1137
|
-
await this.initialize();
|
|
1138
|
-
await this.run(options);
|
|
1139
|
-
return { success: true, command: 'analyze' };
|
|
1140
|
-
} catch (error) {
|
|
1141
|
-
console.error(`Analyze command failed: ${error.message}`);
|
|
1142
|
-
throw error;
|
|
1170
|
+
async execute(options = {}) {
|
|
1171
|
+
try {
|
|
1172
|
+
await this.initialize();
|
|
1173
|
+
const result = await this.run(options);
|
|
1174
|
+
return result && result.success === false ? result : { success: true, command: 'analyze', result };
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
console.error(`Analyze command failed: ${error.message}`);
|
|
1177
|
+
throw error;
|
|
1143
1178
|
}
|
|
1144
1179
|
}
|
|
1145
1180
|
|
|
@@ -122,9 +122,11 @@ class ScannerCommand {
|
|
|
122
122
|
const value = valueParts.join('=');
|
|
123
123
|
|
|
124
124
|
switch (key) {
|
|
125
|
-
case 'source-dir':
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
case 'source-dir':
|
|
126
|
+
case 'code-dir':
|
|
127
|
+
case 'source-code-dir':
|
|
128
|
+
parsed.sourceDir = value || '';
|
|
129
|
+
break;
|
|
128
130
|
case 'framework':
|
|
129
131
|
parsed.framework = value || '';
|
|
130
132
|
break;
|
|
@@ -146,9 +148,13 @@ class ScannerCommand {
|
|
|
146
148
|
case 'output-report':
|
|
147
149
|
parsed.outputReport = true;
|
|
148
150
|
break;
|
|
149
|
-
case 'include-tests':
|
|
150
|
-
parsed.includeTests = true;
|
|
151
|
-
break;
|
|
151
|
+
case 'include-tests':
|
|
152
|
+
parsed.includeTests = true;
|
|
153
|
+
break;
|
|
154
|
+
case 'source-language':
|
|
155
|
+
case 'source-locale':
|
|
156
|
+
parsed.sourceLanguage = value || '';
|
|
157
|
+
break;
|
|
152
158
|
case 'help':
|
|
153
159
|
case 'h':
|
|
154
160
|
parsed.help = true;
|
|
@@ -669,4 +675,4 @@ class ScannerCommand {
|
|
|
669
675
|
}
|
|
670
676
|
}
|
|
671
677
|
|
|
672
|
-
module.exports = ScannerCommand;
|
|
678
|
+
module.exports = ScannerCommand;
|