---
title: "Ejecuta traducciones"
description: "translate, watch y los puntos de entrada en torno a una ejecución, además de pseudolocales y catálogos tipados, todo desde tus propios scripts de Node.js."
type: "reference"
---
# Ejecuta traducciones

translate, watch y los puntos de entrada en torno a una ejecución, además de pseudolocales y catálogos tipados, todo desde tus propios scripts de Node.js.

## Traduce y vigila [#ejecuta-traducciones]

### translate [#translate]

El flujo de una sola pasada: leer el origen, comparar cada locale de destino con la línea base del
bloqueo, enviar al proveedor las claves faltantes y modificadas, ejecutar las comprobaciones de
integridad, escribir los archivos de locale y actualizar el bloqueo.
Úsalo para scripts, pasos de build y jobs de CI.

```ts title="Firma"
function translate(
  input: TranslateInput,
  deps?: TranslateDeps,
): Promise<RunSummary>;
```

| 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 where the lock-file, the translation memory, and the run-status file live. Defaults to the project root of the config object loadConfig returned, else the process working directory; a copied or rebuilt config loses that root, so pass `cwd` from resolveProjectRoot. |
| `locales` | `readonly string[]` | no | Restrict the run to a subset of the configured target locales, which is how you translate one locale at a time against a rate-limited provider. Defaults to every configured target. The result keeps the configured order whatever order the subset is given in, and any locale named here that is not a configured target fails the whole run with `UNKNOWN_LOCALE` before anything is read or spent. An explicit empty array selects no locale at all rather than every locale. Locales left out are untouched: their files, their lock entries, and their translation-memory entries all stay as they were. The run-status file always describes the most recent run alone, so a subset run narrows it to that subset. |
| `dryRun` | `boolean` | no | Compute the whole run but write nothing and call no provider. The returned RunSummary reports what would have happened, which makes this safe to run in CI to preview work without spending anything. Defaults to false. In human-only mode (provider `none`) it reads the translation memory, so the plan matches a live run. |
| `estimate` | `boolean` | no | Compute a pre-run cost estimate and return it on RunSummary.estimate. Implies `dryRun`: an estimate constructs no provider, reads no API key, makes no network call, and writes no file, so it is safe to run anywhere. Defaults to false. The figure bounds the plan rather than the invoice: every provider call a live run schedules is counted, plural generation included, and the prompt is measured from the request payload that would be sent. Provider-side retries, a repair round, and a language that expands past the allowance can each push a live run above it; EstimateCaveatCode names them all. It carries a currency amount only when the config supplies a `rates` block covering the configured provider and model; otherwise it reports the quantity and says why the money is missing. See RunEstimate. |
| `prune` | `boolean` | no | Remove keys that no longer exist in the source. Defaults to the config's `prune`, then to false, so orphaned keys are reported but kept unless removal is asked for explicitly. |
| `generatePlurals` | `boolean` | no | Generate the plural categories a target language requires rather than translating each category separately. Defaults to the config's `generatePlurals`, then to false. Takes effect only for the `i18next-json` format with an LLM provider; any other format, a machine-translation provider, and provider `none` generate nothing. |
| `onLockWait` | `LockWaitListener` | no | Called while waiting on another process's write lock, so a CLI can explain a stall instead of appearing to hang. Never called for a lock this process holds itself, such as the lock-file guard a sibling locale holds on a concurrent run. Fires first once a wait has lasted a second. The SDK writes no output itself. |
| `onProgress` | `ProgressListener` | no | Called as locales and sub-batches start and finish, for progress reporting. The SDK writes no output itself. |
| `lockAcquireTimeoutMs` | `number` | no | How long, in milliseconds, to wait for a locale's write lock before that locale fails with `LOCK_CONTENDED`, and for the lock-file guard before a locale whose respelled state could not be moved fails with `LOCALE_STATE_NOT_CARRIED_OVER`. Both waits happen before any provider call. Defaults to ten minutes. It does not bound the lock-file guard a locale takes to record its result once its target file is written: that wait always allows the ten-minute default, so a written file is not left unrecorded. Not used on a dry run, which takes no lock. |
| `concurrency` | `number` | no | How many locales to run at once. Must be an integer of at least 1; defaults to 1. On a live run it cannot be combined with a token budget, configured or passed as `maxTokens`: the ceiling would still hold, but which locale loses its remaining work would depend on the order the locales interleave, so the run would not be reproducible. A whole-run error raised once locales are running starts no further locale, and the locales in flight finish and release their write locks before `translate` rejects, so the rejection arrives after the slowest one. |
| `cache` | `boolean` | no | Consult and update the translation memory, fuzzy reuse included. Defaults to true. Turning it off forces every key through the provider, which is what to do when you want to re-pay for a fresh translation. A dry run never reads the memory, whatever this says, except in human-only mode (provider `none`), where the memory is all a live run would use. The memory is the local CACHE_FILE_NAME file; `false` is the CLI's `--no-cache`. |
| `humanEdits` | `"overwrite" \| "protect" \| "suggest"` | no | Overrides the config's `humanEdits` for this run only (see HumanEditsPolicy). Set `overwrite` to retranslate stale keys a person wrote, as the CLI's `--include-human` flag does. Keys matching the config's `pinnedKeys` stay protected whatever this says. A dry run reads the provenance file too, unless this resolves to `overwrite`. |
| `maxTokens` | `number` | no | A token ceiling for this run alone, as the CLI's `--max-tokens` flag sets it. It is always a hard stop: a provider request that would pass it is withheld rather than sent, whatever the config's `budgetBehavior` says, and its keys are listed in LocaleSummary.budgetWithheld. When the config also sets `maxTokens`, the lower of the two applies, so this can tighten the configured budget but never loosen it. Must be a whole number of at least 1. Defaults to the config's `maxTokens` and `budgetBehavior` alone. |
| `signal` | `AbortSignal` | no | Cancels the run when aborted. No further locale starts, and a wait for a write lock stops. The locales running at the time send no further request, and an in-flight request is abandoned rather than counted as a provider failure, unless the provider reported a failure of its own. What already arrived is still written and recorded, the keys left over get no lock-file entry and stay pending for the next run, the run status is recorded, and every lock is released. The call then resolves rather than rejects: the result carries RunSummary.cancelled, each locale kept from starting is `failed` with the `RUN_CANCELLED` error code, and locales stopped while running carry a `RUN_CANCELLED` notice and are `partial`, or `failed` when no key landed and others were withheld. A dry run only stops early. Defaults to none. |

**Devuelve:** [`RunSummary`](https://verbatra.kreitz-webdev.de/es/docs/sdk/run-summary.md)

**Lanza:** [`UNKNOWN_LOCALE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#unknown_locale), [`LOCALE_UNSUPPORTED_BY_PROVIDER`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#locale_unsupported_by_provider), [`UNKNOWN_FORMAT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#unknown_format), [`CONCURRENCY_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#concurrency_invalid), [`CONCURRENCY_BUDGET_CONFLICT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#concurrency_budget_conflict), [`MAX_TOKENS_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#max_tokens_invalid), [`LOCK_TIMEOUT_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#lock_timeout_invalid), [`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), [`SOURCE_UNREADABLE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_unreadable), [`SOURCE_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_invalid), [`PROVIDER_CONSTRUCTION_FAILED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#provider_construction_failed), [`NETWORK_POLICY_VIOLATION`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#network_policy_violation), [`CONFIG_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#config_invalid), [`LOCK_FILE_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#lock_file_invalid), [`PROVENANCE_FILE_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#provenance_file_invalid)

**Ejemplo:** [Traduce y lee el resumen](https://verbatra.kreitz-webdev.de/es/docs/sdk-quickstart.md#traduce-y-lee-el-resumen)

Lo que la tabla no cubre:

- `cache` es la [caché de memoria de traducción](https://verbatra.kreitz-webdev.de/es/docs/the-cache.md) local (`verbatra.cache.json`, exportada también como `CACHE_FILE_NAME`).
- `humanEdits` (desde 0.12.0): por defecto, las claves obsoletas cuyo valor escribió, importó o
  cambió una persona fuera de verbatra se conservan y se listan en `LocaleSummary.protected`.
  Consulta [Proteger traducciones humanas](https://verbatra.kreitz-webdev.de/es/docs/protecting-human-translations.md).
- `onProgress` recibe los [eventos de progreso](#eventos-de-progreso) descritos abajo.
- `maxBatchSize` y `budgetBehavior` son solo de configuración; no hay anulación por ejecución.

Los problemas de toda la ejecución lanzan un `SdkError`; que falle un solo locale nunca lanza y
queda como `status: "failed"` en la entrada de ese locale mientras la ejecución continúa. Consulta
[la anatomía de `RunSummary`](https://verbatra.kreitz-webdev.de/es/docs/sdk/run-summary.md#la-anatomía-de-runsummary).

### Eventos de progreso [#eventos-de-progreso]

<AvailableFrom version="0.12.0" />

`onProgress` recibe un `ProgressEvent`, una unión discriminada por `type`. Una versión menor puede añadir tipos, así que ignora un `type` que no reconozcas.

| `type` | Cuándo | Campos |
| --- | --- | --- |
| `locale-started` | empieza el trabajo de un locale | `locale`, `localeIndex`, `totalLocales` |
| `locale-planned` | tras consultar la memoria de traducción y antes de enviar el primer lote; solo cuando se llama a un proveedor | `locale`, `keys`, `batches`, `cacheHits` |
| `sub-batch` | justo antes de intentar cada lote de traducción | `locale`, `batchIndex`, `totalBatches` |
| `provider-retry` | un proveedor integrado reintenta una petición que falló con un estado reintentable | `attempt`, `delayMs?`, `status?` |
| `repair` | un proveedor LLM vuelve a pedir las claves que su primera respuesta omitió | `locale`, `keys` |
| `split-retry` | un lote cuya salida se cortó se reintenta en dos mitades | `locale`, `keys` |
| `batch-finished` | un lote ha terminado, con éxito o no | `locale`, `batchIndex`, `totalBatches`, `durationMs`, `usage?` |
| `writing` | justo antes de escribir el archivo de un locale | `locale` |
| `locale-finished` | termina el trabajo de un locale | `locale`, `status`, `translated`, `localeIndex`, `totalLocales` |
| `run-finished` | todos los locales han terminado | `localesCompleted`, `localesFailed` |
| `change-detected` | solo `watch`: un cambio en el archivo de origen se ha asentado | `paths` |
| `idle` | solo `watch`: una ejecución terminó y no hay otra en cola | ninguno |

`provider-retry` no nombra ningún locale, porque un solo proveedor atiende toda la ejecución. Gemini informa de la espera y del estado, Anthropic, OpenAI, OpenCode y openai-compatible informan del intento cuando su cliente lo inicia, y DeepL, Google Cloud Translation y LibreTranslate no informan de nada. Un dry run y una estimación solo emiten `locale-started`, `locale-finished` y `run-finished`. Los registros de progreso `--json` de la CLI se limitan a los cuatro tipos originales: `locale-started`, `sub-batch`, `locale-finished` y `run-finished`.

`extract`, `diff` con `unused` y `doctor` con `literals` aceptan su propio `onProgress`. Recibe un `ScanProgressEvent`, `{ type: "files-scanned", scanned, total }`, una vez tras cada archivo de código de la aplicación que lee el escaneo.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | `"files-scanned"` | yes | Discriminant, reserved so later scan events can join a union. |
| `scanned` | `number` | yes | How many files have been scanned so far. |
| `total` | `number` | yes | How many files the scan will read in total. |

Un `CreateProvider` propio recibe un tercer argumento, `CreateProviderHooks`, siempre que una ejecución informa de progreso. Llama a su `onRetry` con `{ attempt, delayMs?, status? }` y los reintentos de tu proveedor aparecerán como eventos `provider-retry`.

### resolveDryRun [#resolvedryrun]

<AvailableFrom version="0.11.0" />

Responde si un conjunto de opciones de ejecución se resuelve como un dry run. Toma el
`dryRun` y el `estimate` que estás a punto de pasar a `translate` y devuelve si esa ejecución
construirá un proveedor, escribirá un archivo o gastará algo. Es el único sitio donde se decide
que `estimate` implica `dryRun`, así que recurre a él siempre que algo alrededor de una ejecución tenga
que saberlo de antemano, en lugar de repetir la implicación y acabar desviándote de ella.

```ts title="Firma"
function resolveDryRun(input: {
  readonly dryRun?: boolean | undefined;
  readonly estimate?: boolean | undefined;
}): boolean;
```

**Devuelve:** `boolean`

```ts title="Ejemplo"
import { resolveDryRun, translate, type VerbatraConfig } from "@verbatra/sdk";

declare const config: VerbatraConfig;

const options = { dryRun: false, estimate: true };
resolveDryRun(options); // true

const summary = await translate({ config, ...options });
```

### watch [#watch]

Vigila el archivo del locale de origen y vuelve a ejecutar `translate` en cada cambio asentado por el
anti-rebote. Dispara una ejecución inicial inmediatamente al arrancar, y luego una ejecución por cambio
asentado. Las ejecuciones se serializan: los cambios durante una ejecución se colapsan en un único
seguimiento.

```ts title="Firma"
function watch(
  input: WatchInput,
  deps?: WatchDeps,
): Promise<WatchController>;
```

| 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 where each run's lock-file, translation memory, and run-status file live. Defaults to the project root of the config object loadConfig returned, else the process working directory; a copied or rebuilt config loses that root, so pass `cwd` from resolveProjectRoot. |
| `locales` | `readonly string[]` | no | Restrict every run of the session to a subset of the configured target locales. Validated once when watching starts, so an unconfigured locale throws `UNKNOWN_LOCALE` before any watching begins rather than failing on each run. An explicit empty array selects no locale at all. |
| `debounceMs` | `number` | no | How long to coalesce rapid source changes before running. Defaults to 300 milliseconds. |
| `onRun` | `(result: WatchRunResult) => void` | yes | Called after every run with its outcome. This is the only way to observe a watch session, since watch itself resolves as soon as watching starts. |
| `onReady` | `(() => void)` | no | Called once when the session's startup checks have passed and the watcher is attached, right before the initial run starts, so a caller can announce the session ahead of that run's output. |
| `onLockWait` | `LockWaitListener` | no | Called while waiting on another process's write lock, never for one this process holds. |
| `onProgress` | `ProgressListener` | no | Called as each run plans, sends and writes its locales, and as the watcher detects a change or goes idle, for progress reporting. |
| `lockAcquireTimeoutMs` | `number` | no | How long, in milliseconds, to wait for a locale's write lock, before any provider call, before that locale fails with `LOCK_CONTENDED` on the run's summary. Defaults to ten minutes. The lock-file guard a locale takes to record its result after writing its target file always allows the ten-minute default instead. |
| `concurrency` | `number` | no | How many locales to run at once. Defaults to 1. Validated once when watching starts, and applied to every run. |
| `cache` | `boolean` | no | Consult and update the translation memory, fuzzy reuse included. Defaults to true. |

**Devuelve:** `WatchController`

**Lanza:** [`UNKNOWN_LOCALE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#unknown_locale), [`LOCALE_UNSUPPORTED_BY_PROVIDER`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#locale_unsupported_by_provider), [`CONCURRENCY_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#concurrency_invalid), [`LOCK_TIMEOUT_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#lock_timeout_invalid), [`CONCURRENCY_BUDGET_CONFLICT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#concurrency_budget_conflict), [`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), [`SOURCE_UNREADABLE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_unreadable), [`NETWORK_POLICY_VIOLATION`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#network_policy_violation), [`PROVIDER_CONSTRUCTION_FAILED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#provider_construction_failed), [`CONFIG_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#config_invalid)

**Ejemplo:** [Vigila en un proceso de larga duración](https://verbatra.kreitz-webdev.de/es/docs/programmatic-api.md#vigila-en-un-proceso-de-larga-duración)

`debounceMs` es 300 por defecto. `onRun` se llama una vez por ejecución con un `WatchRunResult`:
`{ status: "succeeded", summary }` o
`{ status: "failed", error: { code, message, causeCode?, hint? } }`, donde `causeCode` nombra el
error con código que envuelve el fallo y `hint` es el siguiente paso que da `errorHint`. `onReady`
se llama una vez, cuando pasan las comprobaciones de arranque y justo antes de la ejecución inicial,
para que puedas anunciar la sesión antes de la salida de esa ejecución. Si `onReady` lanza, el
watcher se cierra y `watch` rechaza con ese error antes de que empiece ninguna ejecución. El SDK no
registra nada; `onRun` es la única salida. `concurrency`, `cache`, `onProgress`, `onLockWait` y
`lockAcquireTimeoutMs` se pasan a cada ejecución y se comportan igual que en `translate`. `locales`
acota cada ejecución de la sesión a ese subconjunto y se valida una sola vez al arrancar, no en cada
ciclo.

Devuelve un `WatchController` con un solo método, `stop()`, que cierra el watcher y espera la ejecución
en curso. Cada código de la línea **Lanza** se rechaza al arrancar, antes de que el watcher exista,
así que `watch` mismo rechaza en lugar de devolver un controlador. Dos de ellos pesan más en una
sesión que en una sola ejecución: un `concurrency` mayor que 1 contra una configuración que fija
`maxTokens` lanza siempre `CONCURRENCY_BUDGET_CONFLICT`, porque no existe un watch en dry run, y,
desde 0.12.0, un proveedor que no se puede construir, por ejemplo porque falta su variable de API
key, lanza `PROVIDER_CONSTRUCTION_FAILED` una sola vez en lugar de hacer fallar cada ejecución.
Todo fallo posterior al arranque se comunica a través de `onRun` y la vigilancia continúa.

### releaseHeldLocks [#releaseheldlocks]

<AvailableFrom version="0.12.0" />

Elimina todos los bloqueos de escritura que tiene el proceso actual, para un proceso que va a
terminar sin dejar que acaben sus ejecuciones en curso: la CLI lo llama cuando una interrupción
detiene `translate` o `import`, y cuando una segunda interrupción fuerza la parada de `watch`. Las
ejecuciones que tomaron esos bloqueos pierden su protección, así que llámalo solo justo antes de que
el proceso termine. No recibe argumentos y rechaza con un `AggregateError` que nombra las rutas de
los bloqueos solo cuando un bloqueo retenido no se pudo borrar, tras intentarlo con cada bloqueo. Un
bloqueo que una operación aún en curso está tomando, o intenta tomar, mientras se ejecuta se rechaza
con `RUN_CANCELLED` y no deja ningún archivo; espera como máximo tres segundos a esa adquisición.

```ts title="Firma"
function releaseHeldLocks(): Promise<void>;
```

**Devuelve:** `void`

Un bloqueo que queda atrás de todos modos, porque un proceso se mató de golpe, no lo necesita: una
ejecución en espera en la misma máquina ve que el proceso registrado ya no existe y recupera el
bloqueo por sí misma. Consulta [el bloqueo de escritura](https://verbatra.kreitz-webdev.de/es/docs/the-lock-file.md#el-bloqueo-de-escritura).

## Pseudolocalización [#pseudolocalización]

### pseudolocalize [#pseudolocalize]

<AvailableFrom version="0.11.0" />

Construye un locale falso solo con las cadenas de origen, de modo que los recortes de maquetación y las
cadenas que nunca se extrajeron para traducir aparezcan antes de que exista clave de API alguna.
Cada valor se acentúa, se expande alrededor de un tercio de su longitud traducible y se envuelve en
los marcadores de límite `[` y `]`, mientras que los placeholders quedan intactos en
todas las sintaxis que reconocen los adaptadores, incluidas las ramas `plural` y `select` de ICU,
las etiquetas de marcado y las secuencias de escape. No construye ningún proveedor, no lee ninguna
clave de API y no hace ninguna petición de red.

```ts title="Firma"
function pseudolocalize(
  input: PseudolocalizeInput,
  deps?: PseudolocalizeDeps,
): Promise<PseudolocalizeResult>;
```

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `config` | `VerbatraConfig` | yes | The resolved project config, normally from loadConfig. |
| `cwd` | `string` | no | Directory the `files.pattern` is resolved against. Defaults to the project root of the config object loadConfig returned, else the process working directory; a copied or rebuilt config loses that root, so pass `cwd` from resolveProjectRoot. |
| `mode` | `"accented" \| "bidi"` | no | The transform to apply. Defaults to `accented`. `bidi` wraps every word of translatable text in a right-to-left mark and override (`U+200F U+202E` before, `U+202C U+200F` after), so it renders right to left while the stored letters keep their order; placeholders, ICU syntax, markup, digits and punctuation stay outside the override, and nothing is accented or padded. |
| `locale` | `string` | no | The pseudolocale's BCP-47 code. Defaults to `en-XA` in `accented` mode and to `ar-XB` in `bidi` mode, a code that resolves to right-to-left text direction, so an application that derives its layout direction from the locale flips without further setup. In `bidi` mode an `i18next-json` plural keyed by suffix also gets every CLDR category the pseudolocale's language needs and the source lacks (for `ar-XB`, `_zero`, `_two`, `_few` and `_many`), filled from the source's `other` form, so no count falls back to another language. It must not be the source locale or any configured target locale, compared case-insensitively, so a pseudolocale can never stand in for a real translation. |
| `out` | `string` | no | Directory the pseudolocale file is written under, relative to `cwd`. Defaults to `.verbatra-local/pseudo`, which `verbatra init` already adds to `.gitignore`. The configured `files.pattern` is expanded inside it, so the file keeps the layout the application expects. It must name a directory inside `cwd`: an absolute path, one that climbs out with `..`, and `cwd` itself are all refused, as is a directory under which the expanded pattern would place the pseudolocale file beside a configured locale file, so a generated pseudolocale never lands outside the project or beside the real translations where nothing ignores it. The same holds after symbolic links are resolved, so a linked directory cannot carry the file outside `cwd` or into the directory of a configured locale file. |

**Devuelve:** `PseudolocalizeResult`

**Lanza:** [`PSEUDO_OUTPUT_CONFLICT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#pseudo_output_conflict), [`UNKNOWN_FORMAT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#unknown_format), [`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), [`SOURCE_UNREADABLE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_unreadable), [`SOURCE_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_invalid), [`TARGET_UNWRITABLE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#target_unwritable)

**Ejemplo:** [Construye un pseudolocale](https://verbatra.kreitz-webdev.de/es/docs/programmatic-api.md#construye-un-pseudolocale)

Cada valor generado pasa por la misma puerta de integridad que debe superar la traducción de
un proveedor. Uno que no la superaría se copia literalmente del origen y se nombra en `copied` en
lugar de escribirse roto, así que la entrada sigue presente y sigue renderizándose.

<AvailableFrom version="0.12.0" />

`mode` elige la transformación de `PSEUDO_MODES`: `accented`, la de por defecto, o `bidi`, que en
su lugar envuelve cada palabra de texto traducible en una marca y una anulación de derecha a
izquierda y deja fuera los placeholders, la sintaxis ICU, el marcado, los dígitos y la puntuación,
sin acentuar nada. En modo `bidi`, `locale` es `ar-XB` por defecto, un código que se resuelve con
dirección de derecha a izquierda; consulta [`verbatra pseudo`](https://verbatra.kreitz-webdev.de/es/docs/cli/pseudo.md) para ver cómo sus
reglas de plural del árabe encajan con las formas de plural de tu origen.

`locale` es `en-XA` por defecto y `out` es
`.verbatra-local/pseudo`, que `verbatra init` ya añade a `.gitignore`; el `files.pattern`
configurado se expande dentro, así que el archivo conserva la disposición que espera la aplicación.
Un pseudolocale que nombre un locale configurado, o que aterrizaría sobre un archivo de locale
configurado, se rechaza con `PSEUDO_OUTPUT_CONFLICT` antes de leer o escribir nada, así que no
puede sobrescribir una traducción real ni hacerse pasar por una; lo mismo ocurre con un `out`
absoluto, que se salga de `cwd` o que nombre el propio `cwd`, como `.`, y, cuando `deps.fs` implementa `realpath` como el valor por
defecto, con uno que acaba así, en el directorio de un archivo de locale configurado o sobre otro
archivo del que depende el proyecto, una vez resueltos los enlaces simbólicos. Como el archivo vive fuera de
`files.pattern`, `translate` nunca gasta en él y `check` y `diff` nunca informan de él como
desfasado.

Devuelve un `PseudolocalizeResult`: el `locale`, el `mode`, el `path` absoluto, cuántas `entries` se
procesaron, cuántas se `transformed`, las claves que se `copied` y si se escribió algo (`written`).
La transformación es determinista, así que una segunda ejecución sobre un origen sin cambios no
reescribe nada e informa de `written` como false. Para `xliff` y `apple-xcstrings`, cuyos escritores
solo parchean un documento que ya existe, la salida se vuelve a copiar desde el origen en cada
escritura, así que una clave añadida al origen después de la primera ejecución sigue llegando al
pseudolocale, y a un XLIFF copiado se le reescribe su `target-language` al pseudolocale.

## Catálogos tipados [#catálogos-tipados]

### generateTypes [#generatetypes]

<AvailableFrom version="0.11.0" />

Declara el catálogo de origen de un proyecto como TypeScript: una unión de todas las claves que
contiene y, por clave, los argumentos que interpola su mensaje. Tipa una función de traducción
contra el resultado y una clave mal escrita o un argumento de interpolación que falta pasa a ser un
error de compilación en lugar de un fallo de búsqueda en tiempo de ejecución. No gasta nada: no se
construye ningún proveedor, no se lee ninguna variable de entorno con una clave de API y no se hace
ninguna petición de red.

```ts title="Firma"
function generateTypes(
  input: GenerateTypesInput,
  deps?: GenerateTypesDeps,
): Promise<GenerateTypesResult>;
```

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `config` | `VerbatraConfig` | yes | The resolved project config, normally from loadConfig. |
| `cwd` | `string` | no | Directory the `files.pattern` and the output path are resolved against. Defaults to the project root of the config object loadConfig returned, else the process working directory; a copied or rebuilt config loses that root, so pass `cwd` from resolveProjectRoot. |
| `out` | `string` | no | Where to write the declaration, relative to `cwd`. Defaults to DEFAULT_TYPES_PATH. Refused with `TYPES_OUTPUT_CONFLICT`, before anything is read or written, when it names no file, is absolute, climbs out of `cwd`, does not end in `.ts`, `.mts` or `.cts`, or names a configured locale file, the lock file, the provenance file, the translation-memory cache, a file verbatra searches for its configuration, the GenerateTypesInput.configPath file, or the GenerateTypesInput.glossaryPath file. When the file-system port implements `realpath`, the same checks run again after symbolic links are resolved. Names are compared case-insensitively. A generating run also refuses to replace an existing file there unless that file begins with the header line verbatra writes, after any leading byte order mark and blank lines, and refuses one too large to verify. |
| `configPath` | `string` | no | The configuration file `config` was loaded from, absolute or relative to `cwd`. It is refused as the output path even when its name is not one verbatra searches for. |
| `glossaryPath` | `string` | no | The glossary file the config names, absolute or relative to `cwd`, normally the `path` of a file-backed LoadedConfig.glossary. It is refused as the output path. |
| `check` | `boolean` | no | Compare instead of writing. The run reports whether the file on disk matches what a fresh generation would produce and leaves every file untouched. |

**Devuelve:** `GenerateTypesResult`

**Lanza:** [`TYPES_OUTPUT_CONFLICT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#types_output_conflict), [`TYPES_UNWRITABLE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#types_unwritable), [`UNKNOWN_FORMAT`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#unknown_format), [`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), [`SOURCE_UNREADABLE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_unreadable), [`SOURCE_INVALID`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#source_invalid)

**Ejemplo:** [Genera un catálogo tipado](https://verbatra.kreitz-webdev.de/es/docs/programmatic-api.md#genera-un-catálogo-tipado)

Lee un archivo (el catálogo del locale de origen) y escribe un archivo (la declaración); nunca
toca un archivo de locale de destino. Las claves son exactamente lo que el adaptador de formato ya
produjo, en orden del documento, así que dos ejecuciones sobre un catálogo sin cambios escriben
bytes idénticos. Los argumentos vienen de los tokens de placeholder que el adaptador extrajo, salvo
en los formatos de mensajes ICU (`next-intl-json` y `arb`), donde cada mensaje se lee con el mismo
parser ICU que usa el adaptador. Cada clave se emite como un literal de cadena entrecomillado, así
que una con un punto, una palabra reservada, un dígito inicial, una comilla, o incluso la clave
vacía, se declara tal cual en lugar de descartarse o volver a partirse, y una clave que el adaptador
informó en `excludedLeafPaths` no se declara en absoluto.

Lo que se niega a afirmar importa tanto como lo que declara. Un mensaje sin placeholders recibe una
forma que convierte en error de tipo pasar cualquier argumento; uno cuyos placeholders nombran sus
argumentos recibe una forma de objeto; uno con placeholders numerados o anónimos recibe una tupla
readonly. Un argumento ICU que solo usan algunas ramas `select` o `plural` se declara como miembro
opcional. Los tipos de argumento vienen solo de lo que el catálogo registra: `number` para un
argumento ICU `number`, `plural` o `selectordinal` y para un formateador o una conversión numéricos,
`Date | number` para un argumento ICU `date` o `time` y un formateador `datetime` de i18next,
`string` para un argumento ICU `select` y una conversión printf de cadena, y un alias de `string |
number` en los demás casos, nunca un `any` permisivo. Una interpolación sin escapar de i18next (`{{-
name}}`) se declara con el nombre detrás de su prefijo. Un mensaje que el adaptador informó como
inválido, uno que parece nombrar y numerar sus argumentos a la vez, y uno con un índice de placeholder
fuera de rango se declaran con sus argumentos informados en `unresolved` en lugar de declararse en
silencio como si no tomaran ninguno. Los hermanos de plural nunca se agrupan: cada uno se declara
por su cuenta, y los que el adaptador marcó como plurales se listan.

`out` toma por defecto
`DEFAULT_TYPES_PATH` (`verbatra-types.d.ts`). Antes de leer o escribir nada, `TYPES_OUTPUT_CONFLICT`
rechaza un `out` que no nombra ningún archivo (también uno que termina en un separador de ruta), es
absoluto, se sale de `cwd` con `..`, nombra el propio `cwd` (`.`) o no termina en `.ts`, `.mts` o
`.cts`, y uno que nombra un
archivo de locale configurado, el archivo de bloqueo, el archivo de procedencia, la caché de memoria de traducción, un archivo
donde verbatra busca su configuración, `configPath`, el archivo de configuración desde el que se
cargó `config`, o `glossaryPath`, el archivo de glosario al que apunta; los nombres se comparan sin
distinguir mayúsculas de minúsculas, y cuando `deps.fs` implementa `realpath`, como hace el valor
por defecto, las mismas comprobaciones se repiten sobre el sitio donde cae la ruta una vez resueltos
los enlaces simbólicos. Una ejecución que genera también se niega, con el mismo código, a reemplazar
un archivo existente que no empieza con la línea de cabecera que escribe verbatra, y deja ese
archivo intacto. La lista completa está en la página de [`verbatra types`](https://verbatra.kreitz-webdev.de/es/docs/cli/types.md). Con
`check` activado no se escribe nada en absoluto, no se rechaza nada por su contenido, y la ejecución
solo informa de si el archivo confirmado sigue coincidiendo, que es justo lo que quiere una puerta
de CI.

Devuelve un `GenerateTypesResult`: el `path` absoluto, el `sourcePath` del que se construyó, cuántas
`keys` se declararon y cuántas de ellas toman al menos un argumento que verbatra pudo determinar
(`withArguments`, que nunca cuenta una clave de `unresolved`), las claves `unresolved` y las
`excluded` y `plural`, si se escribió algo (`written`), si el archivo en disco estaba `stale` al
arrancar la ejecución, si faltaba del todo (`missing`, un archivo que falta también está `stale`), y
si esta fue una ejecución de `check`.
