normalizeTranslation function

ArbFile normalizeTranslation(
  1. ArbFile arb,
  2. Map<String, String> sourceHashes, {
  3. bool stamp = true,
})

Normalize a translation ARB for dialect check --fix:

  • Strip descriptive metadata (namespace, description, context, placeholders) — that belongs only in the source ARB.
  • Preserve state metadata (locked, source_hash) — provenance and human-approval are properties of the translation value.
  • Stamp source_hash = current source hash onto unlocked entries that have a value but no hash yet. New translations (just written by an AI or dialect pull) become tracked-as-fresh; existing hashes are never overwritten, so a stale translation stays stale until it's actually re-translated (which clears the hash) or locked (which re-stamps it).

Locked entries are left to the lock/unlock flow — never auto-stamped.

Set stamp to false to normalize formatting without adding hashes to entries that lack one. This is the authoring pass: the first --fix on a brand-new locale otherwise turns a reviewable ~280-line file of plain "key": "value" pairs into ~1,300 lines, because every key gains a three-line @key block — swamping the human review of the one file whose content most deserves reading. Existing hashes are always preserved; this only defers creating new ones to the next run.

We deliberately did not solve this by moving hashes to a sidecar file (.vi.hashes.json). The ARB would stop being self-describing, and two files that must agree about the same keys is a drift bug waiting to happen — a worse trade than one large diff on one run.

Implementation

ArbFile normalizeTranslation(
  ArbFile arb,
  Map<String, String> sourceHashes, {
  bool stamp = true,
}) {
  final entries = <ArbEntry>[];
  for (final e in arb.entries) {
    final locked = e.metadata?.locked ?? false;
    var hash = e.metadata?.sourceHash;

    if (stamp &&
        !locked &&
        hash == null &&
        e.value.isNotEmpty &&
        sourceHashes.containsKey(e.key)) {
      hash = sourceHashes[e.key];
    }

    final keepMetadata = locked || hash != null;
    entries.add(
      ArbEntry(
        key: e.key,
        value: e.value,
        metadata: keepMetadata
            ? ArbMetadata(locked: locked, sourceHash: hash)
            : null,
      ),
    );
  }

  return ArbFile(
    locale: arb.locale,
    entries: entries,
    fileMetadata: arb.fileMetadata,
    orphanMetadata: const {},
    entryLines: arb.entryLines,
    sourcePath: arb.sourcePath,
  );
}