---
title: "Historique d'une clé"
description: "Lis comment la traduction d'une clé a changé de commit en commit, avec l'auteur, l'origine de la valeur et une différence au niveau du mot."
type: "reference"
---
# Historique d'une clé

Lis comment la traduction d'une clé a changé de commit en commit, avec l'auteur, l'origine de la valeur et une différence au niveau du mot.

<AvailableFrom version="0.13.0" />

## Historique git d'une clé [#historique-git-dune-clé]

### keyHistory [#keyhistory]

Liste les commits où la valeur d'une clé a changé dans une locale cible, du plus récent au plus ancien, chacun avec une différence au niveau du mot par rapport à la valeur précédente. En lecture seule ; il prend une locale et une clé, jamais un chemin, et lance `git log` et `git cat-file` via l'`execFile` injectable.

```ts title="Signature"
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. |

**Renvoie :** `KeyHistoryResult`

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

Chaque révision porte `commit`, `authorDate`, `author` (le nom après `.mailmap`, jamais l'e-mail), `subject`, `value`, `origin`, `sourceChanged` et `diff`. Les caractères de contrôle et de mise en forme bidirectionnelle sont retirés de `author` et `subject`, les clés reconnues qu'ils contiennent sont remplacées par `[REDACTED]` (une ancienne clé personnalisée sous une forme non reconnue peut rester) avant qu'ils soient coupés à 200 et 500 caractères. Un texte qu'une révision trahit comme clé (par exemple un `api_key=` devant un UUID seul) est aussi remplacé partout ailleurs dans la réponse, et `valueRedacted` est activé sur chaque valeur ainsi modifiée. `diff` liste des segments `{ kind, text }` avec `kind` `equal`, `added` ou `removed` : les variables et les balises restent entières, un changement d'espaces apparaît comme un changement, et une différence trop grande pour être alignée devient un bloc supprimé et un bloc ajouté. `origin` vient du fichier de provenance tel qu'il a été commité avec la révision : une origine enregistrée, `unrecorded`, ou `external` quand son enregistrement décrit une autre valeur, et absent si ce fichier n'a pas pu être lu ni analysé, dépasse 4 Mio ou si le temps s'est écoulé avant. `sourceChanged` compare le hash de contenu de l'entrée source actuelle de la clé (son texte, sa description, son sens, son indicateur de pluriel et ses variables) au hash que le lock-file commité a enregistré pour la clé dans cette locale : `true` quand ils diffèrent, `false` quand ils correspondent et `"unknown"` quand le lock-file, son entrée pour la clé ou l'entrée source actuelle manque, n'a pas pu être lu ni analysé, dépasse sa limite ou que le temps s'est écoulé avant. `origin` est distinct : il compare la valeur de la révision à l'enregistrement de provenance et manque, au lieu d'être `"unknown"`, quand ce fichier est inutilisable. Restaurer une valeur dont l'entrée source a changé la fait compter comme à jour.

Chaque valeur et les deux côtés de chaque `diff` passent par `redact`. Une valeur qui contenait une clé d'API revient avec `[REDACTED]` à sa place et `valueRedacted: true` : ce n'est pas le texte enregistré, ne la propose donc pas à la restauration. Une clé qui n'est plus configurée n'est pas reconnue et revient telle qu'elle est écrite.

Les commits viennent de `git log --first-parent` sur le fichier de la locale (et le fichier `.stringsdict` pour `apple-strings`), donc un changement apporté par un merge figure sur le commit de merge. Chaque fichier est lu avec `git cat-file blob` et analysé par l'adaptateur intégré, et un commit n'est listé que si la valeur de la clé diffère de celle du commit juste avant lui dans l'historique du fichier, qui doit avoir été lu. Un commit qui supprime la clé n'est pas listé. Jusqu'à `LOCALE_HISTORY_LIMIT_CAP` commits sont examinés (`LOCALE_HISTORY_LIMIT_DEFAULT` par défaut), quatre fichiers sont lus à la fois, et après `KEY_HISTORY_DEADLINE_MS` (`30000` millisecondes) aucun autre commit n'est examiné et aucune lecture de fichier de commit ne commence (le fichier sur le disque est lu quand même) ; l'appel à `git log`, une requête de taille et une lecture en cours gardent leur propre limite de 10 secondes. `truncated` vaut `true` quand quelque chose que l'appel a examiné a été ignoré, était illisible, dépassait une limite de taille, passait un budget ou a été arrêté par le délai, et `truncatedBy` dit pourquoi, avec chaque cause qui s'applique parmi `KEY_HISTORY_TRUNCATION_REASONS` : `commit-limit` (il existe plus de commits que `limit` n'en examine), `shallow-clone`, `unreadable-revision`, `file-too-large`, `byte-budget`, `deadline`, `source-unreadable`, `records-unreadable` (un lock-file ou fichier de provenance) et `scrub-cap` (plus de 256 clés distinctes, une clé de plus de 4096 caractères ou des clés qui totalisent plus de 65 536 unités de code UTF-16 sont apparues ; le passage ne peut pas couvrir chaque texte, donc chaque valeur, segment de diff, objet et nom d'auteur non vide devient `[REDACTED]`, le texte vide reste vide et `valueRedacted` est activé sur chaque révision). `truncatedBy` est vide exactement quand `truncated` vaut `false`, et le fichier sur le disque n'est signalé que par `workingTreeUnreadable`. Un commit n'est listé que si le commit précédent a été lu, donc un commit dont le voisin plus ancien était illisible n'est pas compté comme un changement de la clé.

Git donne les tailles avant que quoi que ce soit soit analysé, donc le même dépôt prend les mêmes décisions sur les limites de taille et les budgets sur chaque machine ; un appel git qui expire, le délai de 30 secondes et les clés de fournisseur de l'environnement (qui décident de ce qui est masqué) peuvent encore changer une réponse. Un fichier de locale de plus de 512 Kio pour `yaml` et les formats XML (`xliff`, `android-xml`, `resx`, `apple-strings`) ou de plus de 4 Mio pour les autres formats est ignoré sans être analysé, et les deux fichiers d'un commit `apple-strings` comptent comme leur somme. Un appel lit au plus 16 Mio de fichiers de locale pour `yaml` et les formats XML et 128 Mio pour les autres : les commits plus anciens au-delà ne sont pas examinés. Un lock-file ou un fichier de provenance de plus de 4 Mio n'est pas lu, et un appel en lit au plus 128 Mio ensemble : au-delà, les révisions plus anciennes n'ont pas d'`origin` et `sourceChanged` vaut `"unknown"`. Le fichier source est lu jusqu'à la même taille que les fichiers de locale. L'analyse rend la main à la boucle d'événements entre les fichiers. Un commit ignoré à cause de sa taille, illisible ou qui ne s'analyse pas, dont la ligne de log ne s'est pas analysée, ou dont la valeur dépasse `KEY_HISTORY_MAX_VALUE_CHARS` (`4096`) caractères, compte dans `unreadableRevisions` et n'est jamais renvoyé en partie ; un commit au-delà d'un budget n'est pas examiné et ne compte pas. `key` fait au plus `KEY_HISTORY_MAX_KEY_CHARS` (`1024`) caractères et n'atteint jamais git. `workingTree` porte la valeur sur le disque, avec son `diff`, quand elle diffère de `HEAD`. Quand la valeur sur le disque ne peut pas être comparée à `HEAD` (le fichier ne s'analyse pas, comme pendant un conflit de merge, c'est un lien symbolique ou il dépasse sa limite, sa valeur est trop longue, la copie dans `HEAD` n'a pas pu être lue, ou le temps a manqué), les révisions sont quand même listées et `workingTreeUnreadable` vaut `true`.

Les renommages ne sont pas suivis, donc l'historique d'avant un renommage n'est pas listé ; les modifications jamais commitées ne sont pas vues, et une clé renommée dans la source paraît nouvelle. Les garde-fous git sont ceux de `localeGitStatus`, et le résultat est `{ available: false, reason }` avec une raison de `LOCALE_GIT_UNAVAILABLE_REASONS`, `custom-adapter` compris pour un format `custom:`.

## Historique des exécutions [#historique-des-exécutions]

### runHistory [#runhistory]

Lit `.verbatra-local/run-history.jsonl`, où chaque exécution de `translate`, `watch`, `retranslateEntry` et `retranslateEntries` qui a laissé une trace ajoute un enregistrement. Elle n'écrit rien, n'appelle aucun fournisseur et ne lève rien.

```ts title="Signature"
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. |

**Renvoie :** `RunHistoryResult`

`records` arrivent du plus récent au plus ancien, au plus `limit` (`RUN_HISTORY_DEFAULT_LIMIT` vaut 50, `RUN_HISTORY_MAX_LIMIT` vaut 200 ; toute autre valeur est bornée). Chaque enregistrement porte l'heure, le `trigger` (`translate` ou `retranslate`), le fournisseur et le modèle, l'`usage` de tokens, le `cost` et, par locale, les nombres de clés traduites, de succès de cache, de succès approchants, de formes de pluriel générées, de clés élaguées, de refus d'intégrité et d'échecs du fournisseur. Un enregistrement ne contient jamais un nom de clé ni un texte. Une exécution à blanc n'est pas enregistrée, ni une exécution sans trace, comme un cycle de `watch` sans travail ; une exécution en échec ou refusée est enregistrée. `entries` compte les entrées qu'un enregistrement de retraduction a tentées, un appel échoué au fournisseur compris, et un lot de retraductions fait un enregistrement. Le fichier garde les 1 000 plus récents.

`cost` est figé à la fin de l'exécution, d'après les `rates` que la config contenait alors. `pricing` `priced` ajoute `currency`, `asOf` et `amount` ; `no-rate-on-file`, `rate-unit-mismatch` et `no-usage` (des clés ont été envoyées et le fournisseur n'a signalé aucun token) ne portent aucun montant, ce qui veut dire inconnu, pas gratuit ; `not-billed` (verbatra ne chiffre pas ce fournisseur : un serveur `openai-compatible` ou LibreTranslate auto-hébergé, ou `none` ; un point de terminaison hébergé peut quand même te facturer) ne porte ni montant ni `basis`, et porte `USAGE_PARTIAL` quand une partie de son usage n'a pas été signalée. `basis` (l'une de `RUN_COST_BASES`) `usage` est le nombre de tokens du fournisseur. `source-characters` est une estimation pour DeepL et Google Cloud Translation, qui ne signalent aucune consommation : les caractères source des clés envoyées, sans les succès de cache, avec les clés refusées et sans les clés en échec, avec des `caveats` qui nomment ce qu'elle laisse de côté. Un chiffre `usage` portant le caveat `USAGE_PARTIAL` est une borne basse, chiffrée ou non : une locale a envoyé des clés sans signaler de tokens, donc le montant et le nombre de tokens ne couvrent que ce qui a été signalé. Une locale qui a signalé pour certains de ses sous-lots et pas pour d'autres ne se distingue pas et ne porte aucune marque.

Après l'exécution, un `check` sur les locales touchées fixe la `coverage` de chacune (`missing`, `stale`, `upToDate`). Une locale sans elle n'a pas été mesurée, et `coverageUnavailable` indique que la passe a échoué.

`unreadableRecords` compte les lignes qui n'ont pas pu être lues et ont été écartées ; la prochaine exécution enregistrée réécrit le fichier sans elles. `unavailable` (l'une de `RUN_HISTORY_UNAVAILABLE_REASONS` : `unreadable` ou `too-large` au-delà de 8 MiB) indique que le fichier entier n'a pas pu être lu, donc une liste vide ne prouve pas que rien n'a tourné. Un fichier illisible reste tel quel et aucun enregistrement n'y est ajouté, et une ligne qui n'est pas de l'UTF-8 valide compte pour un enregistrement illisible.
