import 'package:build/build.dart'; import 'package:xml/xml.dart'; import 'package:glob/glob.dart'; import 'package:path/path.dart' as path; import 'flutter_string_get_config.dart'; Builder stringXmlWatcherBuilder(BuilderOptions options) { return StringXmlWatcherBuilder(); } class StringXmlWatcherBuilder implements Builder { late final FlutterStringGetConfig _config; late final Glob _targetXmlGlob; StringXmlWatcherBuilder() { _config = FlutterStringGetConfig.fromProject(); print('string_get_runner inputDir ${_config.inputDir}'); print('string_get_runner outputFile ${_config.outputFile}'); print('string_get_runner language ${_config.language}'); _targetXmlGlob = Glob(_config.inputDir); } @override Future build(BuildStep buildStep) async { print('string_get_runner build'); final xmlFiles = await buildStep.findAssets(_targetXmlGlob).toList(); final (buffer, multiBuffer) = await _processXmlFiles(buildStep, xmlFiles); final outputId = AssetId(buildStep.inputId.package, _config.outputFile); await buildStep.writeAsString( outputId, buffer.toString() + multiBuffer.toString()); } Future<(StringBuffer, StringBuffer)> _processXmlFiles( BuildStep buildStep, List xmlFiles, ) async { final buffer = StringBuffer(); final multiBuffer = StringBuffer(); buffer.writeln('import \'package:get/get.dart\';'); buffer.writeln(); buffer.writeln('class StringName {'); buffer.writeln(' StringName._();'); final Map> multiLangMap = {}; for (final file in xmlFiles) { try { final content = await buildStep.readAsString(file); final document = XmlDocument.parse(content); final languageCode = _extractLanguageCode(file.path); print('languageCode :$languageCode'); multiLangMap.putIfAbsent(languageCode, () => {}); for (final element in document.findAllElements('string')) { final name = element.getAttribute('name'); final value = processXmlText(element.text); if (name != null) { multiLangMap[languageCode]![name] = value; if (languageCode == _config.language) { final camelCaseName = _toCamelCase(name); buffer.writeln( ' static String get $camelCaseName => \'$name\'.tr; // $value', ); } } } } catch (e) { print('❌(string_get_runner)Error processing file ${file.path}: $e'); } } buffer.writeln('}'); multiBuffer.writeln('class StringMultiSource {'); multiBuffer.writeln(' StringMultiSource._();'); multiBuffer.writeln( ' static const Map> translations = {'); for (final entry in multiLangMap.entries) { multiBuffer.writeln(' \'${entry.key}\': {'); for (final item in entry.value.entries) { multiBuffer.writeln(' \'${item.key}\': \'${item.value}\','); } multiBuffer.writeln(' },'); } multiBuffer.writeln(' };'); multiBuffer.writeln('}'); print('✅ string_get_runner file generated successfully!'); return (buffer, multiBuffer); } String processXmlText(String original) { return original .replaceAll('\r\n', ' ') .replaceAll('\n', ' ') .replaceAll(RegExp(r'\s+'), ' ') .trim() .replaceAll("'", "\\'"); } String _toCamelCase(String snakeCase) { return snakeCase.split('_').map((word) { if (word == snakeCase.split('_').first) return word; return word[0].toUpperCase() + word.substring(1); }).join(''); } String _extractLanguageCode(String filePath) { final segments = path.split(filePath); final stringIndex = segments.indexOf('string'); if (stringIndex >= 0 && stringIndex + 1 < segments.length) { final langDir = segments[stringIndex + 1]; return langDir == 'base' ? _config.language : langDir; } return _config.language; } @override Map> get buildExtensions => { r'$package$': [path.normalize(_config.outputFile)], }; }