Files
driver_tripz/lib/controller/packages/lingo/lingo_hunter.dart
Hamza-Ayed f3ada31b3b 25-4/14/1
2025-04-04 01:07:13 +03:00

222 lines
7.5 KiB
Dart

import 'dart:io';
import 'dart:convert';
import '../../../print.dart';
abstract class LingoHunter {
/// Extracts translatable strings...
static Future<void> extractAndCreateTranslationFiles({
required String baseLang,
required List<String> langs,
String? projectDirectory,
String? outputDirectory,
bool translateBaseLang = true,
List<RegExp>? additionalRegExps,
bool overrideRegExps = false,
List<String> fileExtensions = const ['.dart'],
}) async {
// 1. Find project root (improved logic)
String projectRoot;
if (projectDirectory != null) {
// Use provided directory, but check if it's valid
if (!await Directory(projectDirectory).exists()) {
throw ArgumentError(
"The provided projectDirectory '$projectDirectory' does not exist.");
}
projectRoot = projectDirectory;
} else {
projectRoot = await _findProjectRoot(); // Use the corrected function
}
// 2. Use the project root as output directory if not specified
final String outputDir = outputDirectory ?? projectRoot;
// 3. Output Directory Verification (Create if necessary)
final Directory outputDirObj = Directory(outputDir);
if (!await outputDirObj.exists()) {
try {
await outputDirObj.create(recursive: true);
} catch (e) {
throw Exception(
"Failed to create output directory: $outputDir. Error: $e");
}
}
print("Project root directory: $projectRoot");
print("Output directory: $outputDir");
// 4. Extract translatable strings
final Set<String> strings = await extractStringsFromFlutterProject(
directory: projectRoot, // Use the validated projectRoot
additionalRegExps: additionalRegExps,
overrideRegExps: overrideRegExps,
fileExtensions: fileExtensions,
);
// 5. Generate translation files
await _createTranslationFiles(
strings: strings,
outputDirectory: outputDir,
baseLang: baseLang,
langs: langs,
translateBaseLang: translateBaseLang,
);
print("Successfully extracted strings and generated translation files.");
}
/// Finds the project's root directory (corrected)
static Future<String> _findProjectRoot() async {
Directory current = Directory.current;
int maxIterations = 10; // Prevent infinite loop
int count = 0;
while (count < maxIterations) {
if (await File('${current.path}/pubspec.yaml').exists()) {
return current.path; // Return the directory *containing* pubspec.yaml
}
if (current.path == current.parent.path) {
break; // Reached root, stop searching
}
current = current.parent;
count++;
}
// If `pubspec.yaml` was not found, throw an exception
throw Exception(
"`pubspec.yaml` not found in the current directory or its parents.");
}
/// Extracts translatable strings (no changes needed here)
static Future<Set<String>> extractStringsFromFlutterProject({
required String directory,
List<RegExp>? additionalRegExps,
bool overrideRegExps = false,
List<String> fileExtensions = const ['.dart'],
}) async {
// ... (rest of the function remains the same) ...
final List<RegExp> defaultPatterns = [
RegExp(r'"([^"]+)"\.tr\(\)'), // "string".tr()
RegExp(r"'([^']+)'\.tr\(\)"), // 'string'.tr()
RegExp(r'"([^"]+)"\.tr'), // "string".tr
RegExp(r"'([^']+)'\.tr"), // 'string'.tr
RegExp(r'"([^"]+)"\.tr\(\w+\)'), // "string".tr(context)
RegExp(r"'([^']+)'\.tr\(\w+\)"), // 'string'.tr(context)
RegExp(r'context\.tr\("([^"]+)"\)'), // context.tr("string")
RegExp(r"context\.tr\('([^']+)'\)"), // context.tr('string')
RegExp(r'tr\(\w+, "([^"]+)"\)'), // tr(context, "string")
RegExp(r"tr\(\w+, '([^']+)'\)"), // tr(context, 'string')
RegExp(r'tr\("([^"]+)"\)'), // tr("string")
RegExp(r"tr\('([^']+)'\)"), // tr('string')
RegExp(r'"([^"]+)"\.tr\(args: \[.*?\]\)'), // "string".tr(args: [])
RegExp(r'"([^"]+)"\.plural\(\d+\)'), // "string".plural(3)
//Intl Package Patterns
RegExp(r'AppLocalizations\.of\(context\)!\.translate\("([^"]+)"\)'),
];
// Determine the patterns to use
List<RegExp> patterns;
if (overrideRegExps && additionalRegExps != null) {
patterns = additionalRegExps;
} else {
patterns = [...defaultPatterns];
if (additionalRegExps != null) {
patterns.addAll(additionalRegExps);
}
}
final Set<String> strings = {};
final Directory projectDirObj = Directory(directory);
// Check if the directory exists *before* listing
if (!await projectDirObj.exists()) {
throw ArgumentError("The directory '$directory' does not exist.");
}
final List<FileSystemEntity> entities =
await projectDirObj.list(recursive: true).toList();
// Filter files by the specified extensions
final List<File> filteredFiles = entities
.whereType<File>()
.where((file) => fileExtensions.any((ext) => file.path.endsWith(ext)))
.toList();
// Extract strings from files
for (final File file in filteredFiles) {
final String content = await file.readAsString();
for (final RegExp pattern in patterns) {
final Iterable<RegExpMatch> matches = pattern.allMatches(content);
for (final RegExpMatch match in matches) {
if (match.groupCount >= 1 && match.group(1) != null) {
strings.add(match.group(1)!);
}
}
}
}
return strings;
}
/// Creates translation files (no changes needed)
static Future<void> _createTranslationFiles({
required Set<String> strings,
required String outputDirectory,
required String baseLang,
required List<String> langs,
bool translateBaseLang = true,
}) async {
// ... (rest of the function remains the same) ...
final Directory outputDir = Directory(outputDirectory);
if (!await outputDir.exists()) {
Log.print('outputDir: ${outputDir}');
await outputDir.create(recursive: true);
}
// Always create the base language file, even if `translateBaseLang` is false
final String baseFilePath = '$outputDirectory/translations_$baseLang.json';
final Map<String, String> baseStrings = {
for (final string in strings) string: translateBaseLang ? string : ""
};
await _writeTranslationFile(baseFilePath, baseStrings);
for (final String lang in langs) {
final String langFilePath = '$outputDirectory/translations_$lang.json';
final Map<String, String> langStrings = {
for (final string in strings) string: ""
};
await _writeTranslationFile(langFilePath, langStrings);
}
}
/// Writes a translation file (no changes needed)
static Future<void> _writeTranslationFile(
String filePath, Map<String, String> strings) async {
// ... (rest of the function remains the same) ...
final File file = File(filePath);
final StringBuffer content = StringBuffer();
content.writeln('{');
int index = 0;
for (final MapEntry<String, String> entry in strings.entries) {
final String comma = (index < strings.length - 1) ? ',' : '';
final String key =
jsonEncode(entry.key).substring(1, jsonEncode(entry.key).length - 1);
final String value = entry.value.isEmpty
? ""
: jsonEncode(entry.value)
.substring(1, jsonEncode(entry.value).length - 1);
content.writeln(' "$key": "$value"$comma');
index++;
}
content.writeln('}');
await file.writeAsString(content.toString());
}
}