UNPKG

@ordojs/cli

Version:

Command-line interface for OrdoJS framework

374 lines 13.3 kB
/** * @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