run method
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 root = argResults!.option('root') ?? Directory.current.path;
final DialectProject project;
try {
project = DialectProject.load(root);
} on FileSystemException catch (e) {
stderr.writeln(e.message);
stderr.writeln('Run `dialect init` first, or run from the project root.');
return 66;
} on FormatException catch (e) {
stderr.writeln('dialect.yaml or an ARB file is malformed:');
stderr.writeln(' ${e.message}');
return 65;
}
final KeySelection selection;
try {
selection = resolveSelection(
project: project,
command: 'accept',
positionals: argResults!.rest,
namespace: argResults!.option('namespace'),
locale: argResults!.option('locale'),
);
} on SelectionFailure catch (f) {
f.lines.forEach(stderr.writeln);
return f.code;
}
// locale -> (key -> rewritten entry). Collected first and flushed once
// per file: writing inside the loop re-serialized the stale in-memory ARB
// each time, so with more than one key only the last one survived.
final pending = <String, Map<String, ArbEntry>>{};
final restamped = <String, Set<String>>{};
final alreadyFresh = <String, Set<String>>{};
final skipped = <String, Set<String>>{};
for (final key in selection.keys) {
final hash = computeSourceHash(project.source.entryFor(key)!.value);
for (final locale in selection.locales) {
final arb = project.translations[locale]!;
final entry = arb.entryFor(key);
if (entry == null || entry.value.isEmpty) {
// No translation to bless — that's a missing_keys / empty_values
// concern, not something `accept` invents a value for.
skipped.putIfAbsent(key, () => <String>{}).add(locale);
continue;
}
if (entry.metadata?.sourceHash == hash) {
alreadyFresh.putIfAbsent(key, () => <String>{}).add(locale);
continue;
}
pending.putIfAbsent(locale, () => <String, ArbEntry>{})[key] = ArbEntry(
key: key,
value: entry.value,
metadata: ArbMetadata(
locked: entry.metadata?.locked ?? false,
sourceHash: hash,
),
);
restamped.putIfAbsent(key, () => <String>{}).add(locale);
}
}
for (final e in pending.entries) {
_flush(project.translations[e.key]!, e.value);
}
if (restamped.isEmpty && alreadyFresh.isEmpty && skipped.isNotEmpty) {
stderr.writeln(
'Nothing to accept: ${describeKeyCount(skipped.length)} '
'(${previewKeys(skipped.keys)}) have no translation yet in '
'${selection.locales.join(', ')}. Translate them first.',
);
return 65;
}
if (restamped.isNotEmpty) {
final locales = <String>{for (final s in restamped.values) ...s}.toList()
..sort();
stdout.writeln(
'✓ accepted ${describeKeyCount(restamped.length)} — re-stamped as '
'current in ${locales.join(', ')}:',
);
stdout.writeln(' ${previewKeys(restamped.keys)}');
}
if (alreadyFresh.isNotEmpty) {
stdout.writeln(
' already current: ${describeKeyCount(alreadyFresh.length)}',
);
}
if (skipped.isNotEmpty) {
stdout.writeln(
' skipped (no translation yet): '
'${describeKeyCount(skipped.length)} — ${previewKeys(skipped.keys)}',
);
}
return 0;
}