i18ntk 2.0.4 â 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 +38 -60
- package/main/i18ntk-analyze.js +49 -44
- 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 +54 -8
- package/main/i18ntk-usage.js +14 -14
- package/main/i18ntk-validate.js +6 -5
- package/main/manage/commands/AnalyzeCommand.js +40 -35
- 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 +147 -75
- package/main/manage/managers/LanguageMenu.js +7 -2
- package/main/manage/services/UsageService.js +7 -7
- package/package.json +269 -290
- package/settings/settings-cli.js +3 -3
- package/ui-locales/de.json +161 -166
- package/ui-locales/en.json +13 -18
- package/ui-locales/es.json +171 -184
- package/ui-locales/fr.json +155 -161
- package/ui-locales/ja.json +192 -243
- package/ui-locales/ru.json +145 -196
- package/ui-locales/zh.json +179 -185
- package/utils/cli-helper.js +26 -98
- package/utils/extractors/regex.js +39 -12
- package/utils/i18n-helper.js +88 -40
- package/{scripts â utils}/locale-optimizer.js +61 -60
- package/utils/security-check-improved.js +16 -13
- package/utils/security.js +6 -4
- 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/main/manage/services/ConfigurationService-fixed.js +0 -449
- 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 -215
- package/scripts/fix-and-purify-i18n.js +0 -213
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish.js +0 -348
- 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 -139
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -607
package/scripts/prepublish.js
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Prepublish Script
|
|
5
|
-
* Cleans up development artifacts before npm publish
|
|
6
|
-
* Ensures fresh config and settings for public package
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
|
|
12
|
-
class PrepublishCleaner {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.projectRoot = path.join(__dirname, '..');
|
|
15
|
-
this.directories = [
|
|
16
|
-
'scripts/debug/logs',
|
|
17
|
-
'scripts/debug/reports',
|
|
18
|
-
'settings/backups',
|
|
19
|
-
'i18ntk-reports',
|
|
20
|
-
'reports'
|
|
21
|
-
];
|
|
22
|
-
this.files = [
|
|
23
|
-
'settings/.i18n-admin-config.json',
|
|
24
|
-
'test-*.json',
|
|
25
|
-
'debug-*.log',
|
|
26
|
-
'npm-debug.log',
|
|
27
|
-
'yarn-error.log'
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
// Essential files that must exist for release
|
|
31
|
-
this.essentialFiles = [
|
|
32
|
-
'package.json',
|
|
33
|
-
'main/manage/index.js',
|
|
34
|
-
'main/i18ntk-init.js',
|
|
35
|
-
'main/i18ntk-analyze.js',
|
|
36
|
-
'main/i18ntk-validate.js',
|
|
37
|
-
'main/i18ntk-usage.js',
|
|
38
|
-
'main/i18ntk-summary.js',
|
|
39
|
-
'main/i18ntk-sizing.js',
|
|
40
|
-
'main/i18ntk-complete.js',
|
|
41
|
-
'main/i18ntk-ui.js',
|
|
42
|
-
'main/i18ntk-autorun.js',
|
|
43
|
-
'utils/i18n-helper.js',
|
|
44
|
-
'utils/security.js',
|
|
45
|
-
'settings/settings-manager.js',
|
|
46
|
-
'settings/settings-cli.js',
|
|
47
|
-
'settings/i18ntk-config.json'
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
// Essential locale files
|
|
51
|
-
this.essentialLocales = [
|
|
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
|
-
];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
log(message) {
|
|
63
|
-
console.log(`[Prepublish] ${message}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async clean() {
|
|
67
|
-
this.log('Starting comprehensive pre-publish validation...');
|
|
68
|
-
|
|
69
|
-
// Validate essential files exist
|
|
70
|
-
await this.validateEssentialFiles();
|
|
71
|
-
|
|
72
|
-
// Validate locale files
|
|
73
|
-
await this.validateLocaleFiles();
|
|
74
|
-
|
|
75
|
-
// Validate package.json
|
|
76
|
-
await this.validatePackageJson();
|
|
77
|
-
|
|
78
|
-
// Clean directories
|
|
79
|
-
for (const dir of this.directories) {
|
|
80
|
-
await this.cleanDirectory(path.join(this.projectRoot, dir));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Clean files
|
|
84
|
-
for (const file of this.files) {
|
|
85
|
-
await this.cleanFile(file);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Reset security settings
|
|
89
|
-
await this.resetSecuritySettings();
|
|
90
|
-
|
|
91
|
-
// Final validation
|
|
92
|
-
await this.finalValidation();
|
|
93
|
-
|
|
94
|
-
this.log('Pre-publish validation completed successfully!');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async cleanDirectory(dirPath) {
|
|
98
|
-
if (!SecurityUtils.safeExistsSync(dirPath)) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
const files = fs.readdirSync(dirPath);
|
|
104
|
-
let deletedCount = 0;
|
|
105
|
-
|
|
106
|
-
for (const file of files) {
|
|
107
|
-
const filePath = path.join(dirPath, file);
|
|
108
|
-
const stat = fs.statSync(filePath);
|
|
109
|
-
|
|
110
|
-
if (stat.isFile()) {
|
|
111
|
-
fs.unlinkSync(filePath);
|
|
112
|
-
deletedCount++;
|
|
113
|
-
} else if (stat.isDirectory()) {
|
|
114
|
-
// Recursively clean subdirectories
|
|
115
|
-
await this.cleanDirectory(filePath);
|
|
116
|
-
// Remove empty directories
|
|
117
|
-
try {
|
|
118
|
-
fs.rmdirSync(filePath);
|
|
119
|
-
} catch (e) {
|
|
120
|
-
// Directory not empty, skip
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (deletedCount > 0) {
|
|
126
|
-
this.log(`Cleaned ${deletedCount} files from ${path.relative(this.projectRoot, dirPath)}`);
|
|
127
|
-
}
|
|
128
|
-
} catch (error) {
|
|
129
|
-
this.log(`Warning: Could not clean ${dirPath}: ${error.message}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async cleanFile(pattern) {
|
|
134
|
-
const searchPath = path.join(this.projectRoot, pattern);
|
|
135
|
-
|
|
136
|
-
if (pattern.includes('*')) {
|
|
137
|
-
// Handle glob patterns
|
|
138
|
-
const dir = path.dirname(searchPath);
|
|
139
|
-
const filenamePattern = path.basename(searchPath);
|
|
140
|
-
|
|
141
|
-
if (SecurityUtils.safeExistsSync(dir)) {
|
|
142
|
-
const files = fs.readdirSync(dir);
|
|
143
|
-
const regex = new RegExp(filenamePattern.replace('*', '.*'));
|
|
144
|
-
|
|
145
|
-
for (const file of files) {
|
|
146
|
-
if (regex.test(file)) {
|
|
147
|
-
const filePath = path.join(dir, file);
|
|
148
|
-
fs.unlinkSync(filePath);
|
|
149
|
-
this.log(`Deleted ${path.relative(this.projectRoot, filePath)}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
// Handle exact files
|
|
155
|
-
if (SecurityUtils.safeExistsSync(searchPath)) {
|
|
156
|
-
fs.unlinkSync(searchPath);
|
|
157
|
-
this.log(`Deleted ${path.relative(this.projectRoot, searchPath)}`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async validateEssentialFiles() {
|
|
163
|
-
this.log('Validating essential files...');
|
|
164
|
-
|
|
165
|
-
let missingFiles = [];
|
|
166
|
-
for (const file of this.essentialFiles) {
|
|
167
|
-
const filePath = path.join(this.projectRoot, file);
|
|
168
|
-
if (!SecurityUtils.safeExistsSync(filePath)) {
|
|
169
|
-
missingFiles.push(file);
|
|
170
|
-
} else if (!fs.statSync(filePath).isFile()) {
|
|
171
|
-
this.log(`â ${file} is not a file`);
|
|
172
|
-
process.exit(1);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (missingFiles.length > 0) {
|
|
177
|
-
this.log(`â Missing essential files: ${missingFiles.join(', ')}`);
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
this.log('â
All essential files present');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async validateLocaleFiles() {
|
|
185
|
-
this.log('Validating locale files...');
|
|
186
|
-
|
|
187
|
-
let invalidFiles = [];
|
|
188
|
-
for (const localeFile of this.essentialLocales) {
|
|
189
|
-
const filePath = path.join(this.projectRoot, localeFile);
|
|
190
|
-
if (!SecurityUtils.safeExistsSync(filePath)) {
|
|
191
|
-
invalidFiles.push(localeFile);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
197
|
-
const parsed = JSON.parse(content);
|
|
198
|
-
|
|
199
|
-
// Validate structure
|
|
200
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
201
|
-
invalidFiles.push(`${localeFile}: Invalid structure`);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Check for required keys
|
|
205
|
-
if (!parsed.settings || !parsed.settings.title) {
|
|
206
|
-
invalidFiles.push(`${localeFile}: Missing required keys`);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
} catch (e) {
|
|
210
|
-
invalidFiles.push(`${localeFile}: ${e.message}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (invalidFiles.length > 0) {
|
|
215
|
-
this.log(`â Invalid locale files: ${invalidFiles.join(', ')}`);
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
this.log('â
All locale files valid');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async validatePackageJson() {
|
|
223
|
-
this.log('Validating package.json...');
|
|
224
|
-
|
|
225
|
-
const packagePath = path.join(this.projectRoot, 'package.json');
|
|
226
|
-
try {
|
|
227
|
-
const pkg = JSON.parse(SecurityUtils.safeReadFileSync(packagePath, path.dirname(packagePath), 'utf8'));
|
|
228
|
-
|
|
229
|
-
// Validate required fields
|
|
230
|
-
const requiredFields = ['name', 'version', 'description', 'main', 'bin', 'files'];
|
|
231
|
-
for (const field of requiredFields) {
|
|
232
|
-
if (!pkg[field]) {
|
|
233
|
-
this.log(`â package.json missing required field: ${field}`);
|
|
234
|
-
process.exit(1);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Validate version format
|
|
239
|
-
if (!/^\d+\.\d+\.\d+/.test(pkg.version)) {
|
|
240
|
-
this.log('â Invalid version format');
|
|
241
|
-
process.exit(1);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Validate bin entries
|
|
245
|
-
const requiredBinEntries = [
|
|
246
|
-
'i18ntk', 'i18ntk-init', 'i18ntk-analyze', 'i18ntk-validate',
|
|
247
|
-
'i18ntk-usage', 'i18ntk-summary', 'i18ntk-sizing', 'i18ntk-complete',
|
|
248
|
-
'i18ntk-ui', 'i18ntk-autorun'
|
|
249
|
-
];
|
|
250
|
-
|
|
251
|
-
for (const bin of requiredBinEntries) {
|
|
252
|
-
if (!pkg.bin || !pkg.bin[bin]) {
|
|
253
|
-
this.log(`â Missing bin entry: ${bin}`);
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const binPath = path.join(this.projectRoot, pkg.bin[bin]);
|
|
258
|
-
if (!SecurityUtils.safeExistsSync(binPath)) {
|
|
259
|
-
this.log(`â Missing bin script: ${pkg.bin[bin]}`);
|
|
260
|
-
process.exit(1);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
this.log('â
package.json validated');
|
|
265
|
-
} catch (e) {
|
|
266
|
-
this.log(`â Invalid package.json: ${e.message}`);
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async finalValidation() {
|
|
272
|
-
this.log('Running final validation checks...');
|
|
273
|
-
|
|
274
|
-
// Check for development artifacts
|
|
275
|
-
const devArtifacts = [
|
|
276
|
-
'dev/debug',
|
|
277
|
-
'benchmarks',
|
|
278
|
-
'.github',
|
|
279
|
-
'test-usage-fix.html',
|
|
280
|
-
'.i18ntk'
|
|
281
|
-
];
|
|
282
|
-
|
|
283
|
-
for (const artifact of devArtifacts) {
|
|
284
|
-
const artifactPath = path.join(this.projectRoot, artifact);
|
|
285
|
-
if (SecurityUtils.safeExistsSync(artifactPath)) {
|
|
286
|
-
this.log(`â ī¸ Development artifact found: ${artifact}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Validate file permissions for executable scripts
|
|
291
|
-
const scripts = [
|
|
292
|
-
'main/manage/index.js',
|
|
293
|
-
'main/i18ntk-init.js',
|
|
294
|
-
'main/i18ntk-analyze.js',
|
|
295
|
-
'main/i18ntk-validate.js',
|
|
296
|
-
'main/i18ntk-usage.js',
|
|
297
|
-
'main/i18ntk-summary.js',
|
|
298
|
-
'main/i18ntk-sizing.js',
|
|
299
|
-
'main/i18ntk-complete.js',
|
|
300
|
-
'main/i18ntk-ui.js',
|
|
301
|
-
'main/i18ntk-autorun.js'
|
|
302
|
-
];
|
|
303
|
-
|
|
304
|
-
for (const script of scripts) {
|
|
305
|
-
const scriptPath = path.join(this.projectRoot, script);
|
|
306
|
-
if (SecurityUtils.safeExistsSync(scriptPath)) {
|
|
307
|
-
try {
|
|
308
|
-
fs.accessSync(scriptPath, fs.constants.X_OK);
|
|
309
|
-
} catch (e) {
|
|
310
|
-
this.log(`â ī¸ Script not executable: ${script}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
this.log('â
Final validation complete');
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async resetSecuritySettings() {
|
|
319
|
-
const configPath = path.join(require('../settings/settings-manager').configDir, '.i18n-admin-config.json');
|
|
320
|
-
|
|
321
|
-
if (SecurityUtils.safeExistsSync(configPath)) {
|
|
322
|
-
const defaultConfig = {
|
|
323
|
-
enabled: false,
|
|
324
|
-
pinHash: null,
|
|
325
|
-
sessionTimeout: 30,
|
|
326
|
-
maxFailedAttempts: 3,
|
|
327
|
-
lockoutDuration: 15,
|
|
328
|
-
lastActivity: null,
|
|
329
|
-
failedAttempts: 0,
|
|
330
|
-
lockedUntil: null
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
SecurityUtils.safeWriteFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
334
|
-
this.log('Reset security settings to defaults');
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Run if called directly
|
|
340
|
-
if (require.main === module) {
|
|
341
|
-
const cleaner = new PrepublishCleaner();
|
|
342
|
-
cleaner.clean().catch(error => {
|
|
343
|
-
console.error('Error during cleanup:', error);
|
|
344
|
-
process.exit(1);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
module.exports = PrepublishCleaner;
|
|
@@ -1,117 +0,0 @@
|
|
|
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;
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Translation Sync Script
|
|
5
|
-
*
|
|
6
|
-
* This script completely replaces all foreign language UI translation files with English content,
|
|
7
|
-
* except for Chinese (zh) which is already fully translated.
|
|
8
|
-
* This shows engineers exactly what needs to be translated without any prefixes.
|
|
9
|
-
*
|
|
10
|
-
* Usage: node scripts/sync-translations.js
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
|
|
16
|
-
// Configuration
|
|
17
|
-
const UI_LOCALES_DIR = path.join(__dirname, '..', 'resources', 'i18n', 'ui-locales');
|
|
18
|
-
const ENGLISH_DIR = path.join(UI_LOCALES_DIR, 'en');
|
|
19
|
-
const TARGET_LANGUAGES = ['de', 'es', 'fr', 'ru', 'ja']; // Exclude Chinese (zh) as it's fully translated
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get all JSON files in the English directory
|
|
23
|
-
*/
|
|
24
|
-
function getEnglishFiles() {
|
|
25
|
-
try {
|
|
26
|
-
const files = fs.readdirSync(ENGLISH_DIR);
|
|
27
|
-
return files.filter(file => file.endsWith('.json'));
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('Error reading English directory:', error.message);
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Read and parse JSON file
|
|
36
|
-
*/
|
|
37
|
-
function readJsonFile(filePath) {
|
|
38
|
-
try {
|
|
39
|
-
const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
|
|
40
|
-
return JSON.parse(content);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.error(`Error reading JSON file ${filePath}:`, error.message);
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Write JSON file with proper formatting
|
|
49
|
-
*/
|
|
50
|
-
function writeJsonFile(filePath, data) {
|
|
51
|
-
try {
|
|
52
|
-
const jsonString = JSON.stringify(data, null, 2);
|
|
53
|
-
SecurityUtils.safeWriteFileSync(filePath, jsonString + '\n');
|
|
54
|
-
return true;
|
|
55
|
-
} catch (error) {
|
|
56
|
-
console.error(`Error writing JSON file ${filePath}:`, error.message);
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Copy English content directly to target language
|
|
63
|
-
*/
|
|
64
|
-
function copyEnglishContent(source) {
|
|
65
|
-
return JSON.parse(JSON.stringify(source)); // Deep copy of English content
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Sync translations for a specific language - completely replace with English
|
|
70
|
-
*/
|
|
71
|
-
function syncLanguage(language) {
|
|
72
|
-
console.log(`đ Syncing translations for ${language}...`);
|
|
73
|
-
|
|
74
|
-
const languageDir = path.join(UI_LOCALES_DIR, language);
|
|
75
|
-
|
|
76
|
-
// Ensure language directory exists
|
|
77
|
-
if (!SecurityUtils.safeExistsSync(languageDir)) {
|
|
78
|
-
fs.mkdirSync(languageDir, { recursive: true });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const englishFiles = getEnglishFiles();
|
|
82
|
-
let syncedFiles = 0;
|
|
83
|
-
|
|
84
|
-
for (const file of englishFiles) {
|
|
85
|
-
const englishFilePath = path.join(ENGLISH_DIR, file);
|
|
86
|
-
const targetFilePath = path.join(languageDir, file);
|
|
87
|
-
|
|
88
|
-
const englishData = readJsonFile(englishFilePath);
|
|
89
|
-
|
|
90
|
-
// Directly copy English content (no merging, no prefixes)
|
|
91
|
-
const newData = copyEnglishContent(englishData);
|
|
92
|
-
|
|
93
|
-
// Write updated file (completely replaces existing content)
|
|
94
|
-
if (writeJsonFile(targetFilePath, newData)) {
|
|
95
|
-
syncedFiles++;
|
|
96
|
-
console.log(` â
${file}: Replaced with English content`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
console.log(`â
${language}: Synced ${syncedFiles} files with English content`);
|
|
101
|
-
return { language, syncedFiles };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Main sync function
|
|
106
|
-
*/
|
|
107
|
-
function syncAllTranslations() {
|
|
108
|
-
console.log('đ Starting translation synchronization...');
|
|
109
|
-
console.log(`đ Source: ${ENGLISH_DIR}`);
|
|
110
|
-
console.log(`đ¯ Target languages: ${TARGET_LANGUAGES.join(', ')}`);
|
|
111
|
-
console.log(`âšī¸ Chinese (zh) skipped - already fully translated`);
|
|
112
|
-
console.log('');
|
|
113
|
-
|
|
114
|
-
const englishFiles = getEnglishFiles();
|
|
115
|
-
if (englishFiles.length === 0) {
|
|
116
|
-
console.error('â No English translation files found!');
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log(`đ Found ${englishFiles.length} English files to sync:`);
|
|
121
|
-
englishFiles.forEach(file => console.log(` đ ${file}`));
|
|
122
|
-
console.log('');
|
|
123
|
-
|
|
124
|
-
const results = [];
|
|
125
|
-
for (const language of TARGET_LANGUAGES) {
|
|
126
|
-
const result = syncLanguage(language);
|
|
127
|
-
results.push(result);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
console.log('');
|
|
131
|
-
console.log('đ Translation synchronization completed!');
|
|
132
|
-
console.log('');
|
|
133
|
-
console.log('đ Summary:');
|
|
134
|
-
results.forEach(result => {
|
|
135
|
-
console.log(` ${result.language}: ${result.syncedFiles} files updated`);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
console.log('');
|
|
139
|
-
console.log('đĄ Next steps:');
|
|
140
|
-
console.log(' 1. All non-Chinese locales now contain English content');
|
|
141
|
-
console.log(' 2. Engineers can see exactly what text needs translation');
|
|
142
|
-
console.log(' 3. Translate each string to the target language');
|
|
143
|
-
console.log(' 4. Follow the same practices as the Chinese locale');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Run the sync
|
|
147
|
-
if (require.main === module) {
|
|
148
|
-
syncAllTranslations();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
module.exports = { syncAllTranslations, syncLanguage };
|
|
@@ -1,20 +0,0 @@
|
|
|
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'));
|
|
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; }
|
|
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; }
|
|
8
|
-
const enFlat = flatten(en);
|
|
9
|
-
for (const file of fs.readdirSync(baseDir)) {
|
|
10
|
-
if (!file.endsWith('.json') || file==='en.json') continue;
|
|
11
|
-
const p = path.join(baseDir,file);
|
|
12
|
-
const data = JSON.parse(SecurityUtils.safeReadFileSync(p, baseDir, 'utf8'));
|
|
13
|
-
const flat = flatten(data);
|
|
14
|
-
let changed = false;
|
|
15
|
-
for (const [k,v] of Object.entries(enFlat)) {
|
|
16
|
-
if (!(k in flat)) { flat[k] = v || 'NOT_TRANSLATED'; changed = true; }
|
|
17
|
-
}
|
|
18
|
-
if (changed) { SecurityUtils.safeWriteFileSync(p, JSON.stringify(unflatten(flat), null, 2), path.dirname(p), 'utf8'); }
|
|
19
|
-
}
|
|
20
|
-
console.log('UI locales synced.');
|