pury
Version:
🛡️ AI-powered security scanner with advanced threat detection, dual reporting system (detailed & summary), and comprehensive code analysis
166 lines • 7.86 kB
JavaScript
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