---
title: "Seguridad de la traducción"
description: "El modelo de seguridad: cadenas no confiables, la puerta de integridad, las señales de revisión, el presupuesto de tokens y errores que nunca filtran nada."
type: "concept"
---
# Seguridad de la traducción

El modelo de seguridad: cadenas no confiables, la puerta de integridad, las señales de revisión, el presupuesto de tokens y errores que nunca filtran nada.

verbatra no se fía ni de las cadenas que traduce ni de lo que devuelve un proveedor.

| Capa | Protege contra | Si falla |
| --- | --- | --- |
| Frontera contra la inyección de prompts | una cadena que parece una instrucción | nada que falle: las cadenas son siempre solo datos |
| Puerta de integridad | placeholders, ICU o marcado rotos, valores vacíos o degenerados | la clave se retiene y se reintenta en la siguiente ejecución |
| Señales de revisión | un valor válido pero sospechoso | la clave se escribe y entra en la cola de una persona |
| Presupuesto de tokens | gastar por encima de un techo que tú fijas | el resto de la ejecución se retiene, o se marca |
| Errores estructurados | una clave o una cabecera que se cuela en un mensaje | un código estable con un mensaje fijo |

## Cadenas no confiables y la frontera contra la inyección de prompts [#cadenas-no-confiables-y-la-frontera-contra-la-inyección-de-prompts]

Una cadena traducible puede contener texto que parece una instrucción ("ignora tus
instrucciones"), así que verbatra trata cada cadena como entrada no confiable. Las reglas de sistema
que se envían a un proveedor LLM son constantes de compilación. Todo lo variable (las entradas, tu
glosario y tu tono, los nombres de idioma, las categorías de destino de un plural) viaja solo en la
carga JSON del turno de usuario, nunca en el canal de instrucciones. La respuesta debe ajustarse a
un esquema fijo; una respuesta con una clave que nunca se pidió hace fallar su sublote entero.

### Nombres de idioma, escritura y región [#nombres-de-idioma-escritura-y-región]

<AvailableFrom version="0.12.0" />

Un código de locale solo no siempre basta: `sr-Latn` a menudo vuelve en cirílico, y `pt-BR` y
`pt-PT` se confunden. Por eso un proveedor LLM también recibe el nombre en inglés, la escritura y la
región de cada idioma, tomados de Node.js para el locale de tu configuración aunque `localeMap` envíe
otro código. Viajan como datos, una regla de sistema fija pide al modelo que escriba en la escritura
y la variante nombradas, y no forman parte de la huella de la [caché](https://verbatra.kreitz-webdev.de/es/docs/the-cache.md). Un código que
Node.js no sabe nombrar, como el de uso privado `qaa`, se envía sin nombre.

## La puerta de integridad [#la-puerta-de-integridad]

Una traducción que cumple el esquema aún puede estar rota, así que una puerta decide a partir del
propio valor candidato si lo acepta; nunca se fía del informe del proveedor. La misma puerta se
aplica en todas las vías de escritura: una respuesta del proveedor, un acierto de caché, una fila
importada de un libro de trabajo y una edición escrita en Studio. Un valor compartido por varias
claves con el mismo texto de origen se comprueba clave por clave frente al origen de cada una.

| Comprobación | Un candidato se rechaza cuando |
| --- | --- |
| Placeholders | sus placeholders difieren de los del origen, comparados como un multiconjunto (el número cuenta, el orden no). Qué cuenta como placeholder lo define cada [formato](https://verbatra.kreitz-webdev.de/es/docs/formats.md). |
| ICU | en next-intl y ARB, ya no se puede analizar como mensaje ICU, o sus ramas de plural no encajan con el idioma de destino (abajo). La comparación tiene en cuenta las ramas, así que se detecta un placeholder inventado dentro de una rama de plural. |
| Tokens inventados | en i18next, ngx-translate y YAML, añade un token con forma `{name}` que el origen nunca tuvo. Quitar uno está permitido, porque ahí las llaves simples son texto literal. |
| Marcado en línea | sus etiquetas HTML o XML difieren de las del origen, o están desequilibradas (abajo). |
| Degeneración | es un bucle de repeticiones, o su longitud se ha disparado respecto al origen. |
| Vacío | está vacío o solo tiene espacios para un origen que no lo está. Vaciar una traducción es un acto deliberado: lo hace el marcador `[[CLEAR]]` del libro de trabajo ([Traducción manual](https://verbatra.kreitz-webdev.de/es/docs/manual-translation.md)). |

Un candidato que incumple varias comprobaciones se rechaza con un solo motivo, el primero que se
aplica en este orden: `empty`, `icu` (no se puede analizar), `placeholder`, `markup`, `icu` (ramas),
`degenerate`. Cada motivo, y los detalles que verbatra imprime con él, está en
[Motivos de rechazo por integridad](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#integrity-refusal-reasons).

Lo que garantiza la puerta: un valor rechazado nunca se escribe. Aparece en el resumen de la
ejecución como discrepancia de integridad, conserva su hash anterior en
[el archivo de bloqueo](https://verbatra.kreitz-webdev.de/es/docs/the-lock-file.md) y se reintenta en la siguiente ejecución. Una clave que
no recibió ninguna traducción (la llamada al proveedor falló, o la clave seguía ausente tras una
petición de reparación) se informa en cambio como fallo del proveedor, y se reintenta igual. Del
lado del origen, una clave cuyo propio valor tiene ICU no válido se aparta antes de traducir.

Lo que no garantiza: que la traducción sea correcta. Demuestra la estructura, no el significado.

### Ramas de plural por idioma de destino [#ramas-de-plural-por-idioma-de-destino]

<AvailableFrom version="0.12.0" />

Un plural ICU tiene una rama por categoría de plural CLDR de su idioma
([ejemplos](https://verbatra.kreitz-webdev.de/es/docs/language-support.md#plurales)). Para next-intl y ARB, verbatra lee las categorías del
idioma de destino de los datos CLDR integrados en Node.js:

- A un proveedor LLM se le piden exactamente esas ramas (cardinales para `plural`, ordinales para
  `selectordinal`). Las categorías viajan como datos, así que la frontera contra la inyección no
  cambia.
- La puerta rechaza un `plural` o `selectordinal` al que le falta una rama obligatoria o que
  conserva una que el idioma no tiene, en todas las vías de escritura, y nombra cada rama incorrecta.
- Las ramas de valor exacto como `=0` deben sobrevivir todas, y una traducción puede añadir más. Las
  ramas de `select` son claves de tu código, así que una traducción conserva exactamente las del
  origen.
- Para un locale del que Node.js no tiene reglas de plural, solo se exige `other`.

Un valor escrito antes de esta comprobación, como un plural francés sin `many`, se queda hasta que
cambie su origen; [`verbatra check --qa`](https://verbatra.kreitz-webdev.de/es/docs/cli/check.md#control-de-calidad) lo encuentra ya.

### Paridad del marcado en línea [#paridad-del-marcado-en-línea]

<AvailableFrom version="0.11.0" />

Una etiqueta en línea perdida o rota (un `<b>` en un bloque `Trans` de i18next, un `&lt;b&gt;`
escapado en un `<string>` de Android, un `<a href>` en gettext) rompe la representación, así que la
puerta compara nombres de etiqueta y de atributo como un multiconjunto:

- **Se rechaza**: una etiqueta quitada, inventada o renombrada; un conjunto distinto de nombres de
  atributo (incluido añadir `dir`); marcado desequilibrado o mal anidado; dos etiquetas fusionadas
  en una.
- **Se acepta**: las mismas etiquetas en otro orden; un valor de atributo traducido como `title` o
  `alt`; `<br>` escrito como `<br/>`.
- **Valores vigilados**: un atributo de URL debe conservar su esquema y su host (`/docs` puede pasar
  a `/de/docs`, nunca a `https://evil.example/docs`), una URL `javascript:` se rechaza, y valores
  como `style`, los manejadores de eventos o un `<meta content>` deben quedar exactamente como en el
  origen.
- **No se traduce**: el contenido de `script`, `style` y elementos parecidos debe volver sin
  cambios. Escapa esa etiqueta en la prosa (`&lt;script&gt;`) para traducir el texto que la rodea.

También se rechaza el marcado inventado en una cadena que no tenía ninguno, mientras que una
palabra entre corchetes angulares corriente como `<Enter>` se lee como texto. Las etiquetas que el
formato ya informa como placeholders se dejan a la comprobación de placeholders. Comentarios,
texto sin procesar y orígenes mal formados: consulta [`markup`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#markup).

## Señales de revisión [#señales-de-revisión]

Una segunda capa, consultiva, marca los resultados sospechosos para una persona. Una señal nunca
retiene una clave: el valor se escribe, se bloquea y entra en la cola de revisión.

| Motivo | Marca una traducción que |
| --- | --- |
| [`LENGTH_RATIO_OUTLIER`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#length_ratio_outlier) | es mucho más corta o más larga que su origen, ponderada por escritura |
| [`MAX_LENGTH_EXCEEDED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#max_length_exceeded) | supera el presupuesto [`maxLength`](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#maxlength) de la clave <AvailableFrom version="0.11.0" /> |
| [`EQUALS_SOURCE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#equals_source) | es idéntica a su texto de origen |
| [`GLOSSARY_TERM_MISSED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#glossary_term_missed) | omite la traducción de un término del glosario, o altera un término que no se traduce |
| [`GLOSSARY_FORBIDDEN_TERM`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#glossary_forbidden_term) | usa una forma que el glosario prohíbe <AvailableFrom version="0.12.0" /> |
| [`INTEGRITY_REORDERED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#integrity_reordered) | conserva cada placeholder pero en otro orden |
| [`PROVIDER_DEGRADED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#provider_degraded) | viene de un lote que el proveedor degradó, como una formalidad perdida |
| [`FUZZY_CACHE_REUSE`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#fuzzy_cache_reuse) | se [reutilizó de un origen parecido y editado](https://verbatra.kreitz-webdev.de/es/docs/the-cache.md#reutilización-difusa) <AvailableFrom version="0.11.0" /> |
| [`FOREIGN_PLACEHOLDER_CHANGED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#foreign_placeholder_changed) | pierde o cambia un token con forma de placeholder que su [formato](https://verbatra.kreitz-webdev.de/es/docs/formats.md) no interpola, como `{name}` en i18next <AvailableFrom version="0.12.0" /> |
| [`BIDI_CONTROLS_CHANGED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#bidi_controls_changed) | deja abierto un control de dirección, o añade una anulación que invierte el texto <AvailableFrom version="0.12.0" /> |

DeepL, Google y LibreTranslate reciben ese token enmascarado y retienen un valor que no pueden
enmascarar ni restaurar ([`PLACEHOLDER_UNSUPPORTED`](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#placeholder_unsupported)); la marca cubre los LLM y a las personas.

Los aciertos de caché y los valores compartidos se marcan como las traducciones nuevas. Las claves
marcadas aparecen como `needsReview` en el resumen de la ejecución, en las columnas de revisión del
libro de trabajo y en la [página Review de Studio](https://verbatra.kreitz-webdev.de/es/docs/review-in-studio.md), hasta que una persona
aprueba, rechaza o reescribe el valor
([Decisiones de revisión](https://verbatra.kreitz-webdev.de/es/docs/the-lock-file.md#decisiones-de-revisión)).

## El presupuesto de tokens [#el-presupuesto-de-tokens]

`maxTokens` cuenta los tokens de una ejecución en todas las llamadas y todos los locales;
`budgetBehavior: "stop"` retiene el resto de la ejecución en cuanto se superaría el techo, para
reintentarlo en la siguiente ([reglas](https://verbatra.kreitz-webdev.de/es/docs/config-file.md#maxtokens-y-budgetbehavior)).

Lo que garantiza el presupuesto: el trabajo hecho antes del techo queda escrito y bloqueado, y nada
se queda a medio escribir. Lo que no: una petición en curso puede costar más de lo proyectado, el
techo acota una ejecución (no una sesión de `watch`), y no cubre las retraducciones desde Studio o
de agentes.

## Los errores nunca filtran nada [#los-errores-nunca-filtran-nada]

Cuando una llamada al proveedor falla, el error bruto del SDK (que puede llevar cabeceras de la
petición o credenciales) se descarta: nunca se registra ni se vuelve a lanzar. verbatra lanza en su
lugar un error estructurado con un [código](https://verbatra.kreitz-webdev.de/es/docs/error-codes.md#provider-error-codes) estable y un
mensaje fijo sin secretos. Cómo se ocultan las claves está en
[Tratamiento de datos](https://verbatra.kreitz-webdev.de/es/docs/data-handling.md#claves-de-api).

## Dónde lo ves [#dónde-lo-ves]

Todo lo anterior son datos en el [resumen de la ejecución](https://verbatra.kreitz-webdev.de/es/docs/sdk/run-summary.md), por locale, y el
fallo de un locale nunca detiene a los demás.

### Valores confirmados [#valores-confirmados]

<AvailableFrom version="0.12.0" />

[`verbatra check --qa`](https://verbatra.kreitz-webdev.de/es/docs/cli/check.md#control-de-calidad) aplica la puerta y las señales de
revisión a los valores confirmados sin llamar al proveedor.

## Relacionado [#relacionado]

- [Cómo funciona una ejecución](https://verbatra.kreitz-webdev.de/es/docs/how-it-works.md): dónde está cada comprobación en la canalización.
- [Tratamiento de datos y privacidad](https://verbatra.kreitz-webdev.de/es/docs/data-handling.md): qué sale de tu máquina.
