Docs / Concepts
How a run works
The translation pipeline end to end, from reading the source locale and finding changed keys to writing the translations and updating the lock file.
A run is a pipeline: read the source locale, diff each target against the lock file, send only what
needs work to the provider, check what comes back, write, and record the result. The pipeline is
the same whether the run came from the CLI, the SDK, or watch.
Read the source locale
The format adapter reads the source file into a neutral set of keyed entries, so every later step
works the same for every format. A source key whose value is invalid for the format's message
syntax (invalid ICU) is set aside and reported rather than sent to the provider in a broken state.
A source file that is missing or cannot be parsed fails the run; see
SOURCE_ and
SOURCE_.
Diff against the lock baseline
The diff is what makes a run incremental. verbatra reads the existing target file (an absent file
counts as empty, though xliff and apple-xcstrings writers refuse a missing target) and compares each source key against the lock file, the
record of what was translated before:
| Bucket | Meaning | What the run does |
|---|---|---|
| missing | the target does not have the key | translates it |
| changed | the source no longer matches the recorded hash | translates it |
| unchanged | the source still matches the recorded hash | leaves it alone |
| orphaned | the key is in the target but not in the source | reports it; removes it only with --prune or prune: true |
Held-back keys
Two kinds of candidate are held back. A changed key whose current value a person wrote or imported
is protected by default (humanEdits), and a key matching pinnedKeys is never machine-translated.
Both stay stale until a person resolves them; see
Protecting human translations.
Batch to the provider
Every candidate is first looked up in the translation cache. A hit is reused and never reaches the provider. Of the misses, keys with the same source content are grouped, so that text costs one request per locale rather than one per key.
The groups go to your provider in sequential sub-batches of at most maxBatchSize (default 50),
each carrying the entries, the glossary terms of that locale and your tone. If a sub-batch fails
(a rate limit, a timeout, a revoked key), its keys are withheld for this run while the other
sub-batches keep going. Providers describes how each provider applies these
inputs.
Validate the response
Provider output is data, never free text. For the LLM providers the response must match a fixed schema:
- A malformed response, or one that contains a key that was never requested, fails the whole sub-batch.
- A requested key that comes back missing or duplicated is requested once more.
- A key still unresolved after that is not translated this run.
The machine-translation providers have no response schema, but their results go through the same integrity gate.
Run the integrity gate
A schema-valid translation can still be broken, so verbatra decides from the candidate value itself whether to accept it and never trusts the provider's own report. A candidate that loses a placeholder, breaks an ICU message or its markup, comes back empty, or degenerates is withheld instead of written. The checks are on Translation safety.
Write through the adapter
Accepted translations are merged into the target and written by the format adapter, atomically and in a stable key order: a key already in the target keeps its position, and new keys are appended after the existing ones, in source order. An interrupted write never leaves a half-written file, and reviewers see small, stable diffs.
Update the lock
After a locale is written, its lock entries are refreshed with the current source hashes. A key withheld this run keeps its previous hash, so the next run still sees it as needing work. Each locale is written under a cross-process write lock, so two overlapping runs never diff against a stale baseline or pay twice for the same call.
One locale at a time, failures as data
Each target locale runs on its own, and one locale failing does not stop the others. The run returns a summary per locale: translated, unchanged, orphaned, withheld, flagged for review, and notices. What each field holds is on RunSummary.
What this design guarantees:
- A second run with no source edits calls the provider for nothing.
- Nothing a check rejected is ever written, and nothing withheld is recorded as done.
- A dry run (
--dry-run) stops after the diff and returns the same summary shape, without building a provider or writing a file.
What it does not do: it never judges whether a translation is good. That is what review flags and a person are for.
Related
verbatra translate: flags, output and exit codes of a run.- The lock file and provenance: what the baseline records.
- The translation cache: how reuse is keyed.
- Translation safety: the integrity gate and review flags.
- Error codes: every code a run can report.