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.
Files changed (108) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1191
  3. package/main/i18ntk-analyze.js +65 -84
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +3 -3
  6. package/main/i18ntk-complete.js +90 -65
  7. package/main/i18ntk-doctor.js +123 -103
  8. package/main/i18ntk-fixer.js +61 -725
  9. package/main/i18ntk-go.js +14 -15
  10. package/main/i18ntk-init.js +77 -26
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +129 -30
  14. package/main/i18ntk-php.js +75 -75
  15. package/main/i18ntk-py.js +55 -56
  16. package/main/i18ntk-scanner.js +59 -57
  17. package/main/i18ntk-setup.js +9 -404
  18. package/main/i18ntk-sizing.js +6 -6
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +54 -18
  22. package/main/i18ntk-validate.js +13 -13
  23. package/main/manage/commands/AnalyzeCommand.js +1124 -0
  24. package/main/manage/commands/BackupCommand.js +62 -0
  25. package/main/manage/commands/CommandRouter.js +295 -0
  26. package/main/manage/commands/CompleteCommand.js +61 -0
  27. package/main/manage/commands/DoctorCommand.js +60 -0
  28. package/main/manage/commands/FixerCommand.js +624 -0
  29. package/main/manage/commands/InitCommand.js +62 -0
  30. package/main/manage/commands/ScannerCommand.js +654 -0
  31. package/main/manage/commands/SizingCommand.js +60 -0
  32. package/main/manage/commands/SummaryCommand.js +61 -0
  33. package/main/manage/commands/UsageCommand.js +60 -0
  34. package/main/manage/commands/ValidateCommand.js +978 -0
  35. package/main/manage/index-fixed.js +1447 -0
  36. package/main/manage/index.js +1462 -0
  37. package/main/manage/managers/DebugMenu.js +140 -0
  38. package/main/manage/managers/InteractiveMenu.js +177 -0
  39. package/main/manage/managers/LanguageMenu.js +62 -0
  40. package/main/manage/managers/SettingsMenu.js +53 -0
  41. package/main/manage/services/AuthenticationService.js +263 -0
  42. package/main/manage/services/ConfigurationService-fixed.js +449 -0
  43. package/main/manage/services/ConfigurationService.js +449 -0
  44. package/main/manage/services/FileManagementService.js +368 -0
  45. package/main/manage/services/FrameworkDetectionService.js +458 -0
  46. package/main/manage/services/InitService.js +1051 -0
  47. package/main/manage/services/SetupService.js +462 -0
  48. package/main/manage/services/SummaryService.js +450 -0
  49. package/main/manage/services/UsageService.js +1502 -0
  50. package/package.json +32 -29
  51. package/runtime/enhanced.d.ts +221 -221
  52. package/runtime/index.d.ts +29 -29
  53. package/runtime/index.full.d.ts +331 -331
  54. package/runtime/index.js +7 -6
  55. package/scripts/build-lite.js +17 -17
  56. package/scripts/deprecate-versions.js +23 -6
  57. package/scripts/export-translations.js +5 -5
  58. package/scripts/fix-all-i18n.js +3 -3
  59. package/scripts/fix-and-purify-i18n.js +3 -2
  60. package/scripts/fix-locale-control-chars.js +110 -0
  61. package/scripts/lint-locales.js +80 -0
  62. package/scripts/locale-optimizer.js +8 -8
  63. package/scripts/prepublish.js +21 -21
  64. package/scripts/security-check.js +117 -117
  65. package/scripts/sync-translations.js +4 -4
  66. package/scripts/sync-ui-locales.js +9 -8
  67. package/scripts/validate-all-translations.js +8 -7
  68. package/scripts/verify-deprecations.js +157 -161
  69. package/scripts/verify-translations.js +6 -5
  70. package/settings/i18ntk-config.json +282 -282
  71. package/settings/language-config.json +5 -5
  72. package/settings/settings-cli.js +9 -9
  73. package/settings/settings-manager.js +18 -18
  74. package/ui-locales/de.json +2417 -2348
  75. package/ui-locales/en.json +2415 -2352
  76. package/ui-locales/es.json +2425 -2353
  77. package/ui-locales/fr.json +2418 -2348
  78. package/ui-locales/ja.json +2463 -2361
  79. package/ui-locales/ru.json +2463 -2359
  80. package/ui-locales/zh.json +2418 -2351
  81. package/utils/admin-auth.js +2 -2
  82. package/utils/admin-cli.js +297 -297
  83. package/utils/admin-pin.js +9 -9
  84. package/utils/cli-helper.js +9 -9
  85. package/utils/config-helper.js +73 -104
  86. package/utils/config-manager.js +204 -171
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +249 -263
  89. package/utils/framework-detector.js +27 -24
  90. package/utils/i18n-helper.js +85 -41
  91. package/utils/init-helper.js +152 -94
  92. package/utils/json-output.js +98 -98
  93. package/utils/mini-commander.js +179 -0
  94. package/utils/missing-key-validator.js +5 -5
  95. package/utils/plugin-loader.js +40 -29
  96. package/utils/prompt.js +14 -44
  97. package/utils/safe-json.js +40 -0
  98. package/utils/secure-errors.js +3 -3
  99. package/utils/security-check-improved.js +390 -0
  100. package/utils/security-config.js +5 -5
  101. package/utils/security-fixed.js +607 -0
  102. package/utils/security.js +652 -602
  103. package/utils/setup-enforcer.js +136 -44
  104. package/utils/setup-validator.js +33 -32
  105. package/utils/ultra-performance-optimizer.js +11 -9
  106. package/utils/watch-locales.js +2 -1
  107. package/utils/prompt-fixed.js +0 -55
  108. package/utils/security-check.js +0 -454
@@ -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/i18ntk-manage.js',
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 (!fs.existsSync(dirPath)) {
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 (fs.existsSync(dir)) {
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 (fs.existsSync(searchPath)) {
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 (!fs.existsSync(filePath)) {
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 (!fs.existsSync(filePath)) {
190
+ if (!SecurityUtils.safeExistsSync(filePath)) {
191
191
  invalidFiles.push(localeFile);
192
192
  continue;
193
193
  }
194
194
 
195
195
  try {
196
- const content = fs.readFileSync(filePath, 'utf8');
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(fs.readFileSync(packagePath, 'utf8'));
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 (!fs.existsSync(binPath)) {
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 (fs.existsSync(artifactPath)) {
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/i18ntk-manage.js',
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 (fs.existsSync(scriptPath)) {
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 (fs.existsSync(configPath)) {
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
- fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
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 (!fs.existsSync(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 = fs.readFileSync(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
+ #!/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 = fs.readFileSync(filePath, 'utf8');
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
- fs.writeFileSync(filePath, jsonString + '\n');
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 (!fs.existsSync(languageDir)) {
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 baseDir = path.join(__dirname, '..', 'ui-locales');
4
- const en = JSON.parse(fs.readFileSync(path.join(baseDir,'en.json'),'utf8'));
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(fs.readFileSync(p,'utf8'));
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) { fs.writeFileSync(p, JSON.stringify(unflatten(flat), null, 2)); }
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(fs.readFileSync(p, 'utf8')); }
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 (fs.existsSync(file)) return file;
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
- fs.writeFileSync(reportFile, JSON.stringify(report, null, 2), 'utf8');
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
+ }