---
title: "Keep human translations"
description: "Keep the strings a translator wrote when the source text changes, get a machine suggestion instead of an overwrite, and pin keys no machine may touch."
type: "how-to"
---
# Keep human translations

Keep the strings a translator wrote when the source text changes, get a machine suggestion instead of an overwrite, and pin keys no machine may touch.

<AvailableFrom version="0.12.0" />

Use this when some translations were written by a person: legal, marketing or brand copy that someone was paid for. When its source text changes, the key goes stale, and a plain machine run would translate over it. verbatra keeps it by default; this page shows how to see those keys, resolve them, and tighten or loosen the rule.

You need a project that has run `translate` at least once, so `verbatra.provenance.json` exists and records who wrote each value.

## What is protected [#what-is-protected]

When a key's source text changes, `translate` and `watch` leave its value alone if a person wrote it:

| Origin | Where the value came from |
| --- | --- |
| `human` | a person's edit, for example in the Studio edit dialog |
| `import` | a row imported from a translator handoff with `verbatra import` |
| `external` | an edit made outside verbatra, such as a translator changing the locale file in a pull request |

Machine output (`machine`, `memory`, `fuzzy`), agent edits (`agent`) and values with no record yet (`unrecorded`) are retranslated as usual. A value a reviewer rejected is never protected. See [Provenance](https://verbatra.kreitz-webdev.de/docs/the-lock-file.md#provenance-who-wrote-each-value) for how each origin is recorded.

## Find the protected keys [#find-the-protected-keys]

A protected key is not sent to the provider, keeps its value, and stays stale, so it stays visible:

```bash
verbatra translate --dry-run
```

```text output
verbatra translate (dry run)
  de: 1 would translate, 12 unchanged, 1 protected
    protected:       checkout.terms (import)
1 succeeded, 0 partial, 0 failed (dry run: nothing written)
```

- `verbatra translate` translates everything else, lists the key as protected, and still exits `0`.
- `verbatra check` counts it as stale and exits `1`, so a CI gate shows that a person has work to do.
- `verbatra diff` lists it under `protected`.
- `verbatra export` hands it to the translator as `changed`, like any stale key.

## Resolving a protected key [#resolving-a-protected-key]

A protected key stops being stale as soon as a person decides what it should say:

- Edit it in Studio, or save it unchanged to keep the text. Either way the value is recorded as `human` against the new source text.
- Export it, let the translator update it, and import the handoff. See [Hand off with Excel or CSV](https://verbatra.kreitz-webdev.de/docs/manual-translation.md).
- Retranslate it on purpose for one run with `verbatra translate --include-human`.

## Suggestions instead of overwrites [#suggestions-instead-of-overwrites]

To see what a machine would write without replacing the person's text, set `humanEdits` to `suggest`:

```ts title="verbatra.config.ts"
export default defineConfig({
  // ...
  humanEdits: "suggest",
});
```

A protected key is then still sent to the provider, but the answer is only a suggestion. It appears next to the key in the run summary, with a `suggestionStatus` saying whether it arrived or why not, and it is kept in the translation memory; it is never written to the locale file. An exact memory entry for the new text can serve as the suggestion, and the next run serves it again without paying. `translate --estimate` counts these keys, since they are sent. In a human-only project there is no provider to ask, so `suggest` behaves like `protect`.

To go back to retranslating everything, set `humanEdits` to `overwrite`. See [`humanEdits` and `pinnedKeys`](https://verbatra.kreitz-webdev.de/docs/config-file.md#humanedits-and-pinnedkeys).

## Pinning keys [#pinning-keys]

Some keys must never be machine-touched at all: a trademark, a legal disclaimer. List them in `pinnedKeys`, where `*` matches any run of characters:

```ts title="verbatra.config.ts"
export default defineConfig({
  // ...
  pinnedKeys: ["legal.*", "brand.name"],
});
```

A pinned key is never translated, suggested or retranslated, even when it is missing and even with `--include-human`. It is listed as protected with the reason `pinned` until a person fills it.

## Agents [#agents]

An AI agent cannot replace a person's work. The MCP tool `translation.retranslateEntry` refuses a protected value with [`KEY_PROTECTED`](https://verbatra.kreitz-webdev.de/docs/error-codes.md#key_protected) and a pinned key with [`KEY_PINNED`](https://verbatra.kreitz-webdev.de/docs/error-codes.md#key_pinned), and offers no override. `translation.translatePending` leaves protected keys alone and lists them. An agent can still write a value with `translation.editEntry` unless the key is pinned; that value is recorded as `agent`, so it never passes for a person's work.

## In Studio [#in-studio]

<AvailableFrom version="0.6.0" pkg="@verbatra/studio" />

The Translations page marks a protected key as **Protected** and lists it in its own group, with the origin of its current value. Open the key to edit it, or save it unchanged to keep the text. Retranslating a protected key from the key drawer asks first and offers **Replace anyway**. See [Review in Studio](https://verbatra.kreitz-webdev.de/docs/review-in-studio.md).

## Edge cases [#edge-cases]

- **A provenance file from a newer verbatra.** No origin can be read, so `translate` protects every stale key with a value until you upgrade, while `check` and `diff` list only pinned keys as protected.
- **A corrupt provenance file.** `translate`, including a dry run, fails with [`PROVENANCE_FILE_INVALID`](https://verbatra.kreitz-webdev.de/docs/error-codes.md#provenance_file_invalid). `check` and `diff` keep working and list only pinned keys as protected.
- **Human-only projects.** Protected keys count toward exit code `3`, like keys the translation memory could not fill: both need a person.

## Related [#related]

- [The config file](https://verbatra.kreitz-webdev.de/docs/config-file.md#humanedits-and-pinnedkeys): `humanEdits` and `pinnedKeys`.
- [`verbatra translate`](https://verbatra.kreitz-webdev.de/docs/cli/translate.md#keys-a-person-wrote): `--include-human`.
- [The lock file](https://verbatra.kreitz-webdev.de/docs/the-lock-file.md#provenance-who-wrote-each-value): how `verbatra.provenance.json` records each origin.
- [Run without machine translation](https://verbatra.kreitz-webdev.de/docs/human-only-workflow.md): projects where every value comes from a person.
