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,335 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Console Translations Checker
5
+ * Ensures the i18n-management-toolkit package itself has complete translation support
6
+ * across all native languages in ui-locales directory.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { performance } = require('perf_hooks');
12
+
13
+ class ConsoleTranslationsChecker {
14
+ constructor() {
15
+ this.uiLocalesDir = path.join(__dirname, '..', '..', 'ui-locales');
16
+ this.referenceLanguage = 'en';
17
+ this.supportedLanguages = ['de', 'es', 'fr', 'ja', 'ru', 'zh'];
18
+ this.results = {
19
+ totalKeys: 0,
20
+ languages: {},
21
+ missingKeys: {},
22
+ extraKeys: {},
23
+ issues: []
24
+ };
25
+ }
26
+
27
+ /**
28
+ * Load and parse a JSON translation file
29
+ */
30
+ loadTranslationFile(language) {
31
+ const filePath = path.join(this.uiLocalesDir, `${language}.json`);
32
+
33
+ try {
34
+ if (!fs.existsSync(filePath)) {
35
+ throw new Error(`Translation file not found: ${filePath}`);
36
+ }
37
+
38
+ const content = fs.readFileSync(filePath, 'utf8');
39
+ return JSON.parse(content);
40
+ } catch (error) {
41
+ this.results.issues.push(`āŒ Error loading ${language}.json: ${error.message}`);
42
+ return null;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Recursively extract all keys from a nested object
48
+ */
49
+ extractKeys(obj, prefix = '') {
50
+ const keys = [];
51
+
52
+ for (const [key, value] of Object.entries(obj)) {
53
+ const fullKey = prefix ? `${prefix}.${key}` : key;
54
+
55
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
56
+ keys.push(...this.extractKeys(value, fullKey));
57
+ } else {
58
+ keys.push(fullKey);
59
+ }
60
+ }
61
+
62
+ return keys;
63
+ }
64
+
65
+ /**
66
+ * Check if a translation value is empty or placeholder
67
+ */
68
+ isEmptyTranslation(value) {
69
+ if (typeof value !== 'string') return false;
70
+
71
+ const trimmed = value.trim();
72
+ return trimmed === '' ||
73
+ trimmed === 'TODO' ||
74
+ trimmed === '[TODO]' ||
75
+ trimmed.startsWith('[') && trimmed.endsWith(']');
76
+ }
77
+
78
+ /**
79
+ * Count empty or incomplete translations in an object
80
+ */
81
+ countEmptyTranslations(obj, prefix = '') {
82
+ let count = 0;
83
+
84
+ for (const [key, value] of Object.entries(obj)) {
85
+ const fullKey = prefix ? `${prefix}.${key}` : key;
86
+
87
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
88
+ count += this.countEmptyTranslations(value, fullKey);
89
+ } else if (this.isEmptyTranslation(value)) {
90
+ count++;
91
+ }
92
+ }
93
+
94
+ return count;
95
+ }
96
+
97
+ /**
98
+ * Analyze translation completeness for all languages
99
+ */
100
+ analyzeTranslations() {
101
+ console.log('🌐 CONSOLE TRANSLATIONS ANALYSIS');
102
+ console.log('============================================================');
103
+ console.log(`šŸ“ UI Locales directory: ${this.uiLocalesDir}`);
104
+ console.log(`šŸ”¤ Reference language: ${this.referenceLanguage}`);
105
+ console.log('');
106
+
107
+ // Load reference language (English)
108
+ const referenceData = this.loadTranslationFile(this.referenceLanguage);
109
+ if (!referenceData) {
110
+ console.log(`āŒ Cannot load reference language: ${this.referenceLanguage}`);
111
+ return false;
112
+ }
113
+
114
+ const referenceKeys = this.extractKeys(referenceData);
115
+ this.results.totalKeys = referenceKeys.length;
116
+
117
+ console.log(`šŸ“Š Total translation keys in reference: ${referenceKeys.length}`);
118
+ console.log('');
119
+
120
+ // Analyze each supported language
121
+ for (const language of this.supportedLanguages) {
122
+ console.log(`šŸ” Analyzing ${language}...`);
123
+
124
+ const languageData = this.loadTranslationFile(language);
125
+ if (!languageData) {
126
+ this.results.languages[language] = {
127
+ status: 'error',
128
+ completeness: 0,
129
+ totalKeys: 0,
130
+ missingKeys: referenceKeys.length,
131
+ emptyTranslations: 0
132
+ };
133
+ continue;
134
+ }
135
+
136
+ const languageKeys = this.extractKeys(languageData);
137
+ const missingKeys = referenceKeys.filter(key => !languageKeys.includes(key));
138
+ const extraKeys = languageKeys.filter(key => !referenceKeys.includes(key));
139
+ const emptyTranslations = this.countEmptyTranslations(languageData);
140
+
141
+ const completeness = Math.round(((referenceKeys.length - missingKeys.length - emptyTranslations) / referenceKeys.length) * 100);
142
+
143
+ this.results.languages[language] = {
144
+ status: missingKeys.length === 0 && emptyTranslations === 0 ? 'complete' : 'incomplete',
145
+ completeness,
146
+ totalKeys: languageKeys.length,
147
+ missingKeys: missingKeys.length,
148
+ emptyTranslations,
149
+ extraKeys: extraKeys.length
150
+ };
151
+
152
+ if (missingKeys.length > 0) {
153
+ this.results.missingKeys[language] = missingKeys;
154
+ }
155
+
156
+ if (extraKeys.length > 0) {
157
+ this.results.extraKeys[language] = extraKeys;
158
+ }
159
+
160
+ // Status icon
161
+ const statusIcon = completeness === 100 ? 'āœ…' : completeness >= 90 ? 'āš ļø' : 'āŒ';
162
+ console.log(` ${statusIcon} ${language}: ${completeness}% complete (${languageKeys.length} keys, ${missingKeys.length} missing, ${emptyTranslations} empty)`);
163
+ }
164
+
165
+ return true;
166
+ }
167
+
168
+ /**
169
+ * Generate detailed report
170
+ */
171
+ generateReport() {
172
+ console.log('');
173
+ console.log('šŸ“Š TRANSLATION COMPLETENESS SUMMARY');
174
+ console.log('============================================================');
175
+
176
+ const languageStats = [];
177
+ let totalComplete = 0;
178
+
179
+ for (const [language, stats] of Object.entries(this.results.languages)) {
180
+ languageStats.push({
181
+ language,
182
+ completeness: stats.completeness,
183
+ status: stats.status
184
+ });
185
+
186
+ if (stats.completeness === 100) {
187
+ totalComplete++;
188
+ }
189
+ }
190
+
191
+ // Sort by completeness
192
+ languageStats.sort((a, b) => b.completeness - a.completeness);
193
+
194
+ console.log(`šŸŒ Languages analyzed: ${this.supportedLanguages.length}`);
195
+ console.log(`āœ… Fully complete: ${totalComplete}/${this.supportedLanguages.length}`);
196
+ console.log(`šŸ“Š Average completeness: ${Math.round(languageStats.reduce((sum, lang) => sum + lang.completeness, 0) / languageStats.length)}%`);
197
+ console.log('');
198
+
199
+ console.log('šŸ“‹ LANGUAGE STATUS:');
200
+ console.log('------------------------------------------------------------');
201
+ for (const lang of languageStats) {
202
+ const icon = lang.completeness === 100 ? 'āœ…' : lang.completeness >= 90 ? 'āš ļø' : 'āŒ';
203
+ const status = lang.status === 'complete' ? 'Complete' : 'Incomplete';
204
+ console.log(`${icon} ${lang.language.toUpperCase()}: ${lang.completeness}% - ${status}`);
205
+ }
206
+
207
+ // Show missing keys for incomplete languages
208
+ const incompleteLanguages = Object.entries(this.results.languages)
209
+ .filter(([, stats]) => stats.completeness < 100)
210
+ .sort(([, a], [, b]) => b.completeness - a.completeness);
211
+
212
+ if (incompleteLanguages.length > 0) {
213
+ console.log('');
214
+ console.log('āš ļø INCOMPLETE TRANSLATIONS:');
215
+ console.log('------------------------------------------------------------');
216
+
217
+ for (const [language, stats] of incompleteLanguages) {
218
+ console.log(`\nšŸ”¤ ${language.toUpperCase()}:`);
219
+ console.log(` Missing keys: ${stats.missingKeys}`);
220
+ console.log(` Empty translations: ${stats.emptyTranslations}`);
221
+
222
+ if (this.results.missingKeys[language] && this.results.missingKeys[language].length > 0) {
223
+ const sampleMissing = this.results.missingKeys[language].slice(0, 5);
224
+ console.log(` Sample missing: ${sampleMissing.join(', ')}`);
225
+ if (this.results.missingKeys[language].length > 5) {
226
+ console.log(` ... and ${this.results.missingKeys[language].length - 5} more`);
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ // Recommendations
233
+ console.log('');
234
+ console.log('šŸ’” RECOMMENDATIONS:');
235
+ console.log('------------------------------------------------------------');
236
+
237
+ if (totalComplete === this.supportedLanguages.length) {
238
+ console.log('šŸŽ‰ Excellent! All console translations are complete.');
239
+ console.log('āœ… The i18n-management-toolkit package has full translation support.');
240
+ } else {
241
+ console.log('šŸ”§ To achieve 100% translation coverage:');
242
+ console.log('1. Complete missing translations in incomplete languages');
243
+ console.log('2. Fill in empty translation values');
244
+ console.log('3. Re-run this checker to verify improvements');
245
+ console.log('');
246
+ console.log('šŸ“ Priority languages (lowest completeness first):');
247
+ const priorityLangs = incompleteLanguages.slice(0, 3);
248
+ for (const [language, stats] of priorityLangs) {
249
+ console.log(` • ${language}: ${stats.completeness}% complete`);
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Save detailed report to file
256
+ */
257
+ saveReport() {
258
+ const reportDir = path.join(__dirname, 'reports');
259
+ if (!fs.existsSync(reportDir)) {
260
+ fs.mkdirSync(reportDir, { recursive: true });
261
+ }
262
+
263
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
264
+ const reportPath = path.join(reportDir, `console-translations-${timestamp}.json`);
265
+
266
+ const report = {
267
+ timestamp: new Date().toISOString(),
268
+ summary: {
269
+ totalKeys: this.results.totalKeys,
270
+ languagesAnalyzed: this.supportedLanguages.length,
271
+ fullyComplete: Object.values(this.results.languages).filter(lang => lang.completeness === 100).length,
272
+ averageCompleteness: Math.round(Object.values(this.results.languages).reduce((sum, lang) => sum + lang.completeness, 0) / this.supportedLanguages.length)
273
+ },
274
+ languages: this.results.languages,
275
+ missingKeys: this.results.missingKeys,
276
+ extraKeys: this.results.extraKeys,
277
+ issues: this.results.issues
278
+ };
279
+
280
+ try {
281
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
282
+ console.log('');
283
+ console.log(`šŸ“„ Detailed report saved: ${reportPath}`);
284
+ } catch (error) {
285
+ console.log(`āŒ Error saving report: ${error.message}`);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Run the complete analysis
291
+ */
292
+ async run() {
293
+ const startTime = performance.now();
294
+
295
+ console.log('šŸš€ Starting Console Translations Analysis...');
296
+ console.log('');
297
+
298
+ // Check if ui-locales directory exists
299
+ if (!fs.existsSync(this.uiLocalesDir)) {
300
+ console.log(`āŒ UI locales directory not found: ${this.uiLocalesDir}`);
301
+ return false;
302
+ }
303
+
304
+ // Run analysis
305
+ const success = this.analyzeTranslations();
306
+ if (!success) {
307
+ return false;
308
+ }
309
+
310
+ // Generate and display report
311
+ this.generateReport();
312
+
313
+ // Save detailed report
314
+ this.saveReport();
315
+
316
+ const endTime = performance.now();
317
+ console.log('');
318
+ console.log(`ā±ļø Analysis completed in ${Math.round(endTime - startTime)}ms`);
319
+
320
+ return true;
321
+ }
322
+ }
323
+
324
+ // Run the checker if called directly
325
+ if (require.main === module) {
326
+ const checker = new ConsoleTranslationsChecker();
327
+ checker.run().then(success => {
328
+ process.exit(success ? 0 : 1);
329
+ }).catch(error => {
330
+ console.error('āŒ Fatal error:', error.message);
331
+ process.exit(1);
332
+ });
333
+ }
334
+
335
+ module.exports = ConsoleTranslationsChecker;