i18ntk 1.10.2 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1191
  3. package/main/i18ntk-analyze.js +65 -84
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +3 -3
  6. package/main/i18ntk-complete.js +90 -65
  7. package/main/i18ntk-doctor.js +123 -103
  8. package/main/i18ntk-fixer.js +61 -725
  9. package/main/i18ntk-go.js +14 -15
  10. package/main/i18ntk-init.js +77 -26
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +129 -30
  14. package/main/i18ntk-php.js +75 -75
  15. package/main/i18ntk-py.js +55 -56
  16. package/main/i18ntk-scanner.js +59 -57
  17. package/main/i18ntk-setup.js +9 -404
  18. package/main/i18ntk-sizing.js +6 -6
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +54 -18
  22. package/main/i18ntk-validate.js +13 -13
  23. package/main/manage/commands/AnalyzeCommand.js +1124 -0
  24. package/main/manage/commands/BackupCommand.js +62 -0
  25. package/main/manage/commands/CommandRouter.js +295 -0
  26. package/main/manage/commands/CompleteCommand.js +61 -0
  27. package/main/manage/commands/DoctorCommand.js +60 -0
  28. package/main/manage/commands/FixerCommand.js +624 -0
  29. package/main/manage/commands/InitCommand.js +62 -0
  30. package/main/manage/commands/ScannerCommand.js +654 -0
  31. package/main/manage/commands/SizingCommand.js +60 -0
  32. package/main/manage/commands/SummaryCommand.js +61 -0
  33. package/main/manage/commands/UsageCommand.js +60 -0
  34. package/main/manage/commands/ValidateCommand.js +978 -0
  35. package/main/manage/index-fixed.js +1447 -0
  36. package/main/manage/index.js +1462 -0
  37. package/main/manage/managers/DebugMenu.js +140 -0
  38. package/main/manage/managers/InteractiveMenu.js +177 -0
  39. package/main/manage/managers/LanguageMenu.js +62 -0
  40. package/main/manage/managers/SettingsMenu.js +53 -0
  41. package/main/manage/services/AuthenticationService.js +263 -0
  42. package/main/manage/services/ConfigurationService-fixed.js +449 -0
  43. package/main/manage/services/ConfigurationService.js +449 -0
  44. package/main/manage/services/FileManagementService.js +368 -0
  45. package/main/manage/services/FrameworkDetectionService.js +458 -0
  46. package/main/manage/services/InitService.js +1051 -0
  47. package/main/manage/services/SetupService.js +462 -0
  48. package/main/manage/services/SummaryService.js +450 -0
  49. package/main/manage/services/UsageService.js +1502 -0
  50. package/package.json +32 -29
  51. package/runtime/enhanced.d.ts +221 -221
  52. package/runtime/index.d.ts +29 -29
  53. package/runtime/index.full.d.ts +331 -331
  54. package/runtime/index.js +7 -6
  55. package/scripts/build-lite.js +17 -17
  56. package/scripts/deprecate-versions.js +23 -6
  57. package/scripts/export-translations.js +5 -5
  58. package/scripts/fix-all-i18n.js +3 -3
  59. package/scripts/fix-and-purify-i18n.js +3 -2
  60. package/scripts/fix-locale-control-chars.js +110 -0
  61. package/scripts/lint-locales.js +80 -0
  62. package/scripts/locale-optimizer.js +8 -8
  63. package/scripts/prepublish.js +21 -21
  64. package/scripts/security-check.js +117 -117
  65. package/scripts/sync-translations.js +4 -4
  66. package/scripts/sync-ui-locales.js +9 -8
  67. package/scripts/validate-all-translations.js +8 -7
  68. package/scripts/verify-deprecations.js +157 -161
  69. package/scripts/verify-translations.js +6 -5
  70. package/settings/i18ntk-config.json +282 -282
  71. package/settings/language-config.json +5 -5
  72. package/settings/settings-cli.js +9 -9
  73. package/settings/settings-manager.js +18 -18
  74. package/ui-locales/de.json +2417 -2348
  75. package/ui-locales/en.json +2415 -2352
  76. package/ui-locales/es.json +2425 -2353
  77. package/ui-locales/fr.json +2418 -2348
  78. package/ui-locales/ja.json +2463 -2361
  79. package/ui-locales/ru.json +2463 -2359
  80. package/ui-locales/zh.json +2418 -2351
  81. package/utils/admin-auth.js +2 -2
  82. package/utils/admin-cli.js +297 -297
  83. package/utils/admin-pin.js +9 -9
  84. package/utils/cli-helper.js +9 -9
  85. package/utils/config-helper.js +73 -104
  86. package/utils/config-manager.js +204 -171
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +249 -263
  89. package/utils/framework-detector.js +27 -24
  90. package/utils/i18n-helper.js +85 -41
  91. package/utils/init-helper.js +152 -94
  92. package/utils/json-output.js +98 -98
  93. package/utils/mini-commander.js +179 -0
  94. package/utils/missing-key-validator.js +5 -5
  95. package/utils/plugin-loader.js +40 -29
  96. package/utils/prompt.js +14 -44
  97. package/utils/safe-json.js +40 -0
  98. package/utils/secure-errors.js +3 -3
  99. package/utils/security-check-improved.js +390 -0
  100. package/utils/security-config.js +5 -5
  101. package/utils/security-fixed.js +607 -0
  102. package/utils/security.js +652 -602
  103. package/utils/setup-enforcer.js +136 -44
  104. package/utils/setup-validator.js +33 -32
  105. package/utils/ultra-performance-optimizer.js +11 -9
  106. package/utils/watch-locales.js +2 -1
  107. package/utils/prompt-fixed.js +0 -55
  108. package/utils/security-check.js +0 -454
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
@@ -55,7 +55,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
55
55
  }
56
56
  })();
57
57
 
58
- loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
58
+ loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', '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, '..', 'ui-locales'));
123
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
124
124
  const projectRoot = path.resolve(this.config.projectRoot || '.');
125
125
  const detected = detectFramework(projectRoot);
126
126
  if (detected) {
@@ -136,7 +136,7 @@ class I18nUsageAnalyzer {
136
136
  this.i18nDir = this.config.i18nDir;
137
137
  this.sourceLanguageDir = path.join(this.i18nDir, this.config.sourceLanguage);
138
138
 
139
- if (!fs.existsSync(this.i18nDir)) {
139
+ if (!SecurityUtils.safeExistsSync(this.i18nDir, process.cwd())) {
140
140
  console.warn(t('usage.i18nDirectoryNotFound', { i18nDir: this.i18nDir }));
141
141
  this.i18nDir = this.sourceDir;
142
142
  this.config.i18nDir = this.i18nDir;
@@ -222,7 +222,7 @@ class I18nUsageAnalyzer {
222
222
  const absoluteDir = path.resolve(currentDir);
223
223
  const validatedPath = SecurityUtils.validatePath(absoluteDir, process.cwd());
224
224
 
225
- if (!validatedPath || !fs.existsSync(validatedPath)) {
225
+ if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
226
226
  return;
227
227
  }
228
228
 
@@ -312,7 +312,7 @@ class I18nUsageAnalyzer {
312
312
  const absoluteDir = path.resolve(currentDir);
313
313
  const validatedPath = SecurityUtils.validatePath(absoluteDir, process.cwd());
314
314
 
315
- if (!validatedPath || !fs.existsSync(validatedPath)) {
315
+ if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
316
316
  return;
317
317
  }
318
318
 
@@ -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, '..', 'ui-locales'));
396
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
397
397
  if (!Array.isArray(this.config.translationPatterns)) {
398
398
  this.config.translationPatterns = [
399
399
  /t\(['"`]([^'"`]+)['"`]/g,
@@ -482,7 +482,7 @@ class I18nUsageAnalyzer {
482
482
 
483
483
  for (const dir of possibleSourceDirs) {
484
484
  const testPath = path.resolve(projectRoot, dir);
485
- if (fs.existsSync(testPath)) {
485
+ if (SecurityUtils.safeExistsSync(testPath)) {
486
486
  this.config.sourceDir = testPath;
487
487
  this.sourceDir = testPath;
488
488
  break;
@@ -501,7 +501,7 @@ class I18nUsageAnalyzer {
501
501
  const fallback = path.resolve(this.config.projectRoot || '.', 'src');
502
502
  console.warn(t('usage.sourceEqualsI18nWarn') ||
503
503
  `⚠️ sourceDir equals i18nDir (${this.sourceDir}). Falling back to ${fallback} for source scanning.`);
504
- if (fs.existsSync(fallback)) {
504
+ if (SecurityUtils.safeExistsSync(fallback)) {
505
505
  this.sourceDir = fallback;
506
506
  } else {
507
507
  console.warn(`⚠️ Fallback directory ${fallback} does not exist. Using project root for source scanning.`);
@@ -682,7 +682,7 @@ Analysis Features (v1.8.3):
682
682
  await SecurityUtils.validatePath(fileInfo.filePath);
683
683
 
684
684
  // Check if file exists and is readable
685
- if (!fs.existsSync(fileInfo.filePath)) {
685
+ if (!SecurityUtils.safeExistsSync(fileInfo.filePath)) {
686
686
  if (isDebug || isStrict) {
687
687
  console.warn(`⚠️ File not found: ${path.basename(fileInfo.filePath)}`);
688
688
  }
@@ -804,7 +804,7 @@ Analysis Features (v1.8.3):
804
804
  // Extract translation keys from source code with enhanced patterns
805
805
  extractKeysFromFile(filePath) {
806
806
  try {
807
- const content = SecurityUtils.safeReadFileSync(filePath);
807
+ const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
808
808
  if (!content) return [];
809
809
 
810
810
  // Skip JSON files entirely to prevent scanning translation files
@@ -827,7 +827,7 @@ Analysis Features (v1.8.3):
827
827
  console.log(t('usage.checkUsage.analyzing_source_files'));
828
828
 
829
829
  // Check if source directory exists
830
- if (!fs.existsSync(this.sourceDir)) {
830
+ if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
831
831
  throw new Error(this.t('usage.sourceDirectoryDoesNotExist', { dir: this.sourceDir }) || `Source directory not found: ${this.sourceDir}`);
832
832
  }
833
833
 
@@ -898,7 +898,7 @@ Analysis Features (v1.8.3):
898
898
  const isStrict = process.argv.includes('--strict');
899
899
 
900
900
  // Check if i18n directory exists
901
- if (!fs.existsSync(this.i18nDir)) {
901
+ if (!SecurityUtils.safeExistsSync(this.i18nDir, process.cwd())) {
902
902
  console.warn(t('usage.i18nDirectoryNotFound', { i18nDir: this.i18nDir }));
903
903
  return;
904
904
  }
@@ -912,7 +912,7 @@ Analysis Features (v1.8.3):
912
912
  .filter(item => {
913
913
  try {
914
914
  const itemPath = path.join(this.i18nDir, item);
915
- return fs.existsSync(itemPath) && fs.statSync(itemPath).isDirectory();
915
+ return SecurityUtils.safeExistsSync(itemPath) && fs.statSync(itemPath).isDirectory();
916
916
  } catch (error) {
917
917
  return false;
918
918
  }
@@ -951,7 +951,7 @@ Analysis Features (v1.8.3):
951
951
 
952
952
  for (const fileInfo of translationFiles) {
953
953
  try {
954
- if (!fs.existsSync(fileInfo.filePath)) {
954
+ if (!SecurityUtils.safeExistsSync(fileInfo.filePath)) {
955
955
  if (isDebug || isStrict) {
956
956
  console.warn(`⚠️ File not found: ${path.basename(fileInfo.filePath)}`);
957
957
  }
@@ -1353,7 +1353,7 @@ Analysis Features (v1.8.3):
1353
1353
  async saveReport(report, outputDir = './i18ntk-reports/usage') {
1354
1354
  try {
1355
1355
  // Ensure output directory exists
1356
- if (!fs.existsSync(outputDir)) {
1356
+ if (!SecurityUtils.safeExistsSync(outputDir)) {
1357
1357
  fs.mkdirSync(outputDir, { recursive: true });
1358
1358
  }
1359
1359
 
@@ -1578,12 +1578,12 @@ Analysis Features (v1.8.3):
1578
1578
  await SecurityUtils.validatePath(this.sourceDir);
1579
1579
  await SecurityUtils.validatePath(this.i18nDir);
1580
1580
 
1581
- if (!fs.existsSync(this.sourceDir)) {
1581
+ if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
1582
1582
  throw new Error(this.t('usage.sourceDirectoryDoesNotExist', { dir: this.sourceDir }) || `Source directory not found: ${this.sourceDir}`);
1583
1583
 
1584
1584
  }
1585
1585
 
1586
- if (!fs.existsSync(this.i18nDir)) {
1586
+ if (!SecurityUtils.safeExistsSync(this.i18nDir, process.cwd())) {
1587
1587
  throw new Error(this.t('usage.i18nDirectoryDoesNotExist', { dir: this.i18nDir }) || `I18n directory not found: ${this.i18nDir}`);
1588
1588
  }
1589
1589
 
@@ -1801,4 +1801,40 @@ if (require.main === module) {
1801
1801
  }
1802
1802
  }
1803
1803
 
1804
- module.exports = I18nUsageAnalyzer;
1804
+ module.exports = I18nUsageAnalyzer;
1805
+
1806
+ // Run if called directly
1807
+ if (require.main === module) {
1808
+ async function main() {
1809
+ try {
1810
+ const cliArgs = parseCommonArgs(process.argv.slice(2));
1811
+
1812
+ if (cliArgs.help) {
1813
+ displayHelp('usage');
1814
+ process.exit(0);
1815
+ }
1816
+
1817
+ // Let run() handle full initialization to avoid duplicate setup output
1818
+ const analyzer = new I18nUsageAnalyzer();
1819
+ await analyzer.run();
1820
+ } catch (error) {
1821
+ console.error('Error:', error.message);
1822
+ process.exit(1);
1823
+ }
1824
+ }
1825
+
1826
+ // Check if we're being called from the menu system (stdin has data)
1827
+ const hasStdinData = !process.stdin.isTTY;
1828
+
1829
+ if (hasStdinData) {
1830
+ // When called from menu, consume stdin data and run with defaults
1831
+ process.stdin.resume();
1832
+ process.stdin.on('data', () => {});
1833
+ process.stdin.on('end', () => {
1834
+ main();
1835
+ });
1836
+ } else {
1837
+ // Normal direct execution
1838
+ main();
1839
+ }
1840
+ }
@@ -60,7 +60,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
60
60
  }
61
61
  })();
62
62
 
63
- loadTranslations( 'en', path.resolve(__dirname, '..', 'ui-locales'));
63
+ loadTranslations( 'en', path.resolve(__dirname, '..', 'resources', 'i18n', '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, '..', 'ui-locales'));
90
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
91
91
 
92
92
  SecurityUtils.logSecurityEvent(
93
93
  'I18n validator initializing',
@@ -112,7 +112,7 @@ class I18nValidator {
112
112
  } else {
113
113
  console.warn(t('config.dirFallbackWarning', { dir: this.sourceDir, fallback: this.sourceLanguageDir }) ||
114
114
  `Warning: Directory ${this.sourceDir} not found. Using ${this.sourceLanguageDir}.`);
115
- if (!fs.existsSync(this.sourceLanguageDir)) {
115
+ if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir)) {
116
116
  fs.mkdirSync(this.sourceLanguageDir, { recursive: true });
117
117
  }
118
118
  }
@@ -189,7 +189,7 @@ class I18nValidator {
189
189
  // Get all available languages
190
190
  getAvailableLanguages() {
191
191
  try {
192
- if (!fs.existsSync(this.sourceDir)) {
192
+ if (!SecurityUtils.safeExistsSync(this.sourceDir)) {
193
193
  throw new Error(`Source directory not found: ${this.sourceDir}`);
194
194
  }
195
195
 
@@ -211,7 +211,7 @@ class I18nValidator {
211
211
  const sanitizedLanguage = SecurityUtils.sanitizeInput(language);
212
212
  const languageDir = path.join(this.sourceDir, sanitizedLanguage);
213
213
 
214
- if (!fs.existsSync(languageDir)) {
214
+ if (!SecurityUtils.safeExistsSync(languageDir)) {
215
215
  return [];
216
216
  }
217
217
 
@@ -265,7 +265,7 @@ class I18nValidator {
265
265
  // Validate JSON file syntax
266
266
  async validateJsonSyntax(filePath) {
267
267
  try {
268
- const content = fs.readFileSync(filePath, 'utf8');
268
+ const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
269
269
  const parsed = SecurityUtils.safeParseJSON(content);
270
270
 
271
271
  SecurityUtils.logSecurityEvent(
@@ -481,7 +481,7 @@ class I18nValidator {
481
481
  };
482
482
 
483
483
  // Check for missing language directory
484
- if (!fs.existsSync(languageDir)) {
484
+ if (!SecurityUtils.safeExistsSync(languageDir)) {
485
485
  this.addError(
486
486
  `Language directory missing: ${sanitizedLanguage}`,
487
487
  { language: sanitizedLanguage, expectedPath: languageDir }
@@ -495,7 +495,7 @@ class I18nValidator {
495
495
  const targetFilePath = path.join(languageDir, fileName);
496
496
 
497
497
  // Check if source file exists
498
- if (!fs.existsSync(sourceFilePath)) {
498
+ if (!SecurityUtils.safeExistsSync(sourceFilePath)) {
499
499
  this.addWarning(
500
500
  `Source file missing: ${this.config.sourceLanguage}/${fileName}`,
501
501
  { fileName, language: this.config.sourceLanguage }
@@ -504,7 +504,7 @@ class I18nValidator {
504
504
  }
505
505
 
506
506
  // Check if target file exists
507
- if (!fs.existsSync(targetFilePath)) {
507
+ if (!SecurityUtils.safeExistsSync(targetFilePath)) {
508
508
  this.addError(
509
509
  `Translation file missing: ${language}/${fileName}`,
510
510
  { fileName, language, expectedPath: targetFilePath }
@@ -639,7 +639,7 @@ class I18nValidator {
639
639
  const reportPath = path.join(process.cwd(), 'validation-report.txt');
640
640
  SecurityUtils.validatePath(reportPath);
641
641
 
642
- if (fs.existsSync(reportPath)) {
642
+ if (SecurityUtils.safeExistsSync(reportPath)) {
643
643
  fs.unlinkSync(reportPath);
644
644
  console.log(t('validate.deletedOldReport'));
645
645
 
@@ -672,7 +672,7 @@ class I18nValidator {
672
672
  // Validate source language directory exists
673
673
  SecurityUtils.validatePath(this.sourceLanguageDir);
674
674
 
675
- if (!fs.existsSync(this.sourceLanguageDir)) {
675
+ if (!SecurityUtils.safeExistsSync(this.sourceLanguageDir)) {
676
676
  const error = t('validate.sourceLanguageDirectoryNotFound', { sourceDir: this.sourceLanguageDir }) || 'Source language directory not found';
677
677
  this.addError(error, { sourceLanguage: this.config.sourceLanguage });
678
678
 
@@ -892,7 +892,7 @@ 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, '..', 'ui-locales'));this.sourceDir = this.config.sourceDir;
895
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));this.sourceDir = this.config.sourceDir;
896
896
  this.sourceLanguageDir = path.join(this.sourceDir, this.config.sourceLanguage);
897
897
  } else {
898
898
  await this.initialize();
@@ -983,7 +983,7 @@ if (require.main === module) {
983
983
  // Initialize translations for CLI usage
984
984
  const config = configManager.getConfig();
985
985
  const uiLanguage = config.language || 'en';
986
- loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'ui-locales'));
986
+ loadTranslations(uiLanguage, path.resolve(__dirname, '..', 'resources', 'i18n', 'ui-locales'));
987
987
 
988
988
  SecurityUtils.logSecurityEvent(t('validate.scriptExecution'), 'info', {
989
989
  script: 'i18ntk-validate.js',