scanProject function
Runs the Flutter String Scanner tool to detect untranslated static strings.
Implementation
Future<void> scanProject({
String projectPath = '.',
List<String> excludeDirs = const ['build', '.dart_tool', 'generated'],
List<String> excludeFiles = const ['main.dart'],
}) async {
final dir = Directory(projectPath);
final staticStrings = <String>[];
// Recursively scan .dart files
await for (final entity in dir.list(recursive: true)) {
if (entity is File && entity.path.endsWith('.dart')) {
final filePath = entity.path;
if (excludeDirs.any((dir) => filePath.contains(dir)) ||
excludeFiles.contains(File(filePath).uri.pathSegments.last)) {
continue;
}
final content = await entity.readAsString();
final lines = content.split('\n');
// Regex for static strings (double or single quotes)
final stringPattern = RegExp(r'''("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')''');
final matches = stringPattern.allMatches(content);
for (final match in matches) {
final string = match.group(0)!;
final startPos = match.start;
final lineNumber = content.substring(0, startPos).split('\n').length;
// Skip strings in comments or localization contexts
final line = lines[lineNumber - 1];
if (line.contains('//') ||
line.contains('AppLocalizations.of(context)') ||
line.contains('S.of(context)')) {
continue;
}
staticStrings.add('File: $filePath, Line: $lineNumber, String: $string');
}
}
}
// Generate report
if (staticStrings.isEmpty) {
print('No untranslated static strings found.');
} else {
print('Untranslated Static Strings Report');
print('=' * 40);
print(staticStrings.join('\n' + '-' * 40 + '\n'));
}
}