run method
Implementation
Future<void> run() async {
if (args.contains('--help') || args.contains('-h')) {
print(helpText);
return;
}
_cwd = Directory.current.path;
final configPath = _getArg('--config') ?? '$_cwd/trconfig.yaml';
final trconfFile = File(configPath).absolute;
if (!trconfFile.existsSync()) {
stderr.writeln('Config not found: $configPath');
exit(1);
}
try {
_config = LlmTranslateConfig.fromFile(
trconfFile,
endpoint: _getArg('--endpoint'),
model: _getArg('--model'),
verifyModel: _getArg('--verify-model'),
apiKey: _getArg('--api-key'),
envFile: _getArg('--env-file'),
headers: _parseCliHeaders(_getMultipleArgs('--header')),
);
} catch (e) {
stderr.writeln('Failed to load config: $e');
exit(1);
}
final only = _getArg('--only');
final parser = YamlStringsParser(_config);
final allKeys = parser.parse();
if (allKeys.isEmpty) {
print('No translatable keys found.');
return;
}
final gitService = GitService.fromCwd(_config, _cwd);
final changed = gitService.gitDiffedKeys(allKeys);
if (!_force && changed.isEmpty) {
print('No changes detected. Use --force to force re-translate.');
return;
}
final keysToTranslate = _force
? Map<String, KeyValue>.from(allKeys)
: <String, KeyValue>{for (final k in changed) k: allKeys[k]!};
if (only != null && only.isNotEmpty) {
keysToTranslate.removeWhere(
(k, _) => !(k == only || k.startsWith('$only.')),
);
}
if (keysToTranslate.isEmpty) {
print(
'No translatable keys found${only != null ? ' for --only $only' : ''}.',
);
return;
}
final arbService = ArbService(_config);
final llmClient = LlmClient(_config);
final engArb = arbService.readArb('en');
final arbMeta = arbService.extractArbMeta(engArb);
var hasErrors = false;
for (final locale in _config.locales.where((l) => l != 'en')) {
final batch = <String, String>{};
for (final entry in keysToTranslate.entries) {
batch[YamlStringsParser.dotToCamel(entry.key)] = entry.value.text;
}
if (batch.isEmpty) continue;
final existing = arbService.readArb(locale);
final referenceContext = arbService.buildReferenceContext(
engArb: engArb,
existing: existing,
batch: batch,
locale: locale,
);
print(
'Translating ${batch.length} keys to $locale${only != null ? ' (only: $only)' : ''}...',
);
Map<String, String> translations;
try {
translations = await llmClient.translateBatch(
batch,
locale,
referenceContext: referenceContext,
);
} catch (e) {
hasErrors = true;
stderr.writeln('Failed to translate to $locale: $e');
stderr.writeln('Re-run to retry.');
continue;
}
if (_verify) {
print('Verifying $locale...');
try {
final fixes = await llmClient.verifyBatch(
batch,
translations,
locale,
referenceContext: referenceContext,
);
translations.addAll(fixes);
} catch (e) {
stderr.writeln('Verify failed for $locale: $e');
}
}
arbService.writeArb(existing, translations, arbMeta, locale, engArb);
for (final entry in translations.entries) {
final enText = allKeys.entries
.firstWhereOrNull(
(e) => YamlStringsParser.dotToCamel(e.key) == entry.key,
)
?.value
.text;
if (enText != null) {
final enParams = YamlStringsParser.extractPlaceholders(enText);
for (final p in enParams) {
if (!entry.value.contains('{$p}')) {
stderr.writeln('WARNING: $locale/${entry.key} missing \${$p}');
}
}
}
}
print(' ✓ $locale done.');
}
if (!_force) {
var deleted = gitService.gitDeletedKeys(allKeys);
if (only != null && only.isNotEmpty) {
deleted = deleted
.where((k) => k == only || k.startsWith('$only.'))
.toSet();
}
if (deleted.isNotEmpty) {
for (final locale in _config.locales) {
final arb = arbService.readArb(locale);
arb.removeWhere(
(k, _) =>
deleted.contains(k) ||
deleted.contains(k.replaceFirst('@', '')),
);
arbService.writeRawArb(arb, locale);
}
print('Removed ${deleted.length} deleted keys.');
}
}
final enKeys = <String, String>{};
for (final entry in allKeys.entries) {
enKeys[YamlStringsParser.dotToCamel(entry.key)] = entry.value.text;
}
final newEnArb = <String, dynamic>{'@@locale': 'en', ...enKeys};
for (final entry in allKeys.entries) {
final arbKey = YamlStringsParser.dotToCamel(entry.key);
final meta = arbService.genArbMeta(entry.key, entry.value.paramTypes);
if (meta != null) {
newEnArb['@$arbKey'] = meta;
}
}
arbService.writeRawArb(newEnArb, 'en');
FlutterL10nRunner.run(_config.configDir);
if (hasErrors) {
stderr.writeln('Completed with translation errors.');
exit(1);
}
print('Done.');
}