i18ntk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +401 -0
  2. package/LICENSE +21 -0
  3. package/README.md +507 -0
  4. package/dev/README.md +37 -0
  5. package/dev/debug/README.md +30 -0
  6. package/dev/debug/complete-console-translations.js +295 -0
  7. package/dev/debug/console-key-checker.js +408 -0
  8. package/dev/debug/console-translations.js +335 -0
  9. package/dev/debug/debugger.js +408 -0
  10. package/dev/debug/export-missing-keys.js +432 -0
  11. package/dev/debug/final-normalize.js +236 -0
  12. package/dev/debug/find-extra-keys.js +68 -0
  13. package/dev/debug/normalize-locales.js +153 -0
  14. package/dev/debug/refactor-locales.js +240 -0
  15. package/dev/debug/reorder-locales.js +85 -0
  16. package/dev/debug/replace-hardcoded-console.js +378 -0
  17. package/docs/INSTALLATION.md +449 -0
  18. package/docs/README.md +222 -0
  19. package/docs/TODO_ROADMAP.md +279 -0
  20. package/docs/api/API_REFERENCE.md +377 -0
  21. package/docs/api/COMPONENTS.md +492 -0
  22. package/docs/api/CONFIGURATION.md +651 -0
  23. package/docs/api/NPM_PUBLISHING_GUIDE.md +434 -0
  24. package/docs/debug/DEBUG_README.md +30 -0
  25. package/docs/debug/DEBUG_TOOLS.md +494 -0
  26. package/docs/development/AGENTS.md +351 -0
  27. package/docs/development/DEVELOPMENT_RULES.md +165 -0
  28. package/docs/development/DEV_README.md +37 -0
  29. package/docs/release-notes/RELEASE_NOTES_v1.0.0.md +173 -0
  30. package/docs/release-notes/RELEASE_NOTES_v1.6.0.md +141 -0
  31. package/docs/release-notes/RELEASE_NOTES_v1.6.1.md +185 -0
  32. package/docs/release-notes/RELEASE_NOTES_v1.6.3.md +199 -0
  33. package/docs/reports/ANALYSIS_README.md +17 -0
  34. package/docs/reports/CONSOLE_MISMATCH_BUG_REPORT_v1.5.0.md +181 -0
  35. package/docs/reports/SIZING_README.md +18 -0
  36. package/docs/reports/SUMMARY_README.md +18 -0
  37. package/docs/reports/TRANSLATION_BUG_REPORT_v1.5.0.md +129 -0
  38. package/docs/reports/USAGE_README.md +18 -0
  39. package/docs/reports/VALIDATION_README.md +18 -0
  40. package/locales/de/auth.json +3 -0
  41. package/locales/de/common.json +16 -0
  42. package/locales/de/pagination.json +6 -0
  43. package/locales/en/auth.json +3 -0
  44. package/locales/en/common.json +16 -0
  45. package/locales/en/pagination.json +6 -0
  46. package/locales/es/auth.json +3 -0
  47. package/locales/es/common.json +16 -0
  48. package/locales/es/pagination.json +6 -0
  49. package/locales/fr/auth.json +3 -0
  50. package/locales/fr/common.json +16 -0
  51. package/locales/fr/pagination.json +6 -0
  52. package/locales/ru/auth.json +3 -0
  53. package/locales/ru/common.json +16 -0
  54. package/locales/ru/pagination.json +6 -0
  55. package/main/i18ntk-analyze.js +625 -0
  56. package/main/i18ntk-autorun.js +461 -0
  57. package/main/i18ntk-complete.js +494 -0
  58. package/main/i18ntk-init.js +686 -0
  59. package/main/i18ntk-manage.js +848 -0
  60. package/main/i18ntk-sizing.js +557 -0
  61. package/main/i18ntk-summary.js +671 -0
  62. package/main/i18ntk-usage.js +1282 -0
  63. package/main/i18ntk-validate.js +762 -0
  64. package/main/ui-i18n.js +332 -0
  65. package/package.json +152 -0
  66. package/scripts/fix-missing-translation-keys.js +214 -0
  67. package/scripts/verify-package.js +168 -0
  68. package/ui-locales/de.json +637 -0
  69. package/ui-locales/en.json +688 -0
  70. package/ui-locales/es.json +637 -0
  71. package/ui-locales/fr.json +637 -0
  72. package/ui-locales/ja.json +637 -0
  73. package/ui-locales/ru.json +637 -0
  74. package/ui-locales/zh.json +637 -0
  75. package/utils/admin-auth.js +317 -0
  76. package/utils/admin-cli.js +353 -0
  77. package/utils/admin-pin.js +409 -0
  78. package/utils/detect-language-mismatches.js +454 -0
  79. package/utils/i18n-helper.js +128 -0
  80. package/utils/maintain-language-purity.js +433 -0
  81. package/utils/native-translations.js +478 -0
  82. package/utils/security.js +384 -0
  83. package/utils/test-complete-system.js +356 -0
  84. package/utils/test-console-i18n.js +402 -0
  85. package/utils/translate-mismatches.js +571 -0
  86. package/utils/validate-language-purity.js +531 -0
@@ -0,0 +1,461 @@
1
+ /**
2
+ * Auto-Run Script for I18N Management Toolkit
3
+ * Automatically executes the complete workflow after initialization
4
+ *
5
+ * Usage:
6
+ * npm run i18ntk:autorun
7
+ * node i18ntk-autorun.js
8
+ */
9
+
10
+ const { execSync, spawn } = require('child_process');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const readline = require('readline');
14
+ const UIi18n = require('./ui-i18n');
15
+ const settingsManager = require('../settings/settings-manager'); // This is already an instance
16
+
17
+ class AutoRunner {
18
+ constructor() {
19
+ this.ui = new UIi18n();
20
+ this.steps = [
21
+ { name: 'Analyze Translations', command: 'node main/i18ntk-analyze.js --no-prompt', required: true },
22
+ { name: 'Validate Translations', command: 'node main/i18ntk-validate.js --no-prompt', required: true },
23
+ { name: 'Check Usage', command: 'node main/i18ntk-usage.js --no-prompt', required: false },
24
+ { name: 'Complete Translations', command: 'node main/i18ntk-complete.js --no-prompt', required: false },
25
+ { name: 'Analyze Sizing', command: 'node main/i18ntk-sizing.js --no-prompt', required: false },
26
+ { name: 'Generate Summary', command: 'node main/i18ntk-summary.js --no-prompt', required: true }
27
+ ];
28
+ this.results = [];
29
+ // FIX: settingsManager is already an instance, not a constructor
30
+ this.settingsManager = settingsManager;
31
+ this.rl = null;
32
+ }
33
+
34
+ /**
35
+ * Initialize readline interface
36
+ */
37
+ initReadline() {
38
+ if (!this.rl && require.main === module) {
39
+ this.rl = readline.createInterface({
40
+ input: process.stdin,
41
+ output: process.stdout
42
+ });
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Close readline interface
48
+ */
49
+ closeReadline() {
50
+ if (this.rl) {
51
+ this.rl.close();
52
+ this.rl = null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Prompt for user input
58
+ */
59
+ async prompt(message) {
60
+ // Only use readline when run as main module
61
+ if (require.main === module) {
62
+ this.initReadline();
63
+ return new Promise((resolve) => {
64
+ this.rl.question(message, (answer) => {
65
+ this.closeReadline();
66
+ resolve(answer);
67
+ });
68
+ });
69
+ }
70
+ // When called from menu, just return empty string
71
+ return '';
72
+ }
73
+
74
+ /**
75
+ * Check if initialization has been completed
76
+ */
77
+ checkInitialization() {
78
+ const requiredFiles = [
79
+ './locales',
80
+ './ui-locales/en.json',
81
+ './settings/user-config.json'
82
+ ];
83
+
84
+ for (const file of requiredFiles) {
85
+ if (!fs.existsSync(file)) {
86
+ console.log(this.ui.t('hardcodedTexts.missingRequiredFile', { file }));
87
+ console.log(this.ui.t('hardcodedTexts.runInitializationFirst'));
88
+ return false;
89
+ }
90
+ }
91
+
92
+ console.log(this.ui.t('hardcodedTexts.initializationCheckPassed'));
93
+ return true;
94
+ }
95
+
96
+ /**
97
+ * Run a single command with proper error handling
98
+ */
99
+ async runCommand(step, quiet = false) {
100
+ const isStandalone = require.main === module;
101
+ const showOutput = isStandalone && !quiet;
102
+
103
+ if (showOutput) {
104
+ console.log(`\n${this.ui.t('hardcodedTexts.runningStep', { stepName: step.name })}`);
105
+ console.log(`${this.ui.t('hardcodedTexts.commandLabel', { command: step.command })}`);
106
+ console.log('-'.repeat(50));
107
+ } else {
108
+ // Show minimal output when called from menu
109
+ console.log(this.ui.t('hardcodedTexts.stepRunning', { stepName: step.name }));
110
+ }
111
+
112
+ try {
113
+ const startTime = Date.now();
114
+
115
+ // Execute command with real-time output
116
+ const result = execSync(step.command, {
117
+ stdio: 'inherit',
118
+ timeout: 120000 // 2 minutes timeout
119
+ });
120
+
121
+ const duration = Date.now() - startTime;
122
+
123
+ if (showOutput) {
124
+ console.log(this.ui.t('hardcodedTexts.stepCompletedWithTime', { stepName: step.name, duration }));
125
+ } else {
126
+ console.log(this.ui.t('hardcodedTexts.stepCompleted', { stepName: step.name }));
127
+ }
128
+
129
+ this.results.push({
130
+ step: step.name,
131
+ command: step.command,
132
+ status: 'success',
133
+ duration: duration,
134
+ required: step.required
135
+ });
136
+
137
+ return true;
138
+
139
+ } catch (error) {
140
+ if (showOutput) {
141
+ console.log(this.ui.t('hardcodedTexts.stepFailed', { stepName: step.name }));
142
+ console.log(this.ui.t('hardcodedTexts.errorLabel', { error: error.message }));
143
+ } else {
144
+ console.log(this.ui.t('hardcodedTexts.stepFailedWithError', { stepName: step.name, error: error.message }));
145
+ }
146
+
147
+ this.results.push({
148
+ step: step.name,
149
+ command: step.command,
150
+ status: 'failed',
151
+ error: error.message,
152
+ required: step.required
153
+ });
154
+
155
+ if (step.required) {
156
+ console.log(this.ui.t('hardcodedTexts.requiredStepFailed'));
157
+ return false;
158
+ } else {
159
+ console.log(this.ui.t('hardcodedTexts.optionalStepFailed'));
160
+ return true;
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Run all steps in sequence
167
+ */
168
+ async runAll(quiet = false) {
169
+ // Only show detailed output when run directly, not from menu
170
+ const isStandalone = require.main === module;
171
+ const showOutput = isStandalone && !quiet;
172
+
173
+ if (showOutput) {
174
+ console.log(this.ui.t('hardcodedTexts.startingAutoRunWorkflow'));
175
+ console.log('=' .repeat(60));
176
+ }
177
+
178
+ // Check initialization
179
+ if (!this.checkInitialization()) {
180
+ process.exit(1);
181
+ }
182
+
183
+ if (showOutput) {
184
+ console.log(`\n${this.ui.t('hardcodedTexts.workflowIncludesSteps', { count: this.steps.length })}`);
185
+ this.steps.forEach((step, index) => {
186
+ const required = step.required ? this.ui.t('hardcodedTexts.stepRequired') : this.ui.t('hardcodedTexts.stepOptional');
187
+ console.log(` ${index + 1}. ${step.name} ${required}`);
188
+ });
189
+
190
+ console.log(`\n${this.ui.t('hardcodedTexts.startingExecution')}`);
191
+ }
192
+
193
+ // Run each step
194
+ for (const step of this.steps) {
195
+ const success = await this.runCommand(step, quiet);
196
+
197
+ if (!success && step.required) {
198
+ if (showOutput) {
199
+ console.log(`\n${this.ui.t('hardcodedTexts.workflowStopped')}`);
200
+ } else {
201
+ console.log(this.ui.t('hardcodedTexts.workflowStopped'));
202
+ }
203
+ this.generateReport(quiet);
204
+ process.exit(1);
205
+ }
206
+
207
+ // Small delay between steps
208
+ await new Promise(resolve => setTimeout(resolve, 500));
209
+ }
210
+
211
+ if (showOutput) {
212
+ console.log(`\n${this.ui.t('hardcodedTexts.workflowCompleted')}`);
213
+ }
214
+
215
+ this.generateReport(quiet);
216
+
217
+ // Only show success message and prompt when run standalone
218
+ if (isStandalone) {
219
+ console.log(this.ui.t('operations.completed'));
220
+ await this.prompt(this.ui.t('hardcodedTexts.pressEnterToContinue'));
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Run specific steps only
226
+ */
227
+ async runSteps(stepNumbers) {
228
+ console.log(this.ui.t('hardcodedTexts.runningSelectedSteps'));
229
+ console.log('=' .repeat(60));
230
+
231
+ if (!this.checkInitialization()) {
232
+ process.exit(1);
233
+ }
234
+
235
+ for (const stepNum of stepNumbers) {
236
+ const stepIndex = stepNum - 1;
237
+ if (stepIndex >= 0 && stepIndex < this.steps.length) {
238
+ const step = this.steps[stepIndex];
239
+ await this.runCommand(step);
240
+ await new Promise(resolve => setTimeout(resolve, 1000));
241
+ } else {
242
+ console.log(this.ui.t('hardcodedTexts.invalidStepNumber', { stepNum }));
243
+ }
244
+ }
245
+
246
+ this.generateReport();
247
+
248
+ // Only show success message and prompt when run standalone or from menu
249
+ // Don't show when run as part of autorun workflow
250
+ if (require.main === module) {
251
+ console.log(this.ui.t('operations.completed'));
252
+ await this.prompt(this.ui.t('hardcodedTexts.pressEnterToContinue'));
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Generate execution report
258
+ */
259
+ generateReport(quiet = false) {
260
+ const isStandalone = require.main === module;
261
+ const showOutput = isStandalone && !quiet;
262
+
263
+ if (showOutput) {
264
+ console.log(`\n${this.ui.t('hardcodedTexts.executionReport')}`);
265
+ console.log('=' .repeat(60));
266
+ }
267
+
268
+ const successful = this.results.filter(r => r.status === 'success').length;
269
+ const failed = this.results.filter(r => r.status === 'failed').length;
270
+ const requiredFailed = this.results.filter(r => r.status === 'failed' && r.required).length;
271
+
272
+ if (showOutput) {
273
+ console.log(this.ui.t('hardcodedTexts.successfulSteps', { count: successful }));
274
+ console.log(this.ui.t('hardcodedTexts.failedSteps', { count: failed }));
275
+ console.log(this.ui.t('hardcodedTexts.requiredFailedSteps', { count: requiredFailed }));
276
+
277
+ console.log(`\n${this.ui.t('hardcodedTexts.stepDetails')}`);
278
+ this.results.forEach((result, index) => {
279
+ const status = result.status === 'success' ? '✅' : '❌';
280
+ const required = result.required ? '[REQ]' : '[OPT]';
281
+ const duration = result.duration ? ` (${result.duration}ms)` : '';
282
+
283
+ console.log(` ${status} ${required} ${result.step}${duration}`);
284
+ if (result.error) {
285
+ console.log(this.ui.t('hardcodedTexts.errorDetails', { error: result.error }));
286
+ }
287
+ });
288
+ }
289
+
290
+ // Save report to file
291
+ const report = {
292
+ timestamp: new Date().toISOString(),
293
+ summary: { successful, failed, requiredFailed },
294
+ steps: this.results
295
+ };
296
+
297
+ const reportPath = './i18ntk-reports/auto-run-report.json';
298
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
299
+
300
+ if (showOutput) {
301
+ console.log(`\n${this.ui.t('hardcodedTexts.reportSavedTo', { path: reportPath })}`);
302
+
303
+ // Overall status
304
+ const overallStatus = requiredFailed === 0 ? this.ui.t('hardcodedTexts.overallStatusSuccess') : this.ui.t('hardcodedTexts.overallStatusFailed');
305
+ console.log(`\n${this.ui.t('hardcodedTexts.overallStatus', { status: overallStatus })}`);
306
+ console.log('=' .repeat(60));
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Prompt for custom settings configuration
312
+ */
313
+ async promptForSettings() {
314
+ const rl = readline.createInterface({
315
+ input: process.stdin,
316
+ output: process.stdout,
317
+ terminal: true,
318
+ historySize: 0
319
+ });
320
+
321
+ const question = (prompt) => new Promise((resolve) => {
322
+ rl.question(prompt, resolve);
323
+ });
324
+
325
+ console.log(`\n${this.ui.t('hardcodedTexts.customSettingsConfiguration')}\n`);
326
+ console.log(`${this.ui.t('hardcodedTexts.pressEnterForDefaults')}\n`);
327
+
328
+ try {
329
+ const currentSettings = this.settingsManager.getSettings();
330
+ const newSettings = { ...currentSettings };
331
+
332
+ // Source directory
333
+ const sourceDir = await question(this.ui.t('hardcodedTexts.sourceDirPrompt', { default: currentSettings.sourceDir || './locales' }));
334
+ if (sourceDir.trim()) {
335
+ newSettings.sourceDir = sourceDir.trim();
336
+ }
337
+
338
+ // Source language
339
+ const sourceLang = await question(this.ui.t('hardcodedTexts.sourceLangPrompt', { default: currentSettings.sourceLanguage || 'en' }));
340
+ if (sourceLang.trim()) {
341
+ newSettings.sourceLanguage = sourceLang.trim();
342
+ }
343
+
344
+ // Target languages
345
+ const defaultLangs = currentSettings.defaultLanguages || ['de', 'es', 'fr', 'ru'];
346
+ const targetLangs = await question(this.ui.t('hardcodedTexts.targetLangsPrompt', { default: defaultLangs.join(', ') }));
347
+ if (targetLangs.trim()) {
348
+ newSettings.defaultLanguages = targetLangs.split(',').map(lang => lang.trim());
349
+ }
350
+
351
+ // Translation marker
352
+ const marker = await question(this.ui.t('hardcodedTexts.translationMarkerPrompt', { default: currentSettings.processing?.notTranslatedMarker || 'NOT_TRANSLATED' }));
353
+ if (marker.trim()) {
354
+ if (!newSettings.processing) newSettings.processing = {};
355
+ newSettings.processing.notTranslatedMarker = marker.trim();
356
+ }
357
+
358
+ // Output directory
359
+ const outputDir = await question(this.ui.t('hardcodedTexts.outputDirPrompt', { default: currentSettings.outputDir || './i18ntk-reports' }));
360
+ if (outputDir.trim()) {
361
+ newSettings.outputDir = outputDir.trim();
362
+ }
363
+
364
+ // Save settings
365
+ this.settingsManager.saveSettings(newSettings);
366
+ console.log(`\n${this.ui.t('hardcodedTexts.settingsUpdatedSuccessfully')}\n`);
367
+
368
+ } catch (error) {
369
+ console.error(this.ui.t('hardcodedTexts.errorConfiguringSettings'), error.message);
370
+ } finally {
371
+ rl.close();
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Show help information
377
+ */
378
+ showHelp() {
379
+ console.log(this.ui.t('hardcodedTexts.autoRunScriptTitle'));
380
+ console.log('=' .repeat(40));
381
+ console.log(`\n${this.ui.t('hardcodedTexts.usageTitle')}`);
382
+ console.log(` ${this.ui.t('hardcodedTexts.runAllSteps')}`);
383
+ console.log(` ${this.ui.t('hardcodedTexts.configureSettingsFirst')}`);
384
+ console.log(` ${this.ui.t('hardcodedTexts.runSpecificSteps')}`);
385
+ console.log(` ${this.ui.t('hardcodedTexts.showHelp')}`);
386
+ console.log(`\n${this.ui.t('hardcodedTexts.availableSteps')}`);
387
+ this.steps.forEach((step, index) => {
388
+ const required = step.required ? this.ui.t('hardcodedTexts.stepRequired') : this.ui.t('hardcodedTexts.stepOptional');
389
+ console.log(` ${index + 1}. ${step.name} ${required}`);
390
+ });
391
+ console.log(`\n${this.ui.t('hardcodedTexts.examplesTitle')}`);
392
+ console.log(` ${this.ui.t('hardcodedTexts.configExample')}`);
393
+ console.log(` ${this.ui.t('hardcodedTexts.stepsExample1')}`);
394
+ console.log(` ${this.ui.t('hardcodedTexts.stepsExample2')}`);
395
+ }
396
+ }
397
+
398
+ // Parse command line arguments
399
+ function parseArgs() {
400
+ const args = process.argv.slice(2);
401
+
402
+ if (args.includes('--help') || args.includes('-h')) {
403
+ return { action: 'help' };
404
+ }
405
+
406
+ if (args.includes('--config') || args.includes('-c')) {
407
+ return { action: 'config' };
408
+ }
409
+
410
+ const stepsIndex = args.indexOf('--steps');
411
+ if (stepsIndex !== -1 && args[stepsIndex + 1]) {
412
+ const stepNumbers = args[stepsIndex + 1]
413
+ .split(',')
414
+ .map(s => parseInt(s.trim()))
415
+ .filter(n => !isNaN(n));
416
+ return { action: 'steps', steps: stepNumbers };
417
+ }
418
+
419
+ return { action: 'all' };
420
+ }
421
+
422
+ // Main execution
423
+ if (require.main === module) {
424
+ const runner = new AutoRunner();
425
+ const args = parseArgs();
426
+
427
+ switch (args.action) {
428
+ case 'help':
429
+ runner.showHelp();
430
+ break;
431
+ case 'config':
432
+ runner.promptForSettings().then(() => {
433
+ console.log(`\n${runner.ui.t('hardcodedTexts.configurationComplete')}`);
434
+ console.log(`${runner.ui.t('hardcodedTexts.runAutoRunCommand')}\n`);
435
+ }).catch(error => {
436
+ console.error(runner.ui.t('hardcodedTexts.configurationFailed'), error.message);
437
+ process.exit(1);
438
+ });
439
+ break;
440
+ case 'steps':
441
+ if (args.steps.length === 0) {
442
+ console.log(runner.ui.t('hardcodedTexts.noValidStepNumbers'));
443
+ runner.showHelp();
444
+ process.exit(1);
445
+ }
446
+ runner.runSteps(args.steps).catch(error => {
447
+ console.error(runner.ui.t('hardcodedTexts.autoRunFailed'), error.message);
448
+ process.exit(1);
449
+ });
450
+ break;
451
+ case 'all':
452
+ default:
453
+ runner.runAll().catch(error => {
454
+ console.error(runner.ui.t('hardcodedTexts.autoRunFailed'), error.message);
455
+ process.exit(1);
456
+ });
457
+ break;
458
+ }
459
+ }
460
+
461
+ module.exports = AutoRunner;