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
@@ -33,7 +33,6 @@
33
33
 
34
34
  const fs = require('fs');
35
35
  const path = require('path');
36
- const { performance } = require('perf_hooks');
37
36
  const { loadTranslations, t } = require('../utils/i18n-helper');
38
37
  const configManager = require('../settings/settings-manager');
39
38
  const SecurityUtils = require('../utils/security');
@@ -51,7 +50,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
51
50
  }
52
51
  })();
53
52
 
54
- loadTranslations(process.env.I18NTK_LANG);
53
+ loadTranslations();
55
54
 
56
55
  // Get configuration from settings manager
57
56
  function getConfig() {
@@ -84,7 +83,7 @@ class I18nSizingAnalyzer {
84
83
 
85
84
  // Initialize i18n with UI language from config
86
85
  const uiLanguage = options.uiLanguage || config.uiLanguage || 'en';
87
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
86
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
88
87
  this.stats = {
89
88
  files: {},
90
89
  languages: {},
@@ -116,23 +115,24 @@ class I18nSizingAnalyzer {
116
115
  throw new Error(t("sizing.invalidSourceDirectoryError", { sourceDir: this.sourceDir }));
117
116
  }
118
117
 
119
- if (!fs.existsSync(validatedSourceDir)) {
118
+ if (!SecurityUtils.safeExistsSync(validatedSourceDir)) {
120
119
  throw new Error(t("sizing.sourceDirectoryNotFoundError", { sourceDir: validatedSourceDir }));
121
120
  }
122
121
 
123
122
  const files = [];
124
- const items = fs.readdirSync(validatedSourceDir);
123
+ const items = SecurityUtils.safeReaddirSync(validatedSourceDir);
125
124
 
126
125
  // Check for nested language directories
127
126
  for (const item of items) {
128
127
  const itemPath = SecurityUtils.validatePath(path.join(validatedSourceDir, item), process.cwd());
129
128
  if (!itemPath) continue;
130
129
 
131
- const stat = fs.statSync(itemPath);
130
+ const stat = SecurityUtils.safeStatSync(itemPath);
131
+ if (!stat) continue;
132
132
 
133
133
  if (stat.isDirectory()) {
134
134
  // This is a language directory, combine all JSON files
135
- const langFiles = fs.readdirSync(itemPath)
135
+ const langFiles = SecurityUtils.safeReaddirSync(itemPath)
136
136
  .filter(file => file.endsWith('.json'))
137
137
  .map(file => SecurityUtils.validatePath(path.join(itemPath, file), process.cwd()))
138
138
  .filter(file => file !== null);
@@ -165,7 +165,7 @@ class I18nSizingAnalyzer {
165
165
 
166
166
  // Analyze file sizes
167
167
  analyzeFileSizes(files) {
168
- console.log(t("sizing.analyzing_file_sizes"));
168
+ SecurityUtils.debugLog('info', t("sizing.analyzing_file_sizes"));
169
169
 
170
170
  files.forEach(({ language, file, path: filePath, files: langFiles }) => {
171
171
  if (langFiles) {
@@ -176,8 +176,10 @@ class I18nSizingAnalyzer {
176
176
  let lastModified = new Date(0);
177
177
 
178
178
  langFiles.forEach(langFile => {
179
- const stats = fs.statSync(langFile);
180
- let content = SecurityUtils.safeReadFileSync(langFile, process.cwd());
179
+ const stats = SecurityUtils.safeStatSync(langFile, process.cwd());
180
+ if (!stats) return;
181
+
182
+ let content = SecurityUtils.safeReadFileSync(langFile, process.cwd(), 'utf8');
181
183
  if (typeof content !== "string") content = "";
182
184
  totalSize += stats.size;
183
185
  totalLines += content.split('\n').length;
@@ -198,8 +200,10 @@ class I18nSizingAnalyzer {
198
200
  };
199
201
  } else {
200
202
  // Handle single file structure
201
- const stats = fs.statSync(filePath);
202
- let content = SecurityUtils.safeReadFileSync(filePath, process.cwd());
203
+ const stats = SecurityUtils.safeStatSync(filePath, process.cwd());
204
+ if (!stats) return;
205
+
206
+ let content = SecurityUtils.safeReadFileSync(filePath, process.cwd(), 'utf8');
203
207
  if (typeof content !== "string") content = "";
204
208
  this.stats.files[language] = {
205
209
  file,
@@ -216,7 +220,7 @@ class I18nSizingAnalyzer {
216
220
 
217
221
  // Analyze translation content
218
222
  analyzeTranslationContent(files) {
219
- console.log(t("sizing.analyzing_translation_content"));
223
+ SecurityUtils.debugLog('info', t("sizing.analyzing_translation_content"));
220
224
 
221
225
  files.forEach(({ language, path: filePath, files: langFiles }) => {
222
226
  try {
@@ -225,7 +229,7 @@ class I18nSizingAnalyzer {
225
229
  if (langFiles) {
226
230
  // Handle nested directory structure - combine all JSON files
227
231
  langFiles.forEach(langFile => {
228
- const rawContent = SecurityUtils.safeReadFileSync(langFile, process.cwd());
232
+ const rawContent = SecurityUtils.safeReadFileSync(langFile, process.cwd(), 'utf8');
229
233
  const fileContent = SecurityUtils.safeParseJSON(rawContent);
230
234
  if (fileContent) {
231
235
  const fileName = path.basename(langFile, '.json');
@@ -234,7 +238,7 @@ class I18nSizingAnalyzer {
234
238
  });
235
239
  } else {
236
240
  // Handle single file structure
237
- const rawContent = SecurityUtils.safeReadFileSync(filePath, process.cwd());
241
+ const rawContent = SecurityUtils.safeReadFileSync(filePath, process.cwd(), 'utf8');
238
242
  combinedContent = SecurityUtils.safeParseJSON(rawContent) || {};
239
243
  }
240
244
 
@@ -262,7 +266,7 @@ class I18nSizingAnalyzer {
262
266
  });
263
267
 
264
268
  } catch (error) {
265
- console.error(t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
269
+ SecurityUtils.debugLog('error', t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
266
270
  }
267
271
  });
268
272
  }
@@ -312,13 +316,13 @@ class I18nSizingAnalyzer {
312
316
 
313
317
  // Generate size comparison analysis
314
318
  generateSizeComparison() {
315
- console.log(t("sizing.generating_size_comparisons"));
319
+ SecurityUtils.debugLog('info', t("sizing.generating_size_comparisons"));
316
320
 
317
321
  const languages = Object.keys(this.stats.languages);
318
322
  const baseLanguage = languages[0]; // Use first language as baseline
319
323
 
320
324
  if (!baseLanguage) {
321
- console.warn(t("sizing.no_languages_found_for_comparison"));
325
+ SecurityUtils.debugLog('warn', t("sizing.no_languages_found_for_comparison"));
322
326
  return;
323
327
  }
324
328
 
@@ -491,7 +495,7 @@ class I18nSizingAnalyzer {
491
495
  async generateHumanReadableReport() {
492
496
  if (!this.outputReport) return;
493
497
 
494
- console.log(t("sizing.generating_detailed_report"));
498
+ SecurityUtils.debugLog('info', t("sizing.generating_detailed_report"));
495
499
 
496
500
  const validatedOutputDir = SecurityUtils.validatePath(this.outputDir, process.cwd());
497
501
  if (!validatedOutputDir) {
@@ -499,8 +503,8 @@ class I18nSizingAnalyzer {
499
503
  }
500
504
 
501
505
  // Ensure output directory exists
502
- if (!fs.existsSync(validatedOutputDir)) {
503
- fs.mkdirSync(validatedOutputDir, { recursive: true });
506
+ if (!SecurityUtils.safeExistsSync(validatedOutputDir)) {
507
+ SecurityUtils.safeMkdirSync(validatedOutputDir, { recursive: true });
504
508
  }
505
509
 
506
510
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
@@ -514,7 +518,7 @@ class I18nSizingAnalyzer {
514
518
  let textReport = this.generateTextReport(timestamp);
515
519
  const textSuccess = await SecurityUtils.safeWriteFile(textReportPath, textReport, process.cwd());
516
520
  if (textSuccess) {
517
- console.log(t("sizing.human_report_saved", { reportPath: textReportPath }));
521
+ SecurityUtils.debugLog('info', t("sizing.human_report_saved", { reportPath: textReportPath }));
518
522
  }
519
523
 
520
524
  // Generate JSON for programmatic access
@@ -660,7 +664,7 @@ Generated: ${new Date().toISOString()}
660
664
 
661
665
  const success = await SecurityUtils.safeWriteFile(csvPath, csvContent, process.cwd());
662
666
  if (success) {
663
- console.log(t("sizing.csv_report_saved_to", { csvPath }));
667
+ SecurityUtils.debugLog('info', t("sizing.csv_report_saved_to", { csvPath }));
664
668
  SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
665
669
  } else {
666
670
  throw new Error(t("sizing.failedToSaveCsvError"));
@@ -669,20 +673,21 @@ Generated: ${new Date().toISOString()}
669
673
 
670
674
  // Main analysis method
671
675
  async analyze() {
672
- const startTime = performance.now();
676
+ const perfTimer = SecurityUtils.getPerformanceTimer();
677
+ const startTime = perfTimer.now();
673
678
 
674
679
  try {
675
- console.log(t("sizing.starting_i18n_sizing_analysis"));
676
- console.log(t("sizing.source_directory", { sourceDir: this.sourceDir }));
680
+ SecurityUtils.debugLog('info', t("sizing.starting_i18n_sizing_analysis"));
681
+ SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
677
682
 
678
683
  const files = this.getLanguageFiles();
679
684
 
680
685
  if (files.length === 0) {
681
- console.log(t("sizing.no_translation_files_found"));
686
+ SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
682
687
  return;
683
688
  }
684
689
 
685
- console.log(t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
690
+ SecurityUtils.debugLog('info', t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
686
691
 
687
692
  this.analyzeFileSizes(files);
688
693
  this.analyzeTranslationContent(files);
@@ -691,16 +696,16 @@ Generated: ${new Date().toISOString()}
691
696
  if (this.format === 'table') {
692
697
  this.displayFolderResults();
693
698
  } else if (this.format === 'json') {
694
- console.log(t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
699
+ SecurityUtils.debugLog('info', t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
695
700
  }
696
701
 
697
702
  await this.generateHumanReadableReport();
698
703
 
699
- const endTime = performance.now();
700
- console.log(t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
704
+ const endTime = perfTimer.now();
705
+ SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
701
706
 
702
707
  } catch (error) {
703
- console.error(t("sizing.analysis_failed", { errorMessage: error.message }));
708
+ SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
704
709
  process.exit(1);
705
710
  }
706
711
  }
@@ -901,19 +906,20 @@ Options:
901
906
  // Main analysis method
902
907
  async analyze() {
903
908
  try {
904
- console.log(t("sizing.starting_analysis"));
905
- console.log(t("sizing.source_directory", { sourceDir: this.sourceDir }));
909
+ SecurityUtils.debugLog('info', t("sizing.starting_analysis"));
910
+ SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
906
911
 
907
- const startTime = performance.now();
912
+ const perfTimer = SecurityUtils.getPerformanceTimer();
913
+ const startTime = perfTimer.now();
908
914
 
909
915
  // Get language files
910
916
  const files = this.getLanguageFiles();
911
917
  if (files.length === 0) {
912
- console.warn(t("sizing.no_translation_files_found"));
918
+ SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
913
919
  return { success: false, error: "No translation files found" };
914
920
  }
915
921
 
916
- console.log(t("sizing.found_files", { count: files.length }));
922
+ SecurityUtils.debugLog('info', t("sizing.found_files", { count: files.length }));
917
923
 
918
924
  // Analyze file sizes
919
925
  this.analyzeFileSizes(files);
@@ -930,15 +936,15 @@ Options:
930
936
  // Generate reports if requested
931
937
  await this.generateHumanReadableReport();
932
938
 
933
- const endTime = performance.now();
939
+ const endTime = perfTimer.now();
934
940
  const duration = ((endTime - startTime) / 1000).toFixed(2);
935
941
 
936
- console.log(t("sizing.analysis_completed", { duration }));
942
+ SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration }));
937
943
 
938
944
  return { success: true, stats: this.stats };
939
945
 
940
946
  } catch (error) {
941
- console.error(t("sizing.analysis_failed", { errorMessage: error.message }));
947
+ SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
942
948
  return { success: false, error: error.message };
943
949
  }
944
950
  }
@@ -19,7 +19,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
19
19
  }
20
20
  })();
21
21
 
22
- loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
22
+ loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
23
23
 
24
24
 
25
25
  class I18nSummaryReporter {
@@ -54,7 +54,7 @@ class I18nSummaryReporter {
54
54
  this.config = baseConfig;
55
55
 
56
56
  const uiLanguage = this.config.uiLanguage || 'en';
57
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
57
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
58
58
 
59
59
  this.sourceDir = this.config.sourceDir;
60
60
 
@@ -112,12 +112,12 @@ class I18nSummaryReporter {
112
112
 
113
113
  // Get all available languages
114
114
  getAvailableLanguages() {
115
- if (!fs.existsSync(this.config.sourceDir)) {
115
+ if (!SecurityUtils.safeExistsSync(this.config.sourceDir, this.config.sourceDir)) {
116
116
  return [];
117
117
  }
118
118
 
119
119
  // Check for monolith JSON files (en.json, es.json, etc.)
120
- const files = fs.readdirSync(this.config.sourceDir);
120
+ const files = SecurityUtils.safeReaddirSync(this.config.sourceDir, this.config.sourceDir) || [];
121
121
  const languages = files
122
122
  .filter(file => file.endsWith('.json'))
123
123
  .map(file => path.basename(file, '.json'));
@@ -126,7 +126,8 @@ class I18nSummaryReporter {
126
126
  const directories = fs.readdirSync(this.config.sourceDir)
127
127
  .filter(item => {
128
128
  const itemPath = path.join(this.config.sourceDir, item);
129
- return fs.statSync(itemPath).isDirectory() &&
129
+ const stats = SecurityUtils.safeStatSync(itemPath, this.config.sourceDir);
130
+ return stats && stats.isDirectory() &&
130
131
  !item.startsWith('.') &&
131
132
  item !== 'node_modules';
132
133
  });
@@ -138,11 +139,11 @@ class I18nSummaryReporter {
138
139
  getLanguageFiles(language) {
139
140
  const languageDir = path.join(this.config.sourceDir, language);
140
141
 
141
- if (!fs.existsSync(languageDir)) {
142
+ if (!SecurityUtils.safeExistsSync(languageDir, this.config.sourceDir)) {
142
143
  return [];
143
144
  }
144
145
 
145
- return fs.readdirSync(languageDir)
146
+ return SecurityUtils.safeReaddirSync(languageDir, this.config.sourceDir) || []
146
147
  .filter(file => {
147
148
  return this.config.supportedExtensions.some(ext => file.endsWith(ext)) &&
148
149
  !this.config.excludeFiles.includes(file);
@@ -153,7 +154,7 @@ class I18nSummaryReporter {
153
154
  // Get file size information
154
155
  getFileSize(filePath) {
155
156
  try {
156
- const stats = fs.statSync(filePath);
157
+ const stats = SecurityUtils.safeStatSync(filePath, this.config.sourceDir);
157
158
  return {
158
159
  size: stats.size,
159
160
  sizeFormatted: this.formatFileSize(stats.size),
@@ -177,10 +178,10 @@ class I18nSummaryReporter {
177
178
  calculateFolderSize(folderPath) {
178
179
  let totalSize = 0;
179
180
  try {
180
- const items = fs.readdirSync(folderPath);
181
+ const items = SecurityUtils.safeReaddirSync(folderPath, this.config.sourceDir) || [];
181
182
  for (const item of items) {
182
183
  const itemPath = path.join(folderPath, item);
183
- const stats = fs.statSync(itemPath);
184
+ const stats = SecurityUtils.safeStatSync(itemPath, this.config.sourceDir);
184
185
  if (stats.isDirectory()) {
185
186
  totalSize += this.calculateFolderSize(itemPath);
186
187
  } else {
@@ -340,7 +341,7 @@ class I18nSummaryReporter {
340
341
  // Calculate folder sizes for each language
341
342
  for (const language of this.stats.languages) {
342
343
  const languageDir = path.join(this.config.sourceDir, language);
343
- if (fs.existsSync(languageDir)) {
344
+ if (SecurityUtils.safeExistsSync(languageDir, this.config.sourceDir)) {
344
345
  this.stats.folderSizes[language] = this.calculateFolderSize(languageDir);
345
346
  }
346
347
  }
@@ -830,7 +831,7 @@ class I18nSummaryReporter {
830
831
  this.config = { ...this.config, ...baseConfig };
831
832
 
832
833
  const uiLanguage = this.config.uiLanguage || 'en';
833
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
834
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
834
835
  if (!this.config.sourceDir) {
835
836
  this.config.sourceDir = this.detectI18nDirectory();
836
837
  }
@@ -865,7 +866,7 @@ class I18nSummaryReporter {
865
866
  }
866
867
 
867
868
  // Validate source directory exists
868
- if (!fs.existsSync(this.config.sourceDir)) {
869
+ if (!SecurityUtils.safeExistsSync(this.config.sourceDir, this.config.sourceDir)) {
869
870
  console.error(t('summary.sourceDirectoryDoesNotExist', { sourceDir: this.config.sourceDir }));
870
871
  process.exit(1);
871
872
  }
@@ -898,7 +899,7 @@ class I18nSummaryReporter {
898
899
 
899
900
  // Create output directory if it doesn't exist
900
901
  const outputDir = path.resolve(process.cwd(), 'i18ntk-reports');
901
- if (!fs.existsSync(outputDir)) {
902
+ if (!SecurityUtils.safeExistsSync(outputDir)) {
902
903
  fs.mkdirSync(outputDir, { recursive: true });
903
904
  }
904
905
 
@@ -923,7 +924,7 @@ class I18nSummaryReporter {
923
924
  if (args.outputFile) {
924
925
  // Always save summary reports to i18ntk-reports
925
926
  const reportsDir = path.resolve(process.cwd(), 'i18ntk-reports');
926
- if (!fs.existsSync(reportsDir)) {
927
+ if (!SecurityUtils.safeExistsSync(reportsDir)) {
927
928
  fs.mkdirSync(reportsDir, { recursive: true });
928
929
  }
929
930
  const outputFileName = args.outputFile ? path.basename(args.outputFile) : `summary-report-${new Date().toISOString().slice(0,10)}.txt`;
@@ -943,8 +944,8 @@ class I18nSummaryReporter {
943
944
  console.log(t('summary.cleaningUpReportFiles'));
944
945
  try {
945
946
  const reportsDir = path.join(this.config.sourceDir, 'scripts', 'i18n', 'reports');
946
- if (fs.existsSync(reportsDir)) {
947
- const files = fs.readdirSync(reportsDir);
947
+ if (SecurityUtils.safeExistsSync(reportsDir)) {
948
+ const files = SecurityUtils.safeReaddirSync(reportsDir, this.config.sourceDir) || [];
948
949
  const reportFiles = files.filter(file =>
949
950
  (file.endsWith('.txt') || file.endsWith('.json') || file.endsWith('.log')) &&
950
951
  file !== path.basename(args.outputFile || '')
@@ -953,7 +954,9 @@ class I18nSummaryReporter {
953
954
  let deletedCount = 0;
954
955
  for (const file of reportFiles) {
955
956
  try {
956
- fs.unlinkSync(path.join(reportsDir, file));
957
+ // Note: Using direct fs.unlinkSync as SecurityUtils.safeUnlinkSync doesn't exist yet
958
+ // This should be replaced with a secure version when available
959
+ fs.unlinkSync(path.join(reportsDir, file));
957
960
  deletedCount++;
958
961
  } catch (error) {
959
962
  console.log(t('summary.couldNotDelete', { file, error: error.message }));
package/main/i18ntk-ui.js CHANGED
@@ -6,6 +6,7 @@
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const SettingsManager = require('../settings/settings-manager');
9
+ const SecurityUtils = require('../utils/security');
9
10
  const legacyConfigManager = require('../utils/config-manager');
10
11
  const { getIcon, isUnicodeSupported } = require('../utils/terminal-icons');
11
12
  const configManager = new SettingsManager();
@@ -14,7 +15,7 @@ class UIi18n {
14
15
  constructor() {
15
16
  this.currentLanguage = 'en';
16
17
  this.translations = {};
17
- this.uiLocalesDir = path.resolve(__dirname, '..', 'ui-locales');
18
+ this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
18
19
  this.availableLanguages = [];
19
20
  this.configFile = path.resolve(configManager.configFile);
20
21
 
@@ -29,7 +30,7 @@ this.translations = {};
29
30
  const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
30
31
 
31
32
  // Use safe defaults if config is not available
32
- this.uiLocalesDir = path.resolve(__dirname, '..', 'ui-locales');
33
+ this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
33
34
  this.availableLanguages = this.detectAvailableLanguages();
34
35
 
35
36
 
@@ -43,7 +44,7 @@ this.translations = {};
43
44
  }
44
45
  } catch (error) {
45
46
  console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
46
- this.uiLocalesDir = path.resolve(__dirname, '..', 'ui-locales');
47
+ this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
47
48
  this.availableLanguages = this.detectAvailableLanguages();
48
49
  this.loadLanguage('en');
49
50
  }
@@ -57,7 +58,7 @@ this.translations = {};
57
58
  const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
58
59
  return all.filter(lang => {
59
60
  const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
60
- return fs.existsSync(filePath);
61
+ return SecurityUtils.safeExistsSync(filePath);
61
62
  });
62
63
  }
63
64
 
@@ -93,9 +94,9 @@ this.translations = {};
93
94
  // Primary: Use monolith JSON file (en.json, de.json, etc.)
94
95
  const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
95
96
 
96
- if (fs.existsSync(monolithTranslationFile)) {
97
+ if (SecurityUtils.safeExistsSync(monolithTranslationFile)) {
97
98
  try {
98
- const content = fs.readFileSync(monolithTranslationFile, 'utf8');
99
+ const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
99
100
  const fullTranslations = JSON.parse(content);
100
101
 
101
102
  // Flatten the nested structure for easier key access
@@ -113,7 +114,7 @@ this.translations = {};
113
114
  // Fallback: Use folder-based structure if monolith file doesn't exist
114
115
  const langDir = path.join(this.uiLocalesDir, language);
115
116
 
116
- if (fs.existsSync(langDir) && fs.statSync(langDir).isDirectory()) {
117
+ if (SecurityUtils.safeExistsSync(langDir) && fs.statSync(langDir).isDirectory()) {
117
118
  const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
118
119
  if (debugEnabled) {
119
120
  console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
@@ -122,7 +123,7 @@ this.translations = {};
122
123
  for (const file of files) {
123
124
  const filePath = path.join(langDir, file);
124
125
  try {
125
- const content = fs.readFileSync(filePath, 'utf8');
126
+ const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
126
127
  const fileTranslations = JSON.parse(content);
127
128
  const moduleName = path.basename(file, '.json');
128
129
  this.translations[moduleName] = this.deepMerge(this.translations[moduleName] || {}, fileTranslations);
@@ -383,8 +384,8 @@ this.translations = {};
383
384
  getEnglishFallback(keyPath, replacements = {}) {
384
385
  try {
385
386
  const englishFile = path.join(this.uiLocalesDir, 'en.json');
386
- if (fs.existsSync(englishFile)) {
387
- const englishContent = fs.readFileSync(englishFile, 'utf8');
387
+ if (SecurityUtils.safeExistsSync(englishFile)) {
388
+ const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
388
389
  const englishTranslations = JSON.parse(englishContent);
389
390
 
390
391
  // Use the same flattening approach for consistency