i18ntk 1.10.1 → 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 (110) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1185
  3. package/main/i18ntk-analyze.js +149 -133
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +4 -4
  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 +76 -25
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +128 -29
  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 +10 -396
  18. package/main/i18ntk-sizing.js +46 -40
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +55 -19
  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 -30
  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 +13 -5
  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 +23 -15
  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 +23 -20
  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 +152 -103
  86. package/utils/config-manager.js +204 -164
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +256 -0
  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/logger.js +6 -2
  94. package/utils/mini-commander.js +179 -0
  95. package/utils/missing-key-validator.js +5 -5
  96. package/utils/plugin-loader.js +29 -11
  97. package/utils/prompt.js +14 -44
  98. package/utils/safe-json.js +40 -0
  99. package/utils/secure-errors.js +3 -3
  100. package/utils/security-check-improved.js +390 -0
  101. package/utils/security-config.js +5 -5
  102. package/utils/security-fixed.js +607 -0
  103. package/utils/security.js +462 -248
  104. package/utils/setup-enforcer.js +136 -44
  105. package/utils/setup-validator.js +33 -32
  106. package/utils/terminal-icons.js +1 -1
  107. package/utils/ultra-performance-optimizer.js +11 -9
  108. package/utils/watch-locales.js +2 -1
  109. package/utils/prompt-fixed.js +0 -55
  110. package/utils/security-check.js +0 -450
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18NTK TRANSLATION ANALYSIS SCRIPT
4
4
  *
@@ -7,7 +7,6 @@
7
7
  *
8
8
  */
9
9
 
10
- const fs = require('fs');
11
10
  const path = require('path');
12
11
  const cliHelper = require('../utils/cli-helper');
13
12
  const { loadTranslations, t } = require('../utils/i18n-helper');
@@ -28,7 +27,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
28
27
  }
29
28
  })();
30
29
 
31
- loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
30
+ loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
32
31
 
33
32
  const PROJECT_ROOT = process.cwd();
34
33
 
@@ -68,7 +67,7 @@ class I18nAnalyzer {
68
67
  this.config = { ...baseConfig, ...(this.config || {}) };
69
68
 
70
69
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
71
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
70
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
72
71
 
73
72
  this.sourceDir = this.config.sourceDir;
74
73
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
@@ -146,7 +145,12 @@ class I18nAnalyzer {
146
145
  // Get all available languages
147
146
  getAvailableLanguages() {
148
147
  try {
149
- const items = fs.readdirSync(this.sourceDir, { withFileTypes: true });
148
+ const items = SecurityUtils.safeReaddirSync(this.sourceDir, process.cwd(), { withFileTypes: true });
149
+ if (!items) {
150
+ console.error('Error reading source directory: Unable to access directory');
151
+ return [];
152
+ }
153
+
150
154
  const languages = [];
151
155
 
152
156
  // Check for directory-based structure
@@ -173,15 +177,17 @@ class I18nAnalyzer {
173
177
  for (const dir of directories) {
174
178
  const dirPath = path.join(this.sourceDir, dir);
175
179
  try {
176
- const dirItems = fs.readdirSync(dirPath, { withFileTypes: true });
177
- const jsonFiles = dirItems
178
- .filter(item => item.isFile() && item.name.endsWith('.json'))
179
- .map(item => item.name.replace('.json', ''));
180
-
181
- // If directory contains JSON files, it's likely a language directory
182
- if (jsonFiles.length > 0) {
183
- if (!languages.includes(dir)) {
184
- languages.push(dir);
180
+ const dirItems = SecurityUtils.safeReaddirSync(dirPath, process.cwd(), { withFileTypes: true });
181
+ if (dirItems) {
182
+ const jsonFiles = dirItems
183
+ .filter(item => item.isFile() && item.name.endsWith('.json'))
184
+ .map(item => item.name.replace('.json', ''));
185
+
186
+ // If directory contains JSON files, it's likely a language directory
187
+ if (jsonFiles.length > 0) {
188
+ if (!languages.includes(dir)) {
189
+ languages.push(dir);
190
+ }
185
191
  }
186
192
  }
187
193
  } catch (error) {
@@ -208,12 +214,14 @@ class I18nAnalyzer {
208
214
  const files = [];
209
215
 
210
216
  // Handle monolith file structure
211
- if (fs.existsSync(languageFile) && fs.statSync(languageFile).isFile()) {
217
+ const languageFileStat = SecurityUtils.safeStatSync(languageFile, this.sourceDir);
218
+ if (languageFileStat && languageFileStat.isFile()) {
212
219
  return [path.basename(languageFile)];
213
220
  }
214
221
 
215
222
  // Handle directory-based structure
216
- if (fs.existsSync(languageDir) && fs.statSync(languageDir).isDirectory()) {
223
+ const languageDirStat = SecurityUtils.safeStatSync(languageDir, this.sourceDir);
224
+ if (languageDirStat && languageDirStat.isDirectory()) {
217
225
  try {
218
226
  // Ensure the path is within the source directory for security
219
227
  const validatedPath = SecurityUtils.validatePath(languageDir, this.sourceDir);
@@ -224,7 +232,9 @@ class I18nAnalyzer {
224
232
 
225
233
  const findJsonFiles = (dir) => {
226
234
  const results = [];
227
- const items = fs.readdirSync(dir, { withFileTypes: true });
235
+ const items = SecurityUtils.safeReaddirSync(dir, this.sourceDir, { withFileTypes: true });
236
+
237
+ if (!items) return results;
228
238
 
229
239
  for (const item of items) {
230
240
  const fullPath = path.join(dir, item.name);
@@ -267,16 +277,20 @@ class I18nAnalyzer {
267
277
  const searchDir = this.sourceDir;
268
278
 
269
279
  try {
270
- if (fs.existsSync(searchDir)) {
271
- const items = fs.readdirSync(searchDir, { withFileTypes: true });
280
+ const searchDirExists = SecurityUtils.safeExistsSync(searchDir, this.sourceDir);
281
+ if (searchDirExists) {
282
+ const items = SecurityUtils.safeReaddirSync(searchDir, this.sourceDir, { withFileTypes: true });
272
283
 
273
- for (const item of items) {
274
- if (item.isDirectory() && !item.name.startsWith('.') && item.name !== 'node_modules') {
275
- const namespaceDir = path.join(searchDir, item.name);
276
- const namespaceFile = path.join(namespaceDir, `${language}.json`);
277
-
278
- if (fs.existsSync(namespaceFile)) {
279
- results.push(path.relative(path.join(this.sourceDir, item.name), namespaceFile));
284
+ if (items) {
285
+ for (const item of items) {
286
+ if (item.isDirectory() && !item.name.startsWith('.') && item.name !== 'node_modules') {
287
+ const namespaceDir = path.join(searchDir, item.name);
288
+ const namespaceFile = path.join(namespaceDir, `${language}.json`);
289
+
290
+ const namespaceFileExists = SecurityUtils.safeExistsSync(namespaceFile, this.sourceDir);
291
+ if (namespaceFileExists) {
292
+ results.push(path.relative(path.join(this.sourceDir, item.name), namespaceFile));
293
+ }
280
294
  }
281
295
  }
282
296
  }
@@ -468,15 +482,28 @@ class I18nAnalyzer {
468
482
  const sourceFullPath = path.join(this.sourceDir, sourceFilePath);
469
483
  const targetFullPath = path.join(this.sourceDir, targetFilePath);
470
484
 
471
- if (!fs.existsSync(sourceFullPath)) {
485
+ const sourceExists = SecurityUtils.safeExistsSync(sourceFullPath, this.sourceDir);
486
+ if (!sourceExists) {
472
487
  continue;
473
488
  }
474
489
 
475
490
  let sourceContent, targetContent;
476
491
 
477
492
  try {
478
- const sourceFileContent = fs.readFileSync(sourceFullPath, 'utf8');
479
- sourceContent = JSON.parse(sourceFileContent);
493
+ const sourceFileContent = SecurityUtils.safeReadFileSync(sourceFullPath, this.sourceDir, 'utf8');
494
+ if (!sourceFileContent) {
495
+ analysis.files[fileName] = {
496
+ error: `Failed to read source file: File not accessible or empty`
497
+ };
498
+ continue;
499
+ }
500
+ sourceContent = SecurityUtils.safeParseJSON(sourceFileContent);
501
+ if (!sourceContent) {
502
+ analysis.files[fileName] = {
503
+ error: `Failed to parse source file: Invalid JSON format`
504
+ };
505
+ continue;
506
+ }
480
507
  } catch (error) {
481
508
  analysis.files[fileName] = {
482
509
  error: `Failed to parse source file: ${error.message}`
@@ -484,7 +511,8 @@ class I18nAnalyzer {
484
511
  continue;
485
512
  }
486
513
 
487
- if (!fs.existsSync(targetFullPath)) {
514
+ const targetExists = SecurityUtils.safeExistsSync(targetFullPath, this.sourceDir);
515
+ if (!targetExists) {
488
516
  analysis.files[fileName] = {
489
517
  status: 'missing',
490
518
  sourceKeys: this.getAllKeys(sourceContent).size
@@ -493,9 +521,15 @@ class I18nAnalyzer {
493
521
  }
494
522
 
495
523
  try {
496
- const targetFileContent = fs.readFileSync(targetFullPath, 'utf8');
497
- const parsed = JSON.parse(targetFileContent);
498
-
524
+ const targetFileContent = SecurityUtils.safeReadFileSync(targetFullPath, this.sourceDir, 'utf8');
525
+ if (!targetFileContent) {
526
+ analysis.files[fileName] = {
527
+ error: `Failed to read target file: File not accessible or empty`
528
+ };
529
+ continue;
530
+ }
531
+
532
+ const parsed = SecurityUtils.safeParseJSON(targetFileContent);
499
533
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
500
534
  analysis.files[fileName] = {
501
535
  error: `Invalid structure in target file: must be a plain object (not array/null/type)`
@@ -644,13 +678,10 @@ try {
644
678
  }
645
679
 
646
680
  // Ensure the output directory exists
647
- try {
648
- fs.mkdirSync(this.outputDir, { recursive: true });
649
- } catch (error) {
650
- if (error.code !== 'EEXIST') {
651
- console.error(`Failed to create output directory ${this.outputDir}:`, error.message);
652
- return null;
653
- }
681
+ const dirCreated = SecurityUtils.safeMkdirSync(this.outputDir, process.cwd(), { recursive: true });
682
+ if (!dirCreated) {
683
+ console.error(`Failed to create output directory ${this.outputDir}`);
684
+ return null;
654
685
  }
655
686
 
656
687
  // Validate the output directory is within the project
@@ -670,14 +701,10 @@ try {
670
701
  return null;
671
702
  }
672
703
 
673
- // Use safeWriteFile if available, otherwise fall back to writeFileSync
674
- if (SecurityUtils.safeWriteFile) {
675
- const success = await SecurityUtils.safeWriteFile(reportPath, report, this.outputDir);
676
- if (!success) {
677
- throw new Error(t('analyze.failedToWriteReportFile') || 'Failed to write report file securely');
678
- }
679
- } else {
680
- fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
704
+ // Use safeWriteFile for secure file writing
705
+ const success = await SecurityUtils.safeWriteFile(reportPath, JSON.stringify(report, null, 2), process.cwd(), 'utf8');
706
+ if (!success) {
707
+ throw new Error(t('analyze.failedToWriteReportFile') || 'Failed to write report file securely');
681
708
  }
682
709
 
683
710
  console.log(`Report saved to: ${reportPath}`);
@@ -709,8 +736,9 @@ try {
709
736
  }
710
737
 
711
738
  // Ensure output directory exists
712
- if (!fs.existsSync(this.outputDir)) {
713
- fs.mkdirSync(this.outputDir, { recursive: true });
739
+ const outputDirExists = SecurityUtils.safeExistsSync(this.outputDir, process.cwd());
740
+ if (!outputDirExists) {
741
+ SecurityUtils.safeMkdirSync(this.outputDir, process.cwd(), { recursive: true });
714
742
  }
715
743
 
716
744
  const languages = this.getAvailableLanguages();
@@ -856,7 +884,7 @@ try {
856
884
  this.config = { ...baseConfig, ...this.config };
857
885
 
858
886
  const uiLanguage = this.config.uiLanguage || 'en';
859
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
887
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
860
888
 
861
889
  this.sourceDir = this.config.sourceDir;
862
890
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
@@ -941,130 +969,110 @@ try {
941
969
  }
942
970
 
943
971
  async runSetupWizard() {
944
- console.log('šŸ§™ā€ā™‚ļø Translation Analysis Setup Wizard');
972
+ console.log('Translation Analysis Setup Wizard');
945
973
  console.log('='.repeat(50));
946
-
947
- const prompts = require('prompts');
948
-
974
+
949
975
  try {
950
- // Detect current structure
951
976
  const structure = this.detectStructureType();
952
977
  console.log(`Current structure detected: ${structure.type}`);
953
-
954
- const questions = [
955
- {
956
- type: 'select',
957
- name: 'structureType',
958
- message: 'Choose your translation file structure:',
959
- choices: [
960
- { title: 'Monolith files (en.json, de.json, etc.)', value: 'monolith' },
961
- { title: 'Directory structure (en/common.json, de/common.json)', value: 'directory' },
962
- { title: 'Namespace structure (common/en.json, forms/en.json)', value: 'namespace' },
963
- { title: 'Mixed structure (auto-detect)', value: 'mixed' }
964
- ],
965
- initial: structure.type === 'monolith' ? 0 : structure.type === 'directory' ? 1 : 2
966
- },
967
- {
968
- type: 'text',
969
- name: 'sourceDir',
970
- message: 'Enter source directory path:',
971
- initial: this.sourceDir,
972
- validate: value => fs.existsSync(value) ? true : 'Directory does not exist'
973
- },
974
- {
975
- type: 'text',
976
- name: 'languages',
977
- message: 'Enter languages to analyze (comma-separated, e.g., de,fr,es):',
978
- initial: 'de,fr,es,ja,ru',
979
- validate: value => value.trim() ? true : 'Please enter at least one language'
980
- },
981
- {
982
- type: 'confirm',
983
- name: 'outputReports',
984
- message: 'Generate detailed reports for each language?',
985
- initial: true
986
- },
987
- {
988
- type: 'text',
989
- name: 'outputDir',
990
- message: 'Enter output directory for reports:',
991
- initial: this.outputDir
992
- }
993
- ];
994
-
995
- const response = await prompts(questions);
996
-
997
- if (!response.structureType) {
998
- console.log('Setup cancelled.');
978
+
979
+ const structureOptions = ['monolith', 'directory', 'namespace', 'mixed'];
980
+ const defaultStructureIndex = structureOptions.indexOf(structure.type);
981
+ const defaultStructureChoice = defaultStructureIndex >= 0 ? String(defaultStructureIndex + 1) : '4';
982
+
983
+ console.log('Choose your translation file structure:');
984
+ console.log('1) Monolith files (en.json, de.json, etc.)');
985
+ console.log('2) Directory structure (en/common.json, de/common.json)');
986
+ console.log('3) Namespace structure (common/en.json, forms/en.json)');
987
+ console.log('4) Mixed structure (auto-detect)');
988
+ const structureChoiceInput = await this.prompt(`Select option [${defaultStructureChoice}]: `);
989
+ const structureChoice = structureChoiceInput.trim() || defaultStructureChoice;
990
+ const structureType = structureOptions[Number(structureChoice) - 1] || 'mixed';
991
+
992
+ const sourceDirInput = await this.prompt(`Enter source directory path [${this.sourceDir}]: `);
993
+ const sourceDir = sourceDirInput.trim() || this.sourceDir;
994
+ if (!SecurityUtils.safeExistsSync(sourceDir, process.cwd())) {
995
+ console.log('Setup cancelled: directory does not exist.');
999
996
  return { success: false, cancelled: true };
1000
997
  }
1001
-
1002
- // Update configuration
998
+
999
+ const languagesInput = await this.prompt('Enter languages to analyze (comma-separated) [de,fr,es,ja,ru]: ');
1000
+ const languagesValue = languagesInput.trim() || 'de,fr,es,ja,ru';
1001
+ const languages = languagesValue.split(',').map(lang => lang.trim()).filter(Boolean);
1002
+ if (languages.length === 0) {
1003
+ console.log('Setup cancelled: no languages provided.');
1004
+ return { success: false, cancelled: true };
1005
+ }
1006
+
1007
+ const outputReportsInput = await this.prompt('Generate detailed reports for each language? (Y/n): ');
1008
+ const outputReports = !['n', 'no'].includes(outputReportsInput.trim().toLowerCase());
1009
+
1010
+ const outputDirInput = await this.prompt(`Enter output directory for reports [${this.outputDir}]: `);
1011
+ const outputDir = outputDirInput.trim() || this.outputDir;
1012
+
1013
+ const response = {
1014
+ structureType,
1015
+ sourceDir,
1016
+ languages: languagesValue,
1017
+ outputReports,
1018
+ outputDir
1019
+ };
1020
+
1003
1021
  this.sourceDir = path.resolve(response.sourceDir);
1004
1022
  this.outputDir = path.resolve(response.outputDir);
1005
1023
  this.outputReports = response.outputReports;
1006
-
1007
- const languages = response.languages.split(',').map(lang => lang.trim()).filter(Boolean);
1008
-
1009
- console.log('\nšŸ“Š Configuration Summary:');
1024
+
1025
+ console.log('\nConfiguration Summary:');
1010
1026
  console.log(`Source: ${this.sourceDir}`);
1011
1027
  console.log(`Output: ${this.outputDir}`);
1012
- console.log(`Languages: ${languages.join(', ')}`);
1028
+ console.log(`Languages: ${languages.join(", ")}`);
1013
1029
  console.log(`Structure: ${response.structureType}`);
1014
-
1015
- const confirm = await prompts({
1016
- type: 'confirm',
1017
- name: 'proceed',
1018
- message: 'Proceed with analysis?',
1019
- initial: true
1020
- });
1021
-
1022
- if (!confirm.proceed) {
1030
+
1031
+ const proceedInput = await this.prompt('Proceed with analysis? (Y/n): ');
1032
+ const proceed = !['n', 'no'].includes(proceedInput.trim().toLowerCase());
1033
+ if (!proceed) {
1023
1034
  console.log('Setup cancelled.');
1024
1035
  return { success: false, cancelled: true };
1025
1036
  }
1026
-
1027
- // Run analysis with specified languages
1037
+
1028
1038
  const results = [];
1029
1039
  for (const language of languages) {
1030
1040
  try {
1031
- console.log(`\nšŸ” Analyzing ${language}...`);
1041
+ console.log(`\nAnalyzing ${language}...`);
1032
1042
  const result = await this.analyzeLanguage(language);
1033
1043
  results.push({ language, ...result });
1034
-
1044
+
1035
1045
  if (this.outputReports) {
1036
1046
  await this.saveReport(language, result);
1037
- console.log(`āœ… Report saved: ${language}.json`);
1047
+ console.log(`Report saved: ${language}.json`);
1038
1048
  }
1039
1049
  } catch (error) {
1040
- console.error(`āŒ Error analyzing ${language}:`, error.message);
1050
+ console.error(`Error analyzing ${language}:`, error.message);
1041
1051
  results.push({ language, error: error.message });
1042
1052
  }
1043
1053
  }
1044
-
1045
- // Generate summary
1054
+
1046
1055
  const summary = {
1047
1056
  totalLanguages: results.length,
1048
1057
  successful: results.filter(r => !r.error).length,
1049
1058
  failed: results.filter(r => r.error).length,
1050
1059
  results
1051
1060
  };
1052
-
1061
+
1053
1062
  await this.saveReport('wizard-summary', {
1054
1063
  summary,
1055
1064
  configuration: response,
1056
1065
  timestamp: new Date().toISOString()
1057
1066
  });
1058
-
1059
- console.log('\nšŸŽ‰ Setup complete!');
1067
+
1068
+ console.log('\nSetup complete.');
1060
1069
  console.log(`Analyzed ${summary.successful}/${summary.totalLanguages} languages successfully`);
1061
-
1070
+
1062
1071
  return {
1063
1072
  success: true,
1064
1073
  summary,
1065
1074
  configuration: response
1066
1075
  };
1067
-
1068
1076
  } catch (error) {
1069
1077
  console.error('Setup wizard error:', error.message);
1070
1078
  return { success: false, error: error.message };
@@ -1100,7 +1108,14 @@ async function analyzeTranslations(datasetPath) {
1100
1108
  analyzer.outputDir = './reports';
1101
1109
 
1102
1110
  // Load and analyze the dataset
1103
- const dataset = JSON.parse(fs.readFileSync(datasetPath, 'utf8'));
1111
+ const datasetContent = SecurityUtils.safeReadFileSync(datasetPath, process.cwd(), 'utf8');
1112
+ if (!datasetContent) {
1113
+ throw new Error('Failed to load dataset for benchmark');
1114
+ }
1115
+ const dataset = SecurityUtils.safeParseJSON(datasetContent);
1116
+ if (!dataset) {
1117
+ throw new Error('Failed to parse dataset JSON');
1118
+ }
1104
1119
 
1105
1120
  // Simulate analysis processing
1106
1121
  const languages = Object.keys(dataset).filter(lang => lang !== 'en');
@@ -1133,4 +1148,5 @@ async function analyzeTranslations(datasetPath) {
1133
1148
  module.exports = {
1134
1149
  I18nAnalyzer,
1135
1150
  analyzeTranslations
1136
- };
1151
+ };
1152
+