UNPKG

pury

Version:

🛡️ AI-powered security scanner with advanced threat detection, dual reporting system (detailed & summary), and comprehensive code analysis

166 lines 7.86 kB
import { Command } from 'commander'; import { resolve } from 'path'; import { FileScanner } from '../../scanner/file-scanner.js'; import { QualityAnalyzer } from '../../analyzers/quality-analyzer.js'; import { logger } from '../../utils/logger.js'; import { fileExists } from '../../utils/file-utils.js'; export function createLocalizeCommand() { return new Command('localize') .description('Convert non-English text in code to English for better international collaboration') .argument('[path]', 'Path to localize (defaults to current directory)', '.') .option('--apply', 'Actually apply the changes (default is dry-run)') .option('--backup', 'Create backup files before modifying') .option('--exclude <patterns...>', 'Patterns to exclude from localization') .option('--include <patterns...>', 'Patterns to include in localization', [ '**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx', '**/*.py', '**/*.java' ]) .option('--target-lang <lang>', 'Target language for translation (default: en)', 'en') .action(async (path, options) => { try { await runLocalize(path, options); } catch (error) { logger.error(`Localization failed: ${error.message}`); process.exit(1); } }); } async function runLocalize(localizePath, options) { const resolvedPath = resolve(localizePath); if (!(await fileExists(resolvedPath))) { throw new Error(`Path does not exist: ${resolvedPath}`); } logger.info(`${options.apply ? 'Localizing' : 'Analyzing'} non-English text in: ${resolvedPath}`); logger.info(`Target language: ${options.targetLang}`); // Initialize scanner const scanner = new FileScanner(options.include || ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx', '**/*.py', '**/*.java'], options.exclude || ['node_modules/**', 'dist/**', '*.min.js', 'coverage/**']); // Initialize quality analyzer for localization analysis const analyzer = new QualityAnalyzer('high'); const spinner = logger.spinner('Scanning for non-English text...'); try { // Scan files const files = await scanner.scan({ path: resolvedPath, recursive: true, followSymlinks: false }); spinner.succeed(`Scanned ${files.length} files`); if (files.length === 0) { logger.warn('No files found to process'); return; } // Analyze for localization issues logger.info('Analyzing for localization opportunities...'); const analysisResult = await analyzer.analyze(files); const localizationFindings = analysisResult.findings.filter(f => f.type === 'localization'); // Show results logger.info(`\nLocalization Analysis Results:`); logger.info(`Files analyzed: ${files.length}`); logger.info(`Non-English text instances found: ${localizationFindings.length}`); if (localizationFindings.length === 0) { logger.success('No non-English text found - code is already internationalized!'); return; } // Group findings by file const findingsByFile = new Map(); for (const finding of localizationFindings) { if (!findingsByFile.has(finding.file)) { findingsByFile.set(finding.file, []); } findingsByFile.get(finding.file).push(finding); } // Show preview of findings logger.info('\\nNon-English text found:'); let previewCount = 0; for (const [filePath, findings] of findingsByFile) { if (previewCount >= 10) break; // Limit preview to first 10 findings logger.info(`\\n📁 ${filePath}:`); for (const finding of findings.slice(0, 3)) { // Show max 3 findings per file logger.info(` Line ${finding.line}: ${finding.evidence}`); logger.info(` 💡 ${finding.suggestion}`); previewCount++; } if (findings.length > 3) { logger.info(` ... and ${findings.length - 3} more instances in this file`); } } if (localizationFindings.length > 10) { logger.info(`\\n... and ${localizationFindings.length - 10} more instances in other files`); } if (options.apply) { logger.warn('\\n⚠️ Automatic translation is not yet implemented.'); logger.info('Localization suggestions have been identified above.'); logger.info('Please review each instance manually and:'); logger.info(' 1. Translate comments and variable names to English'); logger.info(' 2. Move user-facing text to localization files'); logger.info(' 3. Use descriptive English names for better collaboration'); // In a future version, this could integrate with translation APIs // await applyLocalizationChanges(localizationFindings, options.backup, options.targetLang); await applyLocalizationChanges(localizationFindings, options.backup, options.targetLang); } else { logger.info('\\n💡 This was a dry run. Use --apply to see detailed suggestions.'); logger.info('💡 Manual review and translation is recommended for accuracy.'); } // Summary statistics const fileCount = findingsByFile.size; const languageStats = analyzeLanguageDistribution(localizationFindings); logger.info('\\n📊 Summary:'); logger.info(`📝 Files with non-English text: ${fileCount}`); logger.info(`🌍 Languages detected: ${Object.keys(languageStats).join(', ')}`); logger.info(`⚠️ Total instances to review: ${localizationFindings.length}`); } catch (error) { spinner.fail('Failed to analyze files'); throw error; } } function analyzeLanguageDistribution(findings) { const languageStats = {}; for (const finding of findings) { // Simple language detection based on character patterns const text = finding.evidence; if (/[а-яё]/i.test(text)) { languageStats['Russian/Cyrillic'] = (languageStats['Russian/Cyrillic'] || 0) + 1; } else if (/[中文日本語韓國]/.test(text)) { languageStats['CJK (Chinese/Japanese/Korean)'] = (languageStats['CJK (Chinese/Japanese/Korean)'] || 0) + 1; } else if (/[àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ]/i.test(text)) { languageStats['Extended Latin'] = (languageStats['Extended Latin'] || 0) + 1; } else if (/[α-ωάέήίόύώ]/i.test(text)) { languageStats.Greek = (languageStats.Greek || 0) + 1; } else if (/[أ-ي]/i.test(text)) { languageStats.Arabic = (languageStats.Arabic || 0) + 1; } else { languageStats['Other/Mixed'] = (languageStats['Other/Mixed'] || 0) + 1; } } return languageStats; } // Future implementation for automatic translation async function applyLocalizationChanges(_findings, _createBackup, _targetLang) { logger.info('\\n🤖 Automatic translation feature coming soon!'); logger.info('For now, please review the findings manually.'); // This would integrate with translation services like: // - Google Translate API // - Azure Translator // - DeepL API // // And apply intelligent changes based on context: // - Comments: Full translation // - Variable names: Transliteration + meaningful English names // - String literals: Move to i18n files or translate if code-specific } //# sourceMappingURL=localize.js.map