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,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * I18NTK MANAGEMENT TOOLKIT - MAIN MANAGER
4
4
  *
@@ -18,11 +18,12 @@
18
18
  * node i18ntk-manage.js --command=init
19
19
  */
20
20
 
21
- const fs = require('fs');
22
21
  const path = require('path');
23
22
  const UIi18n = require('./i18ntk-ui');
24
23
  const AdminAuth = require('../utils/admin-auth');
25
24
  const SecurityUtils = require('../utils/security');
25
+
26
+
26
27
  const AdminCLI = require('../utils/admin-cli');
27
28
  const configManager = require('../settings/settings-manager');
28
29
  const { showFrameworkWarningOnce } = require('../utils/cli-helper');
@@ -36,6 +37,8 @@ const SettingsCLI = require('../settings/settings-cli');
36
37
  // const I18nDebugger = require('../scripts/debug/debugger');
37
38
  const { createPrompt, isInteractive } = require('../utils/prompt-helper');
38
39
  const { loadTranslations, t, refreshLanguageFromSettings} = require('../utils/i18n-helper');
40
+ // Preload translations early to avoid missing key warnings
41
+ loadTranslations();
39
42
  const cliHelper = require('../utils/cli-helper');
40
43
  const { loadConfig, saveConfig, ensureConfigDefaults } = require('../utils/config');
41
44
  const pkg = require('../package.json');
@@ -50,10 +53,6 @@ async function runInitFlow() {
50
53
  return { i18nDir: settings.i18nDir, sourceDir: settings.sourceDir };
51
54
  }
52
55
 
53
-
54
-
55
-
56
-
57
56
  /**
58
57
  * Ensures the project is properly initialized or exits the process
59
58
  * @param {Object} prompt - Prompt interface for user interaction
@@ -129,7 +128,7 @@ async function ensureInitializedOrExit(prompt) {
129
128
  console.log('Framework detection prompt will be suppressed for this version.');
130
129
  } else if (action === 'detect') {
131
130
  // Run framework detection
132
- const { detectedLanguage, detectedFramework } = detectEnvironmentAndFramework();
131
+ const { detectedLanguage, detectedFramework } = await detectEnvironmentAndFramework();
133
132
 
134
133
  if (detectedFramework && detectedFramework !== 'generic') {
135
134
  console.log(`\nDetected framework: ${detectedFramework}`);
@@ -156,7 +155,90 @@ async function ensureInitializedOrExit(prompt) {
156
155
  return { ...config, ...settings };
157
156
  }
158
157
 
158
+ /**
159
+ * Custom glob implementation using Node.js built-in modules (zero dependencies)
160
+ * @param {string[]} patterns - Array of glob patterns
161
+ * @param {Object} options - Options object with cwd and ignore properties
162
+ * @returns {Promise<string[]>} Array of matching file paths
163
+ */
164
+ async function customGlob(patterns, options = {}) {
165
+ const fs = require('fs');
166
+ const path = require('path');
167
+ const cwd = options.cwd || process.cwd();
168
+ const ignorePatterns = options.ignore || [];
169
+
170
+ function matchesPattern(filename, pattern) {
171
+ // Simple pattern matching for **/*.{js,jsx,ts,tsx} style patterns
172
+ if (pattern.includes('**/*')) {
173
+ const extensionPart = pattern.split('*.')[1];
174
+ if (extensionPart) {
175
+ const extensions = extensionPart.replace('{', '').replace('}', '').split(',');
176
+ return extensions.some(ext => filename.endsWith('.' + ext.trim()));
177
+ }
178
+ }
179
+ return filename.includes(pattern.replace('**/', ''));
180
+ }
181
+
182
+ function shouldIgnore(filePath) {
183
+ return ignorePatterns.some(pattern => {
184
+ if (pattern.includes('**/')) {
185
+ const patternEnd = pattern.replace('**/', '');
186
+ return filePath.includes('/' + patternEnd) || filePath.includes('\\' + patternEnd);
187
+ }
188
+ return filePath.includes(pattern);
189
+ });
190
+ }
191
+
192
+ function findFiles(dir, results = []) {
193
+ try {
194
+ const items = fs.readdirSync(dir);
195
+
196
+ for (const item of items) {
197
+ const fullPath = path.join(dir, item);
198
+ const relativePath = path.relative(cwd, fullPath);
199
+
200
+ if (shouldIgnore(relativePath)) {
201
+ continue;
202
+ }
203
+
204
+ try {
205
+ const stat = fs.statSync(fullPath);
206
+
207
+ if (stat.isDirectory()) {
208
+ findFiles(fullPath, results);
209
+ } else if (stat.isFile()) {
210
+ // Check if file matches any of our patterns
211
+ for (const pattern of patterns) {
212
+ if (matchesPattern(item, pattern)) {
213
+ results.push(relativePath);
214
+ break;
215
+ }
216
+ }
217
+ }
218
+ } catch (error) {
219
+ // Skip files we can't access
220
+ continue;
221
+ }
222
+ }
223
+ } catch (error) {
224
+ // Skip directories we can't access
225
+ }
226
+
227
+ return results;
228
+ }
229
+
230
+ return findFiles(cwd);
231
+ }
232
+
159
233
  async function detectEnvironmentAndFramework() {
234
+ // Defensive check to ensure SecurityUtils is available
235
+ if (!SecurityUtils) {
236
+ throw new Error('SecurityUtils is not available. This may indicate a module loading issue.');
237
+ }
238
+ const fs = require('fs');
239
+ const path = require('path');
240
+
241
+
160
242
  const packageJsonPath = path.join(process.cwd(), 'package.json');
161
243
  const pyprojectPath = path.join(process.cwd(), 'pyproject.toml');
162
244
  const requirementsPath = path.join(process.cwd(), 'requirements.txt');
@@ -167,10 +249,10 @@ async function detectEnvironmentAndFramework() {
167
249
  let detectedLanguage = 'generic';
168
250
  let detectedFramework = 'generic';
169
251
 
170
- if (fs.existsSync(packageJsonPath)) {
252
+ if (SecurityUtils.safeExistsSync(packageJsonPath)) {
171
253
  detectedLanguage = 'javascript';
172
254
  try {
173
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
255
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
174
256
  const deps = {
175
257
  ...(packageJson.dependencies || {}),
176
258
  ...(packageJson.devDependencies || {}),
@@ -189,7 +271,7 @@ async function detectEnvironmentAndFramework() {
189
271
  /require\(['\"]i18ntk[\/\\]runtime['\"]\)/
190
272
  ];
191
273
 
192
- const sourceFiles = await glob(['src/**/*.{js,jsx,ts,tsx}'], {
274
+ const sourceFiles = await customGlob(['src/**/*.{js,jsx,ts,tsx}'], {
193
275
  cwd: process.cwd(),
194
276
  ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
195
277
  });
@@ -223,11 +305,11 @@ async function detectEnvironmentAndFramework() {
223
305
  } catch (error) {
224
306
  detectedFramework = 'generic';
225
307
  }
226
- } else if (fs.existsSync(pyprojectPath) || fs.existsSync(requirementsPath)) {
308
+ } else if (SecurityUtils.safeExistsSync(pyprojectPath) || SecurityUtils.safeExistsSync(requirementsPath)) {
227
309
  detectedLanguage = 'python';
228
310
  try {
229
- if (fs.existsSync(requirementsPath)) {
230
- const requirements = fs.readFileSync(requirementsPath, 'utf8');
311
+ if (SecurityUtils.safeExistsSync(requirementsPath)) {
312
+ const requirements = SecurityUtils.safeReadFileSync(requirementsPath, path.dirname(requirementsPath), 'utf8');
231
313
  if (requirements.includes('django')) detectedFramework = 'django';
232
314
  else if (requirements.includes('flask')) detectedFramework = 'flask';
233
315
  else if (requirements.includes('fastapi')) detectedFramework = 'fastapi';
@@ -236,13 +318,13 @@ async function detectEnvironmentAndFramework() {
236
318
  } catch (error) {
237
319
  detectedFramework = 'generic';
238
320
  }
239
- } else if (fs.existsSync(goModPath)) {
321
+ } else if (SecurityUtils.safeExistsSync(goModPath)) {
240
322
  detectedLanguage = 'go';
241
323
  detectedFramework = 'generic';
242
- } else if (fs.existsSync(pomPath)) {
324
+ } else if (SecurityUtils.safeExistsSync(pomPath)) {
243
325
  detectedLanguage = 'java';
244
326
  try {
245
- const pomContent = fs.readFileSync(pomPath, 'utf8');
327
+ const pomContent = SecurityUtils.safeReadFileSync(pomPath, path.dirname(pomPath), 'utf8');
246
328
  if (pomContent.includes('spring-boot')) detectedFramework = 'spring-boot';
247
329
  else if (pomContent.includes('spring')) detectedFramework = 'spring';
248
330
  else if (pomContent.includes('quarkus')) detectedFramework = 'quarkus';
@@ -250,10 +332,10 @@ async function detectEnvironmentAndFramework() {
250
332
  } catch (error) {
251
333
  detectedFramework = 'generic';
252
334
  }
253
- } else if (fs.existsSync(composerPath)) {
335
+ } else if (SecurityUtils.safeExistsSync(composerPath)) {
254
336
  detectedLanguage = 'php';
255
337
  try {
256
- const composer = JSON.parse(fs.readFileSync(composerPath, 'utf8'));
338
+ const composer = JSON.parse(SecurityUtils.safeReadFileSync(composerPath, path.dirname(composerPath), 'utf8'));
257
339
  const deps = composer.require || {};
258
340
 
259
341
  if (deps['laravel/framework']) detectedFramework = 'laravel';
@@ -461,7 +543,7 @@ class I18nManager {
461
543
  // Only auto-detect if no settings are configured
462
544
  for (const possiblePath of possibleI18nPaths) {
463
545
  const resolvedPath = path.resolve(projectRoot, possiblePath);
464
- if (fs.existsSync(resolvedPath)) {
546
+ if (SecurityUtils.safeExistsSync(resolvedPath)) {
465
547
  // Check if it contains language directories
466
548
  try {
467
549
  const items = fs.readdirSync(resolvedPath);
@@ -487,13 +569,13 @@ class I18nManager {
487
569
  async checkI18nDependencies() {
488
570
  const packageJsonPath = path.resolve('./package.json');
489
571
 
490
- if (!fs.existsSync(packageJsonPath)) {
572
+ if (!SecurityUtils.safeExistsSync(packageJsonPath)) {
491
573
  console.log(this.ui ? this.ui.t('errors.noPackageJson') : 'No package.json found');
492
574
  return false; // Treat as no framework detected
493
575
  }
494
576
 
495
577
  try {
496
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
578
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
497
579
  // Include peerDependencies in the check
498
580
  const dependencies = {
499
581
  ...packageJson.dependencies,
@@ -610,12 +692,29 @@ class I18nManager {
610
692
 
611
693
  // Add this run method after the checkI18nDependencies method
612
694
  async run() {
695
+ // Add timeout to prevent hanging
696
+ const args = this.parseArgs();
697
+
698
+ const timeout = setTimeout(() => {
699
+ console.error('āŒ CLI startup timeout - something is hanging');
700
+ if (args.debug) {
701
+ console.error('šŸ” DEBUG: Last known execution point reached');
702
+ }
703
+ process.exit(1);
704
+ }, 10000); // 10 second timeout
705
+
706
+ if (args.debug) {
707
+ console.log('šŸ” DEBUG: Starting i18ntk-manage.js...');
708
+ console.log('šŸ” DEBUG: Process.argv:', process.argv);
709
+ console.log('šŸ” DEBUG: Parsed args:', args);
710
+ console.log('šŸ” DEBUG: About to call SetupEnforcer.checkSetupCompleteAsync()');
711
+ }
712
+
613
713
  let prompt;
614
714
  try {
615
715
  // Ensure setup is complete before running any operations
616
716
  await SetupEnforcer.checkSetupCompleteAsync();
617
-
618
- const args = this.parseArgs();
717
+
619
718
  prompt = createPrompt({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
620
719
  const interactive = isInteractive({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
621
720
 
@@ -1186,7 +1285,7 @@ class I18nManager {
1186
1285
  console.log(t('debug.runningDebugTool', { displayName }));
1187
1286
  try {
1188
1287
  const toolPath = path.join(__dirname, '..', 'scripts', 'debug', toolName);
1189
- if (fs.existsSync(toolPath)) {
1288
+ if (SecurityUtils.safeExistsSync(toolPath)) {
1190
1289
  console.log(`Debug tool available: ${toolName}`);
1191
1290
  console.log(`To run this tool manually: node "${toolPath}"`);
1192
1291
  console.log(`Working directory: ${path.join(__dirname, '..')}`);
@@ -1209,7 +1308,7 @@ class I18nManager {
1209
1308
 
1210
1309
  try {
1211
1310
  const logsDir = path.join(__dirname, '..', 'scripts', 'debug', 'logs');
1212
- if (fs.existsSync(logsDir)) {
1311
+ if (SecurityUtils.safeExistsSync(logsDir)) {
1213
1312
  const files = fs.readdirSync(logsDir)
1214
1313
  .filter(file => file.endsWith('.log') || file.endsWith('.txt'))
1215
1314
  .sort((a, b) => {
@@ -1230,7 +1329,7 @@ class I18nManager {
1230
1329
  const fileIndex = parseInt(choice) - 1;
1231
1330
 
1232
1331
  if (fileIndex >= 0 && fileIndex < files.length) {
1233
- const logContent = fs.readFileSync(path.join(logsDir, files[fileIndex]), 'utf8');
1332
+ const logContent = SecurityUtils.safeReadFileSync(path.join(logsDir, files[fileIndex]), logsDir, 'utf8');
1234
1333
  console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
1235
1334
  console.log('============================================================');
1236
1335
  console.log(logContent.slice(-2000)); // Show last 2000 characters
@@ -1290,7 +1389,7 @@ class I18nManager {
1290
1389
 
1291
1390
  // Check which directories exist and have files
1292
1391
  for (const dir of targetDirs) {
1293
- if (fs.existsSync(dir.path)) {
1392
+ if (SecurityUtils.safeExistsSync(dir.path)) {
1294
1393
  const files = this.getAllReportFiles(dir.path);
1295
1394
  if (files.length > 0) {
1296
1395
  availableDirs.push({
@@ -1429,7 +1528,7 @@ class I18nManager {
1429
1528
  let files = [];
1430
1529
 
1431
1530
  try {
1432
- if (!fs.existsSync(dir)) {
1531
+ if (!SecurityUtils.safeExistsSync(dir)) {
1433
1532
  return [];
1434
1533
  }
1435
1534
 
@@ -1561,7 +1660,7 @@ if (require.main === module) {
1561
1660
  if (args.includes('--version') || args.includes('-v')) {
1562
1661
  try {
1563
1662
  const packageJsonPath = path.resolve(__dirname, '../package.json');
1564
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
1663
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
1565
1664
  const versionInfo = packageJson.versionInfo || {};
1566
1665
 
1567
1666
  console.log(`\nšŸŒ i18n Toolkit (i18ntk)`);
@@ -1592,4 +1691,4 @@ if (require.main === module) {
1592
1691
  manager.run();
1593
1692
  }
1594
1693
 
1595
- module.exports = I18nManager;
1694
+ module.exports = I18nManager;