Recovery and rollback

How to undo a translate run, why the locale files and the lock file must be reverted together, and what an interrupted run leaves behind.

verbatra has no undo command and keeps no backups. A run rewrites your locale files in place and updates verbatra.lock.json, and nothing on disk remembers the previous state. Version control is the recovery mechanism, which is the practical reason both the locale files and the lock file are committed: together they are the snapshot you can go back to.

What a run writes

A translate, watch, or import run writes two files a rollback has to care about:

  • the target locale files, one per locale, at your files.pattern path
  • verbatra.lock.json, one source content hash per translated key

The rest of what it writes is regenerable sidecar state you never restore: verbatra.cache.json and the .verbatra-local/ directory, both gitignored. A run may also add those entries to an existing .gitignore that is missing them, which is one-time housekeeping rather than part of any rollback.

Both tracked files are written atomically (a temporary file, then a rename), so a reader sees either the old file or the new one, never a half-written middle. There is no partial file to repair.

Roll back a completed run

If the run is not committed yet, discard both files together:

git checkout HEAD -- locales verbatra.lock.json

If the run is already committed, revert that commit:

git revert <commit>

Either way, restore the locale files and verbatra.lock.json in the same operation. The next section is why.

Why both files, or neither

The lock file records a hash of the source value that produced each translation. It records nothing about the translated value itself. So reverting your locale files does not, and cannot, tell the lock file anything.

Reverting only the locale files. The lock still holds the hash of the source as it is now. On the next run, a key the revert restored is present in the target file, so it is not missing; and its recorded hash still matches the current source, so it is not changed. It is unchanged, it is never sent to the provider, and the value you rolled back to stays in place permanently. The keys the run newly created are the exception: the revert removed them from the file, so they come back as missing and are translated again. Everything the run replaced is what gets stranded.

The failure is silent by design, not by accident. verbatra check and verbatra diff compare against the same lock baseline, so they report the locale as clean. No command will tell you those translations are out of date, because as far as the recorded baseline is concerned, they are not.

Reverting only the lock file. The mirror image, and destructive in the other direction. The restored hashes are older than the current source, so the affected keys read as changed, and the next run translates them again and overwrites what is in your locale files, including any manual edits made since. You pay for the provider calls a second time and lose the hand corrections.

Reverting both. The source hashes and the translations they describe go back to the same point in time, the baseline is consistent again, and the next run behaves exactly as it would have if the bad run had never happened.

Re-running after a rollback

Rolling back and running verbatra translate again is a redo, not a second opinion. With the same provider, model, tone, and glossary, the cache fingerprint is unchanged, so the run serves the identical translations back from verbatra.cache.json without calling the provider at all.

To get a different result, change what made the first one wrong. Editing the tone or the glossary, or switching model, changes the fingerprint and re-translates on its own. If you want the same configuration re-sent to the provider, pass --no-cache to translate.

To rebuild a locale from scratch rather than roll one run back, see How do I re-translate everything? in the FAQ. Note that deleting the lock file is not a reset: without a baseline nothing can be detected as stale, so a run after deleting it translates nothing.

An interrupted or failed run

An interrupted run needs no rollback. Just run it again.

Each locale is committed on its own: its target file is written, then its lock entries are recorded. Interrupting the command therefore leaves the locales that had finished fully written and recorded, and the ones it had not reached completely untouched. A re-run picks up the untouched ones and skips the finished ones.

The one gap, between a locale's file write and its lock update, resolves itself on the next run. Keys the run had added are now in the target file with no lock entry, so they are adopted as they stand; keys whose source had drifted still carry their old hash, so they are detected as changed and translated again. Neither case loses work.

Keys withheld during a run (a failed integrity check, a failed provider call, an invalid-ICU source, or a stopped token budget) keep their previous hash rather than being recorded as done, so the next run retries them. See Troubleshooting for what each of those means.

If the interrupted process was killed hard, it may have left a write lock behind under .verbatra-local/. The next run reports LOCK_CONTENDED and prints the path to delete.

Before a run you cannot easily undo

  • Commit your locale files and verbatra.lock.json first, so git checkout HEAD -- is available.
  • Preview with verbatra translate --dry-run, or with verbatra diff for the exact key lists and verbatra check for the counts. None of the three calls a provider or writes a file.
  • Add --locales <locale> to try one locale before running the rest.
Edit on GitHub