Core conceptsThe lock file

The lock file

What verbatra.lock.json records, how drift is detected, how concurrent runs are serialized, and why you commit it.

The lock file, verbatra.lock.json, exists so a run can tell what already got translated and skip it: it is the baseline every run diffs against, and it is what makes verbatra incremental. This page covers what it stores, how it changes, and what happens when it is contended, missing, or corrupt.

What it records

For every target locale, the lock file stores one content hash per translated key, computed from the source value that produced the existing translation. It contains no translations and no secrets: just a version field and hashes. The hash normalizes Unicode to NFC and line endings to LF, so re-saving a source file with different normalization or CRLF endings does not mark anything as changed.

How drift is detected

When a run diffs a target locale, a source key absent from the target is missing. For a key the target does have, the current source hash is compared against the hash the lock recorded: a difference means the source drifted since the key was last translated, so it is changed and gets retranslated; a match means it is unchanged and never sent to the provider. That is why a second run with no source edits calls the provider for nothing, and why stale translations cannot hide behind a key that merely exists. The full pipeline is on How it works.

How it is updated

The lock is updated per locale, and how depends on what ran:

  • A full run (translate, watch, or a workbook import) replaces that locale's entries wholesale with the run's authoritative result.
  • A single-key action (such as a retranslate from Studio) merges only that key's entry, leaving every other recorded key untouched.

In both cases one exception applies: a key withheld this run (a failed integrity check, a failed provider call, or an invalid-ICU source) keeps its prior hash, so the next run still sees it as needing work and retries it instead of recording it as done. Orphaned keys get no entry. The file is serialized with sorted keys, so its diffs stay stable and reviewable.

The write lock

Two runs touching the same locale at the same time (a second terminal, a CI job, a Studio action) could otherwise both diff a stale baseline and both pay for the same provider call. To prevent that, every write to a locale and its lock entries happens under a cross-process write lock scoped to that one locale; a second writer for the same locale waits, then re-reads the lock file fresh and diffs against a baseline that already includes the first writer's result. Different locales do not block each other.

If a lock cannot be acquired, the run fails with LOCK_CONTENDED and names the lock file's path (under the gitignored .verbatra-local/ directory). If no verbatra process is running, that file was left behind by a killed one: delete it and retry.

Missing or corrupt

A missing lock file is not an error: it counts as a first run, where every source key is missing. A lock file that is present but unparseable, structurally wrong, oversized, or at an unsupported version fails the run with LOCK_FILE_INVALID instead of being silently overwritten, so you can inspect or restore it rather than losing the baseline.

Commit it

Commit verbatra.lock.json alongside your locale files. It is the shared baseline your teammates and your CI diff against; keeping it in version control is what makes incremental runs reproducible across machines. Never hand-edit it.

Edit on GitHub