i18ntk 1.10.1 → 2.0.2
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/LICENSE +1 -1
- package/README.md +141 -1185
- package/main/i18ntk-analyze.js +149 -133
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +4 -4
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +76 -25
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +128 -29
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +10 -396
- package/main/i18ntk-sizing.js +46 -40
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +55 -19
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -30
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +13 -5
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +23 -15
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +23 -20
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +152 -103
- package/utils/config-manager.js +204 -164
- package/utils/config.js +5 -4
- package/utils/env-manager.js +256 -0
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/logger.js +6 -2
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +29 -11
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +462 -248
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/terminal-icons.js +1 -1
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -450
package/main/i18ntk-sizing.js
CHANGED
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
|
|
34
34
|
const fs = require('fs');
|
|
35
35
|
const path = require('path');
|
|
36
|
-
const { performance } = require('perf_hooks');
|
|
37
36
|
const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
38
37
|
const configManager = require('../settings/settings-manager');
|
|
39
38
|
const SecurityUtils = require('../utils/security');
|
|
@@ -51,7 +50,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
51
50
|
}
|
|
52
51
|
})();
|
|
53
52
|
|
|
54
|
-
loadTranslations(
|
|
53
|
+
loadTranslations();
|
|
55
54
|
|
|
56
55
|
// Get configuration from settings manager
|
|
57
56
|
function getConfig() {
|
|
@@ -84,7 +83,7 @@ class I18nSizingAnalyzer {
|
|
|
84
83
|
|
|
85
84
|
// Initialize i18n with UI language from config
|
|
86
85
|
const uiLanguage = options.uiLanguage || config.uiLanguage || 'en';
|
|
87
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
86
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
88
87
|
this.stats = {
|
|
89
88
|
files: {},
|
|
90
89
|
languages: {},
|
|
@@ -116,23 +115,24 @@ class I18nSizingAnalyzer {
|
|
|
116
115
|
throw new Error(t("sizing.invalidSourceDirectoryError", { sourceDir: this.sourceDir }));
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
if (!
|
|
118
|
+
if (!SecurityUtils.safeExistsSync(validatedSourceDir)) {
|
|
120
119
|
throw new Error(t("sizing.sourceDirectoryNotFoundError", { sourceDir: validatedSourceDir }));
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
const files = [];
|
|
124
|
-
const items =
|
|
123
|
+
const items = SecurityUtils.safeReaddirSync(validatedSourceDir);
|
|
125
124
|
|
|
126
125
|
// Check for nested language directories
|
|
127
126
|
for (const item of items) {
|
|
128
127
|
const itemPath = SecurityUtils.validatePath(path.join(validatedSourceDir, item), process.cwd());
|
|
129
128
|
if (!itemPath) continue;
|
|
130
129
|
|
|
131
|
-
const stat =
|
|
130
|
+
const stat = SecurityUtils.safeStatSync(itemPath);
|
|
131
|
+
if (!stat) continue;
|
|
132
132
|
|
|
133
133
|
if (stat.isDirectory()) {
|
|
134
134
|
// This is a language directory, combine all JSON files
|
|
135
|
-
const langFiles =
|
|
135
|
+
const langFiles = SecurityUtils.safeReaddirSync(itemPath)
|
|
136
136
|
.filter(file => file.endsWith('.json'))
|
|
137
137
|
.map(file => SecurityUtils.validatePath(path.join(itemPath, file), process.cwd()))
|
|
138
138
|
.filter(file => file !== null);
|
|
@@ -165,7 +165,7 @@ class I18nSizingAnalyzer {
|
|
|
165
165
|
|
|
166
166
|
// Analyze file sizes
|
|
167
167
|
analyzeFileSizes(files) {
|
|
168
|
-
|
|
168
|
+
SecurityUtils.debugLog('info', t("sizing.analyzing_file_sizes"));
|
|
169
169
|
|
|
170
170
|
files.forEach(({ language, file, path: filePath, files: langFiles }) => {
|
|
171
171
|
if (langFiles) {
|
|
@@ -176,8 +176,10 @@ class I18nSizingAnalyzer {
|
|
|
176
176
|
let lastModified = new Date(0);
|
|
177
177
|
|
|
178
178
|
langFiles.forEach(langFile => {
|
|
179
|
-
const stats =
|
|
180
|
-
|
|
179
|
+
const stats = SecurityUtils.safeStatSync(langFile, process.cwd());
|
|
180
|
+
if (!stats) return;
|
|
181
|
+
|
|
182
|
+
let content = SecurityUtils.safeReadFileSync(langFile, process.cwd(), 'utf8');
|
|
181
183
|
if (typeof content !== "string") content = "";
|
|
182
184
|
totalSize += stats.size;
|
|
183
185
|
totalLines += content.split('\n').length;
|
|
@@ -198,8 +200,10 @@ class I18nSizingAnalyzer {
|
|
|
198
200
|
};
|
|
199
201
|
} else {
|
|
200
202
|
// Handle single file structure
|
|
201
|
-
const stats =
|
|
202
|
-
|
|
203
|
+
const stats = SecurityUtils.safeStatSync(filePath, process.cwd());
|
|
204
|
+
if (!stats) return;
|
|
205
|
+
|
|
206
|
+
let content = SecurityUtils.safeReadFileSync(filePath, process.cwd(), 'utf8');
|
|
203
207
|
if (typeof content !== "string") content = "";
|
|
204
208
|
this.stats.files[language] = {
|
|
205
209
|
file,
|
|
@@ -216,7 +220,7 @@ class I18nSizingAnalyzer {
|
|
|
216
220
|
|
|
217
221
|
// Analyze translation content
|
|
218
222
|
analyzeTranslationContent(files) {
|
|
219
|
-
|
|
223
|
+
SecurityUtils.debugLog('info', t("sizing.analyzing_translation_content"));
|
|
220
224
|
|
|
221
225
|
files.forEach(({ language, path: filePath, files: langFiles }) => {
|
|
222
226
|
try {
|
|
@@ -225,7 +229,7 @@ class I18nSizingAnalyzer {
|
|
|
225
229
|
if (langFiles) {
|
|
226
230
|
// Handle nested directory structure - combine all JSON files
|
|
227
231
|
langFiles.forEach(langFile => {
|
|
228
|
-
const rawContent = SecurityUtils.safeReadFileSync(langFile, process.cwd());
|
|
232
|
+
const rawContent = SecurityUtils.safeReadFileSync(langFile, process.cwd(), 'utf8');
|
|
229
233
|
const fileContent = SecurityUtils.safeParseJSON(rawContent);
|
|
230
234
|
if (fileContent) {
|
|
231
235
|
const fileName = path.basename(langFile, '.json');
|
|
@@ -234,7 +238,7 @@ class I18nSizingAnalyzer {
|
|
|
234
238
|
});
|
|
235
239
|
} else {
|
|
236
240
|
// Handle single file structure
|
|
237
|
-
const rawContent = SecurityUtils.safeReadFileSync(filePath, process.cwd());
|
|
241
|
+
const rawContent = SecurityUtils.safeReadFileSync(filePath, process.cwd(), 'utf8');
|
|
238
242
|
combinedContent = SecurityUtils.safeParseJSON(rawContent) || {};
|
|
239
243
|
}
|
|
240
244
|
|
|
@@ -262,7 +266,7 @@ class I18nSizingAnalyzer {
|
|
|
262
266
|
});
|
|
263
267
|
|
|
264
268
|
} catch (error) {
|
|
265
|
-
|
|
269
|
+
SecurityUtils.debugLog('error', t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
|
|
266
270
|
}
|
|
267
271
|
});
|
|
268
272
|
}
|
|
@@ -312,13 +316,13 @@ class I18nSizingAnalyzer {
|
|
|
312
316
|
|
|
313
317
|
// Generate size comparison analysis
|
|
314
318
|
generateSizeComparison() {
|
|
315
|
-
|
|
319
|
+
SecurityUtils.debugLog('info', t("sizing.generating_size_comparisons"));
|
|
316
320
|
|
|
317
321
|
const languages = Object.keys(this.stats.languages);
|
|
318
322
|
const baseLanguage = languages[0]; // Use first language as baseline
|
|
319
323
|
|
|
320
324
|
if (!baseLanguage) {
|
|
321
|
-
|
|
325
|
+
SecurityUtils.debugLog('warn', t("sizing.no_languages_found_for_comparison"));
|
|
322
326
|
return;
|
|
323
327
|
}
|
|
324
328
|
|
|
@@ -491,7 +495,7 @@ class I18nSizingAnalyzer {
|
|
|
491
495
|
async generateHumanReadableReport() {
|
|
492
496
|
if (!this.outputReport) return;
|
|
493
497
|
|
|
494
|
-
|
|
498
|
+
SecurityUtils.debugLog('info', t("sizing.generating_detailed_report"));
|
|
495
499
|
|
|
496
500
|
const validatedOutputDir = SecurityUtils.validatePath(this.outputDir, process.cwd());
|
|
497
501
|
if (!validatedOutputDir) {
|
|
@@ -499,8 +503,8 @@ class I18nSizingAnalyzer {
|
|
|
499
503
|
}
|
|
500
504
|
|
|
501
505
|
// Ensure output directory exists
|
|
502
|
-
if (!
|
|
503
|
-
|
|
506
|
+
if (!SecurityUtils.safeExistsSync(validatedOutputDir)) {
|
|
507
|
+
SecurityUtils.safeMkdirSync(validatedOutputDir, { recursive: true });
|
|
504
508
|
}
|
|
505
509
|
|
|
506
510
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
@@ -514,7 +518,7 @@ class I18nSizingAnalyzer {
|
|
|
514
518
|
let textReport = this.generateTextReport(timestamp);
|
|
515
519
|
const textSuccess = await SecurityUtils.safeWriteFile(textReportPath, textReport, process.cwd());
|
|
516
520
|
if (textSuccess) {
|
|
517
|
-
|
|
521
|
+
SecurityUtils.debugLog('info', t("sizing.human_report_saved", { reportPath: textReportPath }));
|
|
518
522
|
}
|
|
519
523
|
|
|
520
524
|
// Generate JSON for programmatic access
|
|
@@ -660,7 +664,7 @@ Generated: ${new Date().toISOString()}
|
|
|
660
664
|
|
|
661
665
|
const success = await SecurityUtils.safeWriteFile(csvPath, csvContent, process.cwd());
|
|
662
666
|
if (success) {
|
|
663
|
-
|
|
667
|
+
SecurityUtils.debugLog('info', t("sizing.csv_report_saved_to", { csvPath }));
|
|
664
668
|
SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
|
|
665
669
|
} else {
|
|
666
670
|
throw new Error(t("sizing.failedToSaveCsvError"));
|
|
@@ -669,20 +673,21 @@ Generated: ${new Date().toISOString()}
|
|
|
669
673
|
|
|
670
674
|
// Main analysis method
|
|
671
675
|
async analyze() {
|
|
672
|
-
const
|
|
676
|
+
const perfTimer = SecurityUtils.getPerformanceTimer();
|
|
677
|
+
const startTime = perfTimer.now();
|
|
673
678
|
|
|
674
679
|
try {
|
|
675
|
-
|
|
676
|
-
|
|
680
|
+
SecurityUtils.debugLog('info', t("sizing.starting_i18n_sizing_analysis"));
|
|
681
|
+
SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
677
682
|
|
|
678
683
|
const files = this.getLanguageFiles();
|
|
679
684
|
|
|
680
685
|
if (files.length === 0) {
|
|
681
|
-
|
|
686
|
+
SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
|
|
682
687
|
return;
|
|
683
688
|
}
|
|
684
689
|
|
|
685
|
-
|
|
690
|
+
SecurityUtils.debugLog('info', t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
|
|
686
691
|
|
|
687
692
|
this.analyzeFileSizes(files);
|
|
688
693
|
this.analyzeTranslationContent(files);
|
|
@@ -691,16 +696,16 @@ Generated: ${new Date().toISOString()}
|
|
|
691
696
|
if (this.format === 'table') {
|
|
692
697
|
this.displayFolderResults();
|
|
693
698
|
} else if (this.format === 'json') {
|
|
694
|
-
|
|
699
|
+
SecurityUtils.debugLog('info', t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
|
|
695
700
|
}
|
|
696
701
|
|
|
697
702
|
await this.generateHumanReadableReport();
|
|
698
703
|
|
|
699
|
-
const endTime =
|
|
700
|
-
|
|
704
|
+
const endTime = perfTimer.now();
|
|
705
|
+
SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
|
|
701
706
|
|
|
702
707
|
} catch (error) {
|
|
703
|
-
|
|
708
|
+
SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
704
709
|
process.exit(1);
|
|
705
710
|
}
|
|
706
711
|
}
|
|
@@ -901,19 +906,20 @@ Options:
|
|
|
901
906
|
// Main analysis method
|
|
902
907
|
async analyze() {
|
|
903
908
|
try {
|
|
904
|
-
|
|
905
|
-
|
|
909
|
+
SecurityUtils.debugLog('info', t("sizing.starting_analysis"));
|
|
910
|
+
SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
906
911
|
|
|
907
|
-
const
|
|
912
|
+
const perfTimer = SecurityUtils.getPerformanceTimer();
|
|
913
|
+
const startTime = perfTimer.now();
|
|
908
914
|
|
|
909
915
|
// Get language files
|
|
910
916
|
const files = this.getLanguageFiles();
|
|
911
917
|
if (files.length === 0) {
|
|
912
|
-
|
|
918
|
+
SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
|
|
913
919
|
return { success: false, error: "No translation files found" };
|
|
914
920
|
}
|
|
915
921
|
|
|
916
|
-
|
|
922
|
+
SecurityUtils.debugLog('info', t("sizing.found_files", { count: files.length }));
|
|
917
923
|
|
|
918
924
|
// Analyze file sizes
|
|
919
925
|
this.analyzeFileSizes(files);
|
|
@@ -930,15 +936,15 @@ Options:
|
|
|
930
936
|
// Generate reports if requested
|
|
931
937
|
await this.generateHumanReadableReport();
|
|
932
938
|
|
|
933
|
-
const endTime =
|
|
939
|
+
const endTime = perfTimer.now();
|
|
934
940
|
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
|
935
941
|
|
|
936
|
-
|
|
942
|
+
SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration }));
|
|
937
943
|
|
|
938
944
|
return { success: true, stats: this.stats };
|
|
939
945
|
|
|
940
946
|
} catch (error) {
|
|
941
|
-
|
|
947
|
+
SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
942
948
|
return { success: false, error: error.message };
|
|
943
949
|
}
|
|
944
950
|
}
|
package/main/i18ntk-summary.js
CHANGED
|
@@ -19,7 +19,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
19
19
|
}
|
|
20
20
|
})();
|
|
21
21
|
|
|
22
|
-
loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
22
|
+
loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class I18nSummaryReporter {
|
|
@@ -54,7 +54,7 @@ class I18nSummaryReporter {
|
|
|
54
54
|
this.config = baseConfig;
|
|
55
55
|
|
|
56
56
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
57
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
57
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
58
58
|
|
|
59
59
|
this.sourceDir = this.config.sourceDir;
|
|
60
60
|
|
|
@@ -112,12 +112,12 @@ class I18nSummaryReporter {
|
|
|
112
112
|
|
|
113
113
|
// Get all available languages
|
|
114
114
|
getAvailableLanguages() {
|
|
115
|
-
if (!
|
|
115
|
+
if (!SecurityUtils.safeExistsSync(this.config.sourceDir, this.config.sourceDir)) {
|
|
116
116
|
return [];
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// Check for monolith JSON files (en.json, es.json, etc.)
|
|
120
|
-
const files =
|
|
120
|
+
const files = SecurityUtils.safeReaddirSync(this.config.sourceDir, this.config.sourceDir) || [];
|
|
121
121
|
const languages = files
|
|
122
122
|
.filter(file => file.endsWith('.json'))
|
|
123
123
|
.map(file => path.basename(file, '.json'));
|
|
@@ -126,7 +126,8 @@ class I18nSummaryReporter {
|
|
|
126
126
|
const directories = fs.readdirSync(this.config.sourceDir)
|
|
127
127
|
.filter(item => {
|
|
128
128
|
const itemPath = path.join(this.config.sourceDir, item);
|
|
129
|
-
|
|
129
|
+
const stats = SecurityUtils.safeStatSync(itemPath, this.config.sourceDir);
|
|
130
|
+
return stats && stats.isDirectory() &&
|
|
130
131
|
!item.startsWith('.') &&
|
|
131
132
|
item !== 'node_modules';
|
|
132
133
|
});
|
|
@@ -138,11 +139,11 @@ class I18nSummaryReporter {
|
|
|
138
139
|
getLanguageFiles(language) {
|
|
139
140
|
const languageDir = path.join(this.config.sourceDir, language);
|
|
140
141
|
|
|
141
|
-
if (!
|
|
142
|
+
if (!SecurityUtils.safeExistsSync(languageDir, this.config.sourceDir)) {
|
|
142
143
|
return [];
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
return
|
|
146
|
+
return SecurityUtils.safeReaddirSync(languageDir, this.config.sourceDir) || []
|
|
146
147
|
.filter(file => {
|
|
147
148
|
return this.config.supportedExtensions.some(ext => file.endsWith(ext)) &&
|
|
148
149
|
!this.config.excludeFiles.includes(file);
|
|
@@ -153,7 +154,7 @@ class I18nSummaryReporter {
|
|
|
153
154
|
// Get file size information
|
|
154
155
|
getFileSize(filePath) {
|
|
155
156
|
try {
|
|
156
|
-
const stats =
|
|
157
|
+
const stats = SecurityUtils.safeStatSync(filePath, this.config.sourceDir);
|
|
157
158
|
return {
|
|
158
159
|
size: stats.size,
|
|
159
160
|
sizeFormatted: this.formatFileSize(stats.size),
|
|
@@ -177,10 +178,10 @@ class I18nSummaryReporter {
|
|
|
177
178
|
calculateFolderSize(folderPath) {
|
|
178
179
|
let totalSize = 0;
|
|
179
180
|
try {
|
|
180
|
-
const items =
|
|
181
|
+
const items = SecurityUtils.safeReaddirSync(folderPath, this.config.sourceDir) || [];
|
|
181
182
|
for (const item of items) {
|
|
182
183
|
const itemPath = path.join(folderPath, item);
|
|
183
|
-
const stats =
|
|
184
|
+
const stats = SecurityUtils.safeStatSync(itemPath, this.config.sourceDir);
|
|
184
185
|
if (stats.isDirectory()) {
|
|
185
186
|
totalSize += this.calculateFolderSize(itemPath);
|
|
186
187
|
} else {
|
|
@@ -340,7 +341,7 @@ class I18nSummaryReporter {
|
|
|
340
341
|
// Calculate folder sizes for each language
|
|
341
342
|
for (const language of this.stats.languages) {
|
|
342
343
|
const languageDir = path.join(this.config.sourceDir, language);
|
|
343
|
-
if (
|
|
344
|
+
if (SecurityUtils.safeExistsSync(languageDir, this.config.sourceDir)) {
|
|
344
345
|
this.stats.folderSizes[language] = this.calculateFolderSize(languageDir);
|
|
345
346
|
}
|
|
346
347
|
}
|
|
@@ -830,7 +831,7 @@ class I18nSummaryReporter {
|
|
|
830
831
|
this.config = { ...this.config, ...baseConfig };
|
|
831
832
|
|
|
832
833
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
833
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
834
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
834
835
|
if (!this.config.sourceDir) {
|
|
835
836
|
this.config.sourceDir = this.detectI18nDirectory();
|
|
836
837
|
}
|
|
@@ -865,7 +866,7 @@ class I18nSummaryReporter {
|
|
|
865
866
|
}
|
|
866
867
|
|
|
867
868
|
// Validate source directory exists
|
|
868
|
-
if (!
|
|
869
|
+
if (!SecurityUtils.safeExistsSync(this.config.sourceDir, this.config.sourceDir)) {
|
|
869
870
|
console.error(t('summary.sourceDirectoryDoesNotExist', { sourceDir: this.config.sourceDir }));
|
|
870
871
|
process.exit(1);
|
|
871
872
|
}
|
|
@@ -898,7 +899,7 @@ class I18nSummaryReporter {
|
|
|
898
899
|
|
|
899
900
|
// Create output directory if it doesn't exist
|
|
900
901
|
const outputDir = path.resolve(process.cwd(), 'i18ntk-reports');
|
|
901
|
-
if (!
|
|
902
|
+
if (!SecurityUtils.safeExistsSync(outputDir)) {
|
|
902
903
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
903
904
|
}
|
|
904
905
|
|
|
@@ -923,7 +924,7 @@ class I18nSummaryReporter {
|
|
|
923
924
|
if (args.outputFile) {
|
|
924
925
|
// Always save summary reports to i18ntk-reports
|
|
925
926
|
const reportsDir = path.resolve(process.cwd(), 'i18ntk-reports');
|
|
926
|
-
if (!
|
|
927
|
+
if (!SecurityUtils.safeExistsSync(reportsDir)) {
|
|
927
928
|
fs.mkdirSync(reportsDir, { recursive: true });
|
|
928
929
|
}
|
|
929
930
|
const outputFileName = args.outputFile ? path.basename(args.outputFile) : `summary-report-${new Date().toISOString().slice(0,10)}.txt`;
|
|
@@ -943,8 +944,8 @@ class I18nSummaryReporter {
|
|
|
943
944
|
console.log(t('summary.cleaningUpReportFiles'));
|
|
944
945
|
try {
|
|
945
946
|
const reportsDir = path.join(this.config.sourceDir, 'scripts', 'i18n', 'reports');
|
|
946
|
-
if (
|
|
947
|
-
const files =
|
|
947
|
+
if (SecurityUtils.safeExistsSync(reportsDir)) {
|
|
948
|
+
const files = SecurityUtils.safeReaddirSync(reportsDir, this.config.sourceDir) || [];
|
|
948
949
|
const reportFiles = files.filter(file =>
|
|
949
950
|
(file.endsWith('.txt') || file.endsWith('.json') || file.endsWith('.log')) &&
|
|
950
951
|
file !== path.basename(args.outputFile || '')
|
|
@@ -953,7 +954,9 @@ class I18nSummaryReporter {
|
|
|
953
954
|
let deletedCount = 0;
|
|
954
955
|
for (const file of reportFiles) {
|
|
955
956
|
try {
|
|
956
|
-
fs.unlinkSync
|
|
957
|
+
// Note: Using direct fs.unlinkSync as SecurityUtils.safeUnlinkSync doesn't exist yet
|
|
958
|
+
// This should be replaced with a secure version when available
|
|
959
|
+
fs.unlinkSync(path.join(reportsDir, file));
|
|
957
960
|
deletedCount++;
|
|
958
961
|
} catch (error) {
|
|
959
962
|
console.log(t('summary.couldNotDelete', { file, error: error.message }));
|
package/main/i18ntk-ui.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const SettingsManager = require('../settings/settings-manager');
|
|
9
|
+
const SecurityUtils = require('../utils/security');
|
|
9
10
|
const legacyConfigManager = require('../utils/config-manager');
|
|
10
11
|
const { getIcon, isUnicodeSupported } = require('../utils/terminal-icons');
|
|
11
12
|
const configManager = new SettingsManager();
|
|
@@ -14,7 +15,7 @@ class UIi18n {
|
|
|
14
15
|
constructor() {
|
|
15
16
|
this.currentLanguage = 'en';
|
|
16
17
|
this.translations = {};
|
|
17
|
-
this.uiLocalesDir = path.resolve(__dirname, '..', 'ui-locales');
|
|
18
|
+
this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
|
|
18
19
|
this.availableLanguages = [];
|
|
19
20
|
this.configFile = path.resolve(configManager.configFile);
|
|
20
21
|
|
|
@@ -29,7 +30,7 @@ this.translations = {};
|
|
|
29
30
|
const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
|
|
30
31
|
|
|
31
32
|
// Use safe defaults if config is not available
|
|
32
|
-
this.uiLocalesDir = path.resolve(__dirname, '..', 'ui-locales');
|
|
33
|
+
this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
|
|
33
34
|
this.availableLanguages = this.detectAvailableLanguages();
|
|
34
35
|
|
|
35
36
|
|
|
@@ -43,7 +44,7 @@ this.translations = {};
|
|
|
43
44
|
}
|
|
44
45
|
} catch (error) {
|
|
45
46
|
console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
|
|
46
|
-
this.uiLocalesDir = path.resolve(__dirname, '..', 'ui-locales');
|
|
47
|
+
this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
|
|
47
48
|
this.availableLanguages = this.detectAvailableLanguages();
|
|
48
49
|
this.loadLanguage('en');
|
|
49
50
|
}
|
|
@@ -57,7 +58,7 @@ this.translations = {};
|
|
|
57
58
|
const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
|
|
58
59
|
return all.filter(lang => {
|
|
59
60
|
const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
|
|
60
|
-
return
|
|
61
|
+
return SecurityUtils.safeExistsSync(filePath);
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -93,9 +94,9 @@ this.translations = {};
|
|
|
93
94
|
// Primary: Use monolith JSON file (en.json, de.json, etc.)
|
|
94
95
|
const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
|
|
95
96
|
|
|
96
|
-
if (
|
|
97
|
+
if (SecurityUtils.safeExistsSync(monolithTranslationFile)) {
|
|
97
98
|
try {
|
|
98
|
-
const content =
|
|
99
|
+
const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
|
|
99
100
|
const fullTranslations = JSON.parse(content);
|
|
100
101
|
|
|
101
102
|
// Flatten the nested structure for easier key access
|
|
@@ -113,7 +114,7 @@ this.translations = {};
|
|
|
113
114
|
// Fallback: Use folder-based structure if monolith file doesn't exist
|
|
114
115
|
const langDir = path.join(this.uiLocalesDir, language);
|
|
115
116
|
|
|
116
|
-
if (
|
|
117
|
+
if (SecurityUtils.safeExistsSync(langDir) && fs.statSync(langDir).isDirectory()) {
|
|
117
118
|
const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
|
|
118
119
|
if (debugEnabled) {
|
|
119
120
|
console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
|
|
@@ -122,7 +123,7 @@ this.translations = {};
|
|
|
122
123
|
for (const file of files) {
|
|
123
124
|
const filePath = path.join(langDir, file);
|
|
124
125
|
try {
|
|
125
|
-
const content =
|
|
126
|
+
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
126
127
|
const fileTranslations = JSON.parse(content);
|
|
127
128
|
const moduleName = path.basename(file, '.json');
|
|
128
129
|
this.translations[moduleName] = this.deepMerge(this.translations[moduleName] || {}, fileTranslations);
|
|
@@ -383,8 +384,8 @@ this.translations = {};
|
|
|
383
384
|
getEnglishFallback(keyPath, replacements = {}) {
|
|
384
385
|
try {
|
|
385
386
|
const englishFile = path.join(this.uiLocalesDir, 'en.json');
|
|
386
|
-
if (
|
|
387
|
-
const englishContent =
|
|
387
|
+
if (SecurityUtils.safeExistsSync(englishFile)) {
|
|
388
|
+
const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
|
|
388
389
|
const englishTranslations = JSON.parse(englishContent);
|
|
389
390
|
|
|
390
391
|
// Use the same flattening approach for consistency
|