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
@@ -0,0 +1,462 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * I18NTK SETUP SERVICE
5
+ *
6
+ * Core business logic for i18n project setup and configuration.
7
+ * Handles environment detection, framework configuration, and prerequisites validation.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const SecurityUtils = require('../../../utils/security');
13
+ const configManager = require('../../../utils/config-manager');
14
+
15
+ class SetupService {
16
+ constructor() {
17
+ this.config = {
18
+ detectedLanguage: null,
19
+ detectedFramework: null,
20
+ sourceDir: './locales',
21
+ outputDir: './i18ntk-reports',
22
+ frameworkConfig: {},
23
+ prerequisites: {},
24
+ optimization: {
25
+ mode: 'auto',
26
+ cacheEnabled: true,
27
+ batchSize: 1000
28
+ }
29
+ };
30
+ this.supportedLanguages = ['javascript', 'typescript', 'python', 'java', 'go', 'php'];
31
+ this.supportedFrameworks = {
32
+ javascript: ['react', 'vue', 'angular', 'nextjs', 'nuxt', 'svelte'],
33
+ typescript: ['react', 'vue', 'angular', 'nextjs', 'nuxt'],
34
+ python: ['django', 'flask', 'fastapi'],
35
+ java: ['spring', 'spring-boot', 'quarkus'],
36
+ go: ['gin', 'echo', 'fiber'],
37
+ php: ['laravel', 'symfony', 'wordpress']
38
+ };
39
+ }
40
+
41
+ async setup() {
42
+ console.log('🔧 i18n Toolkit - Foundational Setup');
43
+ console.log('=====================================');
44
+
45
+ try {
46
+ await this.detectEnvironment();
47
+ await this.configureFramework();
48
+ await this.validatePrerequisites();
49
+ await this.optimizeForLanguage();
50
+ await this.generateSetupReport();
51
+
52
+ console.log('✅ Setup completed successfully!');
53
+ return this.config;
54
+ } catch (error) {
55
+ console.error('❌ Setup failed:', error.message);
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ async detectEnvironment() {
61
+ console.log('📍 Detecting environment...');
62
+
63
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
64
+ const pyprojectPath = path.join(process.cwd(), 'pyproject.toml');
65
+ const requirementsPath = path.join(process.cwd(), 'requirements.txt');
66
+ const goModPath = path.join(process.cwd(), 'go.mod');
67
+ const pomPath = path.join(process.cwd(), 'pom.xml');
68
+ const composerPath = path.join(process.cwd(), 'composer.json');
69
+
70
+ if (SecurityUtils.safeExistsSync(packageJsonPath)) {
71
+ this.config.detectedLanguage = 'javascript';
72
+ await this.detectNodeFramework(packageJsonPath);
73
+ } else if (SecurityUtils.safeExistsSync(pyprojectPath) || SecurityUtils.safeExistsSync(requirementsPath)) {
74
+ this.config.detectedLanguage = 'python';
75
+ await this.detectPythonFramework();
76
+ } else if (SecurityUtils.safeExistsSync(goModPath)) {
77
+ this.config.detectedLanguage = 'go';
78
+ this.config.detectedFramework = 'generic';
79
+ } else if (SecurityUtils.safeExistsSync(pomPath)) {
80
+ this.config.detectedLanguage = 'java';
81
+ await this.detectJavaFramework(pomPath);
82
+ } else if (SecurityUtils.safeExistsSync(composerPath)) {
83
+ this.config.detectedLanguage = 'php';
84
+ await this.detectPhpFramework(composerPath);
85
+ } else {
86
+ this.config.detectedLanguage = 'generic';
87
+ this.config.detectedFramework = 'generic';
88
+ }
89
+
90
+ console.log(` Language: ${this.config.detectedLanguage}`);
91
+ console.log(` Framework: ${this.config.detectedFramework}`);
92
+ }
93
+
94
+ async detectNodeFramework(packageJsonPath) {
95
+ try {
96
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
97
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
98
+
99
+ if (deps.react || deps['react-dom']) this.config.detectedFramework = 'react';
100
+ else if (deps.vue || deps['vue-router']) this.config.detectedFramework = 'vue';
101
+ else if (deps['@angular/core']) this.config.detectedFramework = 'angular';
102
+ else if (deps.next) this.config.detectedFramework = 'nextjs';
103
+ else if (deps.nuxt) this.config.detectedFramework = 'nuxt';
104
+ else if (deps.svelte) this.config.detectedFramework = 'svelte';
105
+ else this.config.detectedFramework = 'generic';
106
+ } catch (error) {
107
+ this.config.detectedFramework = 'generic';
108
+ }
109
+ }
110
+
111
+ async detectPythonFramework() {
112
+ try {
113
+ const requirementsPath = path.join(process.cwd(), 'requirements.txt');
114
+ if (SecurityUtils.safeExistsSync(requirementsPath)) {
115
+ const requirements = SecurityUtils.safeReadFileSync(requirementsPath, path.dirname(requirementsPath), 'utf8');
116
+ if (requirements.includes('django')) this.config.detectedFramework = 'django';
117
+ else if (requirements.includes('flask')) this.config.detectedFramework = 'flask';
118
+ else if (requirements.includes('fastapi')) this.config.detectedFramework = 'fastapi';
119
+ else this.config.detectedFramework = 'generic';
120
+ } else {
121
+ this.config.detectedFramework = 'generic';
122
+ }
123
+ } catch (error) {
124
+ this.config.detectedFramework = 'generic';
125
+ }
126
+ }
127
+
128
+ async detectJavaFramework(pomPath) {
129
+ try {
130
+ const pomContent = SecurityUtils.safeReadFileSync(pomPath, path.dirname(pomPath), 'utf8');
131
+ if (pomContent.includes('spring-boot')) this.config.detectedFramework = 'spring-boot';
132
+ else if (pomContent.includes('spring')) this.config.detectedFramework = 'spring';
133
+ else if (pomContent.includes('quarkus')) this.config.detectedFramework = 'quarkus';
134
+ else this.config.detectedFramework = 'generic';
135
+ } catch (error) {
136
+ this.config.detectedFramework = 'generic';
137
+ }
138
+ }
139
+
140
+ async detectPhpFramework(composerPath) {
141
+ try {
142
+ const composer = JSON.parse(SecurityUtils.safeReadFileSync(composerPath, path.dirname(composerPath), 'utf8'));
143
+ const deps = composer.require || {};
144
+
145
+ if (deps['laravel/framework']) this.config.detectedFramework = 'laravel';
146
+ else if (deps['symfony/framework-bundle']) this.config.detectedFramework = 'symfony';
147
+ else if (deps['wordpress']) this.config.detectedFramework = 'wordpress';
148
+ else this.config.detectedFramework = 'generic';
149
+ } catch (error) {
150
+ this.config.detectedFramework = 'generic';
151
+ }
152
+ }
153
+
154
+ async configureFramework() {
155
+ console.log('⚙️ Configuring framework...');
156
+
157
+ const frameworkConfigs = {
158
+ javascript: {
159
+ sourcePatterns: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
160
+ i18nLibraries: ['i18next', 'react-i18next', 'vue-i18n', '@angular/localize'],
161
+ defaultLocalePath: './src/locales',
162
+ extractPatterns: [
163
+ /t\(['"`]([^'"`]+)['"`]/g,
164
+ /i18n\.t\(['"`]([^'"`]+)['"`]/g,
165
+ /\$t\(['"`]([^'"`]+)['"`]/g
166
+ ]
167
+ },
168
+ python: {
169
+ sourcePatterns: ['**/*.py'],
170
+ i18nLibraries: ['django', 'flask-babel', 'babel'],
171
+ defaultLocalePath: './locale',
172
+ extractPatterns: [
173
+ /_\(['"`]([^'"`]+)['"`]/g,
174
+ /gettext\(['"`]([^'"`]+)['"`]/g
175
+ ]
176
+ },
177
+ go: {
178
+ sourcePatterns: ['**/*.go'],
179
+ i18nLibraries: ['go-i18n', 'nicksnyder/go-i18n'],
180
+ defaultLocalePath: './locales',
181
+ extractPatterns: [
182
+ /Localize\([^)]*MessageID:\s*['"`]([^'"`]+)['"`]/g
183
+ ]
184
+ },
185
+ java: {
186
+ sourcePatterns: ['**/*.java'],
187
+ i18nLibraries: ['spring-boot-starter-web', 'spring-context'],
188
+ defaultLocalePath: './src/main/resources/messages',
189
+ extractPatterns: [
190
+ /getMessage\(['"`]([^'"`]+)['"`]/g,
191
+ /@Value\(['"`]([^'"`]+)['"`]/g
192
+ ]
193
+ },
194
+ php: {
195
+ sourcePatterns: ['**/*.php'],
196
+ i18nLibraries: ['gettext', 'symfony/translation'],
197
+ defaultLocalePath: './resources/lang',
198
+ extractPatterns: [
199
+ /_\(['"`]([^'"`]+)['"`]/g,
200
+ /trans\(['"`]([^'"`]+)['"`]/g
201
+ ]
202
+ }
203
+ };
204
+
205
+ this.config.frameworkConfig = frameworkConfigs[this.config.detectedLanguage] || frameworkConfigs.javascript;
206
+
207
+ // Auto-detect source directory
208
+ const possiblePaths = [
209
+ this.config.frameworkConfig.defaultLocalePath,
210
+ './locales',
211
+ './i18n',
212
+ './translations',
213
+ './src/i18n',
214
+ './app/i18n'
215
+ ];
216
+
217
+ for (const dirPath of possiblePaths) {
218
+ if (SecurityUtils.safeExistsSync(dirPath)) {
219
+ this.config.sourceDir = dirPath;
220
+ break;
221
+ }
222
+ }
223
+
224
+ console.log(` Source directory: ${this.config.sourceDir}`);
225
+ }
226
+
227
+ async validatePrerequisites() {
228
+ console.log('🔍 Validating prerequisites...');
229
+
230
+ this.config.prerequisites = {
231
+ nodeVersion: process.version,
232
+ nodeVersionValid: parseInt(process.version.slice(1).split('.')[0]) >= 16,
233
+ hasPackageJson: SecurityUtils.safeExistsSync('package.json'),
234
+ hasLocales: SecurityUtils.safeExistsSync(this.config.sourceDir),
235
+ hasGit: this.checkCommand('git'),
236
+ hasNpm: this.checkCommand('npm'),
237
+ hasPython: this.checkCommand('python3') || this.checkCommand('python'),
238
+ hasJava: this.checkCommand('java'),
239
+ hasGo: this.checkCommand('go'),
240
+ hasPhp: this.checkCommand('php')
241
+ };
242
+
243
+ // Check for i18n libraries
244
+ if (this.config.detectedLanguage === 'javascript') {
245
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
246
+ if (SecurityUtils.safeExistsSync(packageJsonPath)) {
247
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
248
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
249
+
250
+ this.config.prerequisites.hasI18nLibrary = Object.keys(deps).some(dep =>
251
+ this.config.frameworkConfig.i18nLibraries.some(lib => dep.includes(lib))
252
+ );
253
+ }
254
+ }
255
+
256
+ Object.entries(this.config.prerequisites).forEach(([key, value]) => {
257
+ if (typeof value === 'boolean') {
258
+ console.log(` ${key}: ${value ? '✅' : '❌'}`);
259
+ } else {
260
+ console.log(` ${key}: ${value}`);
261
+ }
262
+ });
263
+ }
264
+
265
+ checkCommand(command) {
266
+ // Secure command checking without child_process
267
+ const extensions = process.platform === 'win32' ? ['.exe', '.cmd', '.bat'] : [''];
268
+ const pathEnv = process.env.PATH || process.env.Path || '';
269
+ const pathDirs = pathEnv.split(process.platform === 'win32' ? ';' : ':');
270
+
271
+ for (const dir of pathDirs) {
272
+ for (const ext of extensions) {
273
+ const fullPath = path.join(dir, command + ext);
274
+ try {
275
+ // Use direct fs.accessSync for system executables to avoid path validation warnings
276
+ // since these paths are intentionally outside the project directory
277
+ fs.accessSync(fullPath, fs.constants.F_OK);
278
+ if (fs.statSync(fullPath).isFile()) {
279
+ return true;
280
+ }
281
+ } catch {
282
+ // Ignore errors accessing files
283
+ }
284
+ }
285
+ }
286
+ return false;
287
+ }
288
+
289
+ async optimizeForLanguage() {
290
+ console.log('🚀 Optimizing for language...');
291
+
292
+ const optimizationStrategies = {
293
+ javascript: {
294
+ mode: 'extreme',
295
+ cacheEnabled: true,
296
+ batchSize: 1000,
297
+ parallelProcessing: true,
298
+ treeShaking: true
299
+ },
300
+ python: {
301
+ mode: 'ultra',
302
+ cacheEnabled: true,
303
+ batchSize: 500,
304
+ asyncProcessing: true,
305
+ lazyLoading: true
306
+ },
307
+ go: {
308
+ mode: 'extreme',
309
+ cacheEnabled: true,
310
+ batchSize: 2000,
311
+ concurrentProcessing: true,
312
+ memoryOptimization: true
313
+ },
314
+ java: {
315
+ mode: 'ultra',
316
+ cacheEnabled: true,
317
+ batchSize: 800,
318
+ jvmOptimization: true,
319
+ connectionPooling: true
320
+ },
321
+ php: {
322
+ mode: 'optimized',
323
+ cacheEnabled: true,
324
+ batchSize: 300,
325
+ opcacheEnabled: true,
326
+ memoryLimit: '256M'
327
+ }
328
+ };
329
+
330
+ this.config.optimization = {
331
+ ...this.config.optimization,
332
+ ...optimizationStrategies[this.config.detectedLanguage]
333
+ };
334
+
335
+ // Update configuration using SettingsManager
336
+ const configManager = require('../../../utils/config-manager');
337
+
338
+ // Get package version safely
339
+ let packageVersion = '1.10.2'; // fallback version
340
+ try {
341
+ const packageJsonPath = path.join(__dirname, '..', '..', '..', 'package.json');
342
+ if (SecurityUtils.safeExistsSync(packageJsonPath)) {
343
+ const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
344
+ packageVersion = packageJson.version || '1.10.2';
345
+ }
346
+ } catch (error) {
347
+ console.warn('Could not read package.json version, using fallback:', error.message);
348
+ }
349
+
350
+ const setupId = `setup_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
351
+
352
+ console.log('🔧 About to update settings with:', {
353
+ setup: {
354
+ completed: true,
355
+ completedAt: new Date().toISOString(),
356
+ version: packageVersion,
357
+ setupId: setupId
358
+ }
359
+ });
360
+
361
+ try {
362
+ // Load current config and update it
363
+ const currentConfig = configManager.loadConfig() || configManager.DEFAULT_CONFIG;
364
+
365
+ // Update the configuration with setup data
366
+ const updatedConfig = {
367
+ ...currentConfig,
368
+ version: packageVersion,
369
+ sourceDir: this.config.sourceDir,
370
+ outputDir: this.config.outputDir,
371
+ framework: {
372
+ ...currentConfig.framework,
373
+ detected: true,
374
+ preference: this.config.detectedFramework
375
+ },
376
+ processing: {
377
+ ...currentConfig.processing,
378
+ ...this.config.optimization
379
+ },
380
+ security: {
381
+ ...currentConfig.security,
382
+ adminPinEnabled: false,
383
+ sessionTimeout: 1800000,
384
+ maxFailedAttempts: 3
385
+ },
386
+ setup: {
387
+ completed: true,
388
+ completedAt: new Date().toISOString(),
389
+ version: packageVersion,
390
+ setupId: setupId
391
+ }
392
+ };
393
+
394
+ // Save the updated configuration
395
+ await configManager.saveConfig(updatedConfig);
396
+ console.log(` Configuration updated in .i18ntk-settings`);
397
+ } catch (error) {
398
+ console.error('❌ Error updating settings:', error.message);
399
+ throw error;
400
+ }
401
+ }
402
+
403
+ async generateSetupReport() {
404
+ console.log('📊 Generating setup report...');
405
+
406
+ const report = {
407
+ timestamp: new Date().toISOString(),
408
+ setup: {
409
+ language: this.config.detectedLanguage,
410
+ framework: this.config.detectedFramework,
411
+ sourceDirectory: this.config.sourceDir,
412
+ optimizationMode: this.config.optimization.mode,
413
+ prerequisitesMet: Object.values(this.config.prerequisites).filter(v => v === true).length,
414
+ totalPrerequisites: Object.values(this.config.prerequisites).filter(v => typeof v === 'boolean').length
415
+ },
416
+ recommendations: this.generateRecommendations(),
417
+ nextSteps: [
418
+ 'Run i18ntk-init to initialize your project',
419
+ 'Run i18ntk-analyze to scan for translations',
420
+ 'Run i18ntk-validate to validate your setup'
421
+ ]
422
+ };
423
+
424
+ // Also save a local copy for user reference
425
+ const reportPath = path.join(process.cwd(), 'i18ntk-setup-report.json');
426
+ console.log(` Configuration saved to .i18ntk-config`);
427
+ SecurityUtils.safeWriteFileSync(reportPath, JSON.stringify(report, null, 2));
428
+ console.log(` Setup report saved: ${reportPath}`);
429
+ }
430
+
431
+ generateRecommendations() {
432
+ const recommendations = [];
433
+
434
+ if (!this.config.prerequisites.hasLocales) {
435
+ recommendations.push({
436
+ type: 'warning',
437
+ message: 'No locale directory found. Run i18ntk-init to create one.',
438
+ action: 'i18ntk-init'
439
+ });
440
+ }
441
+
442
+ if (this.config.detectedLanguage === 'javascript' && !this.config.prerequisites.hasI18nLibrary) {
443
+ recommendations.push({
444
+ type: 'info',
445
+ message: 'Consider installing an i18n library for better integration',
446
+ action: 'npm install i18next'
447
+ });
448
+ }
449
+
450
+ if (!this.config.prerequisites.nodeVersionValid) {
451
+ recommendations.push({
452
+ type: 'error',
453
+ message: 'Node.js version 16+ required',
454
+ action: 'Upgrade Node.js'
455
+ });
456
+ }
457
+
458
+ return recommendations;
459
+ }
460
+ }
461
+
462
+ module.exports = SetupService;