---
title: "Key-Verlauf"
description: "Lies, wie sich die Übersetzung eines Keys von Commit zu Commit geändert hat, mit Autor, Herkunft des Werts und einem Unterschied auf Wortebene."
type: "reference"
---
# Key-Verlauf

Lies, wie sich die Übersetzung eines Keys von Commit zu Commit geändert hat, mit Autor, Herkunft des Werts und einem Unterschied auf Wortebene.

<AvailableFrom version="0.13.0" />

## Git-Verlauf eines Keys [#git-verlauf-eines-keys]

### keyHistory [#keyhistory]

Listet die Commits, in denen sich der Wert eines Keys in einer Ziel-Locale geändert hat, neueste zuerst, jeweils mit einem Unterschied auf Wortebene zum vorherigen Wert. Nur lesend; es nimmt eine Locale und einen Key, nie einen Pfad, und führt `git log` und `git cat-file` über das injizierbare `execFile` aus.

```ts title="Signatur"
function keyHistory(
  input: KeyHistoryInput,
  deps?: KeyHistoryDeps,
): Promise<KeyHistoryResult>;
```

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `config` | `VerbatraConfig` | yes | The resolved project config, normally from loadConfig. |
| `cwd` | `string` | no | Directory the `files.pattern` is resolved against and git runs in. Defaults to the project root of the config object loadConfig returned, else the process working directory; pass `cwd` from resolveProjectRoot for a copied config. |
| `locale` | `string` | yes | A configured target locale. |
| `key` | `string` | yes | The key, at most KEY_HISTORY_MAX_KEY_CHARS characters. It is compared with parsed keys and never passed to git. |
| `limit` | `number` | no | The most commits to examine. Rounded down and clamped to between 1 and LOCALE_HISTORY_LIMIT_CAP; defaults to LOCALE_HISTORY_LIMIT_DEFAULT. |

**Rückgabe:** `KeyHistoryResult`

**Wirft:** [`UNKNOWN_LOCALE`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#unknown_locale), [`UNKNOWN_KEY`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#unknown_key), [`LOCALE_LAYOUT_INVALID`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#locale_layout_invalid), [`LOCALE_PATH_COLLISION`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#locale_path_collision)

Jede Revision trägt `commit`, `authorDate`, `author` (der Name nach `.mailmap`, nie die E-Mail), `subject`, `value`, `origin`, `sourceChanged` und `diff`. Steuer- und bidirektionale Formatierungszeichen fliegen aus `author` und `subject`, erkannte Schlüssel darin werden durch `[REDACTED]` ersetzt (ein früherer eigener Schlüssel in einer nicht erkannten Form kann bleiben), bevor sie auf 200 und 500 Zeichen gekürzt werden. Ein Text, den eine Revision als Schlüssel verrät (etwa ein `api_key=` vor einer bloßen UUID), wird auch überall sonst in der Antwort ersetzt, und `valueRedacted` ist bei jedem so geänderten Wert gesetzt. `diff` listet Segmente `{ kind, text }` mit `kind` `equal`, `added` oder `removed`: Platzhalter und Tags bleiben ganz, eine Änderung am Leerraum zeigt sich als Änderung, und ein zu großer Unterschied wird ein entfernter und ein hinzugefügter Block. `origin` stammt aus der Provenance-Datei, wie sie mit der Revision committet wurde: ein gespeicherter Ursprung, `unrecorded` oder `external`, wenn ihr Eintrag einen anderen Wert beschreibt; fehlt, wenn die Datei nicht gelesen oder geparst werden konnte, über 4 MiB liegt oder vorher die Zeit ablief. `sourceChanged` vergleicht den Inhalts-Hash des aktuellen Quelleintrags des Keys (Text, Beschreibung, Bedeutung, Plural-Flag und Platzhalter) mit dem Hash, den die committete Lock-Datei für den Key in dieser Locale festgehalten hat: `true`, wenn sie sich unterscheiden, `false`, wenn sie übereinstimmen, und `"unknown"`, wenn die Lock-Datei, ihr Eintrag für den Key oder der aktuelle Quelleintrag fehlt, sich nicht lesen oder parsen lässt, über ihrer Grenze liegt oder vorher die Zeit ablief. `origin` ist davon getrennt: Es vergleicht den Wert der Revision mit dem Provenance-Eintrag und fehlt, statt `"unknown"` zu sein, wenn diese Datei nicht nutzbar ist. Wer einen Wert wiederherstellt, dessen Quelleintrag sich geändert hat, lässt ihn als aktuell zählen.

Jeder Wert und beide Seiten jedes `diff` laufen durch `redact`. Ein Wert, der einen API-Key enthielt, kommt mit `[REDACTED]` an dessen Stelle und `valueRedacted: true` zurück: Er ist nicht der gespeicherte Text, biete ihn also nicht zum Wiederherstellen an. Ein Key, der nicht mehr konfiguriert ist, wird nicht erkannt und so zurückgegeben, wie er geschrieben wurde.

Die Commits kommen aus `git log --first-parent` über die Locale-Datei (bei `apple-strings` auch über die `.stringsdict`-Datei); eine Änderung, die ein Merge mitgebracht hat, steht also am Merge-Commit. Jede Datei wird mit `git cat-file blob` gelesen und vom eingebauten Adapter geparst, und ein Commit wird nur gelistet, wenn der Wert des Keys vom Commit direkt davor im Verlauf der Datei abweicht, der gelesen worden sein muss. Ein Commit, der den Key entfernt, wird nicht gelistet. Bis zu `LOCALE_HISTORY_LIMIT_CAP` Commits werden untersucht (standardmäßig `LOCALE_HISTORY_LIMIT_DEFAULT`), vier Dateien gleichzeitig gelesen, und nach `KEY_HISTORY_DEADLINE_MS` (`30000` Millisekunden) wird kein weiterer Commit untersucht und kein Lesen einer Commit-Datei beginnt (die Datei auf der Platte wird trotzdem gelesen); der `git log`-Aufruf, eine Größenabfrage und ein laufendes Lesen behalten ihr eigenes Limit von 10 Sekunden. `truncated` ist `true`, sobald etwas, das der Aufruf untersucht hat, übersprungen wurde, nicht lesbar war, über einer Größengrenze lag, ein Budget überschritt oder an der Deadline scheiterte, und `truncatedBy` nennt den Grund, mit jeder zutreffenden Ursache aus `KEY_HISTORY_TRUNCATION_REASONS`: `commit-limit` (es gibt mehr Commits, als `limit` untersucht), `shallow-clone`, `unreadable-revision`, `file-too-large`, `byte-budget`, `deadline`, `source-unreadable`, `records-unreadable` (eine Lock- oder Provenance-Datei) und `scrub-cap` (mehr als 256 verschiedene Schlüssel, ein Schlüssel über 4096 Zeichen oder Schlüssel, die zusammen mehr als 65.536 UTF-16-Code-Einheiten ausmachen, tauchten auf; der Durchlauf kann nicht jeden Text abdecken, daher wird jeder nicht leere Wert, jedes Diff-Segment, jeder Betreff und jeder Autorenname zu `[REDACTED]`, leerer Text bleibt leer, und `valueRedacted` ist bei jeder Revision gesetzt). `truncatedBy` ist genau dann leer, wenn `truncated` `false` ist, und die Datei auf der Platte meldet allein `workingTreeUnreadable`. Ein Commit wird nur gelistet, wenn der Commit davor gelesen wurde; ein Commit, dessen älterer Nachbar nicht lesbar war, gilt daher nicht als Änderung des Keys.

Die Größen liefert Git, bevor etwas geparst wird, daher trifft dasselbe Repository auf jeder Maschine dieselben Entscheidungen zu Größengrenzen und Budgets; ein Git-Aufruf mit Timeout, die Deadline von 30 Sekunden und die Provider-Keys in der Umgebung (sie bestimmen, was bereinigt wird) können eine Antwort trotzdem ändern. Eine Locale-Datei über 512 KiB bei `yaml` und den XML-Formaten (`xliff`, `android-xml`, `resx`, `apple-strings`) oder über 4 MiB bei den übrigen Formaten wird ungeparst übersprungen, und die beiden Dateien eines `apple-strings`-Commits zählen als Summe. Ein Aufruf liest höchstens 16 MiB Locale-Dateien für `yaml` und die XML-Formate und 128 MiB für die übrigen: Ältere Commits darüber hinaus werden nicht untersucht. Eine Lock- oder Provenance-Datei über 4 MiB wird nicht gelesen, und ein Aufruf liest davon zusammen höchstens 128 MiB: Darüber hinaus haben ältere Revisionen kein `origin` und `sourceChanged` ist `"unknown"`. Die Quelldatei wird bis zur selben Größe gelesen wie die Locale-Dateien. Das Parsen gibt zwischen den Dateien die Event-Loop frei. Ein Commit, der wegen seiner Größe übersprungen wurde, sich nicht lesen oder parsen lässt, dessen Logzeile nicht parste oder dessen Wert länger als `KEY_HISTORY_MAX_VALUE_CHARS` (`4096`) Zeichen ist, zählt in `unreadableRevisions` und wird nie teilweise zurückgegeben; ein Commit hinter einem Budget wird nicht untersucht und zählt nicht. `key` hat höchstens `KEY_HISTORY_MAX_KEY_CHARS` (`1024`) Zeichen und erreicht Git nie. `workingTree` trägt den Wert auf der Platte samt `diff`, wenn er von `HEAD` abweicht. Lässt sich der Wert auf der Platte nicht mit `HEAD` vergleichen (die Datei parst nicht, etwa bei einem Merge-Konflikt, ist ein symbolischer Link oder liegt über ihrer Grenze, ihr Wert ist zu lang, die Kopie in `HEAD` ließ sich nicht lesen oder die Zeit lief ab), werden die Revisionen trotzdem gelistet und `workingTreeUnreadable` ist `true`.

Umbenennungen werden nicht verfolgt, der Verlauf vor einer Umbenennung fehlt also; nie committete Änderungen sieht es nicht, und ein in der Quelle umbenannter Key wirkt neu. Die Git-Schutzmaßnahmen sind die von `localeGitStatus`, und das Ergebnis ist `{ available: false, reason }` mit einem Grund aus `LOCALE_GIT_UNAVAILABLE_REASONS`, auch `custom-adapter` bei einem `custom:`-Format.

## Laufverlauf [#laufverlauf]

### runHistory [#runhistory]

Liest `.verbatra-local/run-history.jsonl`, in dem jeder Lauf von `translate`, `watch`, `retranslateEntry` und `retranslateEntries`, der eine Spur hinterlassen hat, einen Eintrag hinzufügt. Es schreibt nichts, ruft keinen Provider auf und wirft nichts.

```ts title="Signatur"
function runHistory(
  input?: RunHistoryInput,
  deps?: RunHistoryDeps,
): Promise<RunHistoryResult>;
```

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `cwd` | `string` | no | Directory holding the `.verbatra-local` directory. Defaults to the process working directory, even when the config was found in a parent directory: pass the root resolveProjectRoot returns. |
| `limit` | `number` | no | How many of the newest records to return. A value below 1 returns one record, a value above 200 returns 200, and a value that is not a number returns the default of 50. |

**Gibt zurück:** `RunHistoryResult`

`records` kommen neueste zuerst, höchstens `limit` Stück (`RUN_HISTORY_DEFAULT_LIMIT` ist 50, `RUN_HISTORY_MAX_LIMIT` ist 200; jeder andere Wert wird begrenzt). Jeder Eintrag enthält die Zeit, den `trigger` (`translate` oder `retranslate`), Provider und Modell, den Token-`usage`, die `cost` und pro Locale die Zahl der übersetzten Keys, Cache-Treffer, Fuzzy-Treffer, erzeugten Pluralformen, entfernten Keys, Integritätsablehnungen und Provider-Fehler. Ein Eintrag enthält nie einen Key-Namen oder einen Text. Ein Trockenlauf wird nicht erfasst, ebenso wenig ein Lauf ohne Spur, etwa ein `watch`-Zyklus ohne Arbeit; ein fehlgeschlagener oder abgelehnter Lauf wird erfasst. `entries` zählt die versuchten Einträge eines Retranslate-Eintrags, einen fehlgeschlagenen Provider-Aufruf eingeschlossen, und ein Stapel von Neuübersetzungen ist ein Eintrag. Die Datei behält die neuesten 1.000.

`cost` wird eingefroren, wenn der Lauf endet, aus den `rates`, die die Konfiguration damals hatte. `pricing` `priced` ergänzt `currency`, `asOf` und `amount`; `no-rate-on-file`, `rate-unit-mismatch` und `no-usage` (Keys wurden gesendet und der Provider meldete keine Tokens) tragen keinen Betrag, das heißt unbekannt, nicht gratis; `not-billed` (verbatra bepreist diesen Provider nicht: ein selbst gehosteter `openai-compatible`- oder LibreTranslate-Server oder `none`; ein gehosteter Endpunkt kann dir trotzdem Kosten berechnen) trägt weder Betrag noch `basis` und trägt `USAGE_PARTIAL`, wenn ein Teil seines Verbrauchs nicht gemeldet wurde. `basis` (eines aus `RUN_COST_BASES`) `usage` ist die Token-Zahl des Providers. `source-characters` ist eine Schätzung für DeepL und Google Cloud Translation, die keinen Verbrauch melden: die Quellzeichen der gesendeten Keys, ohne Cache-Treffer, mit abgelehnten Keys, ohne fehlgeschlagene, mit `caveats`, die nennen, was fehlt. Eine `usage`-Zahl mit dem Caveat `USAGE_PARTIAL` ist eine Untergrenze, bepreist oder nicht: eine Locale hat Keys gesendet und keine Tokens gemeldet, Betrag und Token-Zahl decken also nur das Gemeldete. Eine Locale, die für einige ihrer Teilstapel Verbrauch meldete und für andere nicht, lässt sich nicht erkennen und trägt keine Markierung.

Nach dem Lauf setzt ein `check` über die berührten Locales die `coverage` jeder Locale (`missing`, `stale`, `upToDate`). Eine Locale ohne sie wurde nicht gemessen, und `coverageUnavailable` sagt, dass der Durchlauf scheiterte.

`unreadableRecords` zählt Zeilen, die nicht gelesen werden konnten und ausgelassen wurden; der nächste erfasste Lauf schreibt die Datei ohne sie neu. `unavailable` (eines aus `RUN_HISTORY_UNAVAILABLE_REASONS`: `unreadable` oder `too-large` über 8 MiB) sagt, dass die ganze Datei nicht gelesen werden konnte, eine leere Liste ist dann also kein Beweis, dass nichts lief. Eine nicht lesbare Datei bleibt unverändert, und es wird kein Eintrag angefügt; eine Zeile, die kein gültiges UTF-8 ist, zählt als ein nicht lesbarer Eintrag.
