---
title: "Historial de una clave"
description: "Lee cómo cambió la traducción de una clave de commit en commit, con el autor, el origen del valor y una diferencia a nivel de palabra."
type: "reference"
---
# Historial de una clave

Lee cómo cambió la traducción de una clave de commit en commit, con el autor, el origen del valor y una diferencia a nivel de palabra.

<AvailableFrom version="0.13.0" />

## Historial de git de una clave [#historial-de-git-de-una-clave]

### keyHistory [#keyhistory]

Lista los commits en los que cambió el valor de una clave en una locale de destino, del más nuevo al más antiguo, cada uno con una diferencia a nivel de palabra respecto al valor anterior. Solo lectura; recibe una locale y una clave, nunca una ruta, y ejecuta `git log` y `git cat-file` mediante el `execFile` inyectable.

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

**Devuelve:** `KeyHistoryResult`

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

Cada revisión lleva `commit`, `authorDate`, `author` (el nombre tras `.mailmap`, nunca el correo), `subject`, `value`, `origin`, `sourceChanged` y `diff`. Los caracteres de control y de formato bidireccional se quitan de `author` y `subject`, las claves reconocidas que contienen se sustituyen por `[REDACTED]` (una antigua clave propia en una forma no reconocida puede quedar) antes de recortarlos a 200 y 500 caracteres. Un texto que una revisión delata como clave (por ejemplo un `api_key=` ante un UUID suelto) también se sustituye en cualquier otro lugar de la respuesta, y `valueRedacted` se activa en cada valor cambiado así. `diff` lista segmentos `{ kind, text }` con `kind` `equal`, `added` o `removed`: los marcadores de posición y las etiquetas quedan enteros, un cambio de espacios en blanco se muestra como cambio, y una diferencia demasiado grande para alinear pasa a ser un bloque eliminado y otro añadido. `origin` viene del archivo de procedencia tal como se confirmó con la revisión: un origen guardado, `unrecorded`, o `external` cuando su registro describe otro valor, y falta si ese archivo no se pudo leer ni analizar, supera 4 MiB o se acabó antes el tiempo. `sourceChanged` compara el hash de contenido de la entrada fuente actual de la clave (su texto, descripción, significado, indicador de plural y marcadores de posición) con el hash que el lock-file confirmado registró para la clave en esa locale: `true` cuando difieren, `false` cuando coinciden y `"unknown"` cuando faltan el lock-file, su entrada para la clave o la entrada fuente actual, no se pudieron leer ni analizar, superan su límite o se acabó antes el tiempo. `origin` es distinto: compara el valor de la revisión con el registro de procedencia y falta, en lugar de ser `"unknown"`, cuando ese archivo no se puede usar. Restaurar un valor cuya entrada fuente cambió hace que cuente como actualizado.

Cada valor y ambos lados de cada `diff` pasan por `redact`. Un valor que contenía una clave de API vuelve con `[REDACTED]` en su lugar y `valueRedacted: true`: no es el texto guardado, así que no lo ofrezcas para restaurar. Una clave que ya no está configurada no se reconoce y se devuelve tal como está escrita.

Los commits salen de `git log --first-parent` sobre el archivo de la locale (y el archivo `.stringsdict` en `apple-strings`), así que un cambio que trajo un merge aparece en el commit del merge. Cada archivo se lee con `git cat-file blob` y lo analiza el adaptador integrado, y un commit solo se lista si el valor de la clave difiere del commit inmediatamente anterior en el historial del archivo, que debe haberse leído. Un commit que elimina la clave no se lista. Se examinan hasta `LOCALE_HISTORY_LIMIT_CAP` commits (`LOCALE_HISTORY_LIMIT_DEFAULT` por defecto), se leen cuatro archivos a la vez y tras `KEY_HISTORY_DEADLINE_MS` (`30000` milisegundos) no se examina ningún commit más ni empieza ninguna lectura de archivo de un commit (el archivo del disco se lee igualmente); la llamada a `git log`, una consulta de tamaño y una lectura en curso conservan su propio límite de 10 segundos. `truncated` es `true` cuando algo de lo que examinó la llamada se omitió, no se pudo leer, superó un límite de tamaño, pasó un presupuesto o lo detuvo el plazo, y `truncatedBy` dice por qué, con cada causa que aplica de `KEY_HISTORY_TRUNCATION_REASONS`: `commit-limit` (existen más commits de los que examina `limit`), `shallow-clone`, `unreadable-revision`, `file-too-large`, `byte-budget`, `deadline`, `source-unreadable`, `records-unreadable` (un lock-file o archivo de procedencia) y `scrub-cap` (aparecieron más de 256 claves distintas, una clave de más de 4096 caracteres o claves que suman más de 65 536 unidades de código UTF-16; la pasada no puede cubrir todos los textos, así que cada valor, segmento de diff, asunto y nombre de autor no vacío pasa a `[REDACTED]`, el texto vacío sigue vacío y `valueRedacted` se activa en cada revisión). `truncatedBy` está vacío exactamente cuando `truncated` es `false`, y el archivo del disco solo lo señala `workingTreeUnreadable`. Un commit solo se lista si el commit anterior se leyó, así que un commit cuyo vecino más antiguo no se pudo leer no se da por cambio de la clave.

Git da los tamaños antes de analizar nada, así que el mismo repositorio toma las mismas decisiones sobre límites de tamaño y presupuestos en cualquier máquina; una llamada a git que agota su tiempo, el plazo de 30 segundos y las claves de proveedor del entorno (que deciden qué se depura) aún pueden cambiar una respuesta. Un archivo de locale de más de 512 KiB para `yaml` y los formatos XML (`xliff`, `android-xml`, `resx`, `apple-strings`) o de más de 4 MiB para los demás formatos se omite sin analizar, y los dos archivos de un commit de `apple-strings` cuentan como su suma. Una llamada lee como máximo 16 MiB de archivos de locale para `yaml` y los formatos XML y 128 MiB para los demás: los commits más antiguos que eso no se examinan. Un lock-file o archivo de procedencia de más de 4 MiB no se lee, y una llamada lee como máximo 128 MiB de ellos en conjunto: pasado eso, las revisiones más antiguas no tienen `origin` y `sourceChanged` es `"unknown"`. El archivo de origen se lee hasta el mismo tamaño que los archivos de locale. El análisis cede el bucle de eventos entre archivos. Un commit omitido por su tamaño, que no se puede leer ni analizar, cuya línea de registro no se analizó, o cuyo valor supera `KEY_HISTORY_MAX_VALUE_CHARS` (`4096`) caracteres, cuenta en `unreadableRevisions` y nunca se devuelve en parte; un commit pasado un presupuesto no se examina y no cuenta. `key` tiene como máximo `KEY_HISTORY_MAX_KEY_CHARS` (`1024`) caracteres y nunca llega a git. `workingTree` lleva el valor del disco, con su `diff`, cuando difiere de `HEAD`. Si el valor del disco no se puede comparar con `HEAD` (el archivo no se analiza, como en un conflicto de merge, es un enlace simbólico o supera su límite, su valor es demasiado largo, la copia en `HEAD` no se pudo leer o se acabó el tiempo), las revisiones se listan igualmente y `workingTreeUnreadable` es `true`.

Los renombrados no se siguen, así que el historial anterior a un renombrado no se lista; no ve las ediciones que nunca se confirmaron, y una clave renombrada en el origen parece nueva. Las salvaguardas de git son las de `localeGitStatus`, y el resultado es `{ available: false, reason }` con un motivo de `LOCALE_GIT_UNAVAILABLE_REASONS`, incluido `custom-adapter` para un formato `custom:`.

## Historial de ejecuciones [#historial-de-ejecuciones]

### runHistory [#runhistory]

Lee `.verbatra-local/run-history.jsonl`, donde cada ejecución de `translate`, `watch`, `retranslateEntry` y `retranslateEntries` que dejó rastro añade un registro. No escribe nada, no llama a ningún proveedor y no lanza nada.

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

**Devuelve:** `RunHistoryResult`

`records` llegan primero los más recientes, como mucho `limit` (`RUN_HISTORY_DEFAULT_LIMIT` es 50, `RUN_HISTORY_MAX_LIMIT` es 200; cualquier otro valor se acota). Cada registro lleva la hora, el `trigger` (`translate` o `retranslate`), el proveedor y el modelo, el `usage` de tokens, el `cost` y, por locale, los recuentos de claves traducidas, aciertos de caché, aciertos difusos, formas de plural generadas, claves podadas, rechazos de integridad y fallos del proveedor. Un registro nunca lleva el nombre de una clave ni un texto. Una ejecución de prueba no se registra, ni tampoco una que no dejó rastro, como un ciclo de `watch` sin trabajo; una ejecución fallida o rechazada sí se registra. `entries` cuenta las entradas que intentó un registro de retraducción, con la llamada fallida al proveedor incluida, y un lote de retraducciones es un registro. El archivo conserva los 1.000 más recientes.

`cost` se congela al terminar la ejecución, con las `rates` que tenía entonces la configuración. `pricing` `priced` añade `currency`, `asOf` y `amount`; `no-rate-on-file`, `rate-unit-mismatch` y `no-usage` (se enviaron claves y el proveedor no informó de tokens) no llevan importe, lo que significa desconocido, no gratis; `not-billed` (verbatra no pone precio a este proveedor: un servidor `openai-compatible` o LibreTranslate autoalojado, o `none`; un endpoint alojado aún puede facturarte) no lleva ni importe ni `basis`, y lleva `USAGE_PARTIAL` cuando una parte de su uso no se informó. `basis` (uno de `RUN_COST_BASES`) `usage` es el recuento de tokens del proveedor. `source-characters` es una estimación para DeepL y Google Cloud Translation, que no informan del uso: los caracteres de origen de las claves enviadas, sin los aciertos de caché, con las claves rechazadas y sin las fallidas, con `caveats` que nombran lo que deja fuera. Una cifra `usage` con el caveat `USAGE_PARTIAL` es un límite inferior, con precio o sin él: un locale envió claves y no informó de tokens, así que el importe y el número de tokens cubren solo lo informado. Un locale que informó de algunos de sus sublotes y de otros no no se puede distinguir y no lleva marca.

Tras la ejecución, un `check` sobre los locales tocados fija la `coverage` de cada uno (`missing`, `stale`, `upToDate`). Un locale sin ella no se midió, y `coverageUnavailable` indica que la pasada falló.

`unreadableRecords` cuenta las líneas que no se pudieron leer y se dejaron fuera; la siguiente ejecución registrada reescribe el archivo sin ellas. `unavailable` (uno de `RUN_HISTORY_UNAVAILABLE_REASONS`: `unreadable` o `too-large` por encima de 8 MiB) indica que no se pudo leer el archivo entero, así que una lista vacía no prueba que no se ejecutó nada. Un archivo ilegible se deja como está y no se le añade ningún registro, y una línea que no es UTF-8 válido cuenta como un registro ilegible.
