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,531 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Language Purity Validator
5
+ *
6
+ * This script validates that each locale file contains only content in its native language.
7
+ * It can be integrated into CI/CD pipelines and development workflows.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ class LanguagePurityValidator {
14
+ constructor() {
15
+ this.localesDir = path.join(__dirname, 'ui-locales');
16
+ this.sourceLanguage = 'en';
17
+ this.exitCode = 0;
18
+
19
+ // Language validation rules
20
+ this.validationRules = {
21
+ de: {
22
+ name: 'German',
23
+ allowedMarkers: [], // No markers should remain
24
+ forbiddenMarkers: ['[TRANSLATE]', '[DE]', '[NOT TRANSLATED]'],
25
+ forbiddenPhrases: [
26
+ 'debug tools', 'settings', 'configuration', 'invalid choice',
27
+ 'please select', 'back to main menu', 'error', 'warning',
28
+ 'success', 'failed', 'loading', 'saving', 'full system debug',
29
+ 'configuration debug', 'translation debug', 'performance debug',
30
+ 'admin pin setup', 'enter admin pin', 'confirm admin pin',
31
+ 'authentication failed', 'access denied', 'security log'
32
+ ],
33
+ requiredCharacteristics: {
34
+ // German-specific characteristics
35
+ hasGermanArticles: ['der', 'die', 'das', 'ein', 'eine'],
36
+ hasGermanConjunctions: ['und', 'oder', 'aber'],
37
+ hasGermanPrepositions: ['mit', 'von', 'zu', 'für', 'auf', 'in', 'an']
38
+ }
39
+ },
40
+ fr: {
41
+ name: 'French',
42
+ allowedMarkers: [],
43
+ forbiddenMarkers: ['[TRANSLATE]', '[FR]', '[NOT TRANSLATED]'],
44
+ forbiddenPhrases: [
45
+ 'debug tools', 'settings', 'configuration', 'invalid choice',
46
+ 'please select', 'back to main menu', 'error', 'warning',
47
+ 'success', 'failed', 'loading', 'saving', 'full system debug',
48
+ 'configuration debug', 'translation debug', 'performance debug',
49
+ 'admin pin setup', 'enter admin pin', 'confirm admin pin',
50
+ 'authentication failed', 'access denied', 'security log'
51
+ ],
52
+ requiredCharacteristics: {
53
+ // French-specific characteristics
54
+ hasFrenchArticles: ['le', 'la', 'les', 'un', 'une', 'des'],
55
+ hasFrenchConjunctions: ['et', 'ou', 'mais'],
56
+ hasFrenchPrepositions: ['de', 'du', 'pour', 'sur', 'dans', 'par', 'avec']
57
+ }
58
+ },
59
+ es: {
60
+ name: 'Spanish',
61
+ allowedMarkers: [],
62
+ forbiddenMarkers: ['[TRANSLATE]', '[ES]', '[NOT TRANSLATED]'],
63
+ forbiddenPhrases: [
64
+ 'debug tools', 'settings', 'configuration', 'invalid choice',
65
+ 'please select', 'back to main menu', 'error', 'warning',
66
+ 'success', 'failed', 'loading', 'saving', 'full system debug',
67
+ 'configuration debug', 'translation debug', 'performance debug',
68
+ 'admin pin setup', 'enter admin pin', 'confirm admin pin',
69
+ 'authentication failed', 'access denied', 'security log'
70
+ ],
71
+ requiredCharacteristics: {
72
+ // Spanish-specific characteristics
73
+ hasSpanishArticles: ['el', 'la', 'los', 'las', 'un', 'una'],
74
+ hasSpanishConjunctions: ['y', 'o', 'pero'],
75
+ hasSpanishPrepositions: ['de', 'del', 'para', 'por', 'en', 'a', 'con']
76
+ }
77
+ },
78
+ ru: {
79
+ name: 'Russian',
80
+ allowedMarkers: [],
81
+ forbiddenMarkers: ['[TRANSLATE]', '[RU]', '[NOT TRANSLATED]'],
82
+ forbiddenPhrases: [
83
+ 'debug tools', 'settings', 'configuration', 'invalid choice',
84
+ 'please select', 'back to main menu', 'error', 'warning',
85
+ 'success', 'failed', 'loading', 'saving', 'full system debug',
86
+ 'configuration debug', 'translation debug', 'performance debug',
87
+ 'admin pin setup', 'enter admin pin', 'confirm admin pin',
88
+ 'authentication failed', 'access denied', 'security log'
89
+ ],
90
+ requiredCharacteristics: {
91
+ // Russian-specific characteristics (Cyrillic)
92
+ hasCyrillic: true
93
+ }
94
+ },
95
+ ja: {
96
+ name: 'Japanese',
97
+ allowedMarkers: [],
98
+ forbiddenMarkers: ['[TRANSLATE]', '[JA]', '[NOT TRANSLATED]'],
99
+ forbiddenPhrases: [
100
+ 'debug tools', 'settings', 'configuration', 'invalid choice',
101
+ 'please select', 'back to main menu', 'error', 'warning',
102
+ 'success', 'failed', 'loading', 'saving', 'full system debug',
103
+ 'configuration debug', 'translation debug', 'performance debug',
104
+ 'admin pin setup', 'enter admin pin', 'confirm admin pin',
105
+ 'authentication failed', 'access denied', 'security log'
106
+ ],
107
+ requiredCharacteristics: {
108
+ // Japanese-specific characteristics
109
+ hasJapanese: true // Hiragana, Katakana, or Kanji
110
+ }
111
+ },
112
+ zh: {
113
+ name: 'Chinese',
114
+ allowedMarkers: [],
115
+ forbiddenMarkers: ['[TRANSLATE]', '[ZH]', '[NOT TRANSLATED]'],
116
+ forbiddenPhrases: [
117
+ 'debug tools', 'settings', 'configuration', 'invalid choice',
118
+ 'please select', 'back to main menu', 'error', 'warning',
119
+ 'success', 'failed', 'loading', 'saving', 'full system debug',
120
+ 'configuration debug', 'translation debug', 'performance debug',
121
+ 'admin pin setup', 'enter admin pin', 'confirm admin pin',
122
+ 'authentication failed', 'access denied', 'security log'
123
+ ],
124
+ requiredCharacteristics: {
125
+ // Chinese-specific characteristics
126
+ hasChinese: true // Chinese characters
127
+ }
128
+ }
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Validate all locale files
134
+ */
135
+ async validateAll() {
136
+ console.log('šŸ” Language Purity Validator');
137
+ console.log('=============================\n');
138
+
139
+ const localeFiles = this.getLocaleFiles();
140
+ const results = {
141
+ totalFiles: 0,
142
+ validFiles: 0,
143
+ invalidFiles: 0,
144
+ totalViolations: 0,
145
+ fileResults: {}
146
+ };
147
+
148
+ for (const file of localeFiles) {
149
+ if (file.language === this.sourceLanguage) {
150
+ continue; // Skip source language
151
+ }
152
+
153
+ console.log(`šŸ“„ Validating ${file.language}.json (${this.validationRules[file.language]?.name || file.language})...`);
154
+ const fileResult = await this.validateFile(file);
155
+
156
+ results.totalFiles++;
157
+ results.fileResults[file.language] = fileResult;
158
+ results.totalViolations += fileResult.violations.length;
159
+
160
+ if (fileResult.isValid) {
161
+ results.validFiles++;
162
+ console.log(` āœ… Valid - No language purity violations`);
163
+ } else {
164
+ results.invalidFiles++;
165
+ console.log(` āŒ Invalid - ${fileResult.violations.length} violation(s) found`);
166
+ this.exitCode = 1;
167
+ }
168
+ }
169
+
170
+ this.generateSummary(results);
171
+ return results;
172
+ }
173
+
174
+ /**
175
+ * Get all locale files
176
+ */
177
+ getLocaleFiles() {
178
+ return fs.readdirSync(this.localesDir)
179
+ .filter(file => file.endsWith('.json'))
180
+ .map(file => ({
181
+ filename: file,
182
+ language: path.basename(file, '.json'),
183
+ path: path.join(this.localesDir, file)
184
+ }));
185
+ }
186
+
187
+ /**
188
+ * Validate a single file
189
+ */
190
+ async validateFile(file) {
191
+ const result = {
192
+ filename: file.filename,
193
+ language: file.language,
194
+ isValid: true,
195
+ violations: [],
196
+ stats: {
197
+ totalKeys: 0,
198
+ validKeys: 0,
199
+ invalidKeys: 0
200
+ }
201
+ };
202
+
203
+ try {
204
+ const content = fs.readFileSync(file.path, 'utf8');
205
+ const translations = JSON.parse(content);
206
+ const rules = this.validationRules[file.language];
207
+
208
+ if (!rules) {
209
+ result.violations.push({
210
+ type: 'no_validation_rules',
211
+ message: `No validation rules defined for language: ${file.language}`
212
+ });
213
+ result.isValid = false;
214
+ return result;
215
+ }
216
+
217
+ this.validateObject(translations, '', rules, result);
218
+
219
+ result.isValid = result.violations.length === 0;
220
+
221
+ } catch (error) {
222
+ result.violations.push({
223
+ type: 'file_error',
224
+ message: `Error reading file: ${error.message}`
225
+ });
226
+ result.isValid = false;
227
+ }
228
+
229
+ return result;
230
+ }
231
+
232
+ /**
233
+ * Recursively validate object properties
234
+ */
235
+ validateObject(obj, keyPath, rules, result) {
236
+ for (const [key, value] of Object.entries(obj)) {
237
+ const currentPath = keyPath ? `${keyPath}.${key}` : key;
238
+
239
+ if (typeof value === 'string') {
240
+ result.stats.totalKeys++;
241
+ const violations = this.validateString(value, currentPath, rules);
242
+
243
+ if (violations.length > 0) {
244
+ result.violations.push(...violations);
245
+ result.stats.invalidKeys++;
246
+ } else {
247
+ result.stats.validKeys++;
248
+ }
249
+ } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
250
+ this.validateObject(value, currentPath, rules, result);
251
+ } else if (Array.isArray(value)) {
252
+ value.forEach((item, index) => {
253
+ if (typeof item === 'string') {
254
+ result.stats.totalKeys++;
255
+ const violations = this.validateString(item, `${currentPath}[${index}]`, rules);
256
+
257
+ if (violations.length > 0) {
258
+ result.violations.push(...violations);
259
+ result.stats.invalidKeys++;
260
+ } else {
261
+ result.stats.validKeys++;
262
+ }
263
+ }
264
+ });
265
+ }
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Validate a single string
271
+ */
272
+ validateString(text, keyPath, rules) {
273
+ const violations = [];
274
+ const lowerText = text.toLowerCase();
275
+
276
+ // Check for forbidden markers
277
+ for (const marker of rules.forbiddenMarkers) {
278
+ if (text.includes(marker)) {
279
+ violations.push({
280
+ type: 'forbidden_marker',
281
+ key: keyPath,
282
+ value: text,
283
+ issue: `Contains forbidden marker: ${marker}`,
284
+ severity: 'error'
285
+ });
286
+ }
287
+ }
288
+
289
+ // Check for forbidden phrases
290
+ for (const phrase of rules.forbiddenPhrases) {
291
+ if (lowerText.includes(phrase.toLowerCase())) {
292
+ violations.push({
293
+ type: 'forbidden_phrase',
294
+ key: keyPath,
295
+ value: text,
296
+ issue: `Contains English phrase: "${phrase}"`,
297
+ severity: 'error'
298
+ });
299
+ }
300
+ }
301
+
302
+ // Check for common English words that shouldn't be in foreign languages
303
+ const englishWords = ['the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by'];
304
+ const foundEnglishWords = [];
305
+
306
+ for (const word of englishWords) {
307
+ const regex = new RegExp(`\\b${word}\\b`, 'i');
308
+ if (regex.test(text)) {
309
+ foundEnglishWords.push(word);
310
+ }
311
+ }
312
+
313
+ if (foundEnglishWords.length > 0) {
314
+ violations.push({
315
+ type: 'english_words',
316
+ key: keyPath,
317
+ value: text,
318
+ issue: `Contains English words: ${foundEnglishWords.join(', ')}`,
319
+ severity: 'warning'
320
+ });
321
+ }
322
+
323
+ // Language-specific validation
324
+ if (rules.requiredCharacteristics) {
325
+ const langViolations = this.validateLanguageCharacteristics(text, keyPath, rules.requiredCharacteristics);
326
+ violations.push(...langViolations);
327
+ }
328
+
329
+ return violations;
330
+ }
331
+
332
+ /**
333
+ * Validate language-specific characteristics
334
+ */
335
+ validateLanguageCharacteristics(text, keyPath, characteristics) {
336
+ const violations = [];
337
+
338
+ // Check for Cyrillic (Russian)
339
+ if (characteristics.hasCyrillic) {
340
+ const hasCyrillicChars = /[а-яё]/i.test(text);
341
+ if (!hasCyrillicChars && text.length > 10) { // Only check longer strings
342
+ violations.push({
343
+ type: 'missing_cyrillic',
344
+ key: keyPath,
345
+ value: text,
346
+ issue: 'Long text should contain Cyrillic characters for Russian',
347
+ severity: 'warning'
348
+ });
349
+ }
350
+ }
351
+
352
+ // Check for Japanese characters
353
+ if (characteristics.hasJapanese) {
354
+ const hasJapaneseChars = /[ć²ć‚‰ćŒćŖć‚«ć‚æć‚«ćƒŠäø€-龯]/u.test(text);
355
+ if (!hasJapaneseChars && text.length > 10) {
356
+ violations.push({
357
+ type: 'missing_japanese',
358
+ key: keyPath,
359
+ value: text,
360
+ issue: 'Long text should contain Japanese characters',
361
+ severity: 'warning'
362
+ });
363
+ }
364
+ }
365
+
366
+ // Check for Chinese characters
367
+ if (characteristics.hasChinese) {
368
+ const hasChineseChars = /[\u4e00-\u9fff]/u.test(text);
369
+ if (!hasChineseChars && text.length > 10) {
370
+ violations.push({
371
+ type: 'missing_chinese',
372
+ key: keyPath,
373
+ value: text,
374
+ issue: 'Long text should contain Chinese characters',
375
+ severity: 'warning'
376
+ });
377
+ }
378
+ }
379
+
380
+ return violations;
381
+ }
382
+
383
+ /**
384
+ * Generate validation summary
385
+ */
386
+ generateSummary(results) {
387
+ console.log('\nšŸ“Š VALIDATION SUMMARY');
388
+ console.log('======================\n');
389
+
390
+ console.log(`šŸ“‹ Overall Results:`);
391
+ console.log(` Total files validated: ${results.totalFiles}`);
392
+ console.log(` Valid files: ${results.validFiles}`);
393
+ console.log(` Invalid files: ${results.invalidFiles}`);
394
+ console.log(` Total violations: ${results.totalViolations}\n`);
395
+
396
+ if (results.invalidFiles > 0) {
397
+ console.log('āŒ VIOLATIONS BY FILE:\n');
398
+
399
+ for (const [language, fileResult] of Object.entries(results.fileResults)) {
400
+ if (!fileResult.isValid) {
401
+ console.log(`šŸ“„ ${fileResult.filename}:`);
402
+ console.log(` Total keys: ${fileResult.stats.totalKeys}`);
403
+ console.log(` Valid keys: ${fileResult.stats.validKeys}`);
404
+ console.log(` Invalid keys: ${fileResult.stats.invalidKeys}`);
405
+ console.log(` Violations: ${fileResult.violations.length}\n`);
406
+
407
+ // Show first 10 violations
408
+ const violationsToShow = fileResult.violations.slice(0, 10);
409
+ violationsToShow.forEach((violation, index) => {
410
+ const severity = violation.severity === 'error' ? 'āŒ' : 'āš ļø';
411
+ console.log(` ${index + 1}. ${severity} ${violation.key}`);
412
+ console.log(` Issue: ${violation.issue}`);
413
+ if (violation.value.length > 100) {
414
+ console.log(` Value: "${violation.value.substring(0, 100)}..."`);
415
+ } else {
416
+ console.log(` Value: "${violation.value}"`);
417
+ }
418
+ console.log('');
419
+ });
420
+
421
+ if (fileResult.violations.length > 10) {
422
+ console.log(` ... and ${fileResult.violations.length - 10} more violations\n`);
423
+ }
424
+ }
425
+ }
426
+ } else {
427
+ console.log('āœ… All locale files passed language purity validation!\n');
428
+ }
429
+
430
+ // Recommendations
431
+ if (results.totalViolations > 0) {
432
+ console.log('šŸ’” RECOMMENDATIONS:\n');
433
+ console.log('1. Translate all entries marked with [TRANSLATE] or [NOT TRANSLATED]');
434
+ console.log('2. Remove language prefixes like [DE], [FR], [ES], etc.');
435
+ console.log('3. Replace English text with proper native language translations');
436
+ console.log('4. Use language-appropriate characters and writing systems');
437
+ console.log('5. Run automated translation tools to fix common issues');
438
+ console.log('6. Integrate this validator into your CI/CD pipeline\n');
439
+ }
440
+
441
+ // Save detailed report
442
+ this.saveValidationReport(results);
443
+ }
444
+
445
+ /**
446
+ * Save validation report
447
+ */
448
+ saveValidationReport(results) {
449
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
450
+ const reportPath = path.join(__dirname, 'i18ntk-reports', 'language-purity', `language-purity-${timestamp}.json`);
451
+
452
+ // Ensure directory exists
453
+ const reportDir = path.dirname(reportPath);
454
+ if (!fs.existsSync(reportDir)) {
455
+ fs.mkdirSync(reportDir, { recursive: true });
456
+ }
457
+
458
+ const report = {
459
+ timestamp: new Date().toISOString(),
460
+ summary: {
461
+ totalFiles: results.totalFiles,
462
+ validFiles: results.validFiles,
463
+ invalidFiles: results.invalidFiles,
464
+ totalViolations: results.totalViolations,
465
+ overallValid: results.invalidFiles === 0
466
+ },
467
+ fileResults: results.fileResults,
468
+ recommendations: [
469
+ 'Translate all entries marked with [TRANSLATE] or [NOT TRANSLATED]',
470
+ 'Remove language prefixes like [DE], [FR], [ES], etc.',
471
+ 'Replace English text with proper native language translations',
472
+ 'Use language-appropriate characters and writing systems',
473
+ 'Run automated translation tools to fix common issues',
474
+ 'Integrate this validator into your CI/CD pipeline'
475
+ ]
476
+ };
477
+
478
+ try {
479
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
480
+ console.log(`šŸ“„ Detailed validation report saved to: ${reportPath}\n`);
481
+ } catch (error) {
482
+ console.error(`āŒ Error saving validation report: ${error.message}\n`);
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Get exit code for CI/CD integration
488
+ */
489
+ getExitCode() {
490
+ return this.exitCode;
491
+ }
492
+ }
493
+
494
+ // CLI Interface
495
+ if (require.main === module) {
496
+ const args = process.argv.slice(2);
497
+ const validator = new LanguagePurityValidator();
498
+
499
+ if (args.includes('--help') || args.includes('-h')) {
500
+ console.log(`
501
+ Language Purity Validator
502
+
503
+ Usage:
504
+ node validate-language-purity.js [options]
505
+
506
+ Options:
507
+ --ci Run in CI mode (exit with error code if validation fails)
508
+ --help, -h Show this help message
509
+
510
+ Examples:
511
+ node validate-language-purity.js # Validate all locale files
512
+ node validate-language-purity.js --ci # CI/CD integration mode
513
+
514
+ Exit Codes:
515
+ 0 - All validations passed
516
+ 1 - Validation failures found
517
+ `);
518
+ process.exit(0);
519
+ }
520
+
521
+ validator.validateAll().then(() => {
522
+ if (args.includes('--ci')) {
523
+ process.exit(validator.getExitCode());
524
+ }
525
+ }).catch(error => {
526
+ console.error('āŒ Validation Error:', error.message);
527
+ process.exit(1);
528
+ });
529
+ }
530
+
531
+ module.exports = LanguagePurityValidator;