---
title: "verbatra diff"
description: "Zeige, welche Keys ein Lauf pro Locale hinzufügen oder neu übersetzen würde und welche verwaist sind, ohne Dateien zu schreiben oder einen Provider aufzurufen."
type: "reference"
---
# verbatra diff

Zeige, welche Keys ein Lauf pro Locale hinzufügen oder neu übersetzen würde und welche verwaist sind, ohne Dateien zu schreiben oder einen Provider aufzurufen.

Liste pro Ziel-Locale die genauen ausstehenden Keys: die, die ein Lauf hinzufügen würde, die, die er neu übersetzen würde, und die inzwischen verwaisten. `diff` führt dasselbe Lesen und Diffen aus wie [`verbatra translate`](https://verbatra.kreitz-webdev.de/de/docs/cli/translate.md) und hört dann auf: kein Provider-Aufruf, keine geschriebene Datei, keine Aktualisierung der [Lock-Datei](https://verbatra.kreitz-webdev.de/de/docs/the-lock-file.md), kein API-Key nötig.

## Synopsis [#synopsis]

```bash
verbatra diff [flags]
```

## Beispiele [#beispiele]

```bash
# die ausstehenden Keys pro Locale auflisten (Exit 1, wenn welche ausstehen)
verbatra diff

# nur die deutsche und die französische Locale
verbatra diff --locales de,fr

# maschinenlesbare Key-Listen für Tools oder einen PR-Kommentar
verbatra diff --json

# zusätzlich die Quell-Keys auflisten, die dein Code nicht mehr nennt
verbatra diff --unused
```

## Flags [#flags]

| Flag | Argument | Standard | Wirkung |
| --- | --- | --- | --- |
| `--locales` | `<list>` | alle konfigurierten Ziele | nur diese kommagetrennte Teilmenge vergleichen; eine nicht konfigurierte Locale scheitert mit `UNKNOWN_LOCALE`, eine leere Liste mit `INVALID_LOCALES`, beide mit Exit `2` |
| `--unused` | keins | aus | zusätzlich die Keys der Quell-Locale auflisten, die keine Aufrufstelle unter den [`extract`-Roots](https://verbatra.kreitz-webdev.de/de/docs/config-file.md#extract) nennt; siehe [Ungenutzte Quell-Keys](#ungenutzte-quell-keys) |

Nimmt außerdem die [globalen Flags](https://verbatra.kreitz-webdev.de/de/docs/cli.md#globale-flags) `--cwd`, `--config` und `--json`.

## Verhalten [#verhalten]

### Ausstehende und verwaiste Keys [#ausstehende-und-verwaiste-keys]

Jede Locale listet vier Arten von Keys:

| Liste | Bedeutung |
| --- | --- |
| `missing` (angezeigt als `add`) | in der Quelle, im Ziel nicht vorhanden: ein Lauf würde ihn hinzufügen |
| `changed` (angezeigt als `re-translate`) | der Quelltext hat sich seit der letzten Übersetzung geändert: ein Lauf würde ihn neu übersetzen |
| `orphaned` | im Ziel, aber aus der Quelle verschwunden: wird nur gemeldet |
| `emptySource` (angezeigt als `empty source`) | der Quellwert ist leer oder besteht nur aus Leerraum: nichts zu übersetzen, bis du ihn schreibst, nie ausstehend |

Eine Locale hat ausstehende Änderungen, sobald sie mindestens einen fehlenden oder geänderten Key hat. Verwaiste Keys zählen nie als ausstehend, weil ein Standardlauf sie stehen lässt; nur [`verbatra translate --prune`](https://verbatra.kreitz-webdev.de/de/docs/cli/translate.md) entfernt sie. [`verbatra check`](https://verbatra.kreitz-webdev.de/de/docs/cli/check.md) liefert die Zähler, `diff` die Key-Listen.

### Ungenutzte Quell-Keys [#ungenutzte-quell-keys]

<AvailableFrom version="0.11.0" />

`--unused` beantwortet eine andere Frage als `orphaned`: Welche Keys in deiner Quell-Locale-Datei nennt deine Anwendung nicht mehr? Es scannt die `roots` des [`extract`-Blocks](https://verbatra.kreitz-webdev.de/de/docs/config-file.md#extract) mit dem Leser, den [`verbatra extract`](https://verbatra.kreitz-webdev.de/de/docs/cli/extract.md) nutzt, und listet jeden Quell-Key, den keine statische Referenz nennt. Die beiden Listen werden nie zusammengeführt. Der Scan bleibt nur lesend: Nichts wird entfernt, und kein API-Key wird gelesen. Ob du einen Key löschst, entscheidest du.

Der Scan bildet die i18next-Laufzeit nach. Folgendes zählt als Referenz:

- ein Aufruf `t(...)`, `$t(...)` oder `<object>.t(...)` irgendwo unter den Roots, auch über einen Alias wie `const { t: translate } = useTranslation()`, `const tr = i18n.t`, `t.bind(i18n)` oder `i18n.getFixedT(...)`;
- ein statischer `keyPrefix` oder ein statisches drittes `getFixedT`-Argument, das auch die Form mit Präfix jedes statischen Keys dieser Datei abdeckt, und ein statischer `i18nKey` an `Trans` oder in einem Objekt;
- ein Key mit Namespace (`common:nav.home` deckt auch `nav.home` ab), ein Key in natürlicher Sprache und jeder Eintrag eines statischen Key-Arrays;
- eine Plural- oder Kontext-Variante eines referenzierten Keys (`t("items", { count })` deckt `items_one` und `items_other` ab) und jeder Key unter einem referenzierten Eltern-Key.

Keys werden in der eigenen Kodierung des Katalogformats verglichen, sodass ein flacher JSON-Key mit Punkt, ein gettext-Plural oder `msgctxt`-Eintrag und ein Android-`<plurals>`-Eintrag alle zu dem Key passen, den dein Code nennt. Ein Template-Literal mit statischem Anfang wie `` t(`nav.${page}`) `` listet die Keys unter `nav.` getrennt von `unused` als `possiblyDynamic` und lässt den Lauf nie fehlschlagen.

Der Bericht trägt einen `status`: `complete` (jeder erreichbare Key wurde eingegrenzt, `unused` ist also verlässlich), `unreliable` (ein gelisteter Key kann noch genutzt werden; `unreliableBecause` nennt jeden Grund mit Anzahl und Fundstellen) oder `not-run` (nichts wird gelistet; `reason` sagt, warum):

| Grund | `status` | Wann |
| --- | --- | --- |
| `dynamic-keys` | `unreliable` | ein Key ohne statischen Teil: `t(key)`, ein Template-Literal ohne statischen Anfang oder eine Verkettung |
| `dynamic-key-prefix` | `unreliable` | ein `keyPrefix` oder `getFixedT`-Präfix, der kein statischer String ist |
| `trans-without-key` | `unreliable` | ein `Trans`-Element ohne statischen `i18nKey` |
| `aliased-translate-function` | `unreliable` | `t` wird etwas anderem als einer einfachen Variable zugewiesen, etwa `this.tr = t` |
| `translate-function-escapes` | `unreliable` | eine Übersetzungsfunktion wird anders verwendet als im direkten Aufruf, in ihrer Bindung, einem lokalen Alias, `t={t}` an `Trans` oder `Translation` oder einem Hook-Dependency-Array: `renderRow(t)`, `{ t }`, `return t`, `export default t` |
| `unrecognised-translate-source` | `unreliable` | ein `t` oder `$t` aus einer Quelle, die der Scan nicht nachbildet, etwa ein Wrapper-Hook, der `useTranslation(...)` zurückgibt, Hook-Optionen mit Spread oder ein exportiertes `getFixedT`-Ergebnis |
| `template-files-not-scanned` | `unreliable` | `.vue`-, `.svelte`-, `.html`-, `.htm`-, `.astro`-, `.hbs`-, `.handlebars`-, `.ejs`-, `.pug`-, `.njk`-, `.liquid`- oder `.mdx`-Dateien unter den Roots (die ersten 20 werden aufgelistet, alle gezählt) |
| `incomplete-scan` | `unreliable` | eine Datei, die zu groß, unlesbar oder nicht bis zum Ende lesbar ist (auch ein nicht geschlossenes JSX-Element oder ein String, der bis zum Zeilenende läuft), oder ein Verzeichnis, das sich nicht auflisten lässt |
| `EXTRACT_NOT_CONFIGURED` | `not-run` | kein `extract`-Block |
| `EXTRACT_FS_UNSUPPORTED` | `not-run` | das Dateisystem eines SDK-Aufrufers kann keine Verzeichnisse auflisten |
| `FRAMEWORK_NOT_MODELED` | `not-run` | das Format ist `next-intl-json`, `vue-i18n-json` oder `ngx-translate-json`, deren Laufzeit der Scan nicht nachbildet |
| `NO_SOURCE_FILES` | `not-run` | unter den Roots liegt keine Quelldatei |
| `NO_REFERENCES_FOUND` | `not-run` | die gescannten Dateien nennen keinen Key und enthalten keine dynamische Key-Stelle, obwohl der Katalog nicht leer ist; meist verfehlen die Roots deinen Code |

Als Quellen von Übersetzungsfunktionen erkennt der Scan: `useTranslation(...)`, destrukturiert in eine lokale, nicht exportierte Bindung mit statischen Argumenten; ein lokales `i18n.getFixedT(...)` mit statischen Argumenten; einen direkten Aufruf an einem Member `t` (`i18n.t("key")`, `this.props.t("key")`) und seinen lokalen Alias; `<Translation>{(t) => ...}</Translation>`; `withTranslation("ns")(Component)` mit `props.t(...)`; und `import { t } from "i18next"`. Jede Variable und jeder Member namens `t` oder `$t` wird geprüft, ein unbeteiligtes `frame.t + 1` kann den Bericht also unzuverlässig machen. In `.tsx`-, `.jsx`- und `.js`-Dateien werden JSX-Text und Attributwerte in Anführungszeichen als Markup gelesen, nie als Code.

Jeder gelistete Key zeigt seine dekodierte Schreibweise (einen gettext-`msgctxt`-Eintrag als `menu|Open`); `--json` trägt zusätzlich den `catalogKey` so, wie der Adapter ihn schreibt. Ein Key, der nur außerhalb des gescannten Codes genutzt wird (ein Server-Template, ein CMS), gehört in [`extract.unused.ignore`](https://verbatra.kreitz-webdev.de/de/docs/config-file.md#extract): Ein Muster passt auf den dekodierten Key, und ein passender Key erscheint unter `ignored`, er fällt nie stillschweigend weg.

### Geschützte Keys [#geschützte-keys]

<AvailableFrom version="0.12.0" />

Fehlende und geänderte Keys, die `translate` einer Person überlassen würde (eine Person hat den Wert außerhalb von verbatra geschrieben, importiert oder geändert, oder der Key passt auf `pinnedKeys`), erscheinen zusätzlich unter `protected`; siehe [Menschliche Übersetzungen schützen](https://verbatra.kreitz-webdev.de/de/docs/protecting-human-translations.md). Sie bleiben auch in `add` und `re-translate`, weil sie weiter ausstehen. Ist `verbatra.provenance.json` beschädigt oder stammt von einer neueren verbatra-Version, werden nur angeheftete Keys gelistet.

### JSON-Ausgabe [#json-ausgabe]

Schema: [`diff-envelope.json`](https://verbatra.kreitz-webdev.de/schema/v1/diff-envelope.json) validiert den ganzen Envelope; [Schemas](https://verbatra.kreitz-webdev.de/de/docs/cli/output.md#schemas) listet die übrigen auf.

`--json` gibt einen [Envelope](https://verbatra.kreitz-webdev.de/de/docs/cli/output.md#json-ausgabe) aus, dessen `result` eine `DiffSummary` ist. `hasPendingChanges` ist genau dann wahr, wenn der Befehl wegen ausstehender Keys mit `1` endet:

```ts
interface DiffSummary {
  hasPendingChanges: boolean;
  locales: LocaleDiff[];
  unused?: UnusedKeysReport;   // nur mit --unused
}

interface LocaleDiff {
  locale: string;
  missing: string[];           // würde hinzugefügt
  changed: string[];           // würde neu übersetzt
  orphaned: string[];          // wird nur gemeldet
  emptySource?: string[];      // Quellwert leer: nie ausstehend
  hasPendingChanges: boolean;  // missing.length > 0 || changed.length > 0
  changedOrigins?: Record<string, KeyOrigin>; // fehlt, wenn die Provenienz-Datei unlesbar ist
  protected?: string[];        // ausstehende Keys, die translate stehen lässt
}
```

### Rückmeldung im Terminal [#rückmeldung-im-terminal]

<AvailableFrom version="0.12.0" />

`diff` zeigt eine Zeile, während es arbeitet (mit laufender Dateizahl, wenn `--unused` den Code scannt), und schlägt den nächsten Befehl vor:

```text output
verbatra: diffing and scanning the source... done (0.8s)
next: verbatra translate (send the pending keys to your provider)
```

Sind alle ausstehenden Keys geschützt, schlägt es stattdessen `verbatra studio` vor; unter `provider: { id: "none" }` schlägt es `verbatra export` vor. Diese Zeilen gehen auf stderr, und `--quiet` blendet sie aus. Siehe [Terminalausgabe](https://verbatra.kreitz-webdev.de/de/docs/cli/output.md#terminalausgabe).

## Exit-Codes [#exit-codes]

| Code | Bedeutung |
| --- | --- |
| `0` | keine Locale hat einen Key zum Hinzufügen oder Neuübersetzen, und kein vollständiger `--unused`-Scan hat einen ungenutzten Key gefunden |
| `1` | mindestens eine Locale hat einen fehlenden oder geänderten Key, oder ein `complete`-Scan mit `--unused` hat einen ungenutzten Key gefunden |
| `2` | konnte nicht laufen: ein Fehler für den ganzen Lauf, ein nicht konfigurierter `--locales`-Wert (`UNKNOWN_LOCALE`) oder ein Bedienfehler wie eine leere `--locales`-Liste (`INVALID_LOCALES`) |

Verwaiste, möglicherweise dynamische und ignorierte Keys sowie ein Bericht mit `unreliable` oder `not-run` führen nie allein zu `1`. Siehe [den Exit-Code-Vertrag](https://verbatra.kreitz-webdev.de/de/docs/cli/output.md#exit-codes).

## Siehe auch [#siehe-auch]

- [`verbatra check`](https://verbatra.kreitz-webdev.de/de/docs/cli/check.md) liefert die Zähler pro Locale hinter diesen Listen.
- [`verbatra translate`](https://verbatra.kreitz-webdev.de/de/docs/cli/translate.md) übernimmt die ausstehenden Änderungen; `--prune` entfernt verwaiste Keys.
- [Die Konfigurationsdatei](https://verbatra.kreitz-webdev.de/de/docs/config-file.md#extract) beschreibt den `extract`-Block und `unused.ignore`.
- [CI und Exit-Codes](https://verbatra.kreitz-webdev.de/de/docs/ci-and-exit-codes.md) zeigt `diff` in einer Pipeline.
