i18ntk 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/main/i18ntk-analyze.js +63 -63
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +5 -5
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +35 -35
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +95 -96
- package/main/i18ntk-usage.js +14 -14
- package/main/i18ntk-validate.js +6 -5
- package/main/manage/commands/AnalyzeCommand.js +67 -67
- package/main/manage/commands/FixerCommand.js +2 -2
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +9 -9
- package/main/manage/index.js +7 -4
- package/main/manage/managers/LanguageMenu.js +7 -2
- package/main/manage/services/UsageService.js +7 -7
- package/package.json +20 -41
- package/settings/settings-cli.js +3 -3
- package/ui-locales/de.json +12 -11
- package/ui-locales/en.json +12 -11
- package/ui-locales/es.json +12 -11
- package/ui-locales/fr.json +12 -11
- package/ui-locales/ja.json +12 -11
- package/ui-locales/ru.json +12 -11
- package/ui-locales/zh.json +12 -11
- package/utils/i18n-helper.js +161 -166
- package/{scripts → utils}/locale-optimizer.js +61 -60
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -236
- package/scripts/fix-and-purify-i18n.js +0 -233
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish-dev.js +0 -221
- package/scripts/prepublish.js +0 -362
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -195
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -609
package/main/i18ntk-sizing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* I18n Sizing Analyzer
|
|
@@ -33,10 +33,12 @@
|
|
|
33
33
|
|
|
34
34
|
const fs = require('fs');
|
|
35
35
|
const path = require('path');
|
|
36
|
+
const { performance } = require('perf_hooks');
|
|
36
37
|
const { loadTranslations, t } = require('../utils/i18n-helper');
|
|
37
38
|
const configManager = require('../settings/settings-manager');
|
|
38
39
|
const SecurityUtils = require('../utils/security');
|
|
39
40
|
const { getUnifiedConfig } = require('../utils/config-helper');
|
|
41
|
+
const { logger } = require('../utils/logger');
|
|
40
42
|
const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
|
|
41
43
|
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
42
44
|
|
|
@@ -83,7 +85,7 @@ class I18nSizingAnalyzer {
|
|
|
83
85
|
|
|
84
86
|
// Initialize i18n with UI language from config
|
|
85
87
|
const uiLanguage = options.uiLanguage || config.uiLanguage || 'en';
|
|
86
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
88
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
87
89
|
this.stats = {
|
|
88
90
|
files: {},
|
|
89
91
|
languages: {},
|
|
@@ -165,7 +167,7 @@ class I18nSizingAnalyzer {
|
|
|
165
167
|
|
|
166
168
|
// Analyze file sizes
|
|
167
169
|
analyzeFileSizes(files) {
|
|
168
|
-
|
|
170
|
+
logger.info(t("sizing.analyzing_file_sizes"));
|
|
169
171
|
|
|
170
172
|
files.forEach(({ language, file, path: filePath, files: langFiles }) => {
|
|
171
173
|
if (langFiles) {
|
|
@@ -220,7 +222,7 @@ class I18nSizingAnalyzer {
|
|
|
220
222
|
|
|
221
223
|
// Analyze translation content
|
|
222
224
|
analyzeTranslationContent(files) {
|
|
223
|
-
|
|
225
|
+
logger.info(t("sizing.analyzing_translation_content"));
|
|
224
226
|
|
|
225
227
|
files.forEach(({ language, path: filePath, files: langFiles }) => {
|
|
226
228
|
try {
|
|
@@ -266,7 +268,7 @@ class I18nSizingAnalyzer {
|
|
|
266
268
|
});
|
|
267
269
|
|
|
268
270
|
} catch (error) {
|
|
269
|
-
|
|
271
|
+
logger.error(t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
|
|
270
272
|
}
|
|
271
273
|
});
|
|
272
274
|
}
|
|
@@ -316,14 +318,14 @@ class I18nSizingAnalyzer {
|
|
|
316
318
|
|
|
317
319
|
// Generate size comparison analysis
|
|
318
320
|
generateSizeComparison() {
|
|
319
|
-
|
|
321
|
+
logger.info(t("sizing.generating_size_comparisons"));
|
|
320
322
|
|
|
321
323
|
const languages = Object.keys(this.stats.languages);
|
|
322
324
|
const baseLanguage = languages[0]; // Use first language as baseline
|
|
323
325
|
|
|
324
326
|
if (!baseLanguage) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
+
logger.warn(t("sizing.no_languages_found_for_comparison"));
|
|
328
|
+
return;
|
|
327
329
|
}
|
|
328
330
|
|
|
329
331
|
this.stats.summary = {
|
|
@@ -495,7 +497,7 @@ class I18nSizingAnalyzer {
|
|
|
495
497
|
async generateHumanReadableReport() {
|
|
496
498
|
if (!this.outputReport) return;
|
|
497
499
|
|
|
498
|
-
|
|
500
|
+
logger.info(t("sizing.generating_detailed_report"));
|
|
499
501
|
|
|
500
502
|
const validatedOutputDir = SecurityUtils.validatePath(this.outputDir, process.cwd());
|
|
501
503
|
if (!validatedOutputDir) {
|
|
@@ -518,7 +520,7 @@ class I18nSizingAnalyzer {
|
|
|
518
520
|
let textReport = this.generateTextReport(timestamp);
|
|
519
521
|
const textSuccess = await SecurityUtils.safeWriteFile(textReportPath, textReport, process.cwd());
|
|
520
522
|
if (textSuccess) {
|
|
521
|
-
|
|
523
|
+
logger.info(t("sizing.human_report_saved", { reportPath: textReportPath }));
|
|
522
524
|
}
|
|
523
525
|
|
|
524
526
|
// Generate JSON for programmatic access
|
|
@@ -664,8 +666,8 @@ Generated: ${new Date().toISOString()}
|
|
|
664
666
|
|
|
665
667
|
const success = await SecurityUtils.safeWriteFile(csvPath, csvContent, process.cwd());
|
|
666
668
|
if (success) {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
+
logger.info(t("sizing.csv_report_saved_to", { csvPath }));
|
|
670
|
+
SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
|
|
669
671
|
} else {
|
|
670
672
|
throw new Error(t("sizing.failedToSaveCsvError"));
|
|
671
673
|
}
|
|
@@ -673,21 +675,20 @@ Generated: ${new Date().toISOString()}
|
|
|
673
675
|
|
|
674
676
|
// Main analysis method
|
|
675
677
|
async analyze() {
|
|
676
|
-
const
|
|
677
|
-
const startTime = perfTimer.now();
|
|
678
|
+
const startTime = performance.now();
|
|
678
679
|
|
|
679
680
|
try {
|
|
680
|
-
|
|
681
|
-
|
|
681
|
+
logger.info(t("sizing.starting_i18n_sizing_analysis"));
|
|
682
|
+
logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
682
683
|
|
|
683
684
|
const files = this.getLanguageFiles();
|
|
684
685
|
|
|
685
686
|
if (files.length === 0) {
|
|
686
|
-
|
|
687
|
-
|
|
687
|
+
logger.warn(t("sizing.no_translation_files_found"));
|
|
688
|
+
return;
|
|
688
689
|
}
|
|
689
690
|
|
|
690
|
-
|
|
691
|
+
logger.info(t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
|
|
691
692
|
|
|
692
693
|
this.analyzeFileSizes(files);
|
|
693
694
|
this.analyzeTranslationContent(files);
|
|
@@ -696,17 +697,17 @@ Generated: ${new Date().toISOString()}
|
|
|
696
697
|
if (this.format === 'table') {
|
|
697
698
|
this.displayFolderResults();
|
|
698
699
|
} else if (this.format === 'json') {
|
|
699
|
-
|
|
700
|
+
logger.info(t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
|
|
700
701
|
}
|
|
701
702
|
|
|
702
703
|
await this.generateHumanReadableReport();
|
|
703
704
|
|
|
704
|
-
const endTime =
|
|
705
|
-
|
|
705
|
+
const endTime = performance.now();
|
|
706
|
+
logger.info(t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
|
|
706
707
|
|
|
707
708
|
} catch (error) {
|
|
708
|
-
|
|
709
|
-
|
|
709
|
+
logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
710
|
+
process.exit(1);
|
|
710
711
|
}
|
|
711
712
|
}
|
|
712
713
|
|
|
@@ -906,20 +907,19 @@ Options:
|
|
|
906
907
|
// Main analysis method
|
|
907
908
|
async analyze() {
|
|
908
909
|
try {
|
|
909
|
-
|
|
910
|
-
|
|
910
|
+
logger.info(t("sizing.starting_analysis"));
|
|
911
|
+
logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
|
|
911
912
|
|
|
912
|
-
const
|
|
913
|
-
const startTime = perfTimer.now();
|
|
913
|
+
const startTime = performance.now();
|
|
914
914
|
|
|
915
915
|
// Get language files
|
|
916
916
|
const files = this.getLanguageFiles();
|
|
917
917
|
if (files.length === 0) {
|
|
918
|
-
|
|
919
|
-
|
|
918
|
+
logger.warn(t("sizing.no_translation_files_found"));
|
|
919
|
+
return { success: false, error: "No translation files found" };
|
|
920
920
|
}
|
|
921
921
|
|
|
922
|
-
|
|
922
|
+
logger.info(t("sizing.found_files", { count: files.length }));
|
|
923
923
|
|
|
924
924
|
// Analyze file sizes
|
|
925
925
|
this.analyzeFileSizes(files);
|
|
@@ -936,16 +936,16 @@ Options:
|
|
|
936
936
|
// Generate reports if requested
|
|
937
937
|
await this.generateHumanReadableReport();
|
|
938
938
|
|
|
939
|
-
const endTime =
|
|
939
|
+
const endTime = performance.now();
|
|
940
940
|
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
|
941
941
|
|
|
942
|
-
|
|
942
|
+
logger.info(t("sizing.analysis_completed", { duration }));
|
|
943
943
|
|
|
944
944
|
return { success: true, stats: this.stats };
|
|
945
945
|
|
|
946
946
|
} catch (error) {
|
|
947
|
-
|
|
948
|
-
|
|
947
|
+
logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
|
|
948
|
+
return { success: false, error: error.message };
|
|
949
949
|
}
|
|
950
950
|
}
|
|
951
951
|
}
|
package/main/i18ntk-summary.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
@@ -19,7 +19,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
19
19
|
}
|
|
20
20
|
})();
|
|
21
21
|
|
|
22
|
-
loadTranslations(
|
|
22
|
+
loadTranslations('en', path.resolve(__dirname, '..', '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, '..', '
|
|
57
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
58
58
|
|
|
59
59
|
this.sourceDir = this.config.sourceDir;
|
|
60
60
|
|
|
@@ -831,7 +831,7 @@ class I18nSummaryReporter {
|
|
|
831
831
|
this.config = { ...this.config, ...baseConfig };
|
|
832
832
|
|
|
833
833
|
const uiLanguage = this.config.uiLanguage || 'en';
|
|
834
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
834
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
835
835
|
if (!this.config.sourceDir) {
|
|
836
836
|
this.config.sourceDir = this.detectI18nDirectory();
|
|
837
837
|
}
|
package/main/i18ntk-ui.js
CHANGED
|
@@ -11,13 +11,13 @@ const legacyConfigManager = require('../utils/config-manager');
|
|
|
11
11
|
const { getIcon, isUnicodeSupported } = require('../utils/terminal-icons');
|
|
12
12
|
const configManager = new SettingsManager();
|
|
13
13
|
|
|
14
|
-
class UIi18n {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.currentLanguage = 'en';
|
|
17
|
-
this.translations = {};
|
|
18
|
-
this.uiLocalesDir = this.resolveUiLocalesDir();
|
|
19
|
-
this.availableLanguages = [];
|
|
20
|
-
this.configFile = path.resolve(configManager.configFile);
|
|
14
|
+
class UIi18n {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.currentLanguage = 'en';
|
|
17
|
+
this.translations = {};
|
|
18
|
+
this.uiLocalesDir = this.resolveUiLocalesDir();
|
|
19
|
+
this.availableLanguages = [];
|
|
20
|
+
this.configFile = path.resolve(configManager.configFile);
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
// Initialize with safe defaults
|
|
@@ -25,13 +25,13 @@ this.translations = {};
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
initialize() {
|
|
29
|
-
try {
|
|
30
|
-
const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
|
|
31
|
-
|
|
32
|
-
// Use safe defaults if config is not available
|
|
33
|
-
this.uiLocalesDir = this.resolveUiLocalesDir();
|
|
34
|
-
this.availableLanguages = this.detectAvailableLanguages();
|
|
28
|
+
initialize() {
|
|
29
|
+
try {
|
|
30
|
+
const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
|
|
31
|
+
|
|
32
|
+
// Use safe defaults if config is not available
|
|
33
|
+
this.uiLocalesDir = this.resolveUiLocalesDir();
|
|
34
|
+
this.availableLanguages = this.detectAvailableLanguages();
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
const configuredLanguage = config?.language || config?.uiLanguage || 'en';
|
|
@@ -42,71 +42,70 @@ this.translations = {};
|
|
|
42
42
|
} else {
|
|
43
43
|
this.loadLanguage('en');
|
|
44
44
|
}
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
|
|
47
|
-
this.uiLocalesDir = this.resolveUiLocalesDir();
|
|
48
|
-
this.availableLanguages = this.detectAvailableLanguages();
|
|
49
|
-
this.loadLanguage('en');
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
getValidationBase(targetPath) {
|
|
54
|
-
const fallbackBase = path.resolve(__dirname, '..');
|
|
55
|
-
if (!targetPath || typeof targetPath !== 'string') {
|
|
56
|
-
return fallbackBase;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let current = path.resolve(path.dirname(targetPath));
|
|
60
|
-
while (true) {
|
|
61
|
-
try {
|
|
62
|
-
if (fs.statSync(current).isDirectory()) {
|
|
63
|
-
return current;
|
|
64
|
-
}
|
|
65
|
-
} catch (_) {
|
|
66
|
-
// Continue walking up until an existing directory is found.
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const parent = path.dirname(current);
|
|
70
|
-
if (parent === current) {
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
current = parent;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return fallbackBase;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
resolveUiLocalesDir() {
|
|
80
|
-
const candidates = [
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
|
|
47
|
+
this.uiLocalesDir = this.resolveUiLocalesDir();
|
|
48
|
+
this.availableLanguages = this.detectAvailableLanguages();
|
|
49
|
+
this.loadLanguage('en');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getValidationBase(targetPath) {
|
|
54
|
+
const fallbackBase = path.resolve(__dirname, '..');
|
|
55
|
+
if (!targetPath || typeof targetPath !== 'string') {
|
|
56
|
+
return fallbackBase;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let current = path.resolve(path.dirname(targetPath));
|
|
60
|
+
while (true) {
|
|
61
|
+
try {
|
|
62
|
+
if (fs.statSync(current).isDirectory()) {
|
|
63
|
+
return current;
|
|
64
|
+
}
|
|
65
|
+
} catch (_) {
|
|
66
|
+
// Continue walking up until an existing directory is found.
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const parent = path.dirname(current);
|
|
70
|
+
if (parent === current) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
current = parent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return fallbackBase;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
resolveUiLocalesDir() {
|
|
80
|
+
const candidates = [
|
|
81
|
+
path.resolve(__dirname, '..', 'ui-locales')
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const candidate of candidates) {
|
|
85
|
+
try {
|
|
86
|
+
const stats = SecurityUtils.safeStatSync(candidate, this.getValidationBase(candidate));
|
|
87
|
+
if (stats && stats.isDirectory()) {
|
|
88
|
+
return candidate;
|
|
89
|
+
}
|
|
90
|
+
} catch (_) {
|
|
91
|
+
// Try next candidate.
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return candidates[0];
|
|
96
|
+
}
|
|
98
97
|
/**
|
|
99
98
|
/**
|
|
100
99
|
* Detect which UI locales are currently installed
|
|
101
100
|
* @returns {string[]} Array of available language codes
|
|
102
101
|
*/
|
|
103
|
-
detectAvailableLanguages() {
|
|
104
|
-
const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
|
|
105
|
-
return all.filter(lang => {
|
|
106
|
-
const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
|
|
107
|
-
return SecurityUtils.safeExistsSync(filePath, this.getValidationBase(filePath));
|
|
108
|
-
});
|
|
109
|
-
}
|
|
102
|
+
detectAvailableLanguages() {
|
|
103
|
+
const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
|
|
104
|
+
return all.filter(lang => {
|
|
105
|
+
const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
|
|
106
|
+
return SecurityUtils.safeExistsSync(filePath, this.getValidationBase(filePath));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
110
109
|
|
|
111
110
|
/**
|
|
112
111
|
* Refresh the list of available languages and ensure current language is valid
|
|
@@ -137,13 +136,13 @@ this.translations = {};
|
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
try {
|
|
140
|
-
// Primary: Use monolith JSON file (en.json, de.json, etc.)
|
|
141
|
-
const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
|
|
142
|
-
|
|
143
|
-
if (SecurityUtils.safeExistsSync(monolithTranslationFile, this.getValidationBase(monolithTranslationFile))) {
|
|
144
|
-
try {
|
|
145
|
-
const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
|
|
146
|
-
const fullTranslations = JSON.parse(content);
|
|
139
|
+
// Primary: Use monolith JSON file (en.json, de.json, etc.)
|
|
140
|
+
const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
|
|
141
|
+
|
|
142
|
+
if (SecurityUtils.safeExistsSync(monolithTranslationFile, this.getValidationBase(monolithTranslationFile))) {
|
|
143
|
+
try {
|
|
144
|
+
const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
|
|
145
|
+
const fullTranslations = JSON.parse(content);
|
|
147
146
|
|
|
148
147
|
// Flatten the nested structure for easier key access
|
|
149
148
|
this.translations = this.flattenTranslations(fullTranslations);
|
|
@@ -157,14 +156,14 @@ this.translations = {};
|
|
|
157
156
|
this.translations = {};
|
|
158
157
|
}
|
|
159
158
|
} else {
|
|
160
|
-
// Fallback: Use folder-based structure if monolith file doesn't exist
|
|
161
|
-
const langDir = path.join(this.uiLocalesDir, language);
|
|
162
|
-
|
|
163
|
-
const langDirStats = SecurityUtils.safeStatSync(langDir, this.getValidationBase(langDir));
|
|
164
|
-
if (langDirStats && langDirStats.isDirectory()) {
|
|
165
|
-
const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
|
|
166
|
-
if (debugEnabled) {
|
|
167
|
-
console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
|
|
159
|
+
// Fallback: Use folder-based structure if monolith file doesn't exist
|
|
160
|
+
const langDir = path.join(this.uiLocalesDir, language);
|
|
161
|
+
|
|
162
|
+
const langDirStats = SecurityUtils.safeStatSync(langDir, this.getValidationBase(langDir));
|
|
163
|
+
if (langDirStats && langDirStats.isDirectory()) {
|
|
164
|
+
const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
|
|
165
|
+
if (debugEnabled) {
|
|
166
|
+
console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
for (const file of files) {
|
|
@@ -428,12 +427,12 @@ this.translations = {};
|
|
|
428
427
|
* @param {object} replacements - Object with replacement values
|
|
429
428
|
* @returns {string|null} English translation or null if not found
|
|
430
429
|
*/
|
|
431
|
-
getEnglishFallback(keyPath, replacements = {}) {
|
|
432
|
-
try {
|
|
433
|
-
const englishFile = path.join(this.uiLocalesDir, 'en.json');
|
|
434
|
-
if (SecurityUtils.safeExistsSync(englishFile, this.getValidationBase(englishFile))) {
|
|
435
|
-
const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
|
|
436
|
-
const englishTranslations = JSON.parse(englishContent);
|
|
430
|
+
getEnglishFallback(keyPath, replacements = {}) {
|
|
431
|
+
try {
|
|
432
|
+
const englishFile = path.join(this.uiLocalesDir, 'en.json');
|
|
433
|
+
if (SecurityUtils.safeExistsSync(englishFile, this.getValidationBase(englishFile))) {
|
|
434
|
+
const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
|
|
435
|
+
const englishTranslations = JSON.parse(englishContent);
|
|
437
436
|
|
|
438
437
|
// Use the same flattening approach for consistency
|
|
439
438
|
const flattenedEnglish = this.flattenTranslations(englishTranslations);
|
|
@@ -570,4 +569,4 @@ this.translations = {};
|
|
|
570
569
|
}
|
|
571
570
|
|
|
572
571
|
// Export the class, not a singleton instance
|
|
573
|
-
module.exports = UIi18n;
|
|
572
|
+
module.exports = UIi18n;
|
package/main/i18ntk-usage.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* I18NTK USAGE ANALYSIS TOOLKIT - Version 1.8.3
|
|
4
4
|
*
|
|
@@ -55,7 +55,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
55
55
|
}
|
|
56
56
|
})();
|
|
57
57
|
|
|
58
|
-
loadTranslations(
|
|
58
|
+
loadTranslations('en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
59
59
|
|
|
60
60
|
async function getConfig() {
|
|
61
61
|
return await getUnifiedConfig('usage');
|
|
@@ -120,7 +120,7 @@ class I18nUsageAnalyzer {
|
|
|
120
120
|
|
|
121
121
|
// Load translations for UI
|
|
122
122
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
123
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
123
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
124
124
|
const projectRoot = path.resolve(this.config.projectRoot || '.');
|
|
125
125
|
const detected = detectFramework(projectRoot);
|
|
126
126
|
if (detected) {
|
|
@@ -169,9 +169,9 @@ class I18nUsageAnalyzer {
|
|
|
169
169
|
this.config.includeExtensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.pyx', '.pyi'];
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
await SecurityUtils.logSecurityEvent(t('usage.analyzerInitialized'), { component: 'i18ntk-usage' });
|
|
172
|
+
await SecurityUtils.logSecurityEvent(t('usage.analyzerInitialized'), 'info', { component: 'i18ntk-usage' });
|
|
173
173
|
} catch (error) {
|
|
174
|
-
await SecurityUtils.logSecurityEvent(t('usage.analyzerInitFailed'), { component: 'i18ntk-usage', error: error.message });
|
|
174
|
+
await SecurityUtils.logSecurityEvent(t('usage.analyzerInitFailed'), 'error', { component: 'i18ntk-usage', error: error.message });
|
|
175
175
|
throw error;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
@@ -276,7 +276,7 @@ class I18nUsageAnalyzer {
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
} catch (error) {
|
|
279
|
-
await SecurityUtils.logSecurityEvent(t('usage.translationDiscoveryError'), {
|
|
279
|
+
await SecurityUtils.logSecurityEvent(t('usage.translationDiscoveryError'), 'error', {
|
|
280
280
|
component: 'i18ntk-usage',
|
|
281
281
|
directory: currentDir,
|
|
282
282
|
error: error.message
|
|
@@ -350,7 +350,7 @@ class I18nUsageAnalyzer {
|
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
} catch (error) {
|
|
353
|
-
await SecurityUtils.logSecurityEvent(t('usage.fileTraversalError'), {
|
|
353
|
+
await SecurityUtils.logSecurityEvent(t('usage.fileTraversalError'), 'error', {
|
|
354
354
|
component: 'i18ntk-usage',
|
|
355
355
|
directory: currentDir,
|
|
356
356
|
error: error.message
|
|
@@ -393,7 +393,7 @@ class I18nUsageAnalyzer {
|
|
|
393
393
|
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
394
394
|
|
|
395
395
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
396
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
396
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
397
397
|
if (!Array.isArray(this.config.translationPatterns)) {
|
|
398
398
|
this.config.translationPatterns = [
|
|
399
399
|
/t\(['"`]([^'"`]+)['"`]/g,
|
|
@@ -614,7 +614,7 @@ class I18nUsageAnalyzer {
|
|
|
614
614
|
} catch (error) {
|
|
615
615
|
console.error(t('usage.analysisFailedError'), error.message);
|
|
616
616
|
this.closeReadline();
|
|
617
|
-
SecurityUtils.logSecurityEvent(t('usage.usageAnalysisFailed'), {
|
|
617
|
+
SecurityUtils.logSecurityEvent(t('usage.usageAnalysisFailed'), 'error', {
|
|
618
618
|
component: 'i18ntk-usage',
|
|
619
619
|
error: error.message
|
|
620
620
|
});
|
|
@@ -740,7 +740,7 @@ Analysis Features (v1.8.3):
|
|
|
740
740
|
if (isDebug) {
|
|
741
741
|
console.error(error.stack);
|
|
742
742
|
}
|
|
743
|
-
await SecurityUtils.logSecurityEvent(t('usage.translationFileParseError'), {
|
|
743
|
+
await SecurityUtils.logSecurityEvent(t('usage.translationFileParseError'), 'error', {
|
|
744
744
|
component: 'i18ntk-usage',
|
|
745
745
|
file: fileInfo.filePath,
|
|
746
746
|
error: error.message
|
|
@@ -748,7 +748,7 @@ Analysis Features (v1.8.3):
|
|
|
748
748
|
}
|
|
749
749
|
}
|
|
750
750
|
} catch (error) {
|
|
751
|
-
await SecurityUtils.logSecurityEvent(t('usage.translationKeysLoadError'), {
|
|
751
|
+
await SecurityUtils.logSecurityEvent(t('usage.translationKeysLoadError'), 'error', {
|
|
752
752
|
component: 'i18ntk-usage',
|
|
753
753
|
error: error.message
|
|
754
754
|
});
|
|
@@ -1543,7 +1543,7 @@ Analysis Features (v1.8.3):
|
|
|
1543
1543
|
await this.initialize();
|
|
1544
1544
|
}
|
|
1545
1545
|
|
|
1546
|
-
await SecurityUtils.logSecurityEvent('analysis_started', { component: 'i18ntk-usage' });
|
|
1546
|
+
await SecurityUtils.logSecurityEvent('analysis_started', 'info', { component: 'i18ntk-usage' });
|
|
1547
1547
|
|
|
1548
1548
|
console.log(t('usage.checkUsage.title'));
|
|
1549
1549
|
console.log(t("usage.checkUsage.message"));
|
|
@@ -1708,7 +1708,7 @@ Analysis Features (v1.8.3):
|
|
|
1708
1708
|
outputLines.forEach(line => console.log(line));
|
|
1709
1709
|
}
|
|
1710
1710
|
|
|
1711
|
-
await SecurityUtils.logSecurityEvent('analysis_completed', {
|
|
1711
|
+
await SecurityUtils.logSecurityEvent('analysis_completed', 'info', {
|
|
1712
1712
|
component: 'i18ntk-usage',
|
|
1713
1713
|
stats: {
|
|
1714
1714
|
availableKeys: this.availableKeys.size,
|
|
@@ -1749,7 +1749,7 @@ Analysis Features (v1.8.3):
|
|
|
1749
1749
|
console.error(t("checkUsage.usage_analysis_failed"));
|
|
1750
1750
|
console.error(error.message);
|
|
1751
1751
|
|
|
1752
|
-
await SecurityUtils.logSecurityEvent('analysis_failed', {
|
|
1752
|
+
await SecurityUtils.logSecurityEvent('analysis_failed', 'error', {
|
|
1753
1753
|
component: 'i18ntk-usage',
|
|
1754
1754
|
error: error.message
|
|
1755
1755
|
});
|
package/main/i18ntk-validate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* I18NTK TRANSLATION VALIDATION TOOLKIT
|
|
@@ -60,7 +60,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
60
60
|
}
|
|
61
61
|
})();
|
|
62
62
|
|
|
63
|
-
loadTranslations(
|
|
63
|
+
loadTranslations('en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
64
64
|
|
|
65
65
|
class I18nValidator {
|
|
66
66
|
constructor(config = {}) {
|
|
@@ -87,7 +87,7 @@ class I18nValidator {
|
|
|
87
87
|
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
88
88
|
|
|
89
89
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
90
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
90
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
91
91
|
|
|
92
92
|
SecurityUtils.logSecurityEvent(
|
|
93
93
|
'I18n validator initializing',
|
|
@@ -892,7 +892,8 @@ class I18nValidator {
|
|
|
892
892
|
this.config = { ...baseConfig, ...(this.config || {}) };
|
|
893
893
|
|
|
894
894
|
const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
|
|
895
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
895
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
896
|
+
this.sourceDir = this.config.sourceDir;
|
|
896
897
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
897
898
|
} else {
|
|
898
899
|
await this.initialize();
|
|
@@ -983,7 +984,7 @@ if (require.main === module) {
|
|
|
983
984
|
// Initialize translations for CLI usage
|
|
984
985
|
const config = configManager.getConfig();
|
|
985
986
|
const uiLanguage = config.language || 'en';
|
|
986
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', '
|
|
987
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
987
988
|
|
|
988
989
|
SecurityUtils.logSecurityEvent(t('validate.scriptExecution'), 'info', {
|
|
989
990
|
script: 'i18ntk-validate.js',
|