i18ntk 2.1.0 → 2.3.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.
Files changed (73) hide show
  1. package/README.md +87 -50
  2. package/main/i18ntk-analyze.js +63 -63
  3. package/main/i18ntk-backup-class.js +37 -41
  4. package/main/i18ntk-backup.js +28 -30
  5. package/main/i18ntk-complete.js +75 -74
  6. package/main/i18ntk-doctor.js +7 -6
  7. package/main/i18ntk-fixer.js +3 -3
  8. package/main/i18ntk-init.js +49 -13
  9. package/main/i18ntk-scanner.js +2 -2
  10. package/main/i18ntk-sizing.js +36 -37
  11. package/main/i18ntk-summary.js +4 -4
  12. package/main/i18ntk-ui.js +95 -96
  13. package/main/i18ntk-usage.js +31 -19
  14. package/main/i18ntk-validate.js +78 -27
  15. package/main/manage/commands/AnalyzeCommand.js +71 -73
  16. package/main/manage/commands/CommandRouter.js +15 -12
  17. package/main/manage/commands/FixerCommand.js +94 -38
  18. package/main/manage/commands/ScannerCommand.js +2 -2
  19. package/main/manage/commands/ValidateCommand.js +87 -36
  20. package/main/manage/index.js +165 -152
  21. package/main/manage/managers/DebugMenu.js +6 -6
  22. package/main/manage/managers/InteractiveMenu.js +6 -6
  23. package/main/manage/managers/LanguageMenu.js +12 -6
  24. package/main/manage/managers/SettingsMenu.js +6 -6
  25. package/main/manage/services/AuthenticationService.js +5 -6
  26. package/main/manage/services/ConfigurationService.js +22 -34
  27. package/main/manage/services/FileManagementService.js +6 -6
  28. package/main/manage/services/InitService.js +44 -8
  29. package/main/manage/services/UsageService.js +24 -12
  30. package/package.json +21 -42
  31. package/settings/settings-cli.js +5 -5
  32. package/settings/settings-manager.js +984 -968
  33. package/ui-locales/de.json +12 -11
  34. package/ui-locales/en.json +12 -11
  35. package/ui-locales/es.json +12 -11
  36. package/ui-locales/fr.json +12 -11
  37. package/ui-locales/ja.json +12 -11
  38. package/ui-locales/ru.json +12 -11
  39. package/ui-locales/zh.json +12 -11
  40. package/utils/config-helper.js +27 -16
  41. package/utils/config-manager.js +8 -7
  42. package/utils/i18n-helper.js +161 -166
  43. package/utils/init-helper.js +3 -2
  44. package/utils/json-output.js +11 -10
  45. package/{scripts → utils}/locale-optimizer.js +61 -60
  46. package/utils/logger.js +4 -4
  47. package/utils/safe-json.js +3 -3
  48. package/utils/secure-backup.js +8 -7
  49. package/utils/setup-enforcer.js +63 -98
  50. package/main/i18ntk-go.js +0 -283
  51. package/main/i18ntk-java.js +0 -380
  52. package/main/i18ntk-js.js +0 -512
  53. package/main/i18ntk-manage.js +0 -1694
  54. package/main/i18ntk-php.js +0 -462
  55. package/main/i18ntk-py.js +0 -379
  56. package/main/i18ntk-settings.js +0 -23
  57. package/main/manage/index-fixed.js +0 -1447
  58. package/scripts/build-lite.js +0 -279
  59. package/scripts/deprecate-versions.js +0 -317
  60. package/scripts/export-translations.js +0 -84
  61. package/scripts/fix-all-i18n.js +0 -236
  62. package/scripts/fix-and-purify-i18n.js +0 -233
  63. package/scripts/fix-locale-control-chars.js +0 -110
  64. package/scripts/lint-locales.js +0 -80
  65. package/scripts/prepublish-dev.js +0 -221
  66. package/scripts/prepublish.js +0 -362
  67. package/scripts/security-check.js +0 -117
  68. package/scripts/sync-translations.js +0 -151
  69. package/scripts/sync-ui-locales.js +0 -20
  70. package/scripts/validate-all-translations.js +0 -195
  71. package/scripts/verify-deprecations.js +0 -157
  72. package/scripts/verify-translations.js +0 -63
  73. package/utils/security-fixed.js +0 -609
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * I18n Sizing Analyzer
@@ -37,6 +37,7 @@ const { loadTranslations, t } = require('../utils/i18n-helper');
37
37
  const configManager = require('../settings/settings-manager');
38
38
  const SecurityUtils = require('../utils/security');
39
39
  const { getUnifiedConfig } = require('../utils/config-helper');
40
+ const { logger } = require('../utils/logger');
40
41
  const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
41
42
  const SetupEnforcer = require('../utils/setup-enforcer');
42
43
 
@@ -83,7 +84,7 @@ class I18nSizingAnalyzer {
83
84
 
84
85
  // Initialize i18n with UI language from config
85
86
  const uiLanguage = options.uiLanguage || config.uiLanguage || 'en';
86
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
87
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
87
88
  this.stats = {
88
89
  files: {},
89
90
  languages: {},
@@ -165,7 +166,7 @@ class I18nSizingAnalyzer {
165
166
 
166
167
  // Analyze file sizes
167
168
  analyzeFileSizes(files) {
168
- SecurityUtils.debugLog('info', t("sizing.analyzing_file_sizes"));
169
+ logger.info(t("sizing.analyzing_file_sizes"));
169
170
 
170
171
  files.forEach(({ language, file, path: filePath, files: langFiles }) => {
171
172
  if (langFiles) {
@@ -220,7 +221,7 @@ class I18nSizingAnalyzer {
220
221
 
221
222
  // Analyze translation content
222
223
  analyzeTranslationContent(files) {
223
- SecurityUtils.debugLog('info', t("sizing.analyzing_translation_content"));
224
+ logger.info(t("sizing.analyzing_translation_content"));
224
225
 
225
226
  files.forEach(({ language, path: filePath, files: langFiles }) => {
226
227
  try {
@@ -266,7 +267,7 @@ class I18nSizingAnalyzer {
266
267
  });
267
268
 
268
269
  } catch (error) {
269
- SecurityUtils.debugLog('error', t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
270
+ logger.error(t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
270
271
  }
271
272
  });
272
273
  }
@@ -316,14 +317,14 @@ class I18nSizingAnalyzer {
316
317
 
317
318
  // Generate size comparison analysis
318
319
  generateSizeComparison() {
319
- SecurityUtils.debugLog('info', t("sizing.generating_size_comparisons"));
320
+ logger.info(t("sizing.generating_size_comparisons"));
320
321
 
321
322
  const languages = Object.keys(this.stats.languages);
322
323
  const baseLanguage = languages[0]; // Use first language as baseline
323
324
 
324
325
  if (!baseLanguage) {
325
- SecurityUtils.debugLog('warn', t("sizing.no_languages_found_for_comparison"));
326
- return;
326
+ logger.warn(t("sizing.no_languages_found_for_comparison"));
327
+ return;
327
328
  }
328
329
 
329
330
  this.stats.summary = {
@@ -495,7 +496,7 @@ class I18nSizingAnalyzer {
495
496
  async generateHumanReadableReport() {
496
497
  if (!this.outputReport) return;
497
498
 
498
- SecurityUtils.debugLog('info', t("sizing.generating_detailed_report"));
499
+ logger.info(t("sizing.generating_detailed_report"));
499
500
 
500
501
  const validatedOutputDir = SecurityUtils.validatePath(this.outputDir, process.cwd());
501
502
  if (!validatedOutputDir) {
@@ -518,7 +519,7 @@ class I18nSizingAnalyzer {
518
519
  let textReport = this.generateTextReport(timestamp);
519
520
  const textSuccess = await SecurityUtils.safeWriteFile(textReportPath, textReport, process.cwd());
520
521
  if (textSuccess) {
521
- SecurityUtils.debugLog('info', t("sizing.human_report_saved", { reportPath: textReportPath }));
522
+ logger.info(t("sizing.human_report_saved", { reportPath: textReportPath }));
522
523
  }
523
524
 
524
525
  // Generate JSON for programmatic access
@@ -664,8 +665,8 @@ Generated: ${new Date().toISOString()}
664
665
 
665
666
  const success = await SecurityUtils.safeWriteFile(csvPath, csvContent, process.cwd());
666
667
  if (success) {
667
- SecurityUtils.debugLog('info', t("sizing.csv_report_saved_to", { csvPath }));
668
- SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
668
+ logger.info(t("sizing.csv_report_saved_to", { csvPath }));
669
+ SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
669
670
  } else {
670
671
  throw new Error(t("sizing.failedToSaveCsvError"));
671
672
  }
@@ -673,21 +674,20 @@ Generated: ${new Date().toISOString()}
673
674
 
674
675
  // Main analysis method
675
676
  async analyze() {
676
- const perfTimer = SecurityUtils.getPerformanceTimer();
677
- const startTime = perfTimer.now();
677
+ const startTime = Date.now();
678
678
 
679
679
  try {
680
- SecurityUtils.debugLog('info', t("sizing.starting_i18n_sizing_analysis"));
681
- SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
680
+ logger.info(t("sizing.starting_i18n_sizing_analysis"));
681
+ logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
682
682
 
683
683
  const files = this.getLanguageFiles();
684
684
 
685
685
  if (files.length === 0) {
686
- SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
687
- return;
686
+ logger.warn(t("sizing.no_translation_files_found"));
687
+ return;
688
688
  }
689
689
 
690
- SecurityUtils.debugLog('info', t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
690
+ logger.info(t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
691
691
 
692
692
  this.analyzeFileSizes(files);
693
693
  this.analyzeTranslationContent(files);
@@ -696,17 +696,17 @@ Generated: ${new Date().toISOString()}
696
696
  if (this.format === 'table') {
697
697
  this.displayFolderResults();
698
698
  } else if (this.format === 'json') {
699
- SecurityUtils.debugLog('info', t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
699
+ logger.info(t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
700
700
  }
701
701
 
702
702
  await this.generateHumanReadableReport();
703
703
 
704
- const endTime = perfTimer.now();
705
- SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
704
+ const endTime = Date.now();
705
+ logger.info(t("sizing.analysis_completed", { duration: ((endTime - startTime) / 1000).toFixed(2) }));
706
706
 
707
707
  } catch (error) {
708
- SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
709
- process.exit(1);
708
+ logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
709
+ process.exit(1);
710
710
  }
711
711
  }
712
712
 
@@ -906,20 +906,19 @@ Options:
906
906
  // Main analysis method
907
907
  async analyze() {
908
908
  try {
909
- SecurityUtils.debugLog('info', t("sizing.starting_analysis"));
910
- SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
909
+ logger.info(t("sizing.starting_analysis"));
910
+ logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
911
911
 
912
- const perfTimer = SecurityUtils.getPerformanceTimer();
913
- const startTime = perfTimer.now();
912
+ const startTime = Date.now();
914
913
 
915
914
  // Get language files
916
915
  const files = this.getLanguageFiles();
917
916
  if (files.length === 0) {
918
- SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
919
- return { success: false, error: "No translation files found" };
917
+ logger.warn(t("sizing.no_translation_files_found"));
918
+ return { success: false, error: "No translation files found" };
920
919
  }
921
920
 
922
- SecurityUtils.debugLog('info', t("sizing.found_files", { count: files.length }));
921
+ logger.info(t("sizing.found_files", { count: files.length }));
923
922
 
924
923
  // Analyze file sizes
925
924
  this.analyzeFileSizes(files);
@@ -936,16 +935,16 @@ Options:
936
935
  // Generate reports if requested
937
936
  await this.generateHumanReadableReport();
938
937
 
939
- const endTime = perfTimer.now();
940
- const duration = ((endTime - startTime) / 1000).toFixed(2);
938
+ const endTime = Date.now();
939
+ const duration = ((endTime - startTime) / 1000).toFixed(2);
941
940
 
942
- SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration }));
941
+ logger.info(t("sizing.analysis_completed", { duration }));
943
942
 
944
943
  return { success: true, stats: this.stats };
945
944
 
946
945
  } catch (error) {
947
- SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
948
- return { success: false, error: error.message };
946
+ logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
947
+ return { success: false, error: error.message };
949
948
  }
950
949
  }
951
950
  }
@@ -961,4 +960,4 @@ if (require.main === module) {
961
960
  });
962
961
  }
963
962
 
964
- module.exports = I18nSizingAnalyzer;
963
+ module.exports = I18nSizingAnalyzer;
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
@@ -19,7 +19,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
19
19
  }
20
20
  })();
21
21
 
22
- loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
22
+ loadTranslations('en', path.resolve(__dirname, '..', '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, '..', 'resources', 'i18n', 'ui-locales'));
57
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
58
58
 
59
59
  this.sourceDir = this.config.sourceDir;
60
60
 
@@ -831,7 +831,7 @@ class I18nSummaryReporter {
831
831
  this.config = { ...this.config, ...baseConfig };
832
832
 
833
833
  const uiLanguage = this.config.uiLanguage || 'en';
834
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
834
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
835
835
  if (!this.config.sourceDir) {
836
836
  this.config.sourceDir = this.detectI18nDirectory();
837
837
  }
package/main/i18ntk-ui.js CHANGED
@@ -11,13 +11,13 @@ const legacyConfigManager = require('../utils/config-manager');
11
11
  const { getIcon, isUnicodeSupported } = require('../utils/terminal-icons');
12
12
  const configManager = new SettingsManager();
13
13
 
14
- class UIi18n {
15
- constructor() {
16
- this.currentLanguage = 'en';
17
- this.translations = {};
18
- this.uiLocalesDir = this.resolveUiLocalesDir();
19
- this.availableLanguages = [];
20
- this.configFile = path.resolve(configManager.configFile);
14
+ class UIi18n {
15
+ constructor() {
16
+ this.currentLanguage = 'en';
17
+ this.translations = {};
18
+ this.uiLocalesDir = this.resolveUiLocalesDir();
19
+ this.availableLanguages = [];
20
+ this.configFile = path.resolve(configManager.configFile);
21
21
 
22
22
 
23
23
  // Initialize with safe defaults
@@ -25,13 +25,13 @@ this.translations = {};
25
25
  }
26
26
 
27
27
 
28
- initialize() {
29
- try {
30
- const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
31
-
32
- // Use safe defaults if config is not available
33
- this.uiLocalesDir = this.resolveUiLocalesDir();
34
- this.availableLanguages = this.detectAvailableLanguages();
28
+ initialize() {
29
+ try {
30
+ const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
31
+
32
+ // Use safe defaults if config is not available
33
+ this.uiLocalesDir = this.resolveUiLocalesDir();
34
+ this.availableLanguages = this.detectAvailableLanguages();
35
35
 
36
36
 
37
37
  const configuredLanguage = config?.language || config?.uiLanguage || 'en';
@@ -42,71 +42,70 @@ this.translations = {};
42
42
  } else {
43
43
  this.loadLanguage('en');
44
44
  }
45
- } catch (error) {
46
- console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
47
- this.uiLocalesDir = this.resolveUiLocalesDir();
48
- this.availableLanguages = this.detectAvailableLanguages();
49
- this.loadLanguage('en');
50
- }
51
- }
52
-
53
- getValidationBase(targetPath) {
54
- const fallbackBase = path.resolve(__dirname, '..');
55
- if (!targetPath || typeof targetPath !== 'string') {
56
- return fallbackBase;
57
- }
58
-
59
- let current = path.resolve(path.dirname(targetPath));
60
- while (true) {
61
- try {
62
- if (fs.statSync(current).isDirectory()) {
63
- return current;
64
- }
65
- } catch (_) {
66
- // Continue walking up until an existing directory is found.
67
- }
68
-
69
- const parent = path.dirname(current);
70
- if (parent === current) {
71
- break;
72
- }
73
- current = parent;
74
- }
75
-
76
- return fallbackBase;
77
- }
78
-
79
- resolveUiLocalesDir() {
80
- const candidates = [
81
- path.resolve(__dirname, '..', 'ui-locales'),
82
- path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales')
83
- ];
84
-
85
- for (const candidate of candidates) {
86
- try {
87
- const stats = SecurityUtils.safeStatSync(candidate, this.getValidationBase(candidate));
88
- if (stats && stats.isDirectory()) {
89
- return candidate;
90
- }
91
- } catch (_) {
92
- // Try next candidate.
93
- }
94
- }
95
-
96
- return candidates[0];
97
- }
45
+ } catch (error) {
46
+ console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
47
+ this.uiLocalesDir = this.resolveUiLocalesDir();
48
+ this.availableLanguages = this.detectAvailableLanguages();
49
+ this.loadLanguage('en');
50
+ }
51
+ }
52
+
53
+ getValidationBase(targetPath) {
54
+ const fallbackBase = path.resolve(__dirname, '..');
55
+ if (!targetPath || typeof targetPath !== 'string') {
56
+ return fallbackBase;
57
+ }
58
+
59
+ let current = path.resolve(path.dirname(targetPath));
60
+ while (true) {
61
+ try {
62
+ if (fs.statSync(current).isDirectory()) {
63
+ return current;
64
+ }
65
+ } catch (_) {
66
+ // Continue walking up until an existing directory is found.
67
+ }
68
+
69
+ const parent = path.dirname(current);
70
+ if (parent === current) {
71
+ break;
72
+ }
73
+ current = parent;
74
+ }
75
+
76
+ return fallbackBase;
77
+ }
78
+
79
+ resolveUiLocalesDir() {
80
+ const candidates = [
81
+ path.resolve(__dirname, '..', 'ui-locales')
82
+ ];
83
+
84
+ for (const candidate of candidates) {
85
+ try {
86
+ const stats = SecurityUtils.safeStatSync(candidate, this.getValidationBase(candidate));
87
+ if (stats && stats.isDirectory()) {
88
+ return candidate;
89
+ }
90
+ } catch (_) {
91
+ // Try next candidate.
92
+ }
93
+ }
94
+
95
+ return candidates[0];
96
+ }
98
97
  /**
99
98
  /**
100
99
  * Detect which UI locales are currently installed
101
100
  * @returns {string[]} Array of available language codes
102
101
  */
103
- detectAvailableLanguages() {
104
- const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
105
- return all.filter(lang => {
106
- const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
107
- return SecurityUtils.safeExistsSync(filePath, this.getValidationBase(filePath));
108
- });
109
- }
102
+ detectAvailableLanguages() {
103
+ const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
104
+ return all.filter(lang => {
105
+ const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
106
+ return SecurityUtils.safeExistsSync(filePath, this.getValidationBase(filePath));
107
+ });
108
+ }
110
109
 
111
110
  /**
112
111
  * Refresh the list of available languages and ensure current language is valid
@@ -137,13 +136,13 @@ this.translations = {};
137
136
  }
138
137
 
139
138
  try {
140
- // Primary: Use monolith JSON file (en.json, de.json, etc.)
141
- const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
142
-
143
- if (SecurityUtils.safeExistsSync(monolithTranslationFile, this.getValidationBase(monolithTranslationFile))) {
144
- try {
145
- const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
146
- const fullTranslations = JSON.parse(content);
139
+ // Primary: Use monolith JSON file (en.json, de.json, etc.)
140
+ const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
141
+
142
+ if (SecurityUtils.safeExistsSync(monolithTranslationFile, this.getValidationBase(monolithTranslationFile))) {
143
+ try {
144
+ const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
145
+ const fullTranslations = JSON.parse(content);
147
146
 
148
147
  // Flatten the nested structure for easier key access
149
148
  this.translations = this.flattenTranslations(fullTranslations);
@@ -157,14 +156,14 @@ this.translations = {};
157
156
  this.translations = {};
158
157
  }
159
158
  } else {
160
- // Fallback: Use folder-based structure if monolith file doesn't exist
161
- const langDir = path.join(this.uiLocalesDir, language);
162
-
163
- const langDirStats = SecurityUtils.safeStatSync(langDir, this.getValidationBase(langDir));
164
- if (langDirStats && langDirStats.isDirectory()) {
165
- const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
166
- if (debugEnabled) {
167
- console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
159
+ // Fallback: Use folder-based structure if monolith file doesn't exist
160
+ const langDir = path.join(this.uiLocalesDir, language);
161
+
162
+ const langDirStats = SecurityUtils.safeStatSync(langDir, this.getValidationBase(langDir));
163
+ if (langDirStats && langDirStats.isDirectory()) {
164
+ const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
165
+ if (debugEnabled) {
166
+ console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
168
167
  }
169
168
 
170
169
  for (const file of files) {
@@ -428,12 +427,12 @@ this.translations = {};
428
427
  * @param {object} replacements - Object with replacement values
429
428
  * @returns {string|null} English translation or null if not found
430
429
  */
431
- getEnglishFallback(keyPath, replacements = {}) {
432
- try {
433
- const englishFile = path.join(this.uiLocalesDir, 'en.json');
434
- if (SecurityUtils.safeExistsSync(englishFile, this.getValidationBase(englishFile))) {
435
- const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
436
- const englishTranslations = JSON.parse(englishContent);
430
+ getEnglishFallback(keyPath, replacements = {}) {
431
+ try {
432
+ const englishFile = path.join(this.uiLocalesDir, 'en.json');
433
+ if (SecurityUtils.safeExistsSync(englishFile, this.getValidationBase(englishFile))) {
434
+ const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
435
+ const englishTranslations = JSON.parse(englishContent);
437
436
 
438
437
  // Use the same flattening approach for consistency
439
438
  const flattenedEnglish = this.flattenTranslations(englishTranslations);
@@ -570,4 +569,4 @@ this.translations = {};
570
569
  }
571
570
 
572
571
  // Export the class, not a singleton instance
573
- module.exports = UIi18n;
572
+ module.exports = UIi18n;
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18NTK USAGE ANALYSIS TOOLKIT - Version 1.8.3
4
4
  *
@@ -55,7 +55,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
55
55
  }
56
56
  })();
57
57
 
58
- loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
58
+ loadTranslations('en', path.resolve(__dirname, '..', 'ui-locales'));
59
59
 
60
60
  async function getConfig() {
61
61
  return await getUnifiedConfig('usage');
@@ -120,7 +120,7 @@ class I18nUsageAnalyzer {
120
120
 
121
121
  // Load translations for UI
122
122
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
123
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
123
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
124
124
  const projectRoot = path.resolve(this.config.projectRoot || '.');
125
125
  const detected = detectFramework(projectRoot);
126
126
  if (detected) {
@@ -169,9 +169,9 @@ class I18nUsageAnalyzer {
169
169
  this.config.includeExtensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.pyx', '.pyi'];
170
170
  }
171
171
 
172
- await SecurityUtils.logSecurityEvent(t('usage.analyzerInitialized'), { component: 'i18ntk-usage' });
172
+ await SecurityUtils.logSecurityEvent(t('usage.analyzerInitialized'), 'info', { component: 'i18ntk-usage' });
173
173
  } catch (error) {
174
- await SecurityUtils.logSecurityEvent(t('usage.analyzerInitFailed'), { component: 'i18ntk-usage', error: error.message });
174
+ await SecurityUtils.logSecurityEvent(t('usage.analyzerInitFailed'), 'error', { component: 'i18ntk-usage', error: error.message });
175
175
  throw error;
176
176
  }
177
177
  }
@@ -276,7 +276,7 @@ class I18nUsageAnalyzer {
276
276
  }
277
277
  }
278
278
  } catch (error) {
279
- await SecurityUtils.logSecurityEvent(t('usage.translationDiscoveryError'), {
279
+ await SecurityUtils.logSecurityEvent(t('usage.translationDiscoveryError'), 'error', {
280
280
  component: 'i18ntk-usage',
281
281
  directory: currentDir,
282
282
  error: error.message
@@ -350,7 +350,7 @@ class I18nUsageAnalyzer {
350
350
  }
351
351
  }
352
352
  } catch (error) {
353
- await SecurityUtils.logSecurityEvent(t('usage.fileTraversalError'), {
353
+ await SecurityUtils.logSecurityEvent(t('usage.fileTraversalError'), 'error', {
354
354
  component: 'i18ntk-usage',
355
355
  directory: currentDir,
356
356
  error: error.message
@@ -393,7 +393,7 @@ class I18nUsageAnalyzer {
393
393
  this.config = { ...baseConfig, ...(this.config || {}) };
394
394
 
395
395
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
396
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
396
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
397
397
  if (!Array.isArray(this.config.translationPatterns)) {
398
398
  this.config.translationPatterns = [
399
399
  /t\(['"`]([^'"`]+)['"`]/g,
@@ -614,7 +614,7 @@ class I18nUsageAnalyzer {
614
614
  } catch (error) {
615
615
  console.error(t('usage.analysisFailedError'), error.message);
616
616
  this.closeReadline();
617
- SecurityUtils.logSecurityEvent(t('usage.usageAnalysisFailed'), {
617
+ SecurityUtils.logSecurityEvent(t('usage.usageAnalysisFailed'), 'error', {
618
618
  component: 'i18ntk-usage',
619
619
  error: error.message
620
620
  });
@@ -740,7 +740,7 @@ Analysis Features (v1.8.3):
740
740
  if (isDebug) {
741
741
  console.error(error.stack);
742
742
  }
743
- await SecurityUtils.logSecurityEvent(t('usage.translationFileParseError'), {
743
+ await SecurityUtils.logSecurityEvent(t('usage.translationFileParseError'), 'error', {
744
744
  component: 'i18ntk-usage',
745
745
  file: fileInfo.filePath,
746
746
  error: error.message
@@ -748,7 +748,7 @@ Analysis Features (v1.8.3):
748
748
  }
749
749
  }
750
750
  } catch (error) {
751
- await SecurityUtils.logSecurityEvent(t('usage.translationKeysLoadError'), {
751
+ await SecurityUtils.logSecurityEvent(t('usage.translationKeysLoadError'), 'error', {
752
752
  component: 'i18ntk-usage',
753
753
  error: error.message
754
754
  });
@@ -786,10 +786,22 @@ Analysis Features (v1.8.3):
786
786
  return keys;
787
787
  }
788
788
 
789
- collectPlaceholderKeys(obj, prefix = '', language) {
790
- const patterns = this.placeholderStyles[language] || [];
791
- const regexes = patterns.map(p => new RegExp(p));
792
- if (typeof obj !== 'object' || obj === null) return;
789
+ collectPlaceholderKeys(obj, prefix = '', language) {
790
+ const patterns = this.placeholderStyles[language] || [];
791
+ const regexes = patterns.reduce((compiled, pattern) => {
792
+ try {
793
+ compiled.push(new RegExp(pattern));
794
+ } catch (error) {
795
+ SecurityUtils.logSecurityEvent('Invalid placeholder regex pattern skipped', 'warn', {
796
+ component: 'i18ntk-usage',
797
+ language,
798
+ pattern,
799
+ error: error.message
800
+ });
801
+ }
802
+ return compiled;
803
+ }, []);
804
+ if (typeof obj !== 'object' || obj === null) return;
793
805
 
794
806
  for (const [key, value] of Object.entries(obj)) {
795
807
  const fullKey = prefix ? `${prefix}.${key}` : key;
@@ -1543,7 +1555,7 @@ Analysis Features (v1.8.3):
1543
1555
  await this.initialize();
1544
1556
  }
1545
1557
 
1546
- await SecurityUtils.logSecurityEvent('analysis_started', { component: 'i18ntk-usage' });
1558
+ await SecurityUtils.logSecurityEvent('analysis_started', 'info', { component: 'i18ntk-usage' });
1547
1559
 
1548
1560
  console.log(t('usage.checkUsage.title'));
1549
1561
  console.log(t("usage.checkUsage.message"));
@@ -1708,7 +1720,7 @@ Analysis Features (v1.8.3):
1708
1720
  outputLines.forEach(line => console.log(line));
1709
1721
  }
1710
1722
 
1711
- await SecurityUtils.logSecurityEvent('analysis_completed', {
1723
+ await SecurityUtils.logSecurityEvent('analysis_completed', 'info', {
1712
1724
  component: 'i18ntk-usage',
1713
1725
  stats: {
1714
1726
  availableKeys: this.availableKeys.size,
@@ -1749,7 +1761,7 @@ Analysis Features (v1.8.3):
1749
1761
  console.error(t("checkUsage.usage_analysis_failed"));
1750
1762
  console.error(error.message);
1751
1763
 
1752
- await SecurityUtils.logSecurityEvent('analysis_failed', {
1764
+ await SecurityUtils.logSecurityEvent('analysis_failed', 'error', {
1753
1765
  component: 'i18ntk-usage',
1754
1766
  error: error.message
1755
1767
  });
@@ -1837,4 +1849,4 @@ if (require.main === module) {
1837
1849
  // Normal direct execution
1838
1850
  main();
1839
1851
  }
1840
- }
1852
+ }