i18ntk 2.0.4 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +38 -60
  2. package/main/i18ntk-analyze.js +49 -44
  3. package/main/i18ntk-complete.js +75 -74
  4. package/main/i18ntk-fixer.js +3 -3
  5. package/main/i18ntk-init.js +5 -5
  6. package/main/i18ntk-scanner.js +2 -2
  7. package/main/i18ntk-sizing.js +35 -35
  8. package/main/i18ntk-summary.js +4 -4
  9. package/main/i18ntk-ui.js +54 -8
  10. package/main/i18ntk-usage.js +14 -14
  11. package/main/i18ntk-validate.js +6 -5
  12. package/main/manage/commands/AnalyzeCommand.js +40 -35
  13. package/main/manage/commands/FixerCommand.js +2 -2
  14. package/main/manage/commands/ScannerCommand.js +2 -2
  15. package/main/manage/commands/ValidateCommand.js +9 -9
  16. package/main/manage/index.js +147 -75
  17. package/main/manage/managers/LanguageMenu.js +7 -2
  18. package/main/manage/services/UsageService.js +7 -7
  19. package/package.json +269 -290
  20. package/settings/settings-cli.js +3 -3
  21. package/ui-locales/de.json +161 -166
  22. package/ui-locales/en.json +13 -18
  23. package/ui-locales/es.json +171 -184
  24. package/ui-locales/fr.json +155 -161
  25. package/ui-locales/ja.json +192 -243
  26. package/ui-locales/ru.json +145 -196
  27. package/ui-locales/zh.json +179 -185
  28. package/utils/cli-helper.js +26 -98
  29. package/utils/extractors/regex.js +39 -12
  30. package/utils/i18n-helper.js +88 -40
  31. package/{scripts → utils}/locale-optimizer.js +61 -60
  32. package/utils/security-check-improved.js +16 -13
  33. package/utils/security.js +6 -4
  34. package/main/i18ntk-go.js +0 -283
  35. package/main/i18ntk-java.js +0 -380
  36. package/main/i18ntk-js.js +0 -512
  37. package/main/i18ntk-manage.js +0 -1694
  38. package/main/i18ntk-php.js +0 -462
  39. package/main/i18ntk-py.js +0 -379
  40. package/main/i18ntk-settings.js +0 -23
  41. package/main/manage/index-fixed.js +0 -1447
  42. package/main/manage/services/ConfigurationService-fixed.js +0 -449
  43. package/scripts/build-lite.js +0 -279
  44. package/scripts/deprecate-versions.js +0 -317
  45. package/scripts/export-translations.js +0 -84
  46. package/scripts/fix-all-i18n.js +0 -215
  47. package/scripts/fix-and-purify-i18n.js +0 -213
  48. package/scripts/fix-locale-control-chars.js +0 -110
  49. package/scripts/lint-locales.js +0 -80
  50. package/scripts/prepublish.js +0 -348
  51. package/scripts/security-check.js +0 -117
  52. package/scripts/sync-translations.js +0 -151
  53. package/scripts/sync-ui-locales.js +0 -20
  54. package/scripts/validate-all-translations.js +0 -139
  55. package/scripts/verify-deprecations.js +0 -157
  56. package/scripts/verify-translations.js +0 -63
  57. package/utils/security-fixed.js +0 -607
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * I18n Sizing Analyzer
@@ -33,10 +33,12 @@
33
33
 
34
34
  const fs = require('fs');
35
35
  const path = require('path');
36
+ const { performance } = require('perf_hooks');
36
37
  const { loadTranslations, t } = require('../utils/i18n-helper');
37
38
  const configManager = require('../settings/settings-manager');
38
39
  const SecurityUtils = require('../utils/security');
39
40
  const { getUnifiedConfig } = require('../utils/config-helper');
41
+ const { logger } = require('../utils/logger');
40
42
  const { getGlobalReadline, closeGlobalReadline } = require('../utils/cli');
41
43
  const SetupEnforcer = require('../utils/setup-enforcer');
42
44
 
@@ -83,7 +85,7 @@ class I18nSizingAnalyzer {
83
85
 
84
86
  // Initialize i18n with UI language from config
85
87
  const uiLanguage = options.uiLanguage || config.uiLanguage || 'en';
86
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
88
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
87
89
  this.stats = {
88
90
  files: {},
89
91
  languages: {},
@@ -165,7 +167,7 @@ class I18nSizingAnalyzer {
165
167
 
166
168
  // Analyze file sizes
167
169
  analyzeFileSizes(files) {
168
- SecurityUtils.debugLog('info', t("sizing.analyzing_file_sizes"));
170
+ logger.info(t("sizing.analyzing_file_sizes"));
169
171
 
170
172
  files.forEach(({ language, file, path: filePath, files: langFiles }) => {
171
173
  if (langFiles) {
@@ -220,7 +222,7 @@ class I18nSizingAnalyzer {
220
222
 
221
223
  // Analyze translation content
222
224
  analyzeTranslationContent(files) {
223
- SecurityUtils.debugLog('info', t("sizing.analyzing_translation_content"));
225
+ logger.info(t("sizing.analyzing_translation_content"));
224
226
 
225
227
  files.forEach(({ language, path: filePath, files: langFiles }) => {
226
228
  try {
@@ -266,7 +268,7 @@ class I18nSizingAnalyzer {
266
268
  });
267
269
 
268
270
  } catch (error) {
269
- SecurityUtils.debugLog('error', t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
271
+ logger.error(t("sizing.failed_to_parse_language_error", { language, errorMessage: error.message }));
270
272
  }
271
273
  });
272
274
  }
@@ -316,14 +318,14 @@ class I18nSizingAnalyzer {
316
318
 
317
319
  // Generate size comparison analysis
318
320
  generateSizeComparison() {
319
- SecurityUtils.debugLog('info', t("sizing.generating_size_comparisons"));
321
+ logger.info(t("sizing.generating_size_comparisons"));
320
322
 
321
323
  const languages = Object.keys(this.stats.languages);
322
324
  const baseLanguage = languages[0]; // Use first language as baseline
323
325
 
324
326
  if (!baseLanguage) {
325
- SecurityUtils.debugLog('warn', t("sizing.no_languages_found_for_comparison"));
326
- return;
327
+ logger.warn(t("sizing.no_languages_found_for_comparison"));
328
+ return;
327
329
  }
328
330
 
329
331
  this.stats.summary = {
@@ -495,7 +497,7 @@ class I18nSizingAnalyzer {
495
497
  async generateHumanReadableReport() {
496
498
  if (!this.outputReport) return;
497
499
 
498
- SecurityUtils.debugLog('info', t("sizing.generating_detailed_report"));
500
+ logger.info(t("sizing.generating_detailed_report"));
499
501
 
500
502
  const validatedOutputDir = SecurityUtils.validatePath(this.outputDir, process.cwd());
501
503
  if (!validatedOutputDir) {
@@ -518,7 +520,7 @@ class I18nSizingAnalyzer {
518
520
  let textReport = this.generateTextReport(timestamp);
519
521
  const textSuccess = await SecurityUtils.safeWriteFile(textReportPath, textReport, process.cwd());
520
522
  if (textSuccess) {
521
- SecurityUtils.debugLog('info', t("sizing.human_report_saved", { reportPath: textReportPath }));
523
+ logger.info(t("sizing.human_report_saved", { reportPath: textReportPath }));
522
524
  }
523
525
 
524
526
  // Generate JSON for programmatic access
@@ -664,8 +666,8 @@ Generated: ${new Date().toISOString()}
664
666
 
665
667
  const success = await SecurityUtils.safeWriteFile(csvPath, csvContent, process.cwd());
666
668
  if (success) {
667
- SecurityUtils.debugLog('info', t("sizing.csv_report_saved_to", { csvPath }));
668
- SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
669
+ logger.info(t("sizing.csv_report_saved_to", { csvPath }));
670
+ SecurityUtils.logSecurityEvent('CSV report saved', 'info', { csvPath });
669
671
  } else {
670
672
  throw new Error(t("sizing.failedToSaveCsvError"));
671
673
  }
@@ -673,21 +675,20 @@ Generated: ${new Date().toISOString()}
673
675
 
674
676
  // Main analysis method
675
677
  async analyze() {
676
- const perfTimer = SecurityUtils.getPerformanceTimer();
677
- const startTime = perfTimer.now();
678
+ const startTime = performance.now();
678
679
 
679
680
  try {
680
- SecurityUtils.debugLog('info', t("sizing.starting_i18n_sizing_analysis"));
681
- SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
681
+ logger.info(t("sizing.starting_i18n_sizing_analysis"));
682
+ logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
682
683
 
683
684
  const files = this.getLanguageFiles();
684
685
 
685
686
  if (files.length === 0) {
686
- SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
687
- return;
687
+ logger.warn(t("sizing.no_translation_files_found"));
688
+ return;
688
689
  }
689
690
 
690
- SecurityUtils.debugLog('info', t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
691
+ logger.info(t("sizing.found_languages", { languages: files.map(f => f.language).join(', ') }));
691
692
 
692
693
  this.analyzeFileSizes(files);
693
694
  this.analyzeTranslationContent(files);
@@ -696,17 +697,17 @@ Generated: ${new Date().toISOString()}
696
697
  if (this.format === 'table') {
697
698
  this.displayFolderResults();
698
699
  } else if (this.format === 'json') {
699
- SecurityUtils.debugLog('info', t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
700
+ logger.info(t("sizing.analysisStats", { stats: JSON.stringify(this.stats, null, 2) }));
700
701
  }
701
702
 
702
703
  await this.generateHumanReadableReport();
703
704
 
704
- const endTime = perfTimer.now();
705
- SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
705
+ const endTime = performance.now();
706
+ logger.info(t("sizing.analysis_completed", { duration: (endTime - startTime).toFixed(2) }));
706
707
 
707
708
  } catch (error) {
708
- SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
709
- process.exit(1);
709
+ logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
710
+ process.exit(1);
710
711
  }
711
712
  }
712
713
 
@@ -906,20 +907,19 @@ Options:
906
907
  // Main analysis method
907
908
  async analyze() {
908
909
  try {
909
- SecurityUtils.debugLog('info', t("sizing.starting_analysis"));
910
- SecurityUtils.debugLog('info', t("sizing.source_directory", { sourceDir: this.sourceDir }));
910
+ logger.info(t("sizing.starting_analysis"));
911
+ logger.info(t("sizing.source_directory", { sourceDir: this.sourceDir }));
911
912
 
912
- const perfTimer = SecurityUtils.getPerformanceTimer();
913
- const startTime = perfTimer.now();
913
+ const startTime = performance.now();
914
914
 
915
915
  // Get language files
916
916
  const files = this.getLanguageFiles();
917
917
  if (files.length === 0) {
918
- SecurityUtils.debugLog('warn', t("sizing.no_translation_files_found"));
919
- return { success: false, error: "No translation files found" };
918
+ logger.warn(t("sizing.no_translation_files_found"));
919
+ return { success: false, error: "No translation files found" };
920
920
  }
921
921
 
922
- SecurityUtils.debugLog('info', t("sizing.found_files", { count: files.length }));
922
+ logger.info(t("sizing.found_files", { count: files.length }));
923
923
 
924
924
  // Analyze file sizes
925
925
  this.analyzeFileSizes(files);
@@ -936,16 +936,16 @@ Options:
936
936
  // Generate reports if requested
937
937
  await this.generateHumanReadableReport();
938
938
 
939
- const endTime = perfTimer.now();
939
+ const endTime = performance.now();
940
940
  const duration = ((endTime - startTime) / 1000).toFixed(2);
941
941
 
942
- SecurityUtils.debugLog('info', t("sizing.analysis_completed", { duration }));
942
+ logger.info(t("sizing.analysis_completed", { duration }));
943
943
 
944
944
  return { success: true, stats: this.stats };
945
945
 
946
946
  } catch (error) {
947
- SecurityUtils.debugLog('error', t("sizing.analysis_failed", { errorMessage: error.message }));
948
- return { success: false, error: error.message };
947
+ logger.error(t("sizing.analysis_failed", { errorMessage: error.message }));
948
+ return { success: false, error: error.message };
949
949
  }
950
950
  }
951
951
  }
@@ -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
@@ -15,7 +15,7 @@ class UIi18n {
15
15
  constructor() {
16
16
  this.currentLanguage = 'en';
17
17
  this.translations = {};
18
- this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
18
+ this.uiLocalesDir = this.resolveUiLocalesDir();
19
19
  this.availableLanguages = [];
20
20
  this.configFile = path.resolve(configManager.configFile);
21
21
 
@@ -30,7 +30,7 @@ this.translations = {};
30
30
  const config = configManager.loadSettings ? configManager.loadSettings() : configManager.getConfig ? configManager.getConfig() : {};
31
31
 
32
32
  // Use safe defaults if config is not available
33
- this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
33
+ this.uiLocalesDir = this.resolveUiLocalesDir();
34
34
  this.availableLanguages = this.detectAvailableLanguages();
35
35
 
36
36
 
@@ -44,11 +44,56 @@ this.translations = {};
44
44
  }
45
45
  } catch (error) {
46
46
  console.warn('UIi18n: Failed to initialize with config, using defaults:', error.message);
47
- this.uiLocalesDir = path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales');
47
+ this.uiLocalesDir = this.resolveUiLocalesDir();
48
48
  this.availableLanguages = this.detectAvailableLanguages();
49
49
  this.loadLanguage('en');
50
50
  }
51
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
+ }
52
97
  /**
53
98
  /**
54
99
  * Detect which UI locales are currently installed
@@ -58,7 +103,7 @@ this.translations = {};
58
103
  const all = ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'];
59
104
  return all.filter(lang => {
60
105
  const filePath = path.join(this.uiLocalesDir, `${lang}.json`);
61
- return SecurityUtils.safeExistsSync(filePath);
106
+ return SecurityUtils.safeExistsSync(filePath, this.getValidationBase(filePath));
62
107
  });
63
108
  }
64
109
 
@@ -94,7 +139,7 @@ this.translations = {};
94
139
  // Primary: Use monolith JSON file (en.json, de.json, etc.)
95
140
  const monolithTranslationFile = path.join(this.uiLocalesDir, `${language}.json`);
96
141
 
97
- if (SecurityUtils.safeExistsSync(monolithTranslationFile)) {
142
+ if (SecurityUtils.safeExistsSync(monolithTranslationFile, this.getValidationBase(monolithTranslationFile))) {
98
143
  try {
99
144
  const content = SecurityUtils.safeReadFileSync(monolithTranslationFile, path.dirname(monolithTranslationFile), 'utf8');
100
145
  const fullTranslations = JSON.parse(content);
@@ -114,7 +159,8 @@ this.translations = {};
114
159
  // Fallback: Use folder-based structure if monolith file doesn't exist
115
160
  const langDir = path.join(this.uiLocalesDir, language);
116
161
 
117
- if (SecurityUtils.safeExistsSync(langDir) && fs.statSync(langDir).isDirectory()) {
162
+ const langDirStats = SecurityUtils.safeStatSync(langDir, this.getValidationBase(langDir));
163
+ if (langDirStats && langDirStats.isDirectory()) {
118
164
  const files = fs.readdirSync(langDir).filter(file => file.endsWith('.json'));
119
165
  if (debugEnabled) {
120
166
  console.log(`UI: Found files in ${langDir}: ${files.join(', ')}`);
@@ -384,7 +430,7 @@ this.translations = {};
384
430
  getEnglishFallback(keyPath, replacements = {}) {
385
431
  try {
386
432
  const englishFile = path.join(this.uiLocalesDir, 'en.json');
387
- if (SecurityUtils.safeExistsSync(englishFile)) {
433
+ if (SecurityUtils.safeExistsSync(englishFile, this.getValidationBase(englishFile))) {
388
434
  const englishContent = SecurityUtils.safeReadFileSync(englishFile, path.dirname(englishFile), 'utf8');
389
435
  const englishTranslations = JSON.parse(englishContent);
390
436
 
@@ -523,4 +569,4 @@ this.translations = {};
523
569
  }
524
570
 
525
571
  // Export the class, not a singleton instance
526
- 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
  });
@@ -1543,7 +1543,7 @@ Analysis Features (v1.8.3):
1543
1543
  await this.initialize();
1544
1544
  }
1545
1545
 
1546
- await SecurityUtils.logSecurityEvent('analysis_started', { component: 'i18ntk-usage' });
1546
+ await SecurityUtils.logSecurityEvent('analysis_started', 'info', { component: 'i18ntk-usage' });
1547
1547
 
1548
1548
  console.log(t('usage.checkUsage.title'));
1549
1549
  console.log(t("usage.checkUsage.message"));
@@ -1708,7 +1708,7 @@ Analysis Features (v1.8.3):
1708
1708
  outputLines.forEach(line => console.log(line));
1709
1709
  }
1710
1710
 
1711
- await SecurityUtils.logSecurityEvent('analysis_completed', {
1711
+ await SecurityUtils.logSecurityEvent('analysis_completed', 'info', {
1712
1712
  component: 'i18ntk-usage',
1713
1713
  stats: {
1714
1714
  availableKeys: this.availableKeys.size,
@@ -1749,7 +1749,7 @@ Analysis Features (v1.8.3):
1749
1749
  console.error(t("checkUsage.usage_analysis_failed"));
1750
1750
  console.error(error.message);
1751
1751
 
1752
- await SecurityUtils.logSecurityEvent('analysis_failed', {
1752
+ await SecurityUtils.logSecurityEvent('analysis_failed', 'error', {
1753
1753
  component: 'i18ntk-usage',
1754
1754
  error: error.message
1755
1755
  });
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * I18NTK TRANSLATION VALIDATION TOOLKIT
@@ -60,7 +60,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
60
60
  }
61
61
  })();
62
62
 
63
- loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
63
+ loadTranslations('en', path.resolve(__dirname, '..', 'ui-locales'));
64
64
 
65
65
  class I18nValidator {
66
66
  constructor(config = {}) {
@@ -87,7 +87,7 @@ class I18nValidator {
87
87
  this.config = { ...baseConfig, ...(this.config || {}) };
88
88
 
89
89
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
90
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
90
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
91
91
 
92
92
  SecurityUtils.logSecurityEvent(
93
93
  'I18n validator initializing',
@@ -892,7 +892,8 @@ class I18nValidator {
892
892
  this.config = { ...baseConfig, ...(this.config || {}) };
893
893
 
894
894
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
895
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));this.sourceDir = this.config.sourceDir;
895
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
896
+ this.sourceDir = this.config.sourceDir;
896
897
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
897
898
  } else {
898
899
  await this.initialize();
@@ -983,7 +984,7 @@ if (require.main === module) {
983
984
  // Initialize translations for CLI usage
984
985
  const config = configManager.getConfig();
985
986
  const uiLanguage = config.language || 'en';
986
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
987
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
987
988
 
988
989
  SecurityUtils.logSecurityEvent(t('validate.scriptExecution'), 'info', {
989
990
  script: 'i18ntk-validate.js',
@@ -11,12 +11,12 @@ const path = require('path');
11
11
  const cliHelper = require('../../../utils/cli-helper');
12
12
  const { loadTranslations, t } = require('../../../utils/i18n-helper');
13
13
  const { getUnifiedConfig, parseCommonArgs, displayHelp } = require('../../../utils/config-helper');
14
- const SecurityUtils = require('../../../utils/security');
15
- const AdminCLI = require('../../../utils/admin-cli');
16
- const watchLocales = require('../../../utils/watch-locales');
17
- const JsonOutput = require('../../../utils/json-output');
18
-
19
- loadTranslations('en', path.resolve(__dirname, '../../../resources', 'i18n', 'ui-locales'));
14
+ const SecurityUtils = require('../../../utils/security');
15
+ const AdminCLI = require('../../../utils/admin-cli');
16
+ const watchLocales = require('../../../utils/watch-locales');
17
+ const JsonOutput = require('../../../utils/json-output');
18
+
19
+ loadTranslations('en', path.resolve(__dirname, '../../../ui-locales'));
20
20
 
21
21
  const PROJECT_ROOT = process.cwd();
22
22
 
@@ -74,7 +74,7 @@ class AnalyzeCommand {
74
74
  this.config = { ...baseConfig, ...(this.config || {}) };
75
75
 
76
76
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
77
- loadTranslations(uiLanguage, path.resolve(__dirname, '../../../resources', 'i18n', 'ui-locales'));
77
+ loadTranslations(uiLanguage, path.resolve(__dirname, '../../../ui-locales'));
78
78
 
79
79
  this.sourceDir = this.config.sourceDir;
80
80
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
@@ -150,6 +150,17 @@ class AnalyzeCommand {
150
150
  }
151
151
 
152
152
  // Get all available languages
153
+ isValidLanguageCode(code) {
154
+ if (!code || typeof code !== 'string') return false;
155
+ return /^[a-z]{2}(?:-[A-Za-z0-9]{2,8})*$/i.test(code.trim());
156
+ }
157
+
158
+ isExcludedLanguageDirectory(name) {
159
+ if (!name || typeof name !== 'string') return true;
160
+ const lowered = name.toLowerCase();
161
+ return lowered.startsWith('backup-') || lowered === 'backup' || lowered === 'reports' || lowered === 'i18ntk-reports';
162
+ }
163
+
153
164
  getAvailableLanguages() {
154
165
  try {
155
166
  const items = SecurityUtils.safeReaddirSync(this.sourceDir, process.cwd(), { withFileTypes: true });
@@ -164,7 +175,18 @@ class AnalyzeCommand {
164
175
  const directories = items
165
176
  .filter(item => item.isDirectory())
166
177
  .map(item => item.name)
167
- .filter(name => name !== 'node_modules' && !name.startsWith('.') && name !== this.config.sourceLanguage);
178
+ .filter(name =>
179
+ name !== 'node_modules' &&
180
+ !name.startsWith('.') &&
181
+ name !== this.config.sourceLanguage &&
182
+ !this.isExcludedLanguageDirectory(name) &&
183
+ this.isValidLanguageCode(name)
184
+ )
185
+ .filter(name => {
186
+ const dirPath = path.join(this.sourceDir, name);
187
+ const dirItems = SecurityUtils.safeReaddirSync(dirPath, process.cwd(), { withFileTypes: true }) || [];
188
+ return dirItems.some(item => item.isFile() && item.name.endsWith('.json'));
189
+ });
168
190
 
169
191
  // Check for monolith files (language.json files)
170
192
  const files = items
@@ -177,31 +199,14 @@ class AnalyzeCommand {
177
199
  // Add monolith files as languages (without .json extension)
178
200
  const monolithLanguages = files
179
201
  .map(file => file.replace('.json', ''))
180
- .filter(lang => !languages.includes(lang) && lang !== this.config.sourceLanguage);
202
+ .filter(lang =>
203
+ !languages.includes(lang) &&
204
+ lang !== this.config.sourceLanguage &&
205
+ !this.isExcludedLanguageDirectory(lang) &&
206
+ this.isValidLanguageCode(lang)
207
+ );
181
208
  languages.push(...monolithLanguages);
182
209
 
183
- // Check for nested structures
184
- for (const dir of directories) {
185
- const dirPath = path.join(this.sourceDir, dir);
186
- try {
187
- const dirItems = SecurityUtils.safeReaddirSync(dirPath, process.cwd(), { withFileTypes: true });
188
- if (dirItems) {
189
- const jsonFiles = dirItems
190
- .filter(item => item.isFile() && item.name.endsWith('.json'))
191
- .map(item => item.name.replace('.json', ''));
192
-
193
- // If directory contains JSON files, it's likely a language directory
194
- if (jsonFiles.length > 0) {
195
- if (!languages.includes(dir)) {
196
- languages.push(dir);
197
- }
198
- }
199
- }
200
- } catch (error) {
201
- // Skip directories we can't read
202
- }
203
- }
204
-
205
210
  return [...new Set(languages)].sort();
206
211
  } catch (error) {
207
212
  console.error('Error reading source directory:', error.message);
@@ -714,7 +719,6 @@ class AnalyzeCommand {
714
719
  throw new Error(t('analyze.failedToWriteReportFile') || 'Failed to write report file securely');
715
720
  }
716
721
 
717
- console.log(`Report saved to: ${reportPath}`);
718
722
  return reportPath;
719
723
 
720
724
  } catch (error) {
@@ -787,13 +791,14 @@ class AnalyzeCommand {
787
791
 
788
792
  // Save report
789
793
  const reportPath = await this.saveReport(language, report);
794
+ const processedCount = results.length + 1;
790
795
 
791
796
  if (!args.json) {
792
797
  console.log(t('analyze.completed', { language }) || `✅ Analysis completed for ${language}`);
793
798
  console.log(t('analyze.progress', {
794
- translated: results.length,
799
+ translated: processedCount,
795
800
  total: languages.length
796
- }) || ` Progress: ${results.length}/${languages.length} languages processed`);
801
+ }) || ` Progress: ${processedCount}/${languages.length} languages processed`);
797
802
  console.log(t('analyze.reportSaved', { reportPath }) || ` Report saved: ${reportPath}`);
798
803
  }
799
804
 
@@ -891,7 +896,7 @@ class AnalyzeCommand {
891
896
  this.config = { ...baseConfig, ...this.config };
892
897
 
893
898
  const uiLanguage = this.config.uiLanguage || 'en';
894
- loadTranslations(uiLanguage, path.resolve(__dirname, '../../../resources', 'i18n', 'ui-locales'));
899
+ loadTranslations(uiLanguage, path.resolve(__dirname, '../../../ui-locales'));
895
900
 
896
901
  this.sourceDir = this.config.sourceDir;
897
902
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
@@ -64,7 +64,7 @@ class FixerCommand {
64
64
  this.config = { ...baseConfig, ...(this.config || {}) };
65
65
 
66
66
  const uiLanguage = (this.config && this.config.uiLanguage) || 'en';
67
- loadTranslations(uiLanguage, path.resolve(__dirname, '../../../resources', 'i18n', 'ui-locales'));
67
+ loadTranslations(uiLanguage, path.resolve(__dirname, '../../../ui-locales'));
68
68
 
69
69
  this.sourceDir = this.config.sourceDir;
70
70
  this.outputDir = this.config.outputDir;
@@ -567,7 +567,7 @@ class FixerCommand {
567
567
  this.config = { ...baseConfig, ...this.config };
568
568
 
569
569
  const uiLanguage = this.config.uiLanguage || 'en';
570
- loadTranslations(uiLanguage, path.resolve(__dirname, '../../../resources', 'i18n', 'ui-locales'));
570
+ loadTranslations(uiLanguage, path.resolve(__dirname, '../../../ui-locales'));
571
571
 
572
572
  this.sourceDir = this.config.sourceDir;
573
573
  this.outputDir = this.config.outputDir;