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