tractatus/scripts/validate-researcher-i18n.js
TheFlow 5e7b3ef21f feat(i18n): add complete internationalization for researcher page
Implemented full translation infrastructure for researcher.html:
- Added 148 data-i18n attributes across all content sections
- Created 142 translation keys in nested JSON structure
- Translated all keys to German (DE) and French (FR) via DeepL Pro API
- Zero translation errors, all keys validated across 3 languages

Content translated includes:
- Research Context & Scope (4 major paragraphs)
- Theoretical Foundations (Organizational Theory + Values Pluralism accordions)
- Empirical Observations (3 documented failure modes with labels)
- Six-Component Architecture (all services with descriptions)
- Interactive Demonstrations, Resources, Bibliography, Limitations

New scripts:
- translate-researcher-deepl.js: Automated DeepL translation with rate limiting
- validate-researcher-i18n.js: i18n completeness validation tool

Translation quality verified with sample checks. Page ready for multilingual deployment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 00:18:45 +13:00

88 lines
3 KiB
JavaScript

#!/usr/bin/env node
/**
* Validate researcher.html i18n keys against translation files
*/
const fs = require('fs');
const path = require('path');
// Read HTML file and extract data-i18n keys
const htmlPath = path.join(__dirname, '../public/researcher.html');
const html = fs.readFileSync(htmlPath, 'utf8');
const keyPattern = /data-i18n="([^"]+)"/g;
const htmlKeys = new Set();
let match;
while ((match = keyPattern.exec(html)) !== null) {
htmlKeys.add(match[1]);
}
console.log('═══════════════════════════════════════════════════════════');
console.log(' Researcher.html i18n Validation');
console.log('═══════════════════════════════════════════════════════════\n');
console.log(`📄 Total data-i18n keys in HTML: ${htmlKeys.size}`);
// Load translation files
const enPath = path.join(__dirname, '../public/locales/en/researcher.json');
const dePath = path.join(__dirname, '../public/locales/de/researcher.json');
const frPath = path.join(__dirname, '../public/locales/fr/researcher.json');
const enData = JSON.parse(fs.readFileSync(enPath, 'utf8'));
const deData = JSON.parse(fs.readFileSync(dePath, 'utf8'));
const frData = JSON.parse(fs.readFileSync(frPath, 'utf8'));
// Helper to check if nested key exists
function hasNestedKey(obj, keyPath) {
const keys = keyPath.split('.');
let current = obj;
for (const key of keys) {
if (current && typeof current === 'object' && key in current) {
current = current[key];
} else {
return false;
}
}
return typeof current === 'string' && current.length > 0;
}
// Check each language
const languages = [
{ name: 'English (EN)', code: 'en', data: enData },
{ name: 'German (DE)', code: 'de', data: deData },
{ name: 'French (FR)', code: 'fr', data: frData }
];
let allValid = true;
for (const lang of languages) {
const missingKeys = [];
for (const key of htmlKeys) {
if (!hasNestedKey(lang.data, key)) {
missingKeys.push(key);
}
}
console.log(`\n🌐 ${lang.name}`);
if (missingKeys.length === 0) {
console.log(` ✅ All ${htmlKeys.size} keys found`);
} else {
console.log(` ❌ Missing ${missingKeys.length} keys:`);
missingKeys.forEach(key => console.log(`${key}`));
allValid = false;
}
}
console.log('\n═══════════════════════════════════════════════════════════');
if (allValid) {
console.log('✅ VALIDATION PASSED: All i18n keys are properly translated');
} else {
console.log('❌ VALIDATION FAILED: Some keys are missing');
process.exit(1);
}
console.log('═══════════════════════════════════════════════════════════\n');