UNPKG

@akson/cortex-shopify-translations

Version:

Unified Shopify translations management client with product extraction, translation sync, and CLI tools

150 lines (120 loc) • 5.32 kB
#!/usr/bin/env node /** * Check translation progress across all files * Provides detailed stats and visual progress bars */ import fs from 'fs/promises'; import path from 'path'; const COLORS = { reset: '\x1b[0m', bright: '\x1b[1m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', gray: '\x1b[90m' }; function color(text, colorName) { return `${COLORS[colorName]}${text}${COLORS.reset}`; } function progressBar(percentage, width = 30) { const filled = Math.round((percentage / 100) * width); const empty = width - filled; const bar = 'ā–ˆ'.repeat(filled) + 'ā–‘'.repeat(empty); if (percentage >= 80) return color(bar, 'green'); if (percentage >= 50) return color(bar, 'yellow'); return color(bar, 'red'); } async function getStatus(detailed = false) { console.log(color('\n🧠 MyArmy Translation Status', 'cyan')); console.log(color('═'.repeat(60), 'gray')); const contentDir = path.join(process.cwd(), 'translations', 'content'); const statusFile = path.join(process.cwd(), 'translations', 'status.json'); try { // Read global status const status = JSON.parse(await fs.readFile(statusFile, 'utf-8')); // Overall progress console.log(color('\nšŸ“Š Overall Progress', 'bright')); console.log(color('─'.repeat(40), 'gray')); console.log(`Total translations: ${color(status.summary.total_keys, 'cyan')}`); console.log(`Last update: ${new Date(status.summary.last_update).toLocaleString()}`); console.log(); // Language progress const languages = ['de', 'it', 'en']; const langNames = { de: 'German', it: 'Italian', en: 'English' }; languages.forEach(lang => { const prog = status.progress[lang] || { pending: 0, in_progress: 0, completed: 0, failed: 0, reviewed: 0 }; const percentage = status.progress_percentage?.[lang] || 0; const completed = prog.completed + (prog.reviewed || 0); console.log(`${langNames[lang].padEnd(8)} ${progressBar(percentage)} ${color(`${percentage}%`, 'bright')} (${completed}/${status.summary.total_keys})`); if (detailed) { console.log(color(` Pending: ${prog.pending} | In Progress: ${prog.in_progress || 0} | Failed: ${prog.failed || 0} | Reviewed: ${prog.reviewed || 0}`, 'gray')); } }); // Category breakdown if (detailed) { console.log(color('\nšŸ“ Category Breakdown', 'bright')); console.log(color('─'.repeat(40), 'gray')); const categories = Object.keys(status.files).sort(); for (const category of categories) { const catStats = status.files[category]; console.log(`\n${color(category, 'yellow')} (${catStats.total} translations)`); languages.forEach(lang => { const stats = catStats[lang] || { pending: 0, completed: 0, reviewed: 0, failed: 0 }; const completed = stats.completed + (stats.reviewed || 0); const percentage = Math.round((completed / catStats.total) * 100); console.log(` ${langNames[lang].padEnd(8)} ${progressBar(percentage, 20)} ${percentage}%`); }); } } // Problem areas const problemCount = languages.reduce((sum, lang) => sum + (status.progress[lang].failed || 0), 0); if (problemCount > 0) { console.log(color(`\nāš ļø ${problemCount} translations need attention`, 'yellow')); console.log(color('Use --detailed to see category breakdowns', 'gray')); } // Next steps console.log(color('\nšŸ’” Next Steps:', 'bright')); const lowestLang = languages.reduce((min, lang) => (status.progress_percentage?.[lang] || 0) < (status.progress_percentage?.[min] || 0) ? lang : min ); if ((status.progress_percentage?.[lowestLang] || 0) < 100) { console.log(`• Continue translating ${langNames[lowestLang]} (${status.progress_percentage?.[lowestLang] || 0}% complete)`); console.log(` ${color(`node translations/scripts/translate-file.mjs --lang=${lowestLang}`, 'gray')}`); } if (problemCount > 0) { console.log(`• Review and fix failed translations`); console.log(` ${color('node translations/scripts/reset-failed.mjs', 'gray')}`); } // Find category with most work needed let needsWork = null; let maxPending = 0; Object.entries(status.files).forEach(([cat, stats]) => { const totalPending = languages.reduce((sum, lang) => sum + (stats[lang].pending || 0), 0); if (totalPending > maxPending) { maxPending = totalPending; needsWork = cat; } }); if (needsWork && maxPending > 0) { console.log(`• Focus on ${needsWork} category (${maxPending} pending)`); console.log(` ${color(`node translations/scripts/translate-file.mjs --category=${needsWork}`, 'gray')}`); } console.log(); } catch (error) { if (error.code === 'ENOENT') { console.error(color('āŒ Status file not found. Run translations first.', 'red')); console.log(color(' node translations/scripts/translate-file.mjs', 'gray')); } else { console.error(color(`āŒ Error: ${error.message}`, 'red')); } process.exit(1); } } // CLI const args = process.argv.slice(2); const detailed = args.includes('--detailed') || args.includes('-d'); getStatus(detailed);