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
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+
2
3
  /**
3
4
  * I18NTK TEXT SCANNER
4
5
  *
@@ -32,7 +33,7 @@ const SetupEnforcer = require('../utils/setup-enforcer');
32
33
  }
33
34
  })();
34
35
 
35
- loadTranslations(process.env.I18NTK_LANG);
36
+ loadTranslations();
36
37
 
37
38
  class I18nTextScanner {
38
39
  constructor(config = {}) {
@@ -46,12 +47,12 @@ class I18nTextScanner {
46
47
  }
47
48
 
48
49
  loadLocale() {
49
- const uiLocalesDir = path.join(__dirname, '..', 'ui-locales');
50
+ const uiLocalesDir = path.join(__dirname, '..', 'resources', 'i18n', 'ui-locales');
50
51
  const localeFile = path.join(uiLocalesDir, 'en.json');
51
-
52
+
52
53
  try {
53
- const localeContent = fs.readFileSync(localeFile, 'utf8');
54
- return JSON.parse(localeContent);
54
+ const localeContent = SecurityUtils.safeReadFileSync(localeFile, uiLocalesDir, 'utf8');
55
+ return SecurityUtils.safeParseJSON(localeContent);
55
56
  } catch (error) {
56
57
  return {
57
58
  scanner: {
@@ -89,16 +90,16 @@ class I18nTextScanner {
89
90
  const keyStr = String(key || '');
90
91
  const keys = keyStr.split('.');
91
92
  let value = this.locale;
92
-
93
+
93
94
  for (const k of keys) {
94
95
  value = value?.[k];
95
96
  if (value === undefined) break;
96
97
  }
97
-
98
+
98
99
  if (typeof value !== 'string') {
99
100
  return key;
100
101
  }
101
-
102
+
102
103
  return value.replace(/\{([^}]+)\}/g, (match, param) => {
103
104
  return params[param] !== undefined ? params[param] : match;
104
105
  });
@@ -107,12 +108,12 @@ class I18nTextScanner {
107
108
  parseArgs() {
108
109
  const args = process.argv.slice(2);
109
110
  const parsed = {};
110
-
111
+
111
112
  args.forEach(arg => {
112
113
  if (arg.startsWith('--')) {
113
114
  const [key, ...valueParts] = arg.substring(2).split('=');
114
115
  const value = valueParts.join('=');
115
-
116
+
116
117
  switch (key) {
117
118
  case 'source-dir':
118
119
  parsed.sourceDir = value || '';
@@ -148,38 +149,38 @@ class I18nTextScanner {
148
149
  }
149
150
  }
150
151
  });
151
-
152
+
152
153
  return parsed;
153
154
  }
154
155
 
155
156
  detectFramework(projectRoot) {
156
157
  const packagePath = path.join(projectRoot, 'package.json');
157
-
158
+
158
159
  // Check for Python frameworks
159
160
  const requirementsPath = path.join(projectRoot, 'requirements.txt');
160
161
  const setupPath = path.join(projectRoot, 'setup.py');
161
162
  const pyprojectPath = path.join(projectRoot, 'pyproject.toml');
162
-
163
+
163
164
  try {
164
165
  // Check Python frameworks first
165
- if (fs.existsSync(requirementsPath)) {
166
- const requirements = fs.readFileSync(requirementsPath, 'utf8');
166
+ if (SecurityUtils.safeExistsSync(requirementsPath, projectRoot)) {
167
+ const requirements = SecurityUtils.safeReadFileSync(requirementsPath, projectRoot, 'utf8');
167
168
  if (requirements.includes('Django')) return 'django';
168
169
  if (requirements.includes('Flask') || requirements.includes('flask-babel')) return 'flask';
169
170
  }
170
-
171
- if (fs.existsSync(setupPath)) {
172
- const setup = fs.readFileSync(setupPath, 'utf8');
171
+
172
+ if (SecurityUtils.safeExistsSync(setupPath, projectRoot)) {
173
+ const setup = SecurityUtils.safeReadFileSync(setupPath, projectRoot, 'utf8');
173
174
  if (setup.includes('Django')) return 'django';
174
175
  if (setup.includes('Flask')) return 'flask';
175
176
  }
176
-
177
- if (fs.existsSync(pyprojectPath)) {
178
- const pyproject = fs.readFileSync(pyprojectPath, 'utf8');
177
+
178
+ if (SecurityUtils.safeExistsSync(pyprojectPath, projectRoot)) {
179
+ const pyproject = SecurityUtils.safeReadFileSync(pyprojectPath, projectRoot, 'utf8');
179
180
  if (pyproject.includes('Django')) return 'django';
180
181
  if (pyproject.includes('Flask')) return 'flask';
181
182
  }
182
-
183
+
183
184
  // Check for Python files
184
185
  const hasPythonFiles = fs.readdirSync(projectRoot, { recursive: true })
185
186
  .some(file => file.endsWith && file.endsWith('.py'));
@@ -187,17 +188,18 @@ class I18nTextScanner {
187
188
  } catch (error) {
188
189
  // Continue to JS frameworks
189
190
  }
190
-
191
+
191
192
  try {
192
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
193
+ const packageJsonContent = SecurityUtils.safeReadFileSync(packagePath, projectRoot, 'utf8');
194
+ const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
193
195
  const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
194
-
196
+
195
197
  if (deps.react || deps['react-dom']) return 'react';
196
198
  if (deps.vue || deps['vue-router']) return 'vue';
197
199
  if (deps['@angular/core'] || deps.angular) return 'angular';
198
200
  if (deps.next) return 'next';
199
201
  if (deps.svelte) return 'svelte';
200
-
202
+
201
203
  return 'vanilla';
202
204
  } catch (error) {
203
205
  return 'vanilla';
@@ -294,24 +296,24 @@ class I18nTextScanner {
294
296
  // Enhanced text detection for Unicode and multilingual support
295
297
  const trimmed = text.trim();
296
298
  if (trimmed.length < 3) return false;
297
-
299
+
298
300
  // Skip if it's just numbers or special characters
299
301
  if (/^\d+$/.test(trimmed)) return false;
300
302
  if (/^[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>?]+$/.test(trimmed)) return false;
301
-
303
+
302
304
  // Allow Unicode characters including CJK, Cyrillic, etc.
303
305
  const validChars = trimmed.match(/[\p{L}\p{N}\s\-,.!?':"()\[\]{}]/gu) || [];
304
306
  const validRatio = validChars.length / trimmed.length;
305
-
307
+
306
308
  // Must have at least 50% valid characters and some alphabetic characters
307
309
  const hasAlpha = /[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF\u0400-\u04FF\u4E00-\u9FFF\uAC00-\uD7AF]/u.test(trimmed);
308
-
310
+
309
311
  return validRatio >= 0.5 && hasAlpha;
310
312
  }
311
313
 
312
314
  scanFile(filePath, patterns, minLength, maxLength) {
313
315
  try {
314
- const content = fs.readFileSync(filePath, 'utf8');
316
+ const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
315
317
  const lines = content.split('\n');
316
318
  const results = [];
317
319
 
@@ -319,20 +321,20 @@ class I18nTextScanner {
319
321
  let match;
320
322
  while ((match = pattern.exec(content)) !== null) {
321
323
  const text = match[1] || match[0];
322
-
324
+
323
325
  // Skip translation function calls
324
326
  const beforeMatch = content.substring(Math.max(0, match.index - 20), match.index);
325
- if (beforeMatch.includes('t(') || beforeMatch.includes('i18next.t(') ||
327
+ if (beforeMatch.includes('t(') || beforeMatch.includes('i18next.t(') ||
326
328
  beforeMatch.includes('$t(') || beforeMatch.includes('translate(')) {
327
329
  continue;
328
330
  }
329
-
330
- if (text && this.isEnglishText(text) &&
331
+
332
+ if (text && this.isEnglishText(text) &&
331
333
  text.length >= minLength && text.length <= maxLength) {
332
-
334
+
333
335
  const lineNumber = content.substring(0, match.index).split('\n').length;
334
336
  const lineContent = lines[lineNumber - 1] || '';
335
-
337
+
336
338
  results.push({
337
339
  text: text.trim(),
338
340
  line: lineNumber,
@@ -357,7 +359,7 @@ class I18nTextScanner {
357
359
  .replace(/[^a-z0-9\s]/g, '')
358
360
  .replace(/\s+/g, '_')
359
361
  .substring(0, 50);
360
-
362
+
361
363
  return {
362
364
  key: `ui.${key}`,
363
365
  original: text,
@@ -402,28 +404,28 @@ class I18nTextScanner {
402
404
  }
403
405
 
404
406
  async scanDirectory(dir, options = {}) {
405
- const {
406
- patterns = [],
407
- exclusions = [],
408
- minLength = 3,
407
+ const {
408
+ patterns = [],
409
+ exclusions = [],
410
+ minLength = 3,
409
411
  maxLength = 100,
410
- includeTests = false
412
+ includeTests = false
411
413
  } = options;
412
414
 
413
- if (!fs.existsSync(dir)) {
415
+ if (!SecurityUtils.safeExistsSync(dir, path.dirname(dir))) {
414
416
  throw new Error(`Directory does not exist: ${dir}`);
415
417
  }
416
418
 
417
419
  const allResults = [];
418
420
  const extensions = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.html', '.svelte', '.py', '.pyx', '.pyi'];
419
-
421
+
420
422
  const scanRecursive = (currentDir) => {
421
423
  const items = fs.readdirSync(currentDir);
422
-
424
+
423
425
  for (const item of items) {
424
426
  const fullPath = path.join(currentDir, item);
425
427
  const stat = fs.statSync(fullPath);
426
-
428
+
427
429
  if (stat.isDirectory()) {
428
430
  if (!item.startsWith('.') && !this.shouldExcludeFile(fullPath, exclusions)) {
429
431
  scanRecursive(fullPath);
@@ -434,7 +436,7 @@ class I18nTextScanner {
434
436
  if (!includeTests && (item.includes('.test.') || item.includes('.spec.'))) {
435
437
  continue;
436
438
  }
437
-
439
+
438
440
  const results = this.scanFile(fullPath, patterns, minLength, maxLength);
439
441
  if (results.length > 0) {
440
442
  allResults.push({
@@ -452,7 +454,7 @@ class I18nTextScanner {
452
454
  }
453
455
 
454
456
  async generateReport(results, outputDir) {
455
- if (!fs.existsSync(outputDir)) {
457
+ if (!SecurityUtils.safeExistsSync(outputDir, path.dirname(outputDir))) {
456
458
  fs.mkdirSync(outputDir, { recursive: true });
457
459
  }
458
460
 
@@ -470,11 +472,11 @@ class I18nTextScanner {
470
472
  };
471
473
 
472
474
  // JSON report
473
- fs.writeFileSync(reportFile, JSON.stringify(summary, null, 2));
475
+ SecurityUtils.safeWriteFileSync(reportFile, JSON.stringify(summary, null, 2), outputDir);
474
476
 
475
477
  // Markdown summary
476
478
  const mdContent = this.generateMarkdownReport(summary);
477
- fs.writeFileSync(summaryFile, mdContent);
479
+ SecurityUtils.safeWriteFileSync(summaryFile, mdContent, outputDir);
478
480
 
479
481
  return { reportFile, summaryFile, summary };
480
482
  }
@@ -482,10 +484,10 @@ class I18nTextScanner {
482
484
  generateMarkdownReport(summary) {
483
485
  let content = `# Text Analysis Report
484
486
 
485
- **Framework:** ${summary.framework}
486
- **Total Files Scanned:** ${summary.totalFiles}
487
- **Text Instances Found:** ${summary.totalInstances}
488
- **Files with Hardcoded Text:** ${summary.filesWithText}
487
+ **Framework:** ${summary.framework}
488
+ **Total Files Scanned:** ${summary.totalFiles}
489
+ **Text Instances Found:** ${summary.totalInstances}
490
+ **Files with Hardcoded Text:** ${summary.filesWithText}
489
491
  **Generated:** ${summary.timestamp}
490
492
 
491
493
  ## Summary
@@ -552,7 +554,7 @@ class I18nTextScanner {
552
554
 
553
555
  const baseConfig = await getUnifiedConfig('scanner', args);
554
556
  this.config = { ...baseConfig, ...(this.config || {}) };
555
-
557
+
556
558
  this.sourceDir = this.config.sourceDir || './src';
557
559
 
558
560
  // Resolve framework with precedence: CLI arg > config.framework.preference|string > auto-detect > fallback
@@ -574,7 +576,7 @@ class I18nTextScanner {
574
576
  }
575
577
 
576
578
  // Validate source directory
577
- if (!fs.existsSync(this.sourceDir)) {
579
+ if (!SecurityUtils.safeExistsSync(this.sourceDir, path.dirname(this.sourceDir))) {
578
580
  console.error(`❌ Source directory does not exist: ${this.sourceDir}`);
579
581
  process.exit(1);
580
582
  }