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
@@ -7,17 +7,18 @@
7
7
  * during package initialization. Integrates with i18ntk init process.
8
8
  *
9
9
  * Usage:
10
- * node scripts/locale-optimizer.js --interactive
11
- * node scripts/locale-optimizer.js --list
12
- * node scripts/locale-optimizer.js --keep en,es,de
13
- * node scripts/locale-optimizer.js --restore
10
+ * node utils/locale-optimizer.js --interactive
11
+ * node utils/locale-optimizer.js --list
12
+ * node utils/locale-optimizer.js --keep en,es,de
13
+ * node utils/locale-optimizer.js --restore
14
14
  */
15
15
 
16
- const fs = require('fs');
17
- const path = require('path');
18
- const cliHelper = require('../utils/cli-helper');
19
- const JsonOutput = require('../utils/json-output');
20
- const { getGlobalReadline } = require('../utils/cli');
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const SecurityUtils = require('./security');
19
+ const cliHelper = require('./cli-helper');
20
+ const JsonOutput = require('./json-output');
21
+ const { getGlobalReadline } = require('./cli');
21
22
 
22
23
  class LocaleOptimizer {
23
24
  constructor() {
@@ -77,19 +78,19 @@ class LocaleOptimizer {
77
78
  return;
78
79
  }
79
80
 
80
- console.log('\n📊 UI Locale Package Analysis');
81
- console.log(''.repeat(40));
81
+ console.log('\n📊 UI Locale Package Analysis');
82
+ console.log('═'.repeat(40));
82
83
 
83
84
  const available = this.getAvailableLocales();
84
85
  let totalSize = 0;
85
86
 
86
87
  if (available.length === 0) {
87
- console.log(' No locale files found');
88
+ console.log(' ❌ No locale files found');
88
89
  return;
89
90
  }
90
91
 
91
92
  console.log(' Language Size Status');
92
- console.log(' ─────────────────────────');
93
+ console.log(' ─────────────────────────');
93
94
 
94
95
  available.forEach(locale => {
95
96
  const filePath = path.join(this.uiLocalesDir, `${locale}.json`);
@@ -105,11 +106,11 @@ class LocaleOptimizer {
105
106
 
106
107
  const missing = this.allLocales.filter(l => !available.includes(l));
107
108
  if (missing.length > 0) {
108
- console.log(`\n ⚠️ Missing: ${missing.join(', ').toUpperCase()}`);
109
+ console.log(`\n ⚠️ Missing: ${missing.join(', ').toUpperCase()}`);
109
110
  }
110
111
 
111
- console.log(`\n📦 Total package size: ${(totalSize / 1024).toFixed(1)}KB`);
112
- console.log(`💡 Potential savings: ${(totalSize/1024 - this.getLocaleSize('en')).toFixed(1)}KB (English only)`);
112
+ console.log(`\n📦 Total package size: ${(totalSize / 1024).toFixed(1)}KB`);
113
+ console.log(`💡 Potential savings: ${(totalSize/1024 - this.getLocaleSize('en')).toFixed(1)}KB (English only)`);
113
114
  console.log(` Run --interactive to optimize now`);
114
115
  }
115
116
 
@@ -135,7 +136,7 @@ class LocaleOptimizer {
135
136
  keepLocales(localesToKeep) {
136
137
  const keepList = Array.isArray(localesToKeep) ? localesToKeep : localesToKeep.split(',').map(l => l.trim().toLowerCase());
137
138
 
138
- console.log('🎯 Optimizing package size...');
139
+ console.log('🎯 Optimizing package size...');
139
140
  console.log(` Keeping: ${keepList.join(', ').toUpperCase()}`);
140
141
 
141
142
  // Create backup directory
@@ -162,22 +163,22 @@ class LocaleOptimizer {
162
163
  removedCount++;
163
164
  removedLocales.push(locale);
164
165
 
165
- console.log(` Removed ${locale.toUpperCase()} (backed up)`);
166
+ console.log(` ✅ Removed ${locale.toUpperCase()} (backed up)`);
166
167
  }
167
168
  });
168
169
 
169
170
  if (removedCount > 0) {
170
- console.log(`\n⚠️ WARNING: Removing locales may break UI functionality`);
171
- console.log(` If issues occur, restore with: node scripts/locale-optimizer.js --restore`);
171
+ console.log(`\n⚠️ WARNING: Removing locales may break UI functionality`);
172
+ console.log(` If issues occur, restore with: node utils/locale-optimizer.js --restore`);
172
173
  console.log(` Or reinstall the package: npm install -g i18ntk`);
173
174
 
174
175
  // Create warning file
175
176
  const warningPath = path.join(this.backupDir, 'REMOVED_LOCALES.txt');
176
- SecurityUtils.safeWriteFileSync(warningPath, `Removed locales: ${removedLocales.join(',')}\nRestore with: node scripts/locale-optimizer.js --restore`);
177
+ SecurityUtils.safeWriteFileSync(warningPath, `Removed locales: ${removedLocales.join(',')}\nRestore with: node utils/locale-optimizer.js --restore`);
177
178
  }
178
179
 
179
180
  const savedKB = (savedSpace / 1024).toFixed(1);
180
- console.log(`\n🎉 Optimization complete!`);
181
+ console.log(`\n🎉 Optimization complete!`);
181
182
  console.log(` Removed: ${removedCount} locales`);
182
183
  console.log(` Saved: ${savedKB}KB`);
183
184
  console.log(` Backup location: ${this.backupDir}`);
@@ -187,17 +188,17 @@ class LocaleOptimizer {
187
188
  * Restore locales from backup
188
189
  */
189
190
  restoreLocales() {
190
- console.log('🔄 Restoring locales from backup...');
191
+ console.log('🔄 Restoring locales from backup...');
191
192
 
192
193
  if (!SecurityUtils.safeExistsSync(this.backupDir)) {
193
- console.log(' No backup directory found');
194
+ console.log(' ❌ No backup directory found');
194
195
  return;
195
196
  }
196
197
 
197
198
  const backupFiles = fs.readdirSync(this.backupDir).filter(f => f.endsWith('.json'));
198
199
 
199
200
  if (backupFiles.length === 0) {
200
- console.log(' No backup files found');
201
+ console.log(' ❌ No backup files found');
201
202
  return;
202
203
  }
203
204
 
@@ -209,10 +210,10 @@ class LocaleOptimizer {
209
210
 
210
211
  fs.copyFileSync(backupPath, restorePath);
211
212
  restoredCount++;
212
- console.log(` Restored ${file.replace('.json', '').toUpperCase()}`);
213
+ console.log(` ✅ Restored ${file.replace('.json', '').toUpperCase()}`);
213
214
  });
214
215
 
215
- console.log(`\n🎉 Restoration complete!`);
216
+ console.log(`\n🎉 Restoration complete!`);
216
217
  console.log(` Restored: ${restoredCount} locales`);
217
218
  }
218
219
 
@@ -220,13 +221,13 @@ class LocaleOptimizer {
220
221
  * Dry run mode - show optimization scenarios without making changes
221
222
  */
222
223
  dryRun() {
223
- console.log('🔍 LOCALE OPTIMIZER - DRY RUN MODE');
224
- console.log(''.repeat(50));
224
+ console.log('🔍 LOCALE OPTIMIZER - DRY RUN MODE');
225
+ console.log('═'.repeat(50));
225
226
 
226
227
  const available = this.getAvailableLocales();
227
228
  const totalSize = this.getTotalSize();
228
229
 
229
- console.log(`📊 Current state:`);
230
+ console.log(`📊 Current state:`);
230
231
  console.log(` Total locales: ${available.length}`);
231
232
  console.log(` Total package size: ${totalSize.toFixed(1)}KB`);
232
233
  console.log(` Individual locale sizes:`);
@@ -237,7 +238,7 @@ class LocaleOptimizer {
237
238
  console.log(` - ${locale.toUpperCase()}: ${size.toFixed(1)}KB (${status})`);
238
239
  });
239
240
 
240
- console.log('\n🎯 Optimization scenarios:');
241
+ console.log('\n🎯 Optimization scenarios:');
241
242
 
242
243
  // Scenario 1: English only
243
244
  const enSize = this.getLocaleSize('en');
@@ -259,42 +260,42 @@ class LocaleOptimizer {
259
260
  console.log(` - Size: ${enEsFrSize.toFixed(1)}KB`);
260
261
  console.log(` - Savings: ${(totalSize - enEsFrSize).toFixed(1)}KB (${(((totalSize - enEsFrSize) / totalSize) * 100).toFixed(1)}%)`);
261
262
 
262
- console.log('\n💡 To run actual optimization:');
263
- console.log(' node scripts/locale-optimizer.js --interactive');
264
- console.log('\n Dry run complete - no files were modified');
263
+ console.log('\n💡 To run actual optimization:');
264
+ console.log(' node utils/locale-optimizer.js --interactive');
265
+ console.log('\n✅ Dry run complete - no files were modified');
265
266
  }
266
267
 
267
268
  /**
268
269
  * Interactive locale selection with enhanced UI
269
270
  */
270
271
  async interactiveSelect() {
271
- console.log('\n🌍 i18ntk Locale Optimizer');
272
- console.log(''.repeat(50));
272
+ console.log('\n🌍 i18ntk Locale Optimizer');
273
+ console.log('═'.repeat(50));
273
274
  console.log('Select which UI locales to keep (English is always kept)');
274
275
  console.log('This will reduce package size by removing unused language files\n');
275
276
 
276
277
  const available = this.getAvailableLocales();
277
278
  const selections = new Set(['en']); // Always keep English
278
279
 
279
- console.log('📊 Available locales and sizes:');
280
+ console.log('📊 Available locales and sizes:');
280
281
  available.forEach(locale => {
281
282
  const size = this.getLocaleSize(locale);
282
- const status = locale === 'en' ? ' (Required)' : '';
283
+ const status = locale === 'en' ? '✅ (Required)' : '○';
283
284
  console.log(` ${status} ${locale.toUpperCase()}: ${size}KB`);
284
285
  });
285
286
 
286
- console.log('\n💡 Type locale codes separated by commas (e.g., en,es,de)');
287
+ console.log('\n💡 Type locale codes separated by commas (e.g., en,es,de)');
287
288
  console.log(' Press Enter to keep all, or type "cancel" to abort\n');
288
289
 
289
290
  const answer = await cliHelper.prompt('Select locales to keep: ');
290
291
 
291
292
  if (answer.toLowerCase() === 'cancel') {
292
- console.log(' Operation cancelled');
293
+ console.log('❌ Operation cancelled');
293
294
  return false;
294
295
  }
295
296
 
296
297
  if (answer.trim() === '') {
297
- console.log(' Keeping all locales');
298
+ console.log('✅ Keeping all locales');
298
299
  return true;
299
300
  }
300
301
 
@@ -302,7 +303,7 @@ class LocaleOptimizer {
302
303
  const valid = selected.filter(l => available.includes(l));
303
304
 
304
305
  if (valid.length === 0) {
305
- console.log('⚠️ No valid locales selected, keeping all');
306
+ console.log('⚠️ No valid locales selected, keeping all');
306
307
  return true;
307
308
  }
308
309
 
@@ -310,16 +311,16 @@ class LocaleOptimizer {
310
311
  valid.push('en');
311
312
  const unique = [...new Set(valid)];
312
313
 
313
- console.log(`\n🎯 Selected: ${unique.join(', ').toUpperCase()}`);
314
+ console.log(`\n🎯 Selected: ${unique.join(', ').toUpperCase()}`);
314
315
  this.showImpact(unique);
315
316
 
316
317
  const confirm = await cliHelper.prompt('\nProceed? (y/N): ');
317
318
  if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
318
319
  this.keepLocales(unique.join(','));
319
- console.log('\n🎉 Package optimized successfully!');
320
- console.log('💡 Use --restore to bring back removed locales');
320
+ console.log('\n🎉 Package optimized successfully!');
321
+ console.log('💡 Use --restore to bring back removed locales');
321
322
  } else {
322
- console.log(' Operation cancelled');
323
+ console.log('❌ Operation cancelled');
323
324
  }
324
325
  return true;
325
326
  }
@@ -336,7 +337,7 @@ class LocaleOptimizer {
336
337
  const saved = allSize - selectedSize;
337
338
  const percentage = ((saved / allSize) * 100).toFixed(1);
338
339
 
339
- console.log(`\n📈 Impact Analysis:`);
340
+ console.log(`\n📈 Impact Analysis:`);
340
341
  console.log(` Current: ${allSize.toFixed(1)}KB`);
341
342
  console.log(` New: ${selectedSize.toFixed(1)}KB`);
342
343
  console.log(` Saved: ${saved.toFixed(1)}KB (${percentage}%)`);
@@ -536,8 +537,8 @@ async function main() {
536
537
 
537
538
  // Handle --init
538
539
  if (args.includes('--init')) {
539
- console.log('\n📦 Package Size Optimization');
540
- console.log(''.repeat(30));
540
+ console.log('\n📦 Package Size Optimization');
541
+ console.log('═'.repeat(30));
541
542
  optimizer.listLocales();
542
543
 
543
544
  const answer = await cliHelper.prompt('\nOptimize package size now? (Y/n): ');
@@ -552,20 +553,20 @@ async function main() {
552
553
  }
553
554
 
554
555
  if (!options.interactive && !options.list && !options.keep && !options.restore && !options.dryRun && !args.includes('--init')) {
555
- console.log('🌍 i18ntk Locale Optimizer v2.0');
556
- console.log(''.repeat(35));
556
+ console.log('🌍 i18ntk Locale Optimizer v2.0');
557
+ console.log('═'.repeat(35));
557
558
  console.log('Interactive package size optimization for UI locales');
558
559
  console.log('');
559
560
  console.log('Usage:');
560
- console.log(' node scripts/locale-optimizer.js --interactive 🎯 Interactive selection');
561
- console.log(' node scripts/locale-optimizer.js --list 📊 List all locales');
562
- console.log(' node scripts/locale-optimizer.js --keep en,es,de Quick keep');
563
- console.log(' node scripts/locale-optimizer.js --restore 🔄 Restore all locales');
564
- console.log(' node scripts/locale-optimizer.js --init 🚀 Called during init');
565
- console.log(' node scripts/locale-optimizer.js --dry-run 🔍 Simulation mode');
561
+ console.log(' node utils/locale-optimizer.js --interactive 🎯 Interactive selection');
562
+ console.log(' node utils/locale-optimizer.js --list 📊 List all locales');
563
+ console.log(' node utils/locale-optimizer.js --keep en,es,de âš¡ Quick keep');
564
+ console.log(' node utils/locale-optimizer.js --restore 🔄 Restore all locales');
565
+ console.log(' node utils/locale-optimizer.js --init 🚀 Called during init');
566
+ console.log(' node utils/locale-optimizer.js --dry-run 🔍 Simulation mode');
566
567
  console.log('');
567
- console.log('💡 Example: Keep only English and Spanish');
568
- console.log(' node scripts/locale-optimizer.js --keep en,es');
568
+ console.log('💡 Example: Keep only English and Spanish');
569
+ console.log(' node utils/locale-optimizer.js --keep en,es');
569
570
  return;
570
571
  }
571
572
 
@@ -581,4 +582,4 @@ if (require.main === module) {
581
582
  main();
582
583
  }
583
584
 
584
- module.exports = LocaleOptimizer;
585
+ module.exports = LocaleOptimizer;
@@ -125,14 +125,12 @@ class SecurityChecker {
125
125
  }
126
126
  }
127
127
 
128
- // Check for dangerous patterns (excluding the overly broad require pattern)
129
- const dangerousPatterns = [
130
- /fs\.readFileSync\s*\(/g,
131
- /fs\.writeFileSync\s*\(/g,
132
- /fs\.existsSync\s*\(/g,
133
- /eval\s*\(/g,
134
- /Function\s*\(/g
135
- ];
128
+ // Check for dangerous code execution patterns in SecurityUtils itself.
129
+ // Direct fs usage is expected here because this module provides vetted wrappers.
130
+ const dangerousPatterns = [
131
+ /eval\s*\(/g,
132
+ /Function\s*\(/g
133
+ ];
136
134
 
137
135
  for (const pattern of dangerousPatterns) {
138
136
  const matches = content.match(pattern);
@@ -251,11 +249,16 @@ class SecurityChecker {
251
249
  // No action needed for legitimate package requires
252
250
  }
253
251
 
254
- async checkFilePermissions() {
255
- this.log('Checking file permissions...');
256
-
257
- const criticalFiles = [
258
- 'utils/security.js',
252
+ async checkFilePermissions() {
253
+ this.log('Checking file permissions...');
254
+
255
+ // POSIX permission checks are noisy/non-actionable on Windows.
256
+ if (process.platform === 'win32') {
257
+ return;
258
+ }
259
+
260
+ const criticalFiles = [
261
+ 'utils/security.js',
259
262
  'tests/security.test.js',
260
263
  'package.json'
261
264
  ];
package/utils/security.js CHANGED
@@ -531,10 +531,12 @@ class SecurityUtils {
531
531
  'theme', 'ui', 'setup', 'reports', 'display', 'interface',
532
532
  // Security and settings
533
533
  'security', 'settings', 'preferences', 'config', 'configuration',
534
- // Additional common properties
535
- 'autoSave', 'autoBackup', 'validateOnSave', 'showWarnings', 'verbose',
536
- 'timeout', 'retries', 'batchSize', 'maxConcurrency', 'cacheEnabled'
537
- ]);
534
+ // Additional common properties
535
+ 'autoSave', 'autoBackup', 'validateOnSave', 'showWarnings', 'verbose',
536
+ 'timeout', 'retries', 'batchSize', 'maxConcurrency', 'cacheEnabled',
537
+ // Date and reporting options used by existing settings
538
+ 'dateFormat', 'timeFormat', 'timezone', 'reportLanguage', 'dateTime'
539
+ ]);
538
540
 
539
541
  // Remove unknown properties
540
542
  Object.keys(sanitized).forEach(key => {
package/main/i18ntk-go.js DELETED
@@ -1,283 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * i18ntk-go.js - Go Language I18n Management Command
5
- *
6
- * Supports:
7
- * - Standard Go i18n patterns
8
- * - go-i18n library
9
- * - Custom Go i18n implementations
10
- * - Resource file (.json, .toml) management
11
- */
12
-
13
- const fs = require('fs');
14
- const path = require('path');
15
- const SecurityUtils = require(path.join(__dirname, '../utils/security'));
16
- const { getConfig, saveConfig } = require(path.join(__dirname, '../utils/config-helper'));
17
- const I18nHelper = require(path.join(__dirname, '../utils/i18n-helper'));
18
- const SetupEnforcer = require(path.join(__dirname, '../utils/setup-enforcer'));
19
- const { program } = require('../utils/mini-commander');
20
-
21
- (async () => {
22
- try {
23
- await SetupEnforcer.checkSetupCompleteAsync();
24
- } catch (error) {
25
- console.error('Setup check failed:', error.message);
26
- process.exit(1);
27
- }
28
- })();
29
-
30
- class GoI18nManager {
31
- constructor() {
32
- this.supportedPatterns = [
33
- 'T("key")',
34
- 'Localize("key")',
35
- 'i18n.T("key")',
36
- 'tr("key")',
37
- 'message.Printer.Printf',
38
- 'i18n.MustLocalize'
39
- ];
40
-
41
- this.fileExtensions = ['.go', '.mod', '.sum'];
42
- this.resourceFormats = ['.json', '.toml', '.yaml', '.yml'];
43
- }
44
-
45
- async detectFramework(sourceDir) {
46
- const goModPath = path.join(sourceDir, 'go.mod');
47
- if (SecurityUtils.safeExistsSync(goModPath)) {
48
- const content = SecurityUtils.safeReadFileSync(goModPath, sourceDir, 'utf8') || '';
49
-
50
- if (content.includes('go-i18n')) return 'go-i18n-v2';
51
- if (/x\/text\b/.test(content)) return 'golang-text';
52
-
53
- return 'standard-go';
54
- }
55
-
56
- // Check for Go files
57
- const goFiles = this.findFiles(sourceDir, '.go');
58
- if (goFiles.length > 0) {
59
- return 'standard-go';
60
- }
61
-
62
- return 'generic';
63
- }
64
-
65
- async extractTranslations(sourceDir, options = {}) {
66
- const framework = await this.detectFramework(sourceDir);
67
- const translations = new Set();
68
-
69
- const goFiles = this.findFiles(sourceDir, '.go');
70
-
71
- for (const file of goFiles) {
72
- const content = SecurityUtils.safeReadFileSync(file, path.dirname(file), 'utf8') || '';
73
-
74
- // Extract Go i18n patterns
75
- const patterns = [
76
- /T\("([^"]+)"\)/g,
77
- /Localize\("([^"]+)"\)/g,
78
- /i18n\.T\("([^"]+)"\)/g,
79
- /tr\("([^"]+)"\)/g,
80
- /message\.Printer\.Printf\("([^"]+)"/g,
81
- /i18n\.MustLocalize\(&i18n\.LocalizeConfig\{MessageID:\s*"([^"]+)"/g
82
- ];
83
-
84
- for (const pattern of patterns) {
85
- let match;
86
- while ((match = pattern.exec(content)) !== null) {
87
- translations.add(match[1]);
88
- }
89
- }
90
- }
91
-
92
- return {
93
- framework,
94
- translations: Array.from(translations),
95
- files: goFiles.length,
96
- patterns: this.supportedPatterns
97
- };
98
- }
99
-
100
- async createLocaleStructure(outputDir, languages = ['en']) {
101
- const localesDir = path.join(outputDir, 'locales');
102
-
103
- for (const lang of languages) {
104
- const langDir = path.join(localesDir, lang);
105
- fs.mkdirSync(langDir, { recursive: true });
106
-
107
- // Create Go i18n format files
108
- SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.toml'), `# Go i18n translations for ${lang}
109
- [hello]
110
- other = "Hello, World!"
111
-
112
- [items]
113
- one = "{{.Count}} item"
114
- other = "{{.Count}} items"
115
- `);
116
-
117
- SecurityUtils.safeWriteFileSync(path.join(langDir, 'active.en.json'), JSON.stringify({
118
- hello: "Hello, World!",
119
- items: {
120
- one: "{{.Count}} item",
121
- other: "{{.Count}} items"
122
- }
123
- }, null, 2));
124
- }
125
-
126
- return localesDir;
127
- }
128
-
129
- findFiles(dir, extension) {
130
- const files = [];
131
-
132
- function traverse(currentDir) {
133
- const items = fs.readdirSync(currentDir);
134
-
135
- for (const item of items) {
136
- const fullPath = path.join(currentDir, item);
137
- const stat = fs.statSync(fullPath);
138
-
139
- if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
140
- traverse(fullPath);
141
- } else if (stat.isFile() && path.extname(item) === extension) {
142
- files.push(fullPath);
143
- }
144
- }
145
- }
146
-
147
- traverse(dir);
148
- return files;
149
- }
150
-
151
- async generateReport(results, outputDir) {
152
- const report = {
153
- timestamp: new Date().toISOString(),
154
- framework: results.framework,
155
- totalTranslations: results.translations.length,
156
- translations: results.translations,
157
- filesProcessed: results.files,
158
- patterns: results.patterns,
159
- recommendations: this.getRecommendations(results)
160
- };
161
-
162
- const reportPath = path.join(outputDir, 'i18ntk-go-report.json');
163
- SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
164
-
165
- return reportPath;
166
- }
167
-
168
- getRecommendations(results) {
169
- const recommendations = [];
170
-
171
- if (results.translations.length === 0) {
172
- recommendations.push('No translations found. Check your Go i18n patterns');
173
- }
174
-
175
- if (results.files === 0) {
176
- recommendations.push('No Go files found in source directory');
177
- }
178
-
179
- if (results.framework === 'generic') {
180
- recommendations.push('Consider using go-i18n library for better i18n support');
181
- }
182
-
183
- return recommendations;
184
- }
185
- }
186
-
187
- // CLI Implementation
188
- program
189
- .name('i18ntk-go')
190
- .description('Go language i18n management tool')
191
- .version('1.10.1');
192
-
193
- program
194
- .command('init')
195
- .description('Initialize Go i18n structure')
196
- .option('-s, --source-dir <dir>', 'Source directory', './')
197
- .option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
198
- .option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
199
- .action(async (options) => {
200
- try {
201
- const manager = new GoI18nManager();
202
- const languages = options.languages.split(',');
203
-
204
- const localesDir = await manager.createLocaleStructure(options.outputDir, languages);
205
-
206
- console.log(`✅ Go i18n structure initialized in: ${localesDir}`);
207
- console.log(`📊 Languages: ${languages.join(', ')}`);
208
-
209
- } catch (error) {
210
- console.error('❌ Error initializing Go i18n:', error.message);
211
- process.exit(1);
212
- }
213
- });
214
-
215
- program
216
- .command('analyze')
217
- .description('Analyze Go i18n usage')
218
- .option('-s, --source-dir <dir>', 'Source directory', './')
219
- .option('-o, --output-dir <dir>', 'Output directory', './i18ntk-reports')
220
- .option('--dry-run', 'Preview without making changes')
221
- .action(async (options) => {
222
- try {
223
- SecurityUtils.validatePath(options.sourceDir);
224
-
225
- const manager = new GoI18nManager();
226
- const results = await manager.extractTranslations(options.sourceDir);
227
-
228
- console.log(`🔍 Framework detected: ${results.framework}`);
229
- console.log(`📊 Files processed: ${results.files}`);
230
- console.log(`📝 Translations found: ${results.translations.length}`);
231
-
232
- if (!options.dryRun) {
233
- const reportPath = await manager.generateReport(results, options.outputDir);
234
- console.log(`📄 Report saved: ${reportPath}`);
235
- }
236
-
237
- } catch (error) {
238
- console.error('❌ Error analyzing Go i18n:', error.message);
239
- process.exit(1);
240
- }
241
- });
242
-
243
- program
244
- .command('extract')
245
- .description('Extract Go translations')
246
- .option('-s, --source-dir <dir>', 'Source directory', './')
247
- .option('-o, --output-dir <dir>', 'Output directory', './locales')
248
- .option('-l, --languages <langs>', 'Languages (comma-separated)', 'en')
249
- .action(async (options) => {
250
- try {
251
- SecurityUtils.validatePath(options.sourceDir);
252
-
253
- const manager = new GoI18nManager();
254
- const results = await manager.extractTranslations(options.sourceDir);
255
-
256
- await manager.createLocaleStructure(options.outputDir, options.languages.split(','));
257
-
258
- console.log(`✅ Extracted ${results.translations.length} translations`);
259
- console.log(`📁 Locale structure created in: ${options.outputDir}`);
260
-
261
- } catch (error) {
262
- console.error('❌ Error extracting Go translations:', error.message);
263
- process.exit(1);
264
- }
265
- });
266
-
267
- // Handle uncaught errors
268
- process.on('uncaughtException', (error) => {
269
- console.error('❌ Uncaught exception:', error.message);
270
- process.exit(1);
271
- });
272
-
273
- process.on('unhandledRejection', (reason) => {
274
- console.error('❌ Unhandled rejection:', reason);
275
- process.exit(1);
276
- });
277
-
278
- // Export for programmatic use
279
- module.exports = { GoI18nManager };
280
-
281
- if (require.main === module) {
282
- program.parse();
283
- }