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/scripts/prepublish.js
CHANGED
|
@@ -30,7 +30,7 @@ class PrepublishCleaner {
|
|
|
30
30
|
// Essential files that must exist for release
|
|
31
31
|
this.essentialFiles = [
|
|
32
32
|
'package.json',
|
|
33
|
-
'main/
|
|
33
|
+
'main/manage/index.js',
|
|
34
34
|
'main/i18ntk-init.js',
|
|
35
35
|
'main/i18ntk-analyze.js',
|
|
36
36
|
'main/i18ntk-validate.js',
|
|
@@ -49,13 +49,13 @@ class PrepublishCleaner {
|
|
|
49
49
|
|
|
50
50
|
// Essential locale files
|
|
51
51
|
this.essentialLocales = [
|
|
52
|
-
'ui-locales/en.json',
|
|
53
|
-
'ui-locales/es.json',
|
|
54
|
-
'ui-locales/fr.json',
|
|
55
|
-
'ui-locales/de.json',
|
|
56
|
-
'ui-locales/ja.json',
|
|
57
|
-
'ui-locales/ru.json',
|
|
58
|
-
'ui-locales/zh.json'
|
|
52
|
+
'resources/i18n/ui-locales/en.json',
|
|
53
|
+
'resources/i18n/ui-locales/es.json',
|
|
54
|
+
'resources/i18n/ui-locales/fr.json',
|
|
55
|
+
'resources/i18n/ui-locales/de.json',
|
|
56
|
+
'resources/i18n/ui-locales/ja.json',
|
|
57
|
+
'resources/i18n/ui-locales/ru.json',
|
|
58
|
+
'resources/i18n/ui-locales/zh.json'
|
|
59
59
|
];
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -95,7 +95,7 @@ class PrepublishCleaner {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async cleanDirectory(dirPath) {
|
|
98
|
-
if (!
|
|
98
|
+
if (!SecurityUtils.safeExistsSync(dirPath)) {
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -138,7 +138,7 @@ class PrepublishCleaner {
|
|
|
138
138
|
const dir = path.dirname(searchPath);
|
|
139
139
|
const filenamePattern = path.basename(searchPath);
|
|
140
140
|
|
|
141
|
-
if (
|
|
141
|
+
if (SecurityUtils.safeExistsSync(dir)) {
|
|
142
142
|
const files = fs.readdirSync(dir);
|
|
143
143
|
const regex = new RegExp(filenamePattern.replace('*', '.*'));
|
|
144
144
|
|
|
@@ -152,7 +152,7 @@ class PrepublishCleaner {
|
|
|
152
152
|
}
|
|
153
153
|
} else {
|
|
154
154
|
// Handle exact files
|
|
155
|
-
if (
|
|
155
|
+
if (SecurityUtils.safeExistsSync(searchPath)) {
|
|
156
156
|
fs.unlinkSync(searchPath);
|
|
157
157
|
this.log(`Deleted ${path.relative(this.projectRoot, searchPath)}`);
|
|
158
158
|
}
|
|
@@ -165,7 +165,7 @@ class PrepublishCleaner {
|
|
|
165
165
|
let missingFiles = [];
|
|
166
166
|
for (const file of this.essentialFiles) {
|
|
167
167
|
const filePath = path.join(this.projectRoot, file);
|
|
168
|
-
if (!
|
|
168
|
+
if (!SecurityUtils.safeExistsSync(filePath)) {
|
|
169
169
|
missingFiles.push(file);
|
|
170
170
|
} else if (!fs.statSync(filePath).isFile()) {
|
|
171
171
|
this.log(`❌ ${file} is not a file`);
|
|
@@ -187,13 +187,13 @@ class PrepublishCleaner {
|
|
|
187
187
|
let invalidFiles = [];
|
|
188
188
|
for (const localeFile of this.essentialLocales) {
|
|
189
189
|
const filePath = path.join(this.projectRoot, localeFile);
|
|
190
|
-
if (!
|
|
190
|
+
if (!SecurityUtils.safeExistsSync(filePath)) {
|
|
191
191
|
invalidFiles.push(localeFile);
|
|
192
192
|
continue;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
try {
|
|
196
|
-
const content =
|
|
196
|
+
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
197
197
|
const parsed = JSON.parse(content);
|
|
198
198
|
|
|
199
199
|
// Validate structure
|
|
@@ -224,7 +224,7 @@ class PrepublishCleaner {
|
|
|
224
224
|
|
|
225
225
|
const packagePath = path.join(this.projectRoot, 'package.json');
|
|
226
226
|
try {
|
|
227
|
-
const pkg = JSON.parse(
|
|
227
|
+
const pkg = JSON.parse(SecurityUtils.safeReadFileSync(packagePath, path.dirname(packagePath), 'utf8'));
|
|
228
228
|
|
|
229
229
|
// Validate required fields
|
|
230
230
|
const requiredFields = ['name', 'version', 'description', 'main', 'bin', 'files'];
|
|
@@ -255,7 +255,7 @@ class PrepublishCleaner {
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
const binPath = path.join(this.projectRoot, pkg.bin[bin]);
|
|
258
|
-
if (!
|
|
258
|
+
if (!SecurityUtils.safeExistsSync(binPath)) {
|
|
259
259
|
this.log(`❌ Missing bin script: ${pkg.bin[bin]}`);
|
|
260
260
|
process.exit(1);
|
|
261
261
|
}
|
|
@@ -282,14 +282,14 @@ class PrepublishCleaner {
|
|
|
282
282
|
|
|
283
283
|
for (const artifact of devArtifacts) {
|
|
284
284
|
const artifactPath = path.join(this.projectRoot, artifact);
|
|
285
|
-
if (
|
|
285
|
+
if (SecurityUtils.safeExistsSync(artifactPath)) {
|
|
286
286
|
this.log(`⚠️ Development artifact found: ${artifact}`);
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
// Validate file permissions for executable scripts
|
|
291
291
|
const scripts = [
|
|
292
|
-
'main/
|
|
292
|
+
'main/manage/index.js',
|
|
293
293
|
'main/i18ntk-init.js',
|
|
294
294
|
'main/i18ntk-analyze.js',
|
|
295
295
|
'main/i18ntk-validate.js',
|
|
@@ -303,7 +303,7 @@ class PrepublishCleaner {
|
|
|
303
303
|
|
|
304
304
|
for (const script of scripts) {
|
|
305
305
|
const scriptPath = path.join(this.projectRoot, script);
|
|
306
|
-
if (
|
|
306
|
+
if (SecurityUtils.safeExistsSync(scriptPath)) {
|
|
307
307
|
try {
|
|
308
308
|
fs.accessSync(scriptPath, fs.constants.X_OK);
|
|
309
309
|
} catch (e) {
|
|
@@ -318,7 +318,7 @@ class PrepublishCleaner {
|
|
|
318
318
|
async resetSecuritySettings() {
|
|
319
319
|
const configPath = path.join(require('../settings/settings-manager').configDir, '.i18n-admin-config.json');
|
|
320
320
|
|
|
321
|
-
if (
|
|
321
|
+
if (SecurityUtils.safeExistsSync(configPath)) {
|
|
322
322
|
const defaultConfig = {
|
|
323
323
|
enabled: false,
|
|
324
324
|
pinHash: null,
|
|
@@ -330,7 +330,7 @@ class PrepublishCleaner {
|
|
|
330
330
|
lockedUntil: null
|
|
331
331
|
};
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
SecurityUtils.safeWriteFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
334
334
|
this.log('Reset security settings to defaults');
|
|
335
335
|
}
|
|
336
336
|
}
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Security Check Script
|
|
4
|
-
* Validates that no child_process usage exists in production code
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
class SecurityCheck {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.productionDirs = ['main', 'utils', 'settings', 'scripts'];
|
|
13
|
-
this.forbiddenPatterns = [
|
|
14
|
-
/require\(['"]child_process['"]\)/,
|
|
15
|
-
/import.*child_process/,
|
|
16
|
-
/execSync\(/,
|
|
17
|
-
/spawnSync\(/,
|
|
18
|
-
/execFileSync\(/,
|
|
19
|
-
/spawn\(/,
|
|
20
|
-
/exec\(/,
|
|
21
|
-
/execFile\(/
|
|
22
|
-
];
|
|
23
|
-
this.allowedFiles = [
|
|
24
|
-
'dev/', // Development files allowed to use child_process
|
|
25
|
-
'benchmarks/', // Benchmark scripts
|
|
26
|
-
'test/', // Test files
|
|
27
|
-
'scripts/deprecate-versions.js', // Allowed to use child_process for npm commands
|
|
28
|
-
'verify-package.js' // Package verification (development)
|
|
29
|
-
];
|
|
30
|
-
this.violations = [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async run() {
|
|
34
|
-
console.log('🔒 i18ntk Security Check - Production Code Validation');
|
|
35
|
-
console.log('═'.repeat(55));
|
|
36
|
-
|
|
37
|
-
for (const dir of this.productionDirs) {
|
|
38
|
-
await this.checkDirectory(dir);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (this.violations.length > 0) {
|
|
42
|
-
console.error('\n❌ SECURITY VIOLATIONS FOUND:');
|
|
43
|
-
this.violations.forEach(violation => {
|
|
44
|
-
console.error(` ${violation.file}:${violation.line} - ${violation.pattern}`);
|
|
45
|
-
});
|
|
46
|
-
console.error('\n💡 Production code must not use child_process');
|
|
47
|
-
process.exit(1);
|
|
48
|
-
} else {
|
|
49
|
-
console.log('\n✅ All security checks passed - no child_process usage in production code');
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async checkDirectory(dir) {
|
|
54
|
-
const dirPath = path.join(process.cwd(), dir);
|
|
55
|
-
|
|
56
|
-
if (!
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const files = this.getAllFiles(dirPath);
|
|
61
|
-
|
|
62
|
-
for (const file of files) {
|
|
63
|
-
await this.checkFile(file);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
getAllFiles(dirPath) {
|
|
68
|
-
const files = [];
|
|
69
|
-
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
70
|
-
|
|
71
|
-
for (const entry of entries) {
|
|
72
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
73
|
-
|
|
74
|
-
if (entry.isDirectory()) {
|
|
75
|
-
files.push(...this.getAllFiles(fullPath));
|
|
76
|
-
} else if (entry.isFile() && entry.name.endsWith('.js')) {
|
|
77
|
-
files.push(fullPath);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return files;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async checkFile(filePath) {
|
|
85
|
-
const relativePath = path.relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
86
|
-
|
|
87
|
-
for (const allowed of this.allowedFiles) {
|
|
88
|
-
if (relativePath.startsWith(allowed)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const content =
|
|
94
|
-
const lines = content.split('\n');
|
|
95
|
-
|
|
96
|
-
for (let i = 0; i < lines.length; i++) {
|
|
97
|
-
const line = lines[i];
|
|
98
|
-
|
|
99
|
-
for (const pattern of this.forbiddenPatterns) {
|
|
100
|
-
if (pattern.test(line)) {
|
|
101
|
-
this.violations.push({
|
|
102
|
-
file: relativePath,
|
|
103
|
-
line: i + 1,
|
|
104
|
-
pattern: pattern.toString()
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (require.main === module) {
|
|
113
|
-
const check = new SecurityCheck();
|
|
114
|
-
check.run().catch(console.error);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
module.exports = SecurityCheck;
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Security Check Script
|
|
4
|
+
* Validates that no child_process usage exists in production code
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
class SecurityCheck {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.productionDirs = ['main', 'utils', 'settings', 'scripts'];
|
|
13
|
+
this.forbiddenPatterns = [
|
|
14
|
+
/require\(['"]child_process['"]\)/,
|
|
15
|
+
/import.*child_process/,
|
|
16
|
+
/execSync\(/,
|
|
17
|
+
/spawnSync\(/,
|
|
18
|
+
/execFileSync\(/,
|
|
19
|
+
/spawn\(/,
|
|
20
|
+
/exec\(/,
|
|
21
|
+
/execFile\(/
|
|
22
|
+
];
|
|
23
|
+
this.allowedFiles = [
|
|
24
|
+
'dev/', // Development files allowed to use child_process
|
|
25
|
+
'benchmarks/', // Benchmark scripts
|
|
26
|
+
'test/', // Test files
|
|
27
|
+
'scripts/deprecate-versions.js', // Allowed to use child_process for npm commands
|
|
28
|
+
'verify-package.js' // Package verification (development)
|
|
29
|
+
];
|
|
30
|
+
this.violations = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async run() {
|
|
34
|
+
console.log('🔒 i18ntk Security Check - Production Code Validation');
|
|
35
|
+
console.log('═'.repeat(55));
|
|
36
|
+
|
|
37
|
+
for (const dir of this.productionDirs) {
|
|
38
|
+
await this.checkDirectory(dir);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.violations.length > 0) {
|
|
42
|
+
console.error('\n❌ SECURITY VIOLATIONS FOUND:');
|
|
43
|
+
this.violations.forEach(violation => {
|
|
44
|
+
console.error(` ${violation.file}:${violation.line} - ${violation.pattern}`);
|
|
45
|
+
});
|
|
46
|
+
console.error('\n💡 Production code must not use child_process');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
} else {
|
|
49
|
+
console.log('\n✅ All security checks passed - no child_process usage in production code');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async checkDirectory(dir) {
|
|
54
|
+
const dirPath = path.join(process.cwd(), dir);
|
|
55
|
+
|
|
56
|
+
if (!SecurityUtils.safeExistsSync(dirPath)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const files = this.getAllFiles(dirPath);
|
|
61
|
+
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
await this.checkFile(file);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getAllFiles(dirPath) {
|
|
68
|
+
const files = [];
|
|
69
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
70
|
+
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
73
|
+
|
|
74
|
+
if (entry.isDirectory()) {
|
|
75
|
+
files.push(...this.getAllFiles(fullPath));
|
|
76
|
+
} else if (entry.isFile() && entry.name.endsWith('.js')) {
|
|
77
|
+
files.push(fullPath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return files;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async checkFile(filePath) {
|
|
85
|
+
const relativePath = path.relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
86
|
+
|
|
87
|
+
for (const allowed of this.allowedFiles) {
|
|
88
|
+
if (relativePath.startsWith(allowed)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
94
|
+
const lines = content.split('\n');
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < lines.length; i++) {
|
|
97
|
+
const line = lines[i];
|
|
98
|
+
|
|
99
|
+
for (const pattern of this.forbiddenPatterns) {
|
|
100
|
+
if (pattern.test(line)) {
|
|
101
|
+
this.violations.push({
|
|
102
|
+
file: relativePath,
|
|
103
|
+
line: i + 1,
|
|
104
|
+
pattern: pattern.toString()
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (require.main === module) {
|
|
113
|
+
const check = new SecurityCheck();
|
|
114
|
+
check.run().catch(console.error);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = SecurityCheck;
|
|
@@ -14,7 +14,7 @@ const fs = require('fs');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
|
|
16
16
|
// Configuration
|
|
17
|
-
const UI_LOCALES_DIR = path.join(__dirname, '..', 'ui-locales');
|
|
17
|
+
const UI_LOCALES_DIR = path.join(__dirname, '..', 'resources', 'i18n', 'ui-locales');
|
|
18
18
|
const ENGLISH_DIR = path.join(UI_LOCALES_DIR, 'en');
|
|
19
19
|
const TARGET_LANGUAGES = ['de', 'es', 'fr', 'ru', 'ja']; // Exclude Chinese (zh) as it's fully translated
|
|
20
20
|
|
|
@@ -36,7 +36,7 @@ function getEnglishFiles() {
|
|
|
36
36
|
*/
|
|
37
37
|
function readJsonFile(filePath) {
|
|
38
38
|
try {
|
|
39
|
-
const content =
|
|
39
|
+
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
40
40
|
return JSON.parse(content);
|
|
41
41
|
} catch (error) {
|
|
42
42
|
console.error(`Error reading JSON file ${filePath}:`, error.message);
|
|
@@ -50,7 +50,7 @@ function readJsonFile(filePath) {
|
|
|
50
50
|
function writeJsonFile(filePath, data) {
|
|
51
51
|
try {
|
|
52
52
|
const jsonString = JSON.stringify(data, null, 2);
|
|
53
|
-
|
|
53
|
+
SecurityUtils.safeWriteFileSync(filePath, jsonString + '\n');
|
|
54
54
|
return true;
|
|
55
55
|
} catch (error) {
|
|
56
56
|
console.error(`Error writing JSON file ${filePath}:`, error.message);
|
|
@@ -74,7 +74,7 @@ function syncLanguage(language) {
|
|
|
74
74
|
const languageDir = path.join(UI_LOCALES_DIR, language);
|
|
75
75
|
|
|
76
76
|
// Ensure language directory exists
|
|
77
|
-
if (!
|
|
77
|
+
if (!SecurityUtils.safeExistsSync(languageDir)) {
|
|
78
78
|
fs.mkdirSync(languageDir, { recursive: true });
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const
|
|
4
|
-
const
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const SecurityUtils = require('../utils/security');
|
|
4
|
+
const baseDir = path.join(__dirname, '..', 'resources', 'i18n', 'ui-locales');
|
|
5
|
+
const en = JSON.parse(SecurityUtils.safeReadFileSync(path.join(baseDir,'en.json'), baseDir, 'utf8'));
|
|
5
6
|
function flatten(obj,pfx='',out={}){ for(const [k,v] of Object.entries(obj)){ const nk=pfx?`${pfx}.${k}`:k; if(v && typeof v==='object' && !Array.isArray(v)) flatten(v,nk,out); else out[nk]=v; } return out; }
|
|
6
7
|
function unflatten(map){ const root={}; for(const [k,v] of Object.entries(map)){ const parts=k.split('.'); let cur=root; while(parts.length>1){ const p=parts.shift(); cur[p]=cur[p]||{}; cur=cur[p]; } cur[parts[0]]=v; } return root; }
|
|
7
8
|
const enFlat = flatten(en);
|
|
8
9
|
for (const file of fs.readdirSync(baseDir)) {
|
|
9
10
|
if (!file.endsWith('.json') || file==='en.json') continue;
|
|
10
11
|
const p = path.join(baseDir,file);
|
|
11
|
-
const data = JSON.parse(
|
|
12
|
+
const data = JSON.parse(SecurityUtils.safeReadFileSync(p, baseDir, 'utf8'));
|
|
12
13
|
const flat = flatten(data);
|
|
13
14
|
let changed = false;
|
|
14
15
|
for (const [k,v] of Object.entries(enFlat)) {
|
|
15
16
|
if (!(k in flat)) { flat[k] = v || 'NOT_TRANSLATED'; changed = true; }
|
|
16
17
|
}
|
|
17
|
-
if (changed) {
|
|
18
|
-
}
|
|
19
|
-
console.log('UI locales synced.');
|
|
18
|
+
if (changed) { SecurityUtils.safeWriteFileSync(p, JSON.stringify(unflatten(flat), null, 2), path.dirname(p), 'utf8'); }
|
|
19
|
+
}
|
|
20
|
+
console.log('UI locales synced.');
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
* --languages=en,de,es,fr,ru,ja,zh
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const SecurityUtils = require('../utils/security');
|
|
20
21
|
|
|
21
22
|
const argv = Object.fromEntries(
|
|
22
23
|
process.argv.slice(2).map(a => {
|
|
@@ -25,13 +26,13 @@ const argv = Object.fromEntries(
|
|
|
25
26
|
})
|
|
26
27
|
);
|
|
27
28
|
|
|
28
|
-
const I18N_DIR = path.resolve(argv['i18n-dir'] || './ui-locales');
|
|
29
|
+
const I18N_DIR = path.resolve(argv['i18n-dir'] || './resources/i18n/ui-locales');
|
|
29
30
|
const LANGS = (argv.languages || 'en,de,es,fr,ru,ja,zh').split(',').map(s => s.trim());
|
|
30
31
|
const MARKER = argv.marker || '⚠️ TRANSLATION NEEDED ⚠️';
|
|
31
32
|
|
|
32
33
|
// ------------ helpers ------------
|
|
33
34
|
function readJSON(p) {
|
|
34
|
-
try { return JSON.parse(
|
|
35
|
+
try { return JSON.parse(SecurityUtils.safeReadFileSync(p, path.dirname(p), 'utf8')); }
|
|
35
36
|
catch { return {}; }
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -50,7 +51,7 @@ function flatten(obj, prefix = '') {
|
|
|
50
51
|
|
|
51
52
|
function listLocaleFile(lang) {
|
|
52
53
|
const file = path.join(I18N_DIR, `${lang}.json`);
|
|
53
|
-
if (
|
|
54
|
+
if (SecurityUtils.safeExistsSync(file)) return file;
|
|
54
55
|
throw new Error(`Locale file not found: ${file}`);
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -124,7 +125,7 @@ function validate() {
|
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
const reportFile = path.join(I18N_DIR, 'validation-purity-report.json');
|
|
127
|
-
|
|
128
|
+
SecurityUtils.safeWriteFileSync(reportFile, JSON.stringify(report, null, 2), path.dirname(reportFile), 'utf8');
|
|
128
129
|
console.log(`✅ Validation report saved: ${reportFile}`);
|
|
129
130
|
console.log(` Review this file for full details of problematic keys.`);
|
|
130
131
|
}
|
|
@@ -135,4 +136,4 @@ try {
|
|
|
135
136
|
} catch (err) {
|
|
136
137
|
console.error('❌ Validation failed:', err.message);
|
|
137
138
|
process.exit(1);
|
|
138
|
-
}
|
|
139
|
+
}
|