---
title: "The translation cache"
description: "What verbatra.cache.json reuses between runs, how a fingerprint scopes each entry to its config, and why a cache problem never fails a run."
type: "concept"
---
# The translation cache

What verbatra.cache.json reuses between runs, how a fingerprint scopes each entry to its config, and why a cache problem never fails a run.

The translation cache, `verbatra.cache.json`, is a local translation memory keyed by content: it
remembers the translation of a piece of source text, whichever key holds it, so the same text is
never paid for twice. [The lock file](https://verbatra.kreitz-webdev.de/docs/the-lock-file.md) answers "is this key up to date?"; the
cache answers "have we translated this text before?".

## What it reuses [#what-it-reuses]

Before a key goes to the provider, verbatra looks up the hash of its current source entry: the text
plus its description, meaning, plural flag and placeholders. A hit is free. Because the lookup is by
content, reuse survives what the lock file cannot see:

- **A renamed key.** Move `home.title` to `landing.title` and its translation is reused.
- **Duplicate content.** Two keys with the same source text and context share one translation.

A reused value is not trusted blindly. It passes the same
[integrity gate](https://verbatra.kreitz-webdev.de/docs/translation-safety.md#the-integrity-gate) against the current source as a fresh
translation, and one that no longer fits is discarded and translated anew. Cache hits are reported
per locale, apart from the keys translated this run.

A value a reviewer [rejected](https://verbatra.kreitz-webdev.de/docs/the-lock-file.md#review-decisions) is never reused for that key:
the provenance file keeps a hash of the refused text, and a run skips any hit that matches it. The
one gap is a provenance file the run cannot read (one from a newer verbatra, or a corrupt one under
`humanEdits: "overwrite"`); the run then knows no rejection.

## The fingerprint [#the-fingerprint]

Every entry is scoped to the context it was produced in: a fingerprint over the provider, the
model, the tone, the glossary terms of that locale, and the provider's
[`localeMap`](https://verbatra.kreitz-webdev.de/docs/providers.md#locale-codes-sent-to-the-provider) when it has entries. Change any of
them and earlier entries stop matching, so a new tone or an edited glossary retranslates instead of
serving a stale result. The glossary part is the terms verbatra would send to the provider, after any `sensitiveData` setting that withholds or redacts a term, whether or not the configured provider uses glossary terms (DeepL, Google Cloud Translation and LibreTranslate ignore them), so an edited term still retranslates. A term's `caseSensitive` flag is not part of it. Each locale has its own fingerprint, so editing only the German translation
of a glossary term leaves the French entries matching.

The format is not part of the fingerprint; the integrity gate on every hit catches a value that no
longer fits. With provider `none`, every entry goes into one human-only bucket that tone and
glossary do not affect.

## Fuzzy reuse [#fuzzy-reuse]

<AvailableFrom version="0.11.0" />

An exact hit needs the source text to hash to the same value as before, so editing one character
makes it a miss. [`fuzzyCache`](https://verbatra.kreitz-webdev.de/docs/config-file.md#fuzzycache) is an opt-in fallback for that case:
when a key misses, verbatra scores its source against the other entries of the same locale and
fingerprint, and reuses the closest one at or above `threshold`. The score is one minus the
character edit distance divided by the length of the longer string, so `1` is identical.

### The threshold is not a safety setting [#the-threshold-is-not-a-safety-setting]

Character distance is not meaning. Dropping a "not", turning "cannot" into "can" or swapping one
brand name for another are small edits that score above `0.9` and reverse what the sentence says.
The threshold decides how much provider spend you avoid, not how safe a reuse is. Safety comes from
rules that do not depend on the number:

- **It is off unless you turn it on.**
- **Every fuzzy reuse is flagged for review** with
  [`FUZZY_CACHE_REUSE`](https://verbatra.kreitz-webdev.de/docs/error-codes.md#fuzzy_cache_reuse), on every run, whatever it scored. If
  nobody works the [review queue](https://verbatra.kreitz-webdev.de/docs/review-in-studio.md), leave it off.
- **Changed numerals refuse the reuse.** If the numerals in the two sources differ in count, value
  or order, the entry is not a candidate. This covers every Unicode numeric character (`m²` to
  `m³`, `½` to `¼`), but not numbers written as words.
- **A reuse does not settle the key.** The lock keeps the key's old baseline, so it stays changed:
  every later run offers it again at no provider cost, and [`check`](https://verbatra.kreitz-webdev.de/docs/cli/check.md) keeps
  reporting it, until a real translation lands.
- **An unchanged source text never reaches the fuzzy layer.** Editing only a description or a
  plural flag sends the key to the provider as usual.

### What a fuzzy reuse still is not [#what-a-fuzzy-reuse-still-is-not]

A fuzzy reuse is a translation of text that is no longer the source, so it is never presented as an
exact hit:

- It passes the integrity gate like any other candidate; a near match whose placeholders no longer
  fit is refused, and the key goes to the provider.
- It is reported apart from exact hits, as `fuzzy-reused`, with the key, the score and the earlier
  source text; the SDK carries the same on `LocaleSummary.fuzzyHits`.
- It is never written back into the cache, so a later run cannot mistake it for an exact hit.

Scoring stays inside one fingerprint and is bounded: at most 256 candidates per key, and source text
longer than 2,000 characters is skipped. With provider `none`, a fuzzy match is never applied.

## The file [#the-file]

`verbatra.cache.json` is a local, regenerable file next to your lock file. Keep it out of version
control: it is a cost optimization, not a shared baseline. `verbatra init` gitignores it, and
`translate`, `watch`, `import`, `tmx import` and `pseudo` add the entry to an existing `.gitignore`
that lacks it and say so on stderr.

The cache never fails a run. A missing, corrupt or oversized file becomes an empty cache and is
rebuilt. A file from a newer verbatra is also treated as empty for the run, but left on disk
untouched, with a notice. To start over, delete the file. For fuzzy scoring the file stores each
source string beside its translations, so it grows roughly to the size of your source text.

## Moving a memory between tools [#moving-a-memory-between-tools]

<AvailableFrom version="0.11.0" />

[`verbatra tmx`](https://verbatra.kreitz-webdev.de/docs/cli/tmx.md) imports a TMX file from another translation tool into the cache and
exports the cache as TMX. Imported units are stored under the current fingerprint, so they stop
matching when the provider, model, tone, the glossary terms verbatra would send or `localeMap` changes, like everything else. One
caveat to "delete it and it rebuilds": an imported memory is only as recoverable as the TMX file it
came from, so keep that file.

## Bypassing it [#bypassing-it]

`--no-cache` on [`translate`](https://verbatra.kreitz-webdev.de/docs/cli/translate.md) or [`watch`](https://verbatra.kreitz-webdev.de/docs/cli/watch.md) (`cache: false` in
the SDK) makes exactly the provider calls a run without a cache would, fuzzy reuse included, and
leaves the file untouched. To keep exact reuse and drop only the fuzzy fallback, leave `fuzzyCache`
out of the config. A dry run never writes the cache, and reads it only with provider `none`, so
that plan matches a live run.

## Limitation [#limitation]

Plural forms synthesized by `generatePlurals` are not cached, so they always come from the provider.

## Related [#related]

- [`fuzzyCache`](https://verbatra.kreitz-webdev.de/docs/config-file.md#fuzzycache): the config key and its range.
- [`verbatra tmx`](https://verbatra.kreitz-webdev.de/docs/cli/tmx.md): import and export the memory as TMX.
- [How a run works](https://verbatra.kreitz-webdev.de/docs/how-it-works.md): where the cache lookup sits in the pipeline.
- [Estimate cost before a run](https://verbatra.kreitz-webdev.de/docs/estimating-cost.md): what the cache saves before you spend.
