format method
String
format(
- ComparisonResult result,
- ExtractionResult extracted, {
- bool verbose = false,
override
Generate output for the given comparison result.
Implementation
@override
String format(
ComparisonResult result,
ExtractionResult extracted, {
bool verbose = false,
}) {
final buffer = StringBuffer();
final stats = result.stats;
// Header
buffer.writeln('shard_i18n extract - Key Analysis Report');
buffer.writeln('=' * 44);
buffer.writeln();
// Statistics
buffer.writeln('Statistics:');
buffer.writeln(' Files scanned: ${stats.filesScanned}');
buffer.writeln(' JSON files: ${stats.jsonFilesLoaded}');
buffer.writeln(' Keys in code: ${stats.totalKeysInCode}');
buffer.writeln(' Keys in JSON: ${stats.totalKeysInJson}');
buffer.writeln(
' Matched: ${stats.matchedCount} (${stats.coveragePercent.toStringAsFixed(1)}%)',
);
buffer.writeln(' Missing in JSON: ${stats.missingCount}');
buffer.writeln(' Orphaned in JSON: ${stats.orphanedCount}');
buffer.writeln();
// Missing keys
if (result.missingInJson.isNotEmpty) {
buffer.writeln('Missing Keys (in code, not in JSON):');
buffer.writeln('-' * 40);
final sortedMissing = result.missingInJson.toList()..sort();
for (final key in sortedMissing) {
buffer.writeln(' + $key');
// Find locations for this key
final locations = extracted.keys.where((k) => k.key == key);
for (final loc in locations.take(verbose ? 10 : 2)) {
buffer.writeln(' ${loc.filePath}:${loc.line}');
if (loc.snippet != null) {
buffer.writeln(' ${loc.snippet}');
}
}
if (!verbose && locations.length > 2) {
buffer.writeln(' ... and ${locations.length - 2} more');
}
buffer.writeln();
}
}
// Orphaned keys
if (result.orphanedInJson.isNotEmpty) {
buffer.writeln('Orphaned Keys (in JSON, not in code):');
buffer.writeln('-' * 40);
final sortedOrphaned = result.orphanedInJson.toList()..sort();
for (final key in sortedOrphaned) {
buffer.writeln(' - $key');
}
buffer.writeln();
}
// Placeholder mismatches
if (result.placeholderMismatches.isNotEmpty) {
buffer.writeln('Placeholder Mismatches:');
buffer.writeln('-' * 40);
for (final mismatch in result.placeholderMismatches.values) {
buffer.writeln(' ~ ${mismatch.key}');
buffer.writeln(' Code: {${mismatch.inCode.join('}, {')}}');
buffer.writeln(' JSON: {${mismatch.inJson.join('}, {')}}');
buffer.writeln();
}
}
// Plural form issues
if (result.pluralIssues.isNotEmpty) {
buffer.writeln('Plural Form Issues:');
buffer.writeln('-' * 40);
for (final issue in result.pluralIssues.values) {
buffer.writeln(' ! ${issue.key}');
buffer.writeln(' ${issue.message}');
if (issue.availableForms != null) {
buffer.writeln(' Available forms: ${issue.availableForms}');
}
buffer.writeln();
}
}
// Verbose: per-file breakdown
if (verbose && extracted.byFile.isNotEmpty) {
buffer.writeln('Per-File Breakdown:');
buffer.writeln('-' * 40);
final sortedFiles = extracted.byFile.keys.toList()..sort();
for (final file in sortedFiles) {
final keys = extracted.byFile[file]!;
buffer.writeln(' $file (${keys.length} keys)');
for (final key in keys) {
final status = result.missingInJson.contains(key.key)
? '+'
: result.matchedKeys.contains(key.key)
? ' '
: '?';
buffer.writeln(' [$status] ${key.key} (line ${key.line})');
}
buffer.writeln();
}
}
// Summary
if (result.hasDiscrepancies) {
buffer.writeln('Run with --fix to auto-generate missing entries.');
buffer.writeln('Run with --prune to remove orphaned keys.');
buffer.writeln('Run with --verbose for per-file breakdown.');
} else {
buffer.writeln('All keys are in sync!');
}
return buffer.toString();
}