Guest User

Untitled

a guest
Oct 20th, 2025
49
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 3.80 KB | Source Code | 0 0
  1. #!/usr/bin/env dart
  2. // ignore_for_file: avoid_print
  3.  
  4. // find unused i18n strings
  5.  
  6. import 'dart:convert';
  7. import 'dart:io';
  8. import 'package:path/path.dart' as p;
  9.  
  10. void main(List<String> args) {
  11.   final rootDir = findFlutterRoot();
  12.   if (rootDir == null) {
  13.     print('โŒ Could not find a Flutter project root (no pubspec.yaml found).');
  14.     exit(2);
  15.   }
  16.  
  17.   final arbPath = args.isNotEmpty
  18.       ? args[0]
  19.       : p.join(rootDir.path, 'lib', 'l10n', 'app_en.arb');
  20.   final sourceDir = args.length > 1 ? args[1] : p.join(rootDir.path, 'lib');
  21.  
  22.   final arbFile = File(arbPath);
  23.   if (!arbFile.existsSync()) {
  24.     print('โŒ ARB file not found: $arbPath');
  25.     exit(2);
  26.   }
  27.  
  28.   final arbContent =
  29.       jsonDecode(arbFile.readAsStringSync()) as Map<String, dynamic>;
  30.   final keys = arbContent.keys.where((k) => !k.startsWith('@')).toList();
  31.   print(
  32.       '๐Ÿ—  Found ${keys.length} keys in ${p.relative(arbPath, from: rootDir.path)}\n');
  33.  
  34.   // Collect all Dart files except those in lib/l10n/
  35.   final dartFiles = Directory(sourceDir)
  36.       .listSync(recursive: true)
  37.       .whereType<File>()
  38.       .where((f) =>
  39.           f.path.endsWith('.dart') &&
  40.           !f.path.contains(
  41.               RegExp(r'lib[\/\\]l10n[\/\\]'))) // ignore localization files
  42.       .toList();
  43.  
  44.   print('๐Ÿ“ Scanning ${dartFiles.length} Dart files (excluding lib/l10n/)\n');
  45.  
  46.   // Patterns that allow newlines and spaces between symbols (multiline-safe)
  47.   final rawPatterns = <String>[
  48.     r'\bS\.of\([^)]*\)\s*\.\s*{}',
  49.     r'\bS\.of\([^)]*\)\s*!\s*\.\s*{}',
  50.     r'\bAppLocalizations\.of\([^)]*\)\s*\.\s*{}',
  51.     r'\bAppLocalizations\.of\([^)]*\)\s*!\s*\.\s*{}',
  52.     r'\bcontext\.l10n\s*\.\s*{}',
  53.     r'\bl10n\([^)]*\)\s*\.\s*{}',
  54.     r'\.\s*{}', // fallback
  55.   ];
  56.  
  57.   final unused = <String>[];
  58.   final usedDetails = <String, List<String>>{};
  59.  
  60.   for (final key in keys) {
  61.     final matches = <String>[];
  62.     final regexes = rawPatterns
  63.         .map((p) => RegExp(p.replaceAll('{}', RegExp.escape(key)),
  64.             multiLine: true, dotAll: true))
  65.         .toList();
  66.  
  67.     for (final file in dartFiles) {
  68.       final content = file.readAsStringSync();
  69.       if (regexes.any((r) => r.hasMatch(content))) {
  70.         matches.add(p.relative(file.path, from: rootDir.path));
  71.       }
  72.     }
  73.  
  74.     if (matches.isEmpty) {
  75.       unused.add(key);
  76.     } else {
  77.       usedDetails[key] = matches;
  78.     }
  79.   }
  80.  
  81.   // Summary
  82.   print('โœ… Used keys: ${usedDetails.length}');
  83.   print('๐Ÿงน Unused keys: ${unused.length}\n');
  84.  
  85.   if (unused.isNotEmpty) {
  86.     print('=== Unused keys ===');
  87.     for (final k in unused) {
  88.       print(' - $k');
  89.     }
  90.   }
  91.  
  92.   // Write report file
  93.   final out = File(p.join(rootDir.path, 'i10n_usage_report.txt'));
  94.   final buffer = StringBuffer()
  95.     ..writeln('i10n usage report')
  96.     ..writeln('Project root: ${rootDir.path}')
  97.     ..writeln('ARB: ${p.relative(arbPath, from: rootDir.path)}')
  98.     ..writeln(
  99.         'Scanned Dart files under: ${p.relative(sourceDir, from: rootDir.path)} (excluding lib/l10n/)\n');
  100.  
  101.   buffer.writeln('USED KEYS:\n');
  102.   usedDetails.forEach((k, files) {
  103.     buffer.writeln('$k -> ${files.length} files');
  104.     for (final f in files) {
  105.       buffer.writeln('  - $f');
  106.     }
  107.   });
  108.  
  109.   buffer.writeln('\nUNUSED KEYS:\n');
  110.   for (final k in unused) {
  111.     buffer.writeln('- $k');
  112.   }
  113.  
  114.   out.writeAsStringSync(buffer.toString());
  115.   print('\n๐Ÿ“„ Detailed report written to ${out.path}');
  116. }
  117.  
  118. /// Walks upward from the current working directory to find a folder containing pubspec.yaml.
  119. Directory? findFlutterRoot() {
  120.   var dir = Directory.current;
  121.   while (true) {
  122.     final pubspec = File(p.join(dir.path, 'pubspec.yaml'));
  123.     if (pubspec.existsSync()) return dir;
  124.     final parent = dir.parent;
  125.     if (parent.path == dir.path) return null;
  126.     dir = parent;
  127.   }
  128. }
  129.  
Advertisement
Add Comment
Please, Sign In to add comment