i18ntk 1.10.2 → 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 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- 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 +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- 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 +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- 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 -29
- 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 +117 -117
- 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 +157 -161
- 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 +18 -18
- 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 +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- 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/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- 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 +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- 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 -454
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
|
package/main/i18ntk-usage.js
CHANGED
|
@@ -55,7 +55,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
55
55
|
}
|
|
56
56
|
})();
|
|
57
57
|
|
|
58
|
-
loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
58
|
+
loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', '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, '..', 'ui-locales'));
|
|
123
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
124
124
|
const projectRoot = path.resolve(this.config.projectRoot || '.');
|
|
125
125
|
const detected = detectFramework(projectRoot);
|
|
126
126
|
if (detected) {
|
|
@@ -136,7 +136,7 @@ class I18nUsageAnalyzer {
|
|
|
136
136
|
this.i18nDir = this.config.i18nDir;
|
|
137
137
|
this.sourceLanguageDir = path.join(this.i18nDir, this.config.sourceLanguage);
|
|
138
138
|
|
|
139
|
-
if (!
|
|
139
|
+
if (!SecurityUtils.safeExistsSync(this.i18nDir, process.cwd())) {
|
|
140
140
|
console.warn(t('usage.i18nDirectoryNotFound', { i18nDir: this.i18nDir }));
|
|
141
141
|
this.i18nDir = this.sourceDir;
|
|
142
142
|
this.config.i18nDir = this.i18nDir;
|
|
@@ -222,7 +222,7 @@ class I18nUsageAnalyzer {
|
|
|
222
222
|
const absoluteDir = path.resolve(currentDir);
|
|
223
223
|
const validatedPath = SecurityUtils.validatePath(absoluteDir, process.cwd());
|
|
224
224
|
|
|
225
|
-
if (!validatedPath || !
|
|
225
|
+
if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -312,7 +312,7 @@ class I18nUsageAnalyzer {
|
|
|
312
312
|
const absoluteDir = path.resolve(currentDir);
|
|
313
313
|
const validatedPath = SecurityUtils.validatePath(absoluteDir, process.cwd());
|
|
314
314
|
|
|
315
|
-
if (!validatedPath || !
|
|
315
|
+
if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
|
|
316
316
|
return;
|
|
317
317
|
}
|
|
318
318
|
|
|
@@ -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, '..', 'ui-locales'));
|
|
396
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
397
397
|
if (!Array.isArray(this.config.translationPatterns)) {
|
|
398
398
|
this.config.translationPatterns = [
|
|
399
399
|
/t\(['"`]([^'"`]+)['"`]/g,
|
|
@@ -482,7 +482,7 @@ class I18nUsageAnalyzer {
|
|
|
482
482
|
|
|
483
483
|
for (const dir of possibleSourceDirs) {
|
|
484
484
|
const testPath = path.resolve(projectRoot, dir);
|
|
485
|
-
if (
|
|
485
|
+
if (SecurityUtils.safeExistsSync(testPath)) {
|
|
486
486
|
this.config.sourceDir = testPath;
|
|
487
487
|
this.sourceDir = testPath;
|
|
488
488
|
break;
|
|
@@ -501,7 +501,7 @@ class I18nUsageAnalyzer {
|
|
|
501
501
|
const fallback = path.resolve(this.config.projectRoot || '.', 'src');
|
|
502
502
|
console.warn(t('usage.sourceEqualsI18nWarn') ||
|
|
503
503
|
`⚠️ sourceDir equals i18nDir (${this.sourceDir}). Falling back to ${fallback} for source scanning.`);
|
|
504
|
-
if (
|
|
504
|
+
if (SecurityUtils.safeExistsSync(fallback)) {
|
|
505
505
|
this.sourceDir = fallback;
|
|
506
506
|
} else {
|
|
507
507
|
console.warn(`⚠️ Fallback directory ${fallback} does not exist. Using project root for source scanning.`);
|
|
@@ -682,7 +682,7 @@ Analysis Features (v1.8.3):
|
|
|
682
682
|
await SecurityUtils.validatePath(fileInfo.filePath);
|
|
683
683
|
|
|
684
684
|
// Check if file exists and is readable
|
|
685
|
-
if (!
|
|
685
|
+
if (!SecurityUtils.safeExistsSync(fileInfo.filePath)) {
|
|
686
686
|
if (isDebug || isStrict) {
|
|
687
687
|
console.warn(`⚠️ File not found: ${path.basename(fileInfo.filePath)}`);
|
|
688
688
|
}
|
|
@@ -804,7 +804,7 @@ Analysis Features (v1.8.3):
|
|
|
804
804
|
// Extract translation keys from source code with enhanced patterns
|
|
805
805
|
extractKeysFromFile(filePath) {
|
|
806
806
|
try {
|
|
807
|
-
const content = SecurityUtils.safeReadFileSync(filePath);
|
|
807
|
+
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
808
808
|
if (!content) return [];
|
|
809
809
|
|
|
810
810
|
// Skip JSON files entirely to prevent scanning translation files
|
|
@@ -827,7 +827,7 @@ Analysis Features (v1.8.3):
|
|
|
827
827
|
console.log(t('usage.checkUsage.analyzing_source_files'));
|
|
828
828
|
|
|
829
829
|
// Check if source directory exists
|
|
830
|
-
if (!
|
|
830
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
|
|
831
831
|
throw new Error(this.t('usage.sourceDirectoryDoesNotExist', { dir: this.sourceDir }) || `Source directory not found: ${this.sourceDir}`);
|
|
832
832
|
}
|
|
833
833
|
|
|
@@ -898,7 +898,7 @@ Analysis Features (v1.8.3):
|
|
|
898
898
|
const isStrict = process.argv.includes('--strict');
|
|
899
899
|
|
|
900
900
|
// Check if i18n directory exists
|
|
901
|
-
if (!
|
|
901
|
+
if (!SecurityUtils.safeExistsSync(this.i18nDir, process.cwd())) {
|
|
902
902
|
console.warn(t('usage.i18nDirectoryNotFound', { i18nDir: this.i18nDir }));
|
|
903
903
|
return;
|
|
904
904
|
}
|
|
@@ -912,7 +912,7 @@ Analysis Features (v1.8.3):
|
|
|
912
912
|
.filter(item => {
|
|
913
913
|
try {
|
|
914
914
|
const itemPath = path.join(this.i18nDir, item);
|
|
915
|
-
return
|
|
915
|
+
return SecurityUtils.safeExistsSync(itemPath) && fs.statSync(itemPath).isDirectory();
|
|
916
916
|
} catch (error) {
|
|
917
917
|
return false;
|
|
918
918
|
}
|
|
@@ -951,7 +951,7 @@ Analysis Features (v1.8.3):
|
|
|
951
951
|
|
|
952
952
|
for (const fileInfo of translationFiles) {
|
|
953
953
|
try {
|
|
954
|
-
if (!
|
|
954
|
+
if (!SecurityUtils.safeExistsSync(fileInfo.filePath)) {
|
|
955
955
|
if (isDebug || isStrict) {
|
|
956
956
|
console.warn(`⚠️ File not found: ${path.basename(fileInfo.filePath)}`);
|
|
957
957
|
}
|
|
@@ -1353,7 +1353,7 @@ Analysis Features (v1.8.3):
|
|
|
1353
1353
|
async saveReport(report, outputDir = './i18ntk-reports/usage') {
|
|
1354
1354
|
try {
|
|
1355
1355
|
// Ensure output directory exists
|
|
1356
|
-
if (!
|
|
1356
|
+
if (!SecurityUtils.safeExistsSync(outputDir)) {
|
|
1357
1357
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
1358
1358
|
}
|
|
1359
1359
|
|
|
@@ -1578,12 +1578,12 @@ Analysis Features (v1.8.3):
|
|
|
1578
1578
|
await SecurityUtils.validatePath(this.sourceDir);
|
|
1579
1579
|
await SecurityUtils.validatePath(this.i18nDir);
|
|
1580
1580
|
|
|
1581
|
-
if (!
|
|
1581
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
|
|
1582
1582
|
throw new Error(this.t('usage.sourceDirectoryDoesNotExist', { dir: this.sourceDir }) || `Source directory not found: ${this.sourceDir}`);
|
|
1583
1583
|
|
|
1584
1584
|
}
|
|
1585
1585
|
|
|
1586
|
-
if (!
|
|
1586
|
+
if (!SecurityUtils.safeExistsSync(this.i18nDir, process.cwd())) {
|
|
1587
1587
|
throw new Error(this.t('usage.i18nDirectoryDoesNotExist', { dir: this.i18nDir }) || `I18n directory not found: ${this.i18nDir}`);
|
|
1588
1588
|
}
|
|
1589
1589
|
|
|
@@ -1801,4 +1801,40 @@ if (require.main === module) {
|
|
|
1801
1801
|
}
|
|
1802
1802
|
}
|
|
1803
1803
|
|
|
1804
|
-
module.exports = I18nUsageAnalyzer;
|
|
1804
|
+
module.exports = I18nUsageAnalyzer;
|
|
1805
|
+
|
|
1806
|
+
// Run if called directly
|
|
1807
|
+
if (require.main === module) {
|
|
1808
|
+
async function main() {
|
|
1809
|
+
try {
|
|
1810
|
+
const cliArgs = parseCommonArgs(process.argv.slice(2));
|
|
1811
|
+
|
|
1812
|
+
if (cliArgs.help) {
|
|
1813
|
+
displayHelp('usage');
|
|
1814
|
+
process.exit(0);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// Let run() handle full initialization to avoid duplicate setup output
|
|
1818
|
+
const analyzer = new I18nUsageAnalyzer();
|
|
1819
|
+
await analyzer.run();
|
|
1820
|
+
} catch (error) {
|
|
1821
|
+
console.error('Error:', error.message);
|
|
1822
|
+
process.exit(1);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// Check if we're being called from the menu system (stdin has data)
|
|
1827
|
+
const hasStdinData = !process.stdin.isTTY;
|
|
1828
|
+
|
|
1829
|
+
if (hasStdinData) {
|
|
1830
|
+
// When called from menu, consume stdin data and run with defaults
|
|
1831
|
+
process.stdin.resume();
|
|
1832
|
+
process.stdin.on('data', () => {});
|
|
1833
|
+
process.stdin.on('end', () => {
|
|
1834
|
+
main();
|
|
1835
|
+
});
|
|
1836
|
+
} else {
|
|
1837
|
+
// Normal direct execution
|
|
1838
|
+
main();
|
|
1839
|
+
}
|
|
1840
|
+
}
|
package/main/i18ntk-validate.js
CHANGED
|
@@ -60,7 +60,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
|
|
|
60
60
|
}
|
|
61
61
|
})();
|
|
62
62
|
|
|
63
|
-
loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
|
|
63
|
+
loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', '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, '..', 'ui-locales'));
|
|
90
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
91
91
|
|
|
92
92
|
SecurityUtils.logSecurityEvent(
|
|
93
93
|
'I18n validator initializing',
|
|
@@ -112,7 +112,7 @@ class I18nValidator {
|
|
|
112
112
|
} else {
|
|
113
113
|
console.warn(t('config.dirFallbackWarning', { dir: this.sourceDir, fallback: this.sourceLanguageDir }) ||
|
|
114
114
|
`Warning: Directory ${this.sourceDir} not found. Using ${this.sourceLanguageDir}.`);
|
|
115
|
-
if (!
|
|
115
|
+
if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir)) {
|
|
116
116
|
fs.mkdirSync(this.sourceLanguageDir, { recursive: true });
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -189,7 +189,7 @@ class I18nValidator {
|
|
|
189
189
|
// Get all available languages
|
|
190
190
|
getAvailableLanguages() {
|
|
191
191
|
try {
|
|
192
|
-
if (!
|
|
192
|
+
if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
|
|
193
193
|
throw new Error(`Source directory not found: ${this.sourceDir}`);
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -211,7 +211,7 @@ class I18nValidator {
|
|
|
211
211
|
const sanitizedLanguage = SecurityUtils.sanitizeInput(language);
|
|
212
212
|
const languageDir = path.join(this.sourceDir, sanitizedLanguage);
|
|
213
213
|
|
|
214
|
-
if (!
|
|
214
|
+
if (!SecurityUtils.safeExistsSync(languageDir)) {
|
|
215
215
|
return [];
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -265,7 +265,7 @@ class I18nValidator {
|
|
|
265
265
|
// Validate JSON file syntax
|
|
266
266
|
async validateJsonSyntax(filePath) {
|
|
267
267
|
try {
|
|
268
|
-
const content =
|
|
268
|
+
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
269
269
|
const parsed = SecurityUtils.safeParseJSON(content);
|
|
270
270
|
|
|
271
271
|
SecurityUtils.logSecurityEvent(
|
|
@@ -481,7 +481,7 @@ class I18nValidator {
|
|
|
481
481
|
};
|
|
482
482
|
|
|
483
483
|
// Check for missing language directory
|
|
484
|
-
if (!
|
|
484
|
+
if (!SecurityUtils.safeExistsSync(languageDir)) {
|
|
485
485
|
this.addError(
|
|
486
486
|
`Language directory missing: ${sanitizedLanguage}`,
|
|
487
487
|
{ language: sanitizedLanguage, expectedPath: languageDir }
|
|
@@ -495,7 +495,7 @@ class I18nValidator {
|
|
|
495
495
|
const targetFilePath = path.join(languageDir, fileName);
|
|
496
496
|
|
|
497
497
|
// Check if source file exists
|
|
498
|
-
if (!
|
|
498
|
+
if (!SecurityUtils.safeExistsSync(sourceFilePath)) {
|
|
499
499
|
this.addWarning(
|
|
500
500
|
`Source file missing: ${this.config.sourceLanguage}/${fileName}`,
|
|
501
501
|
{ fileName, language: this.config.sourceLanguage }
|
|
@@ -504,7 +504,7 @@ class I18nValidator {
|
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
// Check if target file exists
|
|
507
|
-
if (!
|
|
507
|
+
if (!SecurityUtils.safeExistsSync(targetFilePath)) {
|
|
508
508
|
this.addError(
|
|
509
509
|
`Translation file missing: ${language}/${fileName}`,
|
|
510
510
|
{ fileName, language, expectedPath: targetFilePath }
|
|
@@ -639,7 +639,7 @@ class I18nValidator {
|
|
|
639
639
|
const reportPath = path.join(process.cwd(), 'validation-report.txt');
|
|
640
640
|
SecurityUtils.validatePath(reportPath);
|
|
641
641
|
|
|
642
|
-
if (
|
|
642
|
+
if (SecurityUtils.safeExistsSync(reportPath)) {
|
|
643
643
|
fs.unlinkSync(reportPath);
|
|
644
644
|
console.log(t('validate.deletedOldReport'));
|
|
645
645
|
|
|
@@ -672,7 +672,7 @@ class I18nValidator {
|
|
|
672
672
|
// Validate source language directory exists
|
|
673
673
|
SecurityUtils.validatePath(this.sourceLanguageDir);
|
|
674
674
|
|
|
675
|
-
if (!
|
|
675
|
+
if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir)) {
|
|
676
676
|
const error = t('validate.sourceLanguageDirectoryNotFound', { sourceDir: this.sourceLanguageDir }) || 'Source language directory not found';
|
|
677
677
|
this.addError(error, { sourceLanguage: this.config.sourceLanguage });
|
|
678
678
|
|
|
@@ -892,7 +892,7 @@ 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, '..', 'ui-locales'));this.sourceDir = this.config.sourceDir;
|
|
895
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));this.sourceDir = this.config.sourceDir;
|
|
896
896
|
this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
|
|
897
897
|
} else {
|
|
898
898
|
await this.initialize();
|
|
@@ -983,7 +983,7 @@ if (require.main === module) {
|
|
|
983
983
|
// Initialize translations for CLI usage
|
|
984
984
|
const config = configManager.getConfig();
|
|
985
985
|
const uiLanguage = config.language || 'en';
|
|
986
|
-
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
|
|
986
|
+
loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
|
|
987
987
|
|
|
988
988
|
SecurityUtils.logSecurityEvent(t('validate.scriptExecution'), 'info', {
|
|
989
989
|
script: 'i18ntk-validate.js',
|