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.
- package/CHANGELOG.md +401 -0
- package/LICENSE +21 -0
- package/README.md +507 -0
- package/dev/README.md +37 -0
- package/dev/debug/README.md +30 -0
- package/dev/debug/complete-console-translations.js +295 -0
- package/dev/debug/console-key-checker.js +408 -0
- package/dev/debug/console-translations.js +335 -0
- package/dev/debug/debugger.js +408 -0
- package/dev/debug/export-missing-keys.js +432 -0
- package/dev/debug/final-normalize.js +236 -0
- package/dev/debug/find-extra-keys.js +68 -0
- package/dev/debug/normalize-locales.js +153 -0
- package/dev/debug/refactor-locales.js +240 -0
- package/dev/debug/reorder-locales.js +85 -0
- package/dev/debug/replace-hardcoded-console.js +378 -0
- package/docs/INSTALLATION.md +449 -0
- package/docs/README.md +222 -0
- package/docs/TODO_ROADMAP.md +279 -0
- package/docs/api/API_REFERENCE.md +377 -0
- package/docs/api/COMPONENTS.md +492 -0
- package/docs/api/CONFIGURATION.md +651 -0
- package/docs/api/NPM_PUBLISHING_GUIDE.md +434 -0
- package/docs/debug/DEBUG_README.md +30 -0
- package/docs/debug/DEBUG_TOOLS.md +494 -0
- package/docs/development/AGENTS.md +351 -0
- package/docs/development/DEVELOPMENT_RULES.md +165 -0
- package/docs/development/DEV_README.md +37 -0
- package/docs/release-notes/RELEASE_NOTES_v1.0.0.md +173 -0
- package/docs/release-notes/RELEASE_NOTES_v1.6.0.md +141 -0
- package/docs/release-notes/RELEASE_NOTES_v1.6.1.md +185 -0
- package/docs/release-notes/RELEASE_NOTES_v1.6.3.md +199 -0
- package/docs/reports/ANALYSIS_README.md +17 -0
- package/docs/reports/CONSOLE_MISMATCH_BUG_REPORT_v1.5.0.md +181 -0
- package/docs/reports/SIZING_README.md +18 -0
- package/docs/reports/SUMMARY_README.md +18 -0
- package/docs/reports/TRANSLATION_BUG_REPORT_v1.5.0.md +129 -0
- package/docs/reports/USAGE_README.md +18 -0
- package/docs/reports/VALIDATION_README.md +18 -0
- package/locales/de/auth.json +3 -0
- package/locales/de/common.json +16 -0
- package/locales/de/pagination.json +6 -0
- package/locales/en/auth.json +3 -0
- package/locales/en/common.json +16 -0
- package/locales/en/pagination.json +6 -0
- package/locales/es/auth.json +3 -0
- package/locales/es/common.json +16 -0
- package/locales/es/pagination.json +6 -0
- package/locales/fr/auth.json +3 -0
- package/locales/fr/common.json +16 -0
- package/locales/fr/pagination.json +6 -0
- package/locales/ru/auth.json +3 -0
- package/locales/ru/common.json +16 -0
- package/locales/ru/pagination.json +6 -0
- package/main/i18ntk-analyze.js +625 -0
- package/main/i18ntk-autorun.js +461 -0
- package/main/i18ntk-complete.js +494 -0
- package/main/i18ntk-init.js +686 -0
- package/main/i18ntk-manage.js +848 -0
- package/main/i18ntk-sizing.js +557 -0
- package/main/i18ntk-summary.js +671 -0
- package/main/i18ntk-usage.js +1282 -0
- package/main/i18ntk-validate.js +762 -0
- package/main/ui-i18n.js +332 -0
- package/package.json +152 -0
- package/scripts/fix-missing-translation-keys.js +214 -0
- package/scripts/verify-package.js +168 -0
- package/ui-locales/de.json +637 -0
- package/ui-locales/en.json +688 -0
- package/ui-locales/es.json +637 -0
- package/ui-locales/fr.json +637 -0
- package/ui-locales/ja.json +637 -0
- package/ui-locales/ru.json +637 -0
- package/ui-locales/zh.json +637 -0
- package/utils/admin-auth.js +317 -0
- package/utils/admin-cli.js +353 -0
- package/utils/admin-pin.js +409 -0
- package/utils/detect-language-mismatches.js +454 -0
- package/utils/i18n-helper.js +128 -0
- package/utils/maintain-language-purity.js +433 -0
- package/utils/native-translations.js +478 -0
- package/utils/security.js +384 -0
- package/utils/test-complete-system.js +356 -0
- package/utils/test-console-i18n.js +402 -0
- package/utils/translate-mismatches.js +571 -0
- 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;
|