@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
JavaScript
/**
* 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);