#!/usr/bin/env node /** * Translation Verification Script * * Verifies that all documents have been translated correctly: * - Checks which documents have German (de) translations * - Checks which documents have French (fr) translations * - Reports translation completeness and metadata */ require('dotenv').config(); const mongoose = require('mongoose'); const Document = require('../src/models/Document.model'); async function main() { console.log('═══════════════════════════════════════════════════════════'); console.log(' TRANSLATION VERIFICATION'); console.log('═══════════════════════════════════════════════════════════\n'); // Connect to MongoDB console.log('📡 Connecting to MongoDB...'); await mongoose.connect('mongodb://localhost:27017/tractatus_dev', { serverSelectionTimeoutMS: 5000 }); console.log('✓ Connected to tractatus_dev\n'); // Fetch all public documents const documents = await Document.list({ filter: { visibility: 'public' }, limit: 1000, sort: { order: 1 } }); console.log(`📚 Analyzing ${documents.length} public documents...\n`); const stats = { total: documents.length, withDE: 0, withFR: 0, withBoth: 0, withNone: 0, details: [] }; // Check each document for (const doc of documents) { const hasDE = doc.translations && doc.translations.de && doc.translations.de.title; const hasFR = doc.translations && doc.translations.fr && doc.translations.fr.title; if (hasDE) stats.withDE++; if (hasFR) stats.withFR++; if (hasDE && hasFR) stats.withBoth++; if (!hasDE && !hasFR) stats.withNone++; stats.details.push({ slug: doc.slug, title: doc.title, de: hasDE, fr: hasFR, de_title: hasDE ? doc.translations.de.title : null, fr_title: hasFR ? doc.translations.fr.title : null, de_chars: hasDE ? doc.translations.de.content_markdown?.length || 0 : 0, fr_chars: hasFR ? doc.translations.fr.content_markdown?.length || 0 : 0, de_metadata: hasDE ? doc.translations.de.metadata : null, fr_metadata: hasFR ? doc.translations.fr.metadata : null }); } // Display summary console.log('═══════════════════════════════════════════════════════════'); console.log(' SUMMARY'); console.log('═══════════════════════════════════════════════════════════\n'); console.log(` Total documents: ${stats.total}`); console.log(` With German (DE): ${stats.withDE} (${(stats.withDE / stats.total * 100).toFixed(1)}%)`); console.log(` With French (FR): ${stats.withFR} (${(stats.withFR / stats.total * 100).toFixed(1)}%)`); console.log(` With both languages: ${stats.withBoth} (${(stats.withBoth / stats.total * 100).toFixed(1)}%)`); console.log(` With no translations: ${stats.withNone}\n`); // Display details console.log('═══════════════════════════════════════════════════════════'); console.log(' DOCUMENT DETAILS'); console.log('═══════════════════════════════════════════════════════════\n'); stats.details.forEach((detail, index) => { const deStatus = detail.de ? '✓' : '✗'; const frStatus = detail.fr ? '✓' : '✗'; console.log(`${index + 1}. ${detail.title}`); console.log(` Slug: ${detail.slug}`); console.log(` DE: ${deStatus} ${detail.de ? `(${detail.de_chars.toLocaleString()} chars)` : ''}`); if (detail.de) { console.log(` Title: "${detail.de_title}"`); console.log(` Translated: ${detail.de_metadata?.translated_at || 'unknown'}`); } console.log(` FR: ${frStatus} ${detail.fr ? `(${detail.fr_chars.toLocaleString()} chars)` : ''}`); if (detail.fr) { console.log(` Title: "${detail.fr_title}"`); console.log(` Translated: ${detail.fr_metadata?.translated_at || 'unknown'}`); } console.log(''); }); // Missing translations const missing = stats.details.filter(d => !d.de || !d.fr); if (missing.length > 0) { console.log('═══════════════════════════════════════════════════════════'); console.log(' MISSING TRANSLATIONS'); console.log('═══════════════════════════════════════════════════════════\n'); missing.forEach(doc => { const missingLangs = []; if (!doc.de) missingLangs.push('DE'); if (!doc.fr) missingLangs.push('FR'); console.log(` ${doc.slug}: Missing ${missingLangs.join(', ')}`); }); console.log(''); } await mongoose.disconnect(); console.log('✓ Database disconnected\n'); process.exit(0); } // Run main().catch(err => { console.error('\n❌ Fatal error:', err.message); console.error(err.stack); process.exit(1); });