run method

  1. @override
Future<int> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<int> run() async {
  final currentDir = Directory.current.path;
  final translationsDir = p.join(currentDir, 'lib', 'core', 'translations');

  if (!Directory(translationsDir).existsSync()) {
    logger.err('Translations directory not found at $translationsDir');
    return ExitCode.noInput.code;
  }

  final sourceLang = argResults!['source'] as String;
  final sourceFile = File(p.join(translationsDir, '$sourceLang.dart'));
  final keysFile = File(p.join(translationsDir, 'translation_keys.dart'));

  if (!sourceFile.existsSync() || !keysFile.existsSync()) {
    logger.err(
      'Reference files ($sourceLang.dart or translation_keys.dart) not found.',
    );
    return ExitCode.noInput.code;
  }

  final translationFiles = Directory(translationsDir)
      .listSync()
      .whereType<File>()
      .where(
        (file) =>
            p.basename(file.path).endsWith('.dart') &&
            p.basename(file.path) != '$sourceLang.dart' &&
            p.basename(file.path) != 'translation_keys.dart' &&
            p.basename(file.path) != 'app_translations.dart' &&
            p.basename(file.path) != 'translations.dart',
      )
      .toList();

  if (translationFiles.isEmpty) {
    logger.info('No target translation files found to update.');
    return ExitCode.success.code;
  }

  logger.info(
    'Found ${translationFiles.length} translation files to update.',
  );

  // Read reference files once
  final sourceContent = sourceFile.readAsStringSync();

  for (final file in translationFiles) {
    final fileName = p.basename(file.path);
    final language = fileName.replaceAll('.dart', '').toUpperCase();
    logger.info('Translating for $language ($fileName)...');

    final targetContent = file.readAsStringSync();

    final prompt =
        '''
You are a Flutter localization expert.
I need to update the '$fileName' file for language code '$language'.

Reference '$sourceLang.dart' (Source of Truth):
```dart
$sourceContent
```

Current '$fileName' (Target):
```dart
$targetContent
```

Task:
1. Update '$fileName' so it contains all keys found in '$sourceLang.dart'.
2. Translate any missing keys from $sourceLang to $language.
3. Keep existing translations in '$fileName' if the keys still exist in '$sourceLang.dart'.
4. Remove keys in '$fileName' that are no longer in '$sourceLang.dart'.
5. Output ONLY the complete Dart code for '$fileName'.
6. Do NOT use markdown code blocks, just raw code.
''';

    // Execute GEMINI CLI
    // We use 'gemini -p' (no -y needed as we don't want it to run actions, just output text)
    // Actually, we keep -y and --no-telemetry for consistency, but we capture stdout.

    // Get model from env or use default 'gemini-flash-latest' as it is a valid, working model name.
    final model =
        Platform.environment['GEMINI_MODEL'] ?? 'gemini-flash-latest';

    int attempts = 0;
    bool success = false;
    while (attempts < 3 && !success) {
      if (attempts > 0) {
        logger.info('Retrying... (Attempt ${attempts + 1})');
        await Future.delayed(Duration(seconds: 2 * attempts));
      }

      final result = await Process.run('gemini', ['-m', model, '-p', prompt]);

      if (result.exitCode == 0) {
        final generatedCode = result.stdout.toString().trim();
        // Basic cleanup if Gemini returns markdown code blocks despite instructions
        final cleanCode = generatedCode
            .replaceAll('```dart', '')
            .replaceAll('```', '')
            .trim();

        if (cleanCode.isNotEmpty && cleanCode.startsWith('import')) {
          file.writeAsStringSync(cleanCode);
          logger.success('Successfully updated translations for $language.');
          success = true;
        } else {
          logger.warn(
            'Gemini returned invalid or empty code for $language. Output:\n$generatedCode',
          );
          // Consider this a failure?
          attempts++;
        }
      } else {
        final stderr = result.stderr.toString();
        if (stderr.contains('429') || stderr.contains('RESOURCE_EXHAUSTED')) {
          logger.warn(
            'Rate limit exceeded for $language. Waiting before retry...',
          );
          attempts++;
        } else {
          logger.err('Failed to translate for $language: $stderr');
          break; // Non-retriable error
        }
      }
    }
  }

  return ExitCode.success.code;
}