@ordojs/cli
Version:
Command-line interface for OrdoJS framework
374 lines • 13.3 kB
JavaScript
/**
* @fileoverview CLI command for internationalization features
*/
import { Command } from 'commander';
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs';
import { extname, join } from 'path';
import { I18nManager } from '../../core/src/i18n/i18n-manager.js';
/**
* Internationalization command for managing translations and locales
*/
export function createI18nCommand() {
const command = new Command('i18n')
.description('Manage internationalization and translations')
.option('-i, --input <path>', 'Input directory or file', './src')
.option('-o, --output <path>', 'Output directory for translations', './translations')
.option('-l, --locale <locale>', 'Target locale', 'en')
.option('-e, --extract', 'Extract translatable strings', false)
.option('-g, --generate', 'Generate translation templates', false)
.option('-v, --validate', 'Validate translations', false)
.option('-m, --merge', 'Merge translation files', false)
.option('--list', 'List supported locales', false)
.option('--verbose', 'Verbose output', false)
.action(async (options) => {
try {
await runI18nCommand(options);
}
catch (error) {
console.error('I18n command failed:', error);
process.exit(1);
}
});
return command;
}
/**
* Run the i18n command
*/
async function runI18nCommand(options) {
console.log('🌍 Running OrdoJS Internationalization...\n');
const i18nManager = new I18nManager({
defaultLocale: 'en',
supportedLocales: ['en', 'es', 'fr', 'de', 'ar', 'zh-CN', 'ja'],
fallbackLocale: 'en',
translationPath: options.output
});
if (options.list) {
await listLocales(i18nManager);
}
if (options.extract) {
await extractStrings(i18nManager, options);
}
if (options.generate) {
await generateTemplates(i18nManager, options);
}
if (options.validate) {
await validateTranslations(i18nManager, options);
}
if (options.merge) {
await mergeTranslations(i18nManager, options);
}
console.log('✅ Internationalization completed successfully!');
}
/**
* List supported locales
*/
async function listLocales(i18nManager) {
console.log('📋 Supported Locales:\n');
const locales = i18nManager.getSupportedLocales();
locales.forEach(locale => {
const config = i18nManager.getLocaleConfig(locale);
const isRTL = i18nManager.isRTL(locale);
const direction = isRTL ? 'RTL' : 'LTR';
console.log(` ${locale.padEnd(8)} | ${config?.name.padEnd(20)} | ${direction}`);
});
console.log(`\nTotal: ${locales.length} locales supported`);
}
/**
* Extract translatable strings from source files
*/
async function extractStrings(i18nManager, options) {
console.log('🔍 Extracting translatable strings...\n');
const workflow = i18nManager.createTranslationWorkflow();
const strings = [];
// Scan input directory for source files
const sourceFiles = findSourceFiles(options.input);
console.log(`Found ${sourceFiles.length} source files to process\n`);
sourceFiles.forEach(file => {
try {
const content = readFileSync(file, 'utf-8');
const extracted = workflow.extractStrings(content);
strings.push(...extracted);
if (options.verbose) {
console.log(` 📄 ${file}: ${extracted.length} strings extracted`);
}
}
catch (error) {
console.warn(`⚠️ Failed to process ${file}: ${error}`);
}
});
// Remove duplicates
const uniqueStrings = [...new Set(strings)];
console.log(`📊 Extracted ${uniqueStrings.length} unique translatable strings\n`);
if (options.output) {
const template = workflow.generateTemplate(options.locale, uniqueStrings);
const outputFile = join(options.output, `${options.locale}.json`);
// Ensure output directory exists
const outputDir = join(options.output);
if (!existsSync(outputDir)) {
// In a real implementation, you would create the directory
console.log(`📁 Creating output directory: ${outputDir}`);
}
writeFileSync(outputFile, JSON.stringify(template, null, 2));
console.log(`💾 Translation template saved to: ${outputFile}`);
}
}
/**
* Generate translation templates
*/
async function generateTemplates(i18nManager, options) {
console.log('📝 Generating translation templates...\n');
const workflow = i18nManager.createTranslationWorkflow();
const locales = i18nManager.getSupportedLocales();
// Sample translatable strings for template generation
const sampleStrings = [
'welcome',
'hello',
'goodbye',
'loading',
'error',
'success',
'cancel',
'save',
'delete',
'edit',
'add',
'remove',
'search',
'filter',
'sort',
'next',
'previous',
'first',
'last',
'page',
'of',
'items',
'results',
'no_results',
'loading_error',
'network_error',
'validation_error',
'required_field',
'invalid_email',
'password_too_short',
'confirm_password',
'passwords_dont_match',
'login',
'logout',
'register',
'profile',
'settings',
'dashboard',
'home',
'about',
'contact',
'help',
'privacy',
'terms',
'copyright',
'all_rights_reserved'
];
locales.forEach(locale => {
const template = workflow.generateTemplate(locale, sampleStrings);
const outputFile = join(options.output || './translations', `${locale}.json`);
writeFileSync(outputFile, JSON.stringify(template, null, 2));
console.log(`📄 Generated template for ${locale}: ${outputFile}`);
});
console.log(`\n✅ Generated templates for ${locales.length} locales`);
}
/**
* Validate translations
*/
async function validateTranslations(i18nManager, options) {
console.log('✅ Validating translations...\n');
const locales = i18nManager.getSupportedLocales();
const baseLocale = 'en';
locales.forEach(locale => {
if (locale === baseLocale)
return;
console.log(`🔍 Validating ${locale}...`);
const validation = i18nManager.createTranslationWorkflow().validateTranslations(locale);
if (validation.valid) {
console.log(` ✅ ${locale}: All translations are valid`);
}
else {
console.log(` ❌ ${locale}: Found ${validation.errors.length} issues`);
validation.errors.forEach(error => {
console.log(` - ${error}`);
});
}
});
console.log('\n📊 Validation Summary:');
console.log(` Base locale: ${baseLocale}`);
console.log(` Target locales: ${locales.filter(l => l !== baseLocale).join(', ')}`);
}
/**
* Merge translation files
*/
async function mergeTranslations(i18nManager, options) {
console.log('🔄 Merging translation files...\n');
const workflow = i18nManager.createTranslationWorkflow();
const translationDir = options.output || './translations';
if (!existsSync(translationDir)) {
console.log(`📁 Creating translation directory: ${translationDir}`);
return;
}
const files = readdirSync(translationDir)
.filter(file => extname(file) === '.json')
.map(file => file.replace('.json', ''));
console.log(`Found ${files.length} translation files to merge\n`);
files.forEach(locale => {
const filePath = join(translationDir, `${locale}.json`);
try {
const content = readFileSync(filePath, 'utf-8');
const translations = JSON.parse(content);
i18nManager.importTranslations(locale, translations);
console.log(` ✅ Merged ${locale}: ${Object.keys(translations).length} translations`);
}
catch (error) {
console.warn(` ⚠️ Failed to merge ${locale}: ${error}`);
}
});
console.log('\n✅ Translation merge completed');
}
/**
* Find source files in directory
*/
function findSourceFiles(inputPath) {
const files = [];
if (!existsSync(inputPath)) {
console.warn(`⚠️ Input path does not exist: ${inputPath}`);
return files;
}
function scanDirectory(dir) {
try {
const items = readdirSync(dir);
items.forEach(item => {
const fullPath = join(dir, item);
if (extname(item) === '.ordo' || extname(item) === '.js' || extname(item) === '.ts') {
files.push(fullPath);
}
});
}
catch (error) {
console.warn(`⚠️ Failed to scan directory ${dir}: ${error}`);
}
}
scanDirectory(inputPath);
return files;
}
/**
* Create sample translation files
*/
export function createSampleTranslations(outputDir) {
const translations = {
en: {
welcome: 'Welcome',
hello: 'Hello {{name}}',
goodbye: 'Goodbye',
loading: 'Loading...',
error: 'An error occurred',
success: 'Operation completed successfully',
cancel: 'Cancel',
save: 'Save',
delete: 'Delete',
edit: 'Edit',
add: 'Add',
remove: 'Remove',
search: 'Search',
filter: 'Filter',
sort: 'Sort',
next: 'Next',
previous: 'Previous',
first: 'First',
last: 'Last',
page: 'Page',
of: 'of',
items: {
one: '{{count}} item',
other: '{{count}} items'
},
results: 'Results',
no_results: 'No results found',
loading_error: 'Failed to load data',
network_error: 'Network error',
validation_error: 'Validation error',
required_field: 'This field is required',
invalid_email: 'Please enter a valid email address',
password_too_short: 'Password must be at least 8 characters',
confirm_password: 'Confirm password',
passwords_dont_match: 'Passwords do not match',
login: 'Login',
logout: 'Logout',
register: 'Register',
profile: 'Profile',
settings: 'Settings',
dashboard: 'Dashboard',
home: 'Home',
about: 'About',
contact: 'Contact',
help: 'Help',
privacy: 'Privacy Policy',
terms: 'Terms of Service',
copyright: 'Copyright',
all_rights_reserved: 'All rights reserved'
},
es: {
welcome: 'Bienvenido',
hello: 'Hola {{name}}',
goodbye: 'Adiós',
loading: 'Cargando...',
error: 'Ocurrió un error',
success: 'Operación completada exitosamente',
cancel: 'Cancelar',
save: 'Guardar',
delete: 'Eliminar',
edit: 'Editar',
add: 'Agregar',
remove: 'Quitar',
search: 'Buscar',
filter: 'Filtrar',
sort: 'Ordenar',
next: 'Siguiente',
previous: 'Anterior',
first: 'Primero',
last: 'Último',
page: 'Página',
of: 'de',
items: {
one: '{{count}} elemento',
other: '{{count}} elementos'
},
results: 'Resultados',
no_results: 'No se encontraron resultados',
loading_error: 'Error al cargar datos',
network_error: 'Error de red',
validation_error: 'Error de validación',
required_field: 'Este campo es obligatorio',
invalid_email: 'Por favor ingrese un email válido',
password_too_short: 'La contraseña debe tener al menos 8 caracteres',
confirm_password: 'Confirmar contraseña',
passwords_dont_match: 'Las contraseñas no coinciden',
login: 'Iniciar sesión',
logout: 'Cerrar sesión',
register: 'Registrarse',
profile: 'Perfil',
settings: 'Configuración',
dashboard: 'Panel de control',
home: 'Inicio',
about: 'Acerca de',
contact: 'Contacto',
help: 'Ayuda',
privacy: 'Política de privacidad',
terms: 'Términos de servicio',
copyright: 'Derechos de autor',
all_rights_reserved: 'Todos los derechos reservados'
}
};
Object.entries(translations).forEach(([locale, data]) => {
const filePath = join(outputDir, `${locale}.json`);
writeFileSync(filePath, JSON.stringify(data, null, 2));
console.log(`📄 Created sample translation file: ${filePath}`);
});
}
//# sourceMappingURL=i18n.js.map