i18ntk 2.1.0 → 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.
- package/README.md +10 -6
- package/main/i18ntk-analyze.js +63 -63
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +5 -5
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +35 -35
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +95 -96
- package/main/i18ntk-usage.js +14 -14
- package/main/i18ntk-validate.js +6 -5
- package/main/manage/commands/AnalyzeCommand.js +67 -67
- package/main/manage/commands/FixerCommand.js +2 -2
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +9 -9
- package/main/manage/index.js +7 -4
- package/main/manage/managers/LanguageMenu.js +7 -2
- package/main/manage/services/UsageService.js +7 -7
- package/package.json +20 -41
- package/settings/settings-cli.js +3 -3
- package/ui-locales/de.json +12 -11
- package/ui-locales/en.json +12 -11
- package/ui-locales/es.json +12 -11
- package/ui-locales/fr.json +12 -11
- package/ui-locales/ja.json +12 -11
- package/ui-locales/ru.json +12 -11
- package/ui-locales/zh.json +12 -11
- package/utils/i18n-helper.js +161 -166
- package/{scripts → utils}/locale-optimizer.js +61 -60
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -236
- package/scripts/fix-and-purify-i18n.js +0 -233
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish-dev.js +0 -221
- package/scripts/prepublish.js +0 -362
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -195
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -609
|
@@ -1,1447 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* I18NTK MANAGEMENT TOOLKIT - MAIN MANAGER
|
|
3
|
-
*
|
|
4
|
-
* This is the main entry point for all i18n operations.
|
|
5
|
-
* It provides an interactive interface to manage translations.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* npm run i18ntk:manage
|
|
9
|
-
* npm run i18ntk:manage -- --command=init
|
|
10
|
-
* npm run i18ntk:manage -- --command=analyze
|
|
11
|
-
* npm run i18ntk:manage -- --command=validate
|
|
12
|
-
* npm run i18ntk:manage -- --help
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const path = require('path');
|
|
16
|
-
const AdminAuth = require('../../utils/admin-auth');
|
|
17
|
-
const SecurityUtils = require('../../utils/security');
|
|
18
|
-
const AdminCLI = require('../../utils/admin-cli');
|
|
19
|
-
const configManager = require('../../settings/settings-manager');
|
|
20
|
-
const { showFrameworkWarningOnce } = require('../../utils/cli-helper');
|
|
21
|
-
const { createPrompt, isInteractive } = require('../../utils/prompt-helper');
|
|
22
|
-
const { loadTranslations, t, refreshLanguageFromSettings} = require('../../utils/i18n-helper');
|
|
23
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
24
|
-
const { loadConfig, saveConfig, ensureConfigDefaults } = require('../../utils/config');
|
|
25
|
-
const pkg = require('../../package.json');
|
|
26
|
-
const SetupEnforcer = require('../../utils/setup-enforcer');
|
|
27
|
-
const CommandRouter = require('./commands/CommandRouter');
|
|
28
|
-
|
|
29
|
-
// Import services to replace circular dependencies
|
|
30
|
-
const ConfigurationService = require('./services/ConfigurationService');
|
|
31
|
-
const SummaryService = require('./services/SummaryService');
|
|
32
|
-
|
|
33
|
-
// Preload translations early to avoid missing key warnings
|
|
34
|
-
loadTranslations();
|
|
35
|
-
|
|
36
|
-
class I18nManager {
|
|
37
|
-
constructor(config = {}) {
|
|
38
|
-
this.config = config;
|
|
39
|
-
this.rl = null;
|
|
40
|
-
this.isReadlineClosed = false;
|
|
41
|
-
this.isAuthenticated = false;
|
|
42
|
-
this.ui = null;
|
|
43
|
-
this.adminAuth = new AdminAuth();
|
|
44
|
-
this.commandRouter = null;
|
|
45
|
-
|
|
46
|
-
// Initialize services
|
|
47
|
-
this.configurationService = new ConfigurationService(config);
|
|
48
|
-
this.summaryService = new SummaryService(config);
|
|
49
|
-
|
|
50
|
-
// No longer create readline interface here - use CLI helpers
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
initializeReadline() {
|
|
54
|
-
// Use centralized CLI helper instead of direct readline
|
|
55
|
-
this.rl = null;
|
|
56
|
-
this.isReadlineClosed = false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Initialize configuration using unified system
|
|
60
|
-
async initialize() {
|
|
61
|
-
try {
|
|
62
|
-
// Parse args here for other initialization needs (but language is already loaded)
|
|
63
|
-
const args = this.parseArgs();
|
|
64
|
-
if (args.help) {
|
|
65
|
-
this.showHelp();
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Load translations for the UI language
|
|
70
|
-
const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
71
|
-
const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
|
|
72
|
-
loadTranslations(uiLanguage);
|
|
73
|
-
|
|
74
|
-
// Validate source directory exists
|
|
75
|
-
const {validateSourceDir, displayPaths} = require('../../utils/config-helper');
|
|
76
|
-
try {
|
|
77
|
-
validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
|
|
78
|
-
} catch (err) {
|
|
79
|
-
console.log(t('init.requiredTitle'));
|
|
80
|
-
console.log(t('init.requiredBody'));
|
|
81
|
-
const answer = await cliHelper.prompt(t('init.promptRunNow'));
|
|
82
|
-
if (answer.trim().toLowerCase() === 'y') {
|
|
83
|
-
// Note: Initialization should be handled by the calling code
|
|
84
|
-
// to avoid circular dependencies
|
|
85
|
-
console.log('Please run initialization manually or use the init command');
|
|
86
|
-
} else {
|
|
87
|
-
throw err;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
} catch (error) {
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Auto-detect i18n directory from common locations only if not configured in settings
|
|
97
|
-
detectI18nDirectory() {
|
|
98
|
-
const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
99
|
-
const projectRoot = path.resolve(settings.projectRoot || this.config.projectRoot || '.');
|
|
100
|
-
|
|
101
|
-
// Use per-script directory configuration if available, fallback to global sourceDir
|
|
102
|
-
const sourceDir = settings.scriptDirectories?.manage || settings.sourceDir;
|
|
103
|
-
|
|
104
|
-
if (sourceDir) {
|
|
105
|
-
this.config.sourceDir = path.resolve(projectRoot, sourceDir);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Define possible i18n paths for auto-detection
|
|
110
|
-
const possibleI18nPaths = [
|
|
111
|
-
'./locales',
|
|
112
|
-
'./src/locales',
|
|
113
|
-
'./src/i18n',
|
|
114
|
-
'./src/i18n/locales',
|
|
115
|
-
'./app/locales',
|
|
116
|
-
'./app/i18n',
|
|
117
|
-
'./public/locales',
|
|
118
|
-
'./assets/locales',
|
|
119
|
-
'./translations',
|
|
120
|
-
'./lang'
|
|
121
|
-
];
|
|
122
|
-
|
|
123
|
-
// Only auto-detect if no settings are configured
|
|
124
|
-
for (const possiblePath of possibleI18nPaths) {
|
|
125
|
-
const resolvedPath = path.resolve(projectRoot, possiblePath);
|
|
126
|
-
if (SecurityUtils.safeExistsSync(resolvedPath)) {
|
|
127
|
-
// Check if it contains language directories
|
|
128
|
-
try {
|
|
129
|
-
const items = require('fs').readdirSync(resolvedPath);
|
|
130
|
-
const hasLanguageDirs = items.some(item => {
|
|
131
|
-
const itemPath = path.join(resolvedPath, item);
|
|
132
|
-
return require('fs').statSync(itemPath).isDirectory() &&
|
|
133
|
-
['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(item);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
if (hasLanguageDirs) {
|
|
137
|
-
this.config.sourceDir = possiblePath;
|
|
138
|
-
t('init.autoDetectedI18nDirectory', { path: possiblePath });
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
} catch (error) {
|
|
142
|
-
// Continue checking other paths
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Check if i18n framework is installed - configuration-based check without prompts
|
|
149
|
-
async checkI18nDependencies() {
|
|
150
|
-
const packageJsonPath = path.resolve('./package.json');
|
|
151
|
-
|
|
152
|
-
if (!SecurityUtils.safeExistsSync(packageJsonPath)) {
|
|
153
|
-
console.log(this.ui ? this.ui.t('errors.noPackageJson') : 'No package.json found');
|
|
154
|
-
return false; // Treat as no framework detected
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
|
|
159
|
-
// Include peerDependencies in the check
|
|
160
|
-
const dependencies = {
|
|
161
|
-
...packageJson.dependencies,
|
|
162
|
-
...packageJson.devDependencies,
|
|
163
|
-
...packageJson.peerDependencies
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const i18nFrameworks = [
|
|
167
|
-
'react-i18next',
|
|
168
|
-
'vue-i18n',
|
|
169
|
-
'angular-i18n',
|
|
170
|
-
'i18next',
|
|
171
|
-
'next-i18next',
|
|
172
|
-
'svelte-i18n',
|
|
173
|
-
'@nuxtjs/i18n',
|
|
174
|
-
'i18ntk-runtime'
|
|
175
|
-
];
|
|
176
|
-
|
|
177
|
-
const installedFrameworks = i18nFrameworks.filter(framework => dependencies[framework]);
|
|
178
|
-
|
|
179
|
-
if (installedFrameworks.length > 0) {
|
|
180
|
-
if (this.ui && this.ui.t) {
|
|
181
|
-
console.log(this.ui.t('init.detectedFrameworks', { frameworks: installedFrameworks.join(', ') }));
|
|
182
|
-
} else {
|
|
183
|
-
console.log(`Detected frameworks: ${installedFrameworks.join(', ')}`);
|
|
184
|
-
}
|
|
185
|
-
const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
186
|
-
cfg.framework = cfg.framework || {};
|
|
187
|
-
cfg.framework.detected = true;
|
|
188
|
-
cfg.framework.installed = installedFrameworks;
|
|
189
|
-
if (configManager.saveSettings) {
|
|
190
|
-
configManager.saveSettings(cfg);
|
|
191
|
-
} else if (configManager.saveConfig) {
|
|
192
|
-
configManager.saveConfig(cfg);
|
|
193
|
-
}
|
|
194
|
-
return true;
|
|
195
|
-
} else {
|
|
196
|
-
const cfg = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
197
|
-
if (cfg.framework) {
|
|
198
|
-
cfg.framework.detected = false;
|
|
199
|
-
if (configManager.saveSettings) {
|
|
200
|
-
configManager.saveSettings(cfg);
|
|
201
|
-
} else if (configManager.saveConfig) {
|
|
202
|
-
configManager.saveConfig(cfg);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
// Always signal that frameworks were not detected
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
} catch (error) {
|
|
209
|
-
console.log(t('init.errors.packageJsonRead'));
|
|
210
|
-
return false; // Treat as no framework detected on error
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Parse command line arguments
|
|
215
|
-
parseArgs() {
|
|
216
|
-
const args = process.argv.slice(2);
|
|
217
|
-
const parsed = {};
|
|
218
|
-
|
|
219
|
-
args.forEach(arg => {
|
|
220
|
-
if (arg.startsWith('--')) {
|
|
221
|
-
const [key, value] = arg.substring(2).split('=');
|
|
222
|
-
const sanitizedKey = key?.trim();
|
|
223
|
-
const sanitizedValue = value !== undefined ? value.trim() : true;
|
|
224
|
-
|
|
225
|
-
switch (sanitizedKey) {
|
|
226
|
-
case 'source-dir':
|
|
227
|
-
parsed.sourceDir = sanitizedValue;
|
|
228
|
-
break;
|
|
229
|
-
case 'i18n-dir':
|
|
230
|
-
parsed.i18nDir = sanitizedValue;
|
|
231
|
-
break;
|
|
232
|
-
case 'output-dir':
|
|
233
|
-
parsed.outputDir = sanitizedValue;
|
|
234
|
-
break;
|
|
235
|
-
case 'source-language':
|
|
236
|
-
parsed.sourceLanguage = sanitizedValue;
|
|
237
|
-
break;
|
|
238
|
-
case 'ui-language':
|
|
239
|
-
parsed.uiLanguage = sanitizedValue;
|
|
240
|
-
break;
|
|
241
|
-
case 'no-prompt':
|
|
242
|
-
parsed.noPrompt = true;
|
|
243
|
-
break;
|
|
244
|
-
case 'admin-pin':
|
|
245
|
-
parsed.adminPin = sanitizedValue || '';
|
|
246
|
-
break;
|
|
247
|
-
case 'help':
|
|
248
|
-
case 'h':
|
|
249
|
-
parsed.help = true;
|
|
250
|
-
break;
|
|
251
|
-
default:
|
|
252
|
-
// Handle language shorthand flags like --de, --fr
|
|
253
|
-
if (['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(sanitizedKey)) {
|
|
254
|
-
parsed.uiLanguage = sanitizedKey;
|
|
255
|
-
}
|
|
256
|
-
break;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
return parsed;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Add this run method after the checkI18nDependencies method
|
|
265
|
-
async run() {
|
|
266
|
-
// Add timeout to prevent hanging
|
|
267
|
-
const args = this.parseArgs();
|
|
268
|
-
|
|
269
|
-
const timeout = setTimeout(() => {
|
|
270
|
-
console.error('❌ CLI startup timeout - something is hanging');
|
|
271
|
-
if (args.debug) {
|
|
272
|
-
console.error('🔍 DEBUG: Last known execution point reached');
|
|
273
|
-
}
|
|
274
|
-
process.exit(1);
|
|
275
|
-
}, 10000); // 10 second timeout
|
|
276
|
-
|
|
277
|
-
if (args.debug) {
|
|
278
|
-
console.log('🔍 DEBUG: Starting i18ntk-manage.js...');
|
|
279
|
-
console.log('🔍 DEBUG: Process.argv:', process.argv);
|
|
280
|
-
console.log('🔍 DEBUG: Parsed args:', args);
|
|
281
|
-
console.log('🔍 DEBUG: About to call SetupEnforcer.checkSetupCompleteAsync()');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let prompt;
|
|
285
|
-
try {
|
|
286
|
-
// Ensure setup is complete before running any operations
|
|
287
|
-
await SetupEnforcer.checkSetupCompleteAsync();
|
|
288
|
-
|
|
289
|
-
prompt = createPrompt({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
|
|
290
|
-
const interactive = isInteractive({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
|
|
291
|
-
|
|
292
|
-
// Load settings and UI language
|
|
293
|
-
const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
294
|
-
const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
|
|
295
|
-
loadTranslations(uiLanguage);
|
|
296
|
-
|
|
297
|
-
// Initialize CommandRouter
|
|
298
|
-
this.commandRouter = new CommandRouter(this.config, this.ui, this.adminAuth);
|
|
299
|
-
|
|
300
|
-
if (args.adminPin) {
|
|
301
|
-
this.adminAuth.verifyPin = async () => true;
|
|
302
|
-
this.prompt = async () => '';
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (args.help) {
|
|
306
|
-
this.showHelp();
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
let cfgAfterInitCheck = {};
|
|
311
|
-
if (interactive) {
|
|
312
|
-
cfgAfterInitCheck = await this.ensureInitializedOrExit(prompt);
|
|
313
|
-
const frameworksDetected = await this.checkI18nDependencies();
|
|
314
|
-
if (!frameworksDetected) {
|
|
315
|
-
await this.maybePromptFramework(prompt, cfgAfterInitCheck, pkg.version);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
this.config = { ...this.config, ...cfgAfterInitCheck };
|
|
320
|
-
await this.initialize();
|
|
321
|
-
|
|
322
|
-
const rawArgs = process.argv.slice(2); // Preserve original CLI args array for positional checks
|
|
323
|
-
let commandToExecute = null;
|
|
324
|
-
|
|
325
|
-
// Define valid direct commands
|
|
326
|
-
const directCommands = [
|
|
327
|
-
'init', 'analyze', 'validate', 'usage', 'scanner', 'sizing', 'complete', 'fix', 'summary', 'debug', 'workflow'
|
|
328
|
-
];
|
|
329
|
-
|
|
330
|
-
// Handle help immediately without dependency checks
|
|
331
|
-
if (args.help) {
|
|
332
|
-
this.showHelp();
|
|
333
|
-
this.safeClose();
|
|
334
|
-
process.exit(0);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Handle debug flag
|
|
338
|
-
if (args.debug) {
|
|
339
|
-
// Enable debug mode for this session
|
|
340
|
-
const { blue } = require('../../utils/colors-new');
|
|
341
|
-
console.log(blue('Debug mode enabled'));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Check for --command= argument first
|
|
345
|
-
const commandFlagArg = rawArgs.find(arg => arg.startsWith('--command='));
|
|
346
|
-
if (commandFlagArg) {
|
|
347
|
-
commandToExecute = commandFlagArg.split('=')[1];
|
|
348
|
-
} else if (rawArgs.length > 0 && directCommands.includes(rawArgs[0])) {
|
|
349
|
-
// If no --command=, check if the first argument is a direct command
|
|
350
|
-
commandToExecute = rawArgs[0];
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if (commandToExecute) {
|
|
354
|
-
console.log(t('ui.executingCommand', { command: commandToExecute }));
|
|
355
|
-
await this.executeCommand(commandToExecute);
|
|
356
|
-
this.safeClose();
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// If no command provided and --no-prompt is set, exit gracefully
|
|
361
|
-
if (args.noPrompt) {
|
|
362
|
-
this.safeClose();
|
|
363
|
-
process.exit(0);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Framework detection is now handled by maybePromptFramework above
|
|
367
|
-
// Skip the redundant checkI18nDependencies prompt
|
|
368
|
-
|
|
369
|
-
// Interactive mode - showInteractiveMenu will handle the title
|
|
370
|
-
await this.showInteractiveMenu();
|
|
371
|
-
|
|
372
|
-
} catch (error) {
|
|
373
|
-
if (this.ui && this.ui.t) {
|
|
374
|
-
console.error(t('common.genericError', { error: error.message }));
|
|
375
|
-
} else {
|
|
376
|
-
console.error(`Error: ${error.message}`);
|
|
377
|
-
}
|
|
378
|
-
process.exit(1);
|
|
379
|
-
} finally {
|
|
380
|
-
if (prompt && typeof prompt.close === 'function') {
|
|
381
|
-
prompt.close();
|
|
382
|
-
}
|
|
383
|
-
this.safeClose();
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
showHelp() {
|
|
388
|
-
const localT = this.ui && this.ui.t ? this.ui.t.bind(this.ui) : (key) => {
|
|
389
|
-
// Fallback help text when UI is not initialized
|
|
390
|
-
const helpTexts = {
|
|
391
|
-
'help.usage': 'Usage: npm run i18ntk [command] [options]',
|
|
392
|
-
'help.interactiveMode': 'Run without arguments for interactive mode',
|
|
393
|
-
'help.initProject': ' init - Initialize i18n project structure',
|
|
394
|
-
'help.analyzeTranslations': ' analyze - Analyze translation files',
|
|
395
|
-
'help.validateTranslations': ' validate - Validate translations for errors',
|
|
396
|
-
'help.checkUsage': ' usage - Check translation usage in code',
|
|
397
|
-
'help.showHelp': ' help - Show this help message',
|
|
398
|
-
'help.availableCommands': '\nAvailable commands:',
|
|
399
|
-
'help.initCommand': ' --command=init Initialize i18n project',
|
|
400
|
-
'help.analyzeCommand': ' --command=analyze Analyze translations',
|
|
401
|
-
'help.validateCommand': ' --command=validate Validate translations',
|
|
402
|
-
'help.usageCommand': ' --command=usage Check translation usage',
|
|
403
|
-
'help.sizingCommand': ' --command=sizing Analyze translation sizing',
|
|
404
|
-
'help.completeCommand': ' --command=complete Run complete analysis',
|
|
405
|
-
'help.summaryCommand': ' --command=summary Generate summary report',
|
|
406
|
-
'help.debugCommand': ' --command=debug Run debug utilities',
|
|
407
|
-
'help.scannerCommand': ' --command=scanner Scan for translation keys'
|
|
408
|
-
};
|
|
409
|
-
return helpTexts[key] || key;
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
console.log(t('help.usage'));
|
|
413
|
-
console.log(t('help.interactiveMode'));
|
|
414
|
-
console.log(t('help.initProject'));
|
|
415
|
-
console.log(t('help.analyzeTranslations'));
|
|
416
|
-
console.log(t('help.validateTranslations'));
|
|
417
|
-
console.log(t('help.checkUsage'));
|
|
418
|
-
console.log(t('help.showHelp'));
|
|
419
|
-
console.log(t('help.availableCommands'));
|
|
420
|
-
console.log(t('help.initCommand'));
|
|
421
|
-
console.log(t('help.analyzeCommand'));
|
|
422
|
-
console.log(t('help.validateCommand'));
|
|
423
|
-
console.log(t('help.usageCommand'));
|
|
424
|
-
console.log(t('help.sizingCommand'));
|
|
425
|
-
console.log(t('help.completeCommand'));
|
|
426
|
-
console.log(t('help.summaryCommand'));
|
|
427
|
-
console.log(t('help.debugCommand'));
|
|
428
|
-
console.log(t('help.scannerCommand'));
|
|
429
|
-
|
|
430
|
-
// Ensure proper exit for direct command execution
|
|
431
|
-
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
432
|
-
this.safeClose();
|
|
433
|
-
process.exit(0);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Execute a command using the new CommandRouter system
|
|
439
|
-
*/
|
|
440
|
-
async executeCommand(command, options = {}) {
|
|
441
|
-
if (!this.commandRouter) {
|
|
442
|
-
throw new Error('CommandRouter not initialized');
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Set runtime dependencies for the command router
|
|
446
|
-
this.commandRouter.setRuntimeDependencies(
|
|
447
|
-
this.prompt.bind(this),
|
|
448
|
-
this.isNonInteractiveMode(),
|
|
449
|
-
this.safeClose.bind(this)
|
|
450
|
-
);
|
|
451
|
-
|
|
452
|
-
return await this.commandRouter.executeCommand(command, options);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// ... existing code for ensureInitializedOrExit, maybePromptFramework, showInteractiveMenu, etc. ...
|
|
456
|
-
|
|
457
|
-
async ensureInitializedOrExit(prompt) {
|
|
458
|
-
const { checkInitialized } = require('../../utils/init-helper');
|
|
459
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
460
|
-
const pkg = require('../../package.json');
|
|
461
|
-
|
|
462
|
-
const { initialized, config } = await checkInitialized();
|
|
463
|
-
|
|
464
|
-
if (!initialized) {
|
|
465
|
-
console.log('\nThis project is not yet initialized with i18ntk.');
|
|
466
|
-
const shouldInitialize = await cliHelper.confirm('Would you like to initialize it now?');
|
|
467
|
-
|
|
468
|
-
if (!shouldInitialize) {
|
|
469
|
-
console.log('Exiting. Please initialize the project first.');
|
|
470
|
-
process.exit(1);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// The initialization will be handled by the init command
|
|
474
|
-
return config;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Check if we need to prompt for framework detection
|
|
478
|
-
const settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
479
|
-
|
|
480
|
-
// Ensure framework configuration exists with all required fields
|
|
481
|
-
if (!settings.framework) {
|
|
482
|
-
settings.framework = {
|
|
483
|
-
detected: false,
|
|
484
|
-
preference: null,
|
|
485
|
-
prompt: 'always',
|
|
486
|
-
lastPromptedVersion: null,
|
|
487
|
-
installed: [],
|
|
488
|
-
version: '1.0' // Schema version for future compatibility
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Check if we need to prompt for framework detection
|
|
493
|
-
if (!settings.framework.detected &&
|
|
494
|
-
settings.framework.prompt !== 'suppress' &&
|
|
495
|
-
settings.framework.lastPromptedVersion !== pkg.version) {
|
|
496
|
-
|
|
497
|
-
console.log('\nWe noticed you haven\'t set up an i18n framework yet.');
|
|
498
|
-
console.log('Would you like to detect your i18n framework automatically?');
|
|
499
|
-
|
|
500
|
-
console.log('1. Detect automatically');
|
|
501
|
-
console.log('2. I\'ll set it up manually');
|
|
502
|
-
console.log('3. Don\'t show this again');
|
|
503
|
-
|
|
504
|
-
const answer = await prompt.question('\nSelect an option (1-3): ');
|
|
505
|
-
const choice = answer.trim();
|
|
506
|
-
|
|
507
|
-
let action;
|
|
508
|
-
if (choice === '1') action = 'detect';
|
|
509
|
-
else if (choice === '2') action = 'manual';
|
|
510
|
-
else if (choice === '3') action = 'dont-show';
|
|
511
|
-
else action = 'manual'; // default fallback
|
|
512
|
-
|
|
513
|
-
if (action === 'dont-show') {
|
|
514
|
-
// Update settings to suppress future prompts for this version
|
|
515
|
-
settings.framework.prompt = 'suppress';
|
|
516
|
-
settings.framework.lastPromptedVersion = pkg.version;
|
|
517
|
-
|
|
518
|
-
if (configManager.saveSettings) {
|
|
519
|
-
await configManager.saveSettings(settings);
|
|
520
|
-
} else if (configManager.saveConfig) {
|
|
521
|
-
await configManager.saveConfig(settings);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
console.log('Framework detection prompt will be suppressed for this version.');
|
|
525
|
-
} else if (action === 'detect') {
|
|
526
|
-
// Run framework detection
|
|
527
|
-
const { detectedLanguage, detectedFramework } = await this.detectEnvironmentAndFramework();
|
|
528
|
-
|
|
529
|
-
if (detectedFramework && detectedFramework !== 'generic') {
|
|
530
|
-
console.log(`\nDetected framework: ${detectedFramework}`);
|
|
531
|
-
|
|
532
|
-
// Update settings with detected framework
|
|
533
|
-
settings.framework.detected = true;
|
|
534
|
-
settings.framework.preference = detectedFramework;
|
|
535
|
-
settings.framework.lastDetected = new Date().toISOString();
|
|
536
|
-
|
|
537
|
-
if (configManager.saveSettings) {
|
|
538
|
-
await configManager.saveSettings(settings);
|
|
539
|
-
} else if (configManager.saveConfig) {
|
|
540
|
-
await configManager.saveConfig(settings);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
console.log(`Framework set to: ${detectedFramework}`);
|
|
544
|
-
} else {
|
|
545
|
-
console.log('\nCould not detect a specific i18n framework.');
|
|
546
|
-
console.log('Please set up your i18n framework manually.');
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
return { ...config, ...settings };
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
async detectEnvironmentAndFramework() {
|
|
555
|
-
// Defensive check to ensure SecurityUtils is available
|
|
556
|
-
if (!SecurityUtils) {
|
|
557
|
-
throw new Error('SecurityUtils is not available. This may indicate a module loading issue.');
|
|
558
|
-
}
|
|
559
|
-
const fs = require('fs');
|
|
560
|
-
const path = require('path');
|
|
561
|
-
|
|
562
|
-
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
563
|
-
const pyprojectPath = path.join(process.cwd(), 'pyproject.toml');
|
|
564
|
-
const requirementsPath = path.join(process.cwd(), 'requirements.txt');
|
|
565
|
-
const goModPath = path.join(process.cwd(), 'go.mod');
|
|
566
|
-
const pomPath = path.join(process.cwd(), 'pom.xml');
|
|
567
|
-
const composerPath = path.join(process.cwd(), 'composer.json');
|
|
568
|
-
|
|
569
|
-
let detectedLanguage = 'generic';
|
|
570
|
-
let detectedFramework = 'generic';
|
|
571
|
-
|
|
572
|
-
if (SecurityUtils.safeExistsSync(packageJsonPath)) {
|
|
573
|
-
detectedLanguage = 'javascript';
|
|
574
|
-
try {
|
|
575
|
-
const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
|
|
576
|
-
const deps = {
|
|
577
|
-
...(packageJson.dependencies || {}),
|
|
578
|
-
...(packageJson.devDependencies || {}),
|
|
579
|
-
...(packageJson.peerDependencies || {})
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
// Check for i18ntk-runtime first (check both package names)
|
|
583
|
-
const hasI18nTkRuntime = deps['i18ntk-runtime'] || deps['i18ntk/runtime'];
|
|
584
|
-
|
|
585
|
-
// Check for common i18n patterns in source code if not found in package.json
|
|
586
|
-
if (!hasI18nTkRuntime) {
|
|
587
|
-
const i18nPatterns = [
|
|
588
|
-
/i18n\.t\(['\"`]/,
|
|
589
|
-
/useI18n\(/,
|
|
590
|
-
/from ['\"]i18ntk[\/\\]runtime['\"]/,
|
|
591
|
-
/require\(['\"]i18ntk[\/\\]runtime['\"]\)/
|
|
592
|
-
];
|
|
593
|
-
|
|
594
|
-
const sourceFiles = await this.customGlob(['src/**/*.{js,jsx,ts,tsx}'], {
|
|
595
|
-
cwd: process.cwd(),
|
|
596
|
-
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**']
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
for (const file of sourceFiles) {
|
|
600
|
-
try {
|
|
601
|
-
const content = await fs.promises.readFile(path.join(process.cwd(), file), 'utf8');
|
|
602
|
-
if (i18nPatterns.some(pattern => pattern.test(content))) {
|
|
603
|
-
detectedFramework = 'i18ntk-runtime';
|
|
604
|
-
break;
|
|
605
|
-
}
|
|
606
|
-
} catch (e) {
|
|
607
|
-
// Skip files we can't read
|
|
608
|
-
continue;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
} else {
|
|
612
|
-
detectedFramework = 'i18ntk-runtime';
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// Only check other frameworks if i18ntk-runtime wasn't detected
|
|
616
|
-
if (detectedFramework !== 'i18ntk-runtime') {
|
|
617
|
-
if (deps.react || deps['react-dom']) detectedFramework = 'react';
|
|
618
|
-
else if (deps.vue || deps['vue-router']) detectedFramework = 'vue';
|
|
619
|
-
else if (deps['@angular/core']) detectedFramework = 'angular';
|
|
620
|
-
else if (deps.next) detectedFramework = 'nextjs';
|
|
621
|
-
else if (deps.nuxt) detectedFramework = 'nuxt';
|
|
622
|
-
else if (deps.svelte) detectedFramework = 'svelte';
|
|
623
|
-
else detectedFramework = 'generic';
|
|
624
|
-
}
|
|
625
|
-
} catch (error) {
|
|
626
|
-
detectedFramework = 'generic';
|
|
627
|
-
}
|
|
628
|
-
} else if (SecurityUtils.safeExistsSync(pyprojectPath) || SecurityUtils.safeExistsSync(requirementsPath)) {
|
|
629
|
-
detectedLanguage = 'python';
|
|
630
|
-
try {
|
|
631
|
-
if (SecurityUtils.safeExistsSync(requirementsPath)) {
|
|
632
|
-
const requirements = SecurityUtils.safeReadFileSync(requirementsPath, path.dirname(requirementsPath), 'utf8');
|
|
633
|
-
if (requirements.includes('django')) detectedFramework = 'django';
|
|
634
|
-
else if (requirements.includes('flask')) detectedFramework = 'flask';
|
|
635
|
-
else if (requirements.includes('fastapi')) detectedFramework = 'fastapi';
|
|
636
|
-
else detectedFramework = 'generic';
|
|
637
|
-
}
|
|
638
|
-
} catch (error) {
|
|
639
|
-
detectedFramework = 'generic';
|
|
640
|
-
}
|
|
641
|
-
} else if (SecurityUtils.safeExistsSync(goModPath)) {
|
|
642
|
-
detectedLanguage = 'go';
|
|
643
|
-
detectedFramework = 'generic';
|
|
644
|
-
} else if (SecurityUtils.safeExistsSync(pomPath)) {
|
|
645
|
-
detectedLanguage = 'java';
|
|
646
|
-
try {
|
|
647
|
-
const pomContent = SecurityUtils.safeReadFileSync(pomPath, path.dirname(pomPath), 'utf8');
|
|
648
|
-
if (pomContent.includes('spring-boot')) detectedFramework = 'spring-boot';
|
|
649
|
-
else if (pomContent.includes('spring')) detectedFramework = 'spring';
|
|
650
|
-
else if (pomContent.includes('quarkus')) detectedFramework = 'quarkus';
|
|
651
|
-
else detectedFramework = 'generic';
|
|
652
|
-
} catch (error) {
|
|
653
|
-
detectedFramework = 'generic';
|
|
654
|
-
}
|
|
655
|
-
} else if (SecurityUtils.safeExistsSync(composerPath)) {
|
|
656
|
-
detectedLanguage = 'php';
|
|
657
|
-
try {
|
|
658
|
-
const composer = JSON.parse(SecurityUtils.safeReadFileSync(composerPath, path.dirname(composerPath), 'utf8'));
|
|
659
|
-
const deps = composer.require || {};
|
|
660
|
-
|
|
661
|
-
if (deps['laravel/framework']) detectedFramework = 'laravel';
|
|
662
|
-
else if (deps['symfony/framework-bundle']) detectedFramework = 'symfony';
|
|
663
|
-
else if (deps['wordpress']) detectedFramework = 'wordpress';
|
|
664
|
-
else detectedFramework = 'generic';
|
|
665
|
-
} catch (error) {
|
|
666
|
-
detectedFramework = 'generic';
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
return { detectedLanguage, detectedFramework };
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
async customGlob(patterns, options = {}) {
|
|
674
|
-
const fs = require('fs');
|
|
675
|
-
const path = require('path');
|
|
676
|
-
const cwd = options.cwd || process.cwd();
|
|
677
|
-
const ignorePatterns = options.ignore || [];
|
|
678
|
-
|
|
679
|
-
function matchesPattern(filename, pattern) {
|
|
680
|
-
// Simple pattern matching for **/*.{js,jsx,ts,tsx} style patterns
|
|
681
|
-
if (pattern.includes('**/*')) {
|
|
682
|
-
const extensionPart = pattern.split('*.')[1];
|
|
683
|
-
if (extensionPart) {
|
|
684
|
-
const extensions = extensionPart.replace('{', '').replace('}', '').split(',');
|
|
685
|
-
return extensions.some(ext => filename.endsWith('.' + ext.trim()));
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
return filename.includes(pattern.replace('**/', ''));
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
function shouldIgnore(filePath) {
|
|
692
|
-
return ignorePatterns.some(pattern => {
|
|
693
|
-
if (pattern.includes('**/')) {
|
|
694
|
-
const patternEnd = pattern.replace('**/', '');
|
|
695
|
-
return filePath.includes('/' + patternEnd) || filePath.includes('\\' + patternEnd);
|
|
696
|
-
}
|
|
697
|
-
return filePath.includes(pattern);
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
function findFiles(dir, results = []) {
|
|
702
|
-
try {
|
|
703
|
-
const items = fs.readdirSync(dir);
|
|
704
|
-
|
|
705
|
-
for (const item of items) {
|
|
706
|
-
const fullPath = path.join(dir, item);
|
|
707
|
-
const relativePath = path.relative(cwd, fullPath);
|
|
708
|
-
|
|
709
|
-
if (shouldIgnore(relativePath)) {
|
|
710
|
-
continue;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
try {
|
|
714
|
-
const stat = fs.statSync(fullPath);
|
|
715
|
-
|
|
716
|
-
if (stat.isDirectory()) {
|
|
717
|
-
findFiles(fullPath, results);
|
|
718
|
-
} else if (stat.isFile()) {
|
|
719
|
-
// Check if file matches any of our patterns
|
|
720
|
-
for (const pattern of patterns) {
|
|
721
|
-
if (matchesPattern(item, pattern)) {
|
|
722
|
-
results.push(relativePath);
|
|
723
|
-
break;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
} catch (error) {
|
|
728
|
-
// Skip files we can't access
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
} catch (error) {
|
|
733
|
-
// Skip directories we can't access
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
return results;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return findFiles(cwd);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
async maybePromptFramework(prompt, cfg, currentVersion) {
|
|
743
|
-
// Load current settings to check framework configuration
|
|
744
|
-
let settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
745
|
-
|
|
746
|
-
// Ensure framework configuration exists with all required fields
|
|
747
|
-
if (!settings.framework) {
|
|
748
|
-
settings.framework = {
|
|
749
|
-
detected: false,
|
|
750
|
-
preference: null,
|
|
751
|
-
prompt: 'always',
|
|
752
|
-
lastPromptedVersion: null,
|
|
753
|
-
installed: [],
|
|
754
|
-
version: '1.0' // Schema version for future compatibility
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
// Save the updated settings
|
|
758
|
-
if (configManager.saveSettings) {
|
|
759
|
-
await configManager.saveSettings(settings);
|
|
760
|
-
} else if (configManager.saveConfig) {
|
|
761
|
-
await configManager.saveConfig(settings);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// Reload settings to ensure we have the latest framework detection results
|
|
766
|
-
const freshSettings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
|
|
767
|
-
if (freshSettings.framework) {
|
|
768
|
-
settings.framework = { ...settings.framework, ...freshSettings.framework };
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// Check if framework is already detected or preference is explicitly set to none
|
|
772
|
-
if (settings.framework.detected || settings.framework.preference === 'none') {
|
|
773
|
-
return cfg;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// Check if DNR (Do Not Remind) is active for this version
|
|
777
|
-
if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion === currentVersion) {
|
|
778
|
-
return cfg;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// Reset DNR if version changed
|
|
782
|
-
if (settings.framework.prompt === 'suppress' && settings.framework.lastPromptedVersion !== currentVersion) {
|
|
783
|
-
settings.framework.prompt = 'always';
|
|
784
|
-
settings.framework.lastPromptedVersion = null;
|
|
785
|
-
|
|
786
|
-
// Save the updated settings
|
|
787
|
-
if (configManager.saveSettings) {
|
|
788
|
-
await configManager.saveSettings(settings);
|
|
789
|
-
} else if (configManager.saveConfig) {
|
|
790
|
-
await configManager.saveConfig(settings);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// This function is now handled by ensureInitializedOrExit for better flow control
|
|
795
|
-
|
|
796
|
-
return cfg;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
async showInteractiveMenu() {
|
|
800
|
-
// Check if we're in non-interactive mode (like echo 0 | node script)
|
|
801
|
-
if (this.isNonInteractiveMode()) {
|
|
802
|
-
console.log(`\n${t('menu.title')}`);
|
|
803
|
-
console.log(t('menu.separator'));
|
|
804
|
-
console.log(`1. ${t('menu.options.init')}`);
|
|
805
|
-
console.log(`2. ${t('menu.options.analyze')}`);
|
|
806
|
-
console.log(`3. ${t('menu.options.validate')}`);
|
|
807
|
-
console.log(`4. ${t('menu.options.usage')}`);
|
|
808
|
-
console.log(`5. ${t('menu.options.complete')}`);
|
|
809
|
-
console.log(`6. ${t('menu.options.sizing')}`);
|
|
810
|
-
console.log(`7. ${t('menu.options.fix')}`);
|
|
811
|
-
console.log(`8. ${t('menu.options.status')}`);
|
|
812
|
-
console.log(`9. ${t('menu.options.delete')}`);
|
|
813
|
-
console.log(`10. ${t('menu.options.settings')}`);
|
|
814
|
-
console.log(`11. ${t('menu.options.help')}`);
|
|
815
|
-
console.log(`12. ${t('menu.options.language')}`);
|
|
816
|
-
console.log(`13. ${t('menu.options.scanner')}`);
|
|
817
|
-
console.log(`0. ${t('menu.options.exit')}`);
|
|
818
|
-
|
|
819
|
-
console.log('\n' + t('menu.nonInteractiveModeWarning'));
|
|
820
|
-
console.log(t('menu.useDirectExecution'));
|
|
821
|
-
console.log(t('menu.useHelpForCommands'));
|
|
822
|
-
this.safeClose();
|
|
823
|
-
process.exit(0);
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
console.log(`\n${t('menu.title')}`);
|
|
828
|
-
console.log(t('menu.separator'));
|
|
829
|
-
console.log(`1. ${t('menu.options.init')}`);
|
|
830
|
-
console.log(`2. ${t('menu.options.analyze')}`);
|
|
831
|
-
console.log(`3. ${t('menu.options.validate')}`);
|
|
832
|
-
console.log(`4. ${t('menu.options.usage')}`);
|
|
833
|
-
console.log(`5. ${t('menu.options.complete')}`);
|
|
834
|
-
console.log(`6. ${t('menu.options.sizing')}`);
|
|
835
|
-
console.log(`7. ${t('menu.options.fix')}`);
|
|
836
|
-
console.log(`8. ${t('menu.options.status')}`);
|
|
837
|
-
console.log(`9. ${t('menu.options.delete')}`);
|
|
838
|
-
console.log(`10. ${t('menu.options.settings')}`);
|
|
839
|
-
console.log(`11. ${t('menu.options.help')}`);
|
|
840
|
-
console.log(`12. ${t('menu.options.language')}`);
|
|
841
|
-
console.log(`13. ${t('menu.options.scanner')}`);
|
|
842
|
-
console.log(`0. ${t('menu.options.exit')}`);
|
|
843
|
-
|
|
844
|
-
const choice = await this.prompt('\n' + t('menu.selectOptionPrompt'));
|
|
845
|
-
|
|
846
|
-
switch (choice.trim()) {
|
|
847
|
-
case '1':
|
|
848
|
-
await this.executeCommand('init', {fromMenu: true});
|
|
849
|
-
break;
|
|
850
|
-
case '2':
|
|
851
|
-
await this.executeCommand('analyze', {fromMenu: true});
|
|
852
|
-
break;
|
|
853
|
-
case '3':
|
|
854
|
-
await this.executeCommand('validate', {fromMenu: true});
|
|
855
|
-
break;
|
|
856
|
-
case '4':
|
|
857
|
-
await this.executeCommand('usage', {fromMenu: true});
|
|
858
|
-
break;
|
|
859
|
-
case '5':
|
|
860
|
-
await this.executeCommand('complete', {fromMenu: true});
|
|
861
|
-
break;
|
|
862
|
-
case '6':
|
|
863
|
-
await this.executeCommand('sizing', {fromMenu: true});
|
|
864
|
-
break;
|
|
865
|
-
case '7':
|
|
866
|
-
await this.executeCommand('fix', {fromMenu: true});
|
|
867
|
-
break;
|
|
868
|
-
case '8':
|
|
869
|
-
// Check for PIN protection
|
|
870
|
-
const authRequired = await this.adminAuth.isAuthRequiredForScript('summaryReports');
|
|
871
|
-
if (authRequired) {
|
|
872
|
-
console.log(`\n${t('adminCli.protectedAccess')}`);
|
|
873
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
874
|
-
const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
|
|
875
|
-
const isValid = await this.adminAuth.verifyPin(pin);
|
|
876
|
-
|
|
877
|
-
if (!isValid) {
|
|
878
|
-
console.log(t('adminCli.invalidPin'));
|
|
879
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
880
|
-
await this.showInteractiveMenu();
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
console.log(t('adminCli.accessGranted'));
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
console.log(t('summary.status.generating'));
|
|
888
|
-
try {
|
|
889
|
-
// Use SummaryService instead of direct import
|
|
890
|
-
this.summaryService.initialize(configManager);
|
|
891
|
-
const report = await this.summaryService.run({ fromMenu: true });
|
|
892
|
-
console.log(report);
|
|
893
|
-
console.log(t('summary.status.completed'));
|
|
894
|
-
|
|
895
|
-
// Check if we're in interactive mode before prompting
|
|
896
|
-
if (!this.isNonInteractiveMode()) {
|
|
897
|
-
try {
|
|
898
|
-
await this.prompt('\n' + t('debug.pressEnterToContinue'));
|
|
899
|
-
await this.showInteractiveMenu();
|
|
900
|
-
} catch (error) {
|
|
901
|
-
console.log(t('menu.returning'));
|
|
902
|
-
process.exit(0);
|
|
903
|
-
}
|
|
904
|
-
} else {
|
|
905
|
-
console.log(t('status.exitingCompleted'));
|
|
906
|
-
process.exit(0);
|
|
907
|
-
}
|
|
908
|
-
} catch (error) {
|
|
909
|
-
console.error(t('common.errorGeneratingStatusSummary', { error: error.message }));
|
|
910
|
-
|
|
911
|
-
// Check if we're in interactive mode before prompting
|
|
912
|
-
if (!this.isNonInteractiveMode()) {
|
|
913
|
-
try {
|
|
914
|
-
await this.prompt('\n' + t('debug.pressEnterToContinue'));
|
|
915
|
-
await this.showInteractiveMenu();
|
|
916
|
-
} catch (error) {
|
|
917
|
-
console.log(t('menu.returning'));
|
|
918
|
-
process.exit(0);
|
|
919
|
-
}
|
|
920
|
-
} else {
|
|
921
|
-
console.log(t('common.errorExiting'));
|
|
922
|
-
process.exit(1);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
break;
|
|
926
|
-
case '9':
|
|
927
|
-
await this.deleteReports();
|
|
928
|
-
break;
|
|
929
|
-
case '10':
|
|
930
|
-
await this.showSettingsMenu();
|
|
931
|
-
break;
|
|
932
|
-
case '11':
|
|
933
|
-
this.showHelp();
|
|
934
|
-
await this.prompt(t('menu.returnToMainMenu'));
|
|
935
|
-
await this.showInteractiveMenu();
|
|
936
|
-
break;
|
|
937
|
-
case '12':
|
|
938
|
-
await this.showLanguageMenu();
|
|
939
|
-
break;
|
|
940
|
-
case '13':
|
|
941
|
-
await this.executeCommand('scanner', {fromMenu: true});
|
|
942
|
-
break;
|
|
943
|
-
case '0':
|
|
944
|
-
console.log(t('menu.goodbye'));
|
|
945
|
-
this.safeClose();
|
|
946
|
-
process.exit(0);
|
|
947
|
-
default:
|
|
948
|
-
console.log(t('menu.invalidChoice'));
|
|
949
|
-
await this.showInteractiveMenu();
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// ... existing code for showLanguageMenu, showDebugMenu, deleteReports, showSettingsMenu, etc. ...
|
|
954
|
-
|
|
955
|
-
async showLanguageMenu() {
|
|
956
|
-
console.log(`\n${t('language.title')}`);
|
|
957
|
-
console.log(t('language.separator'));
|
|
958
|
-
console.log(t('language.current', { language: 'English' })); // Simplified since we don't have UIi18n
|
|
959
|
-
console.log('\n' + t('language.available'));
|
|
960
|
-
|
|
961
|
-
const languages = [
|
|
962
|
-
{ code: 'en', name: 'English' },
|
|
963
|
-
{ code: 'de', name: 'Deutsch' },
|
|
964
|
-
{ code: 'es', name: 'Español' },
|
|
965
|
-
{ code: 'fr', name: 'Français' },
|
|
966
|
-
{ code: 'ru', name: 'Русский' },
|
|
967
|
-
{ code: 'ja', name: '日本語' },
|
|
968
|
-
{ code: 'zh', name: '中文' }
|
|
969
|
-
];
|
|
970
|
-
|
|
971
|
-
languages.forEach((lang, index) => {
|
|
972
|
-
const current = 'en' === 'en' ? ' ✓' : '';
|
|
973
|
-
console.log(t('language.languageOption', { index: index + 1, displayName: lang.name, current }));
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
console.log(`0. ${t('language.backToMainMenu')}`);
|
|
977
|
-
|
|
978
|
-
const choice = await this.prompt('\n' + t('language.prompt'));
|
|
979
|
-
const choiceNum = parseInt(choice);
|
|
980
|
-
|
|
981
|
-
if (choiceNum === 0) {
|
|
982
|
-
await this.showInteractiveMenu();
|
|
983
|
-
return;
|
|
984
|
-
} else if (choiceNum >= 1 && choiceNum <= languages.length) {
|
|
985
|
-
const selectedLang = languages[choiceNum - 1];
|
|
986
|
-
console.log(t('language.changed', { language: selectedLang.name }));
|
|
987
|
-
|
|
988
|
-
// Force reload translations for the entire system
|
|
989
|
-
const { loadTranslations } = require('../../utils/i18n-helper');
|
|
990
|
-
loadTranslations(selectedLang.code);
|
|
991
|
-
|
|
992
|
-
// Return to main menu with new language
|
|
993
|
-
await this.prompt('\n' + t('language.pressEnterToContinue'));
|
|
994
|
-
await this.showInteractiveMenu();
|
|
995
|
-
} else {
|
|
996
|
-
console.log(t('language.invalid'));
|
|
997
|
-
await this.prompt('\n' + t('language.pressEnterToContinue'));
|
|
998
|
-
await this.showLanguageMenu();
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
async showDebugMenu() {
|
|
1003
|
-
// Check for PIN protection
|
|
1004
|
-
const authRequired = await this.adminAuth.isAuthRequiredForScript('debugMenu');
|
|
1005
|
-
if (authRequired) {
|
|
1006
|
-
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
1007
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
1008
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1009
|
-
const isValid = await this.adminAuth.verifyPin(pin);
|
|
1010
|
-
|
|
1011
|
-
if (!isValid) {
|
|
1012
|
-
console.log(t('adminPin.invalidPin'));
|
|
1013
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1014
|
-
await this.showInteractiveMenu();
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
console.log(t('adminPin.accessGranted'));
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
console.log(`\n${t('debug.title')}`);
|
|
1022
|
-
console.log(t('debug.separator'));
|
|
1023
|
-
console.log(t('debug.mainDebuggerSystemDiagnostics'));
|
|
1024
|
-
console.log(t('debug.debugLogs'));
|
|
1025
|
-
console.log(t('debug.backToMainMenu'));
|
|
1026
|
-
|
|
1027
|
-
const choice = await this.prompt('\n' + t('debug.selectOption'));
|
|
1028
|
-
|
|
1029
|
-
switch (choice.trim()) {
|
|
1030
|
-
case '1':
|
|
1031
|
-
await this.runDebugTool('debugger.js', 'Main Debugger');
|
|
1032
|
-
break;
|
|
1033
|
-
case '2':
|
|
1034
|
-
await this.viewDebugLogs();
|
|
1035
|
-
break;
|
|
1036
|
-
case '0':
|
|
1037
|
-
await this.showInteractiveMenu();
|
|
1038
|
-
return;
|
|
1039
|
-
default:
|
|
1040
|
-
console.log(t('debug.invalidChoiceSelectRange'));
|
|
1041
|
-
await this.showDebugMenu();
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
async runDebugTool(toolName, displayName) {
|
|
1046
|
-
console.log(t('debug.runningDebugTool', { displayName }));
|
|
1047
|
-
try {
|
|
1048
|
-
const toolPath = path.join(__dirname, '..', '..', 'scripts', 'debug', toolName);
|
|
1049
|
-
if (SecurityUtils.safeExistsSync(toolPath)) {
|
|
1050
|
-
console.log(`Debug tool available: ${toolName}`);
|
|
1051
|
-
console.log(`To run this tool manually: node "${toolPath}"`);
|
|
1052
|
-
console.log(`Working directory: ${path.join(__dirname, '..', '..')}`);
|
|
1053
|
-
} else {
|
|
1054
|
-
console.log(t('debug.debugToolNotFound', { toolName }));
|
|
1055
|
-
}
|
|
1056
|
-
} catch (error) {
|
|
1057
|
-
console.error(t('debug.errorRunningDebugTool', { displayName, error: error.message }));
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
await this.prompt('\n' + t('menu.pressEnterToContinue'));
|
|
1061
|
-
await this.showDebugMenu();
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
async viewDebugLogs() {
|
|
1065
|
-
console.log(`\n${t('debug.recentDebugLogs')}`);
|
|
1066
|
-
console.log('============================================================');
|
|
1067
|
-
|
|
1068
|
-
try {
|
|
1069
|
-
const logsDir = path.join(__dirname, '..', '..', 'scripts', 'debug', 'logs');
|
|
1070
|
-
if (SecurityUtils.safeExistsSync(logsDir)) {
|
|
1071
|
-
const files = require('fs').readdirSync(logsDir)
|
|
1072
|
-
.filter(file => file.endsWith('.log') || file.endsWith('.txt'))
|
|
1073
|
-
.sort((a, b) => {
|
|
1074
|
-
const statA = require('fs').statSync(path.join(logsDir, a));
|
|
1075
|
-
const statB = require('fs').statSync(path.join(logsDir, b));
|
|
1076
|
-
return statB.mtime - statA.mtime;
|
|
1077
|
-
})
|
|
1078
|
-
.slice(0, 5);
|
|
1079
|
-
|
|
1080
|
-
if (files.length > 0) {
|
|
1081
|
-
files.forEach((file, index) => {
|
|
1082
|
-
const filePath = path.join(logsDir, file);
|
|
1083
|
-
const stats = require('fs').statSync(filePath);
|
|
1084
|
-
console.log(`${index + 1}. ${file} (${stats.mtime.toLocaleString()})`);
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
const choice = await this.prompt('\n' + t('debug.selectLogPrompt', { count: files.length }));
|
|
1088
|
-
const fileIndex = parseInt(choice) - 1;
|
|
1089
|
-
|
|
1090
|
-
if (fileIndex >= 0 && fileIndex < files.length) {
|
|
1091
|
-
const logContent = SecurityUtils.safeReadFileSync(path.join(logsDir, files[fileIndex]), logsDir, 'utf8');
|
|
1092
|
-
console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
|
|
1093
|
-
console.log('============================================================');
|
|
1094
|
-
console.log(logContent.slice(-2000)); // Show last 2000 characters
|
|
1095
|
-
console.log('============================================================');
|
|
1096
|
-
}
|
|
1097
|
-
} else {
|
|
1098
|
-
console.log(t('debug.noDebugLogsFound'));
|
|
1099
|
-
}
|
|
1100
|
-
} else {
|
|
1101
|
-
console.log(t('debug.debugLogsDirectoryNotFound'));
|
|
1102
|
-
}
|
|
1103
|
-
} catch (error) {
|
|
1104
|
-
console.error(t('errors.errorReadingDebugLogs', { error: error.message }));
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
await this.prompt('\n' + t('menu.pressEnterToContinue'));
|
|
1108
|
-
await this.showInteractiveMenu();
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
async deleteReports() {
|
|
1112
|
-
// Check for PIN protection
|
|
1113
|
-
const authRequired = await this.adminAuth.isAuthRequiredForScript('deleteReports');
|
|
1114
|
-
if (authRequired) {
|
|
1115
|
-
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
1116
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
1117
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1118
|
-
const isValid = await this.adminAuth.verifyPin(pin);
|
|
1119
|
-
|
|
1120
|
-
if (!isValid) {
|
|
1121
|
-
console.log(t('adminPin.invalidPin'));
|
|
1122
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1123
|
-
await this.showInteractiveMenu();
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
console.log(t('adminPin.accessGranted'));
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
console.log(`\n${t('operations.deleteReportsTitle')}`);
|
|
1131
|
-
console.log('============================================================');
|
|
1132
|
-
|
|
1133
|
-
const targetDirs = [
|
|
1134
|
-
{ path: path.join(process.cwd(), 'i18ntk-reports'), name: 'Reports', type: 'reports' },
|
|
1135
|
-
{ path: path.join(process.cwd(), 'reports'), name: 'Legacy Reports', type: 'reports' },
|
|
1136
|
-
{ path: path.join(process.cwd(), 'reports', 'backups'), name: 'Reports Backups', type: 'backups' },
|
|
1137
|
-
{ path: path.join(process.cwd(), 'scripts', 'debug', 'logs'), name: 'Debug Logs', type: 'logs' },
|
|
1138
|
-
{ path: path.join(process.cwd(), 'scripts', 'debug', 'reports'), name: 'Debug Reports', type: 'reports' },
|
|
1139
|
-
{ path: path.join(process.cwd(), 'settings', 'backups'), name: 'Settings Backups', type: 'backups' },
|
|
1140
|
-
{ path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' }
|
|
1141
|
-
].filter(dir => dir.path && typeof dir.path === 'string');
|
|
1142
|
-
|
|
1143
|
-
try {
|
|
1144
|
-
console.log(t('operations.scanningForFiles'));
|
|
1145
|
-
|
|
1146
|
-
let availableDirs = [];
|
|
1147
|
-
|
|
1148
|
-
// Check which directories exist and have files
|
|
1149
|
-
for (const dir of targetDirs) {
|
|
1150
|
-
if (SecurityUtils.safeExistsSync(dir.path)) {
|
|
1151
|
-
const files = this.getAllReportFiles(dir.path);
|
|
1152
|
-
if (files.length > 0) {
|
|
1153
|
-
availableDirs.push({
|
|
1154
|
-
...dir,
|
|
1155
|
-
files: files.map(file => ({ path: file, dir: dir.path })),
|
|
1156
|
-
count: files.length
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
if (availableDirs.length === 0) {
|
|
1163
|
-
console.log(t('operations.noFilesFoundToDelete'));
|
|
1164
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1165
|
-
await this.showInteractiveMenu();
|
|
1166
|
-
return;
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// Show available directories
|
|
1170
|
-
console.log(t('operations.availableDirectories'));
|
|
1171
|
-
availableDirs.forEach((dir, index) => {
|
|
1172
|
-
console.log(` ${index + 1}. ${dir.name} (${dir.count} files)`);
|
|
1173
|
-
});
|
|
1174
|
-
console.log(` ${availableDirs.length + 1}. ${t('operations.allDirectories')}`);
|
|
1175
|
-
console.log(` 0. ${t('operations.cancelOption')}`);
|
|
1176
|
-
|
|
1177
|
-
const dirChoice = await this.prompt(`\nSelect directory to clean (0-${availableDirs.length + 1}): `);
|
|
1178
|
-
const dirIndex = parseInt(dirChoice) - 1;
|
|
1179
|
-
|
|
1180
|
-
let selectedDirs = [];
|
|
1181
|
-
|
|
1182
|
-
if (dirChoice.trim() === '0') {
|
|
1183
|
-
console.log(t('operations.cancelled'));
|
|
1184
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1185
|
-
await this.showInteractiveMenu();
|
|
1186
|
-
return;
|
|
1187
|
-
} else if (dirIndex === availableDirs.length) {
|
|
1188
|
-
selectedDirs = availableDirs;
|
|
1189
|
-
} else if (dirIndex >= 0 && dirIndex < availableDirs.length) {
|
|
1190
|
-
selectedDirs = [availableDirs[dirIndex]];
|
|
1191
|
-
} else {
|
|
1192
|
-
console.log(t('operations.invalidSelection'));
|
|
1193
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1194
|
-
await this.showInteractiveMenu();
|
|
1195
|
-
return;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
// Collect all files from selected directories
|
|
1199
|
-
let allFiles = [];
|
|
1200
|
-
selectedDirs.forEach(dir => {
|
|
1201
|
-
allFiles.push(...dir.files);
|
|
1202
|
-
});
|
|
1203
|
-
|
|
1204
|
-
console.log(t('operations.foundFilesInSelectedDirectories', { count: allFiles.length }));
|
|
1205
|
-
selectedDirs.forEach(dir => {
|
|
1206
|
-
console.log(` 📁 ${dir.name}: ${dir.count} files`);
|
|
1207
|
-
});
|
|
1208
|
-
|
|
1209
|
-
// Show deletion options
|
|
1210
|
-
console.log(t('operations.deletionOptions'));
|
|
1211
|
-
console.log(` 1. ${t('operations.deleteAllFiles')}`);
|
|
1212
|
-
console.log(` 2. ${t('operations.keepLast3Files')}`);
|
|
1213
|
-
console.log(` 3. ${t('operations.keepLast5Files')}`);
|
|
1214
|
-
console.log(` 0. ${t('operations.cancelReportOption')}`);
|
|
1215
|
-
|
|
1216
|
-
const option = await this.prompt('\nSelect option (0-3): ');
|
|
1217
|
-
|
|
1218
|
-
let filesToDelete = [];
|
|
1219
|
-
|
|
1220
|
-
switch (option.trim()) {
|
|
1221
|
-
case '1':
|
|
1222
|
-
filesToDelete = allFiles;
|
|
1223
|
-
break;
|
|
1224
|
-
case '2':
|
|
1225
|
-
filesToDelete = this.getFilesToDeleteKeepLast(allFiles, 3);
|
|
1226
|
-
break;
|
|
1227
|
-
case '3':
|
|
1228
|
-
filesToDelete = this.getFilesToDeleteKeepLast(allFiles, 5);
|
|
1229
|
-
break;
|
|
1230
|
-
case '0':
|
|
1231
|
-
console.log(t('operations.cancelled'));
|
|
1232
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1233
|
-
await this.showInteractiveMenu();
|
|
1234
|
-
return;
|
|
1235
|
-
default:
|
|
1236
|
-
console.log(t('menu.invalidOption'));
|
|
1237
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1238
|
-
await this.showInteractiveMenu();
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
if (filesToDelete.length === 0) {
|
|
1243
|
-
console.log(t('operations.noFilesToDelete'));
|
|
1244
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1245
|
-
await this.showInteractiveMenu();
|
|
1246
|
-
return;
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
console.log(t('operations.filesToDeleteCount', { count: filesToDelete.length }));
|
|
1250
|
-
console.log(t('operations.filesToKeepCount', { count: allFiles.length - filesToDelete.length }));
|
|
1251
|
-
|
|
1252
|
-
const confirm = await this.prompt(t('operations.confirmDeletion'));
|
|
1253
|
-
|
|
1254
|
-
if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
|
|
1255
|
-
let deletedCount = 0;
|
|
1256
|
-
|
|
1257
|
-
for (const fileInfo of filesToDelete) {
|
|
1258
|
-
try {
|
|
1259
|
-
require('fs').unlinkSync(fileInfo.path);
|
|
1260
|
-
console.log(t('operations.deletedFile', { filename: path.basename(fileInfo.path) }));
|
|
1261
|
-
deletedCount++;
|
|
1262
|
-
} catch (error) {
|
|
1263
|
-
console.log(t('operations.failedToDeleteFile', { filename: path.basename(fileInfo.path), error: error.message }));
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
console.log(`\n🎉 Successfully deleted ${deletedCount} files!`);
|
|
1268
|
-
} else {
|
|
1269
|
-
console.log(t('operations.cancelled'));
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
} catch (error) {
|
|
1273
|
-
console.error(`❌ Error during deletion process: ${error.message}`);
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1277
|
-
await this.showInteractiveMenu();
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
getAllReportFiles(dir) {
|
|
1281
|
-
if (!dir || typeof dir !== 'string') {
|
|
1282
|
-
return [];
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
let files = [];
|
|
1286
|
-
|
|
1287
|
-
try {
|
|
1288
|
-
if (!SecurityUtils.safeExistsSync(dir)) {
|
|
1289
|
-
return [];
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
const items = require('fs').readdirSync(dir);
|
|
1293
|
-
for (const item of items) {
|
|
1294
|
-
const fullPath = path.join(dir, item);
|
|
1295
|
-
|
|
1296
|
-
try {
|
|
1297
|
-
const stat = require('fs').statSync(fullPath);
|
|
1298
|
-
|
|
1299
|
-
if (stat.isDirectory()) {
|
|
1300
|
-
files.push(...this.getAllReportFiles(fullPath));
|
|
1301
|
-
} else if (
|
|
1302
|
-
// Common report file extensions
|
|
1303
|
-
item.endsWith('.json') ||
|
|
1304
|
-
item.endsWith('.html') ||
|
|
1305
|
-
item.endsWith('.txt') ||
|
|
1306
|
-
item.endsWith('.log') ||
|
|
1307
|
-
item.endsWith('.csv') ||
|
|
1308
|
-
item.endsWith('.md') ||
|
|
1309
|
-
// Specific report filename patterns
|
|
1310
|
-
item.includes('-report.') ||
|
|
1311
|
-
item.includes('_report.') ||
|
|
1312
|
-
item.includes('report-') ||
|
|
1313
|
-
item.includes('report_') ||
|
|
1314
|
-
item.includes('analysis-') ||
|
|
1315
|
-
item.includes('validation-')
|
|
1316
|
-
) {
|
|
1317
|
-
files.push(fullPath);
|
|
1318
|
-
}
|
|
1319
|
-
} catch (error) {
|
|
1320
|
-
// Skip individual files that can't be accessed
|
|
1321
|
-
continue;
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
} catch (error) {
|
|
1325
|
-
// Silent fail for inaccessible directories
|
|
1326
|
-
console.log(`⚠️ Could not access directory: ${dir}`);
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
return files;
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
getFilesToDeleteKeepLast(allFiles, keepCount = 3) {
|
|
1333
|
-
// Sort files by modification time (newest first)
|
|
1334
|
-
const sortedFiles = allFiles.sort((a, b) => {
|
|
1335
|
-
try {
|
|
1336
|
-
const statA = require('fs').statSync(a.path || a);
|
|
1337
|
-
const statB = require('fs').statSync(b.path || b);
|
|
1338
|
-
return statB.mtime.getTime() - statA.mtime.getTime();
|
|
1339
|
-
} catch (error) {
|
|
1340
|
-
// If stat fails, sort by filename as fallback
|
|
1341
|
-
const pathA = a.path || a;
|
|
1342
|
-
const pathB = b.path || b;
|
|
1343
|
-
return pathB.localeCompare(pathA);
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
|
|
1347
|
-
// Keep the N newest files, delete the rest
|
|
1348
|
-
return sortedFiles.slice(keepCount);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
async showSettingsMenu() {
|
|
1352
|
-
try {
|
|
1353
|
-
// Check for PIN protection
|
|
1354
|
-
const authRequired = await this.adminAuth.isAuthRequiredForScript('settingsMenu');
|
|
1355
|
-
if (authRequired) {
|
|
1356
|
-
console.log(`\n${t('adminPin.protectedAccess')}`);
|
|
1357
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
1358
|
-
const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
|
|
1359
|
-
const isValid = await this.adminAuth.verifyPin(pin);
|
|
1360
|
-
|
|
1361
|
-
if (!isValid) {
|
|
1362
|
-
console.log(t('adminPin.invalidPin'));
|
|
1363
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1364
|
-
await this.showInteractiveMenu();
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
console.log(t('adminPin.accessGranted'));
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
const SettingsCLI = require('../../settings/settings-cli');
|
|
1372
|
-
const settingsCLI = new SettingsCLI();
|
|
1373
|
-
await settingsCLI.run();
|
|
1374
|
-
} catch (error) {
|
|
1375
|
-
console.error('❌ Error opening settings:', error.message);
|
|
1376
|
-
await this.prompt(t('menu.pressEnterToContinue'));
|
|
1377
|
-
}
|
|
1378
|
-
await this.showInteractiveMenu();
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
prompt(question) {
|
|
1382
|
-
const cliHelper = require('../../utils/cli-helper');
|
|
1383
|
-
// If interactive not available, return empty string to avoid hangs
|
|
1384
|
-
if (!process.stdin.isTTY || process.stdin.destroyed) {
|
|
1385
|
-
console.log('\n⚠️ Interactive input not available, using default response.');
|
|
1386
|
-
return Promise.resolve('');
|
|
1387
|
-
}
|
|
1388
|
-
return cliHelper.prompt(`${question} `);
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
// Safe method to check if we're in non-interactive mode
|
|
1392
|
-
isNonInteractiveMode() {
|
|
1393
|
-
return !process.stdin.isTTY || process.stdin.destroyed || this.isReadlineClosed;
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
safeClose() {
|
|
1397
|
-
if (this.rl && !this.isReadlineClosed) {
|
|
1398
|
-
try {
|
|
1399
|
-
this.rl.close();
|
|
1400
|
-
this.isReadlineClosed = true;
|
|
1401
|
-
} catch (error) {
|
|
1402
|
-
// Ignore close errors
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
// Run if called directly
|
|
1409
|
-
if (require.main === module) {
|
|
1410
|
-
// Handle version and help immediately before any initialization
|
|
1411
|
-
const args = process.argv.slice(2);
|
|
1412
|
-
|
|
1413
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
1414
|
-
try {
|
|
1415
|
-
const packageJsonPath = path.resolve(__dirname, '..', '..', 'package.json');
|
|
1416
|
-
const packageJson = JSON.parse(SecurityUtils.safeReadFileSync(packageJsonPath, path.dirname(packageJsonPath), 'utf8'));
|
|
1417
|
-
const versionInfo = packageJson.versionInfo || {};
|
|
1418
|
-
|
|
1419
|
-
console.log(`\n🌍 i18n Toolkit (i18ntk)`);
|
|
1420
|
-
console.log(`Version: ${packageJson.version}`);
|
|
1421
|
-
console.log(`Release Date: ${versionInfo.releaseDate || 'N/A'}`);
|
|
1422
|
-
console.log(`Maintainer: ${versionInfo.maintainer || packageJson.author}`);
|
|
1423
|
-
console.log(`Node.js: ${versionInfo.supportedNodeVersions || packageJson.engines?.node || '>=16.0.0'}`);
|
|
1424
|
-
console.log(`License: ${packageJson.license}`);
|
|
1425
|
-
|
|
1426
|
-
if (versionInfo.majorChanges && versionInfo.majorChanges.length > 0) {
|
|
1427
|
-
console.log(`\n✨ What's New in ${packageJson.version}:`);
|
|
1428
|
-
versionInfo.majorChanges.forEach(change => {
|
|
1429
|
-
console.log(` • ${change}`);
|
|
1430
|
-
});
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
console.log(`\n📖 Documentation: ${packageJson.homepage}`);
|
|
1434
|
-
console.log(`🐛 Report Issues: ${packageJson.bugs?.url}`);
|
|
1435
|
-
|
|
1436
|
-
} catch (error) {
|
|
1437
|
-
console.log(`\n❌ Version information unavailable`);
|
|
1438
|
-
console.log(`Error: ${error.message}`);
|
|
1439
|
-
}
|
|
1440
|
-
process.exit(0);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
const manager = new I18nManager();
|
|
1444
|
-
manager.run();
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
module.exports = I18nManager;
|