---
title: "Übersetzungssicherheit"
description: "Das Sicherheitsmodell: nicht vertrauenswürdige Strings, das Integritäts-Gate, Review-Flags, das Token-Budget und Fehler, die nie etwas preisgeben."
type: "concept"
---
# Übersetzungssicherheit

Das Sicherheitsmodell: nicht vertrauenswürdige Strings, das Integritäts-Gate, Review-Flags, das Token-Budget und Fehler, die nie etwas preisgeben.

verbatra vertraut weder den Strings, die es übersetzt, noch dem, was ein Provider zurückschickt.

| Schicht | Schützt vor | Bei einem Fehlschlag |
| --- | --- | --- |
| Prompt-Injection-Grenze | einem String, der wie eine Anweisung klingt | nichts scheitert: Strings sind immer nur Daten |
| Integritäts-Gate | kaputten Platzhaltern, ICU, Markup, leeren oder entarteten Werten | der Key wird zurückgehalten und im nächsten Lauf erneut versucht |
| Review-Flags | einem gültigen, aber verdächtigen Wert | der Key wird geschrieben und für einen Menschen eingereiht |
| Token-Budget | Ausgaben über eine Obergrenze, die du setzt | der Rest des Laufs wird zurückgehalten oder markiert |
| Strukturierte Fehler | einem Key oder Header, der in eine Meldung gerät | ein stabiler Code mit fester Meldung |

## Nicht vertrauenswürdige Strings und die Prompt-Injection-Grenze [#nicht-vertrauenswürdige-strings-und-die-prompt-injection-grenze]

Ein übersetzbarer String kann Text enthalten, der wie eine Anweisung aussieht ("ignoriere deine
Anweisungen"), deshalb behandelt verbatra jeden String als nicht vertrauenswürdige Eingabe. Die
Systemregeln an einen LLM-Provider sind Konstanten zur Compile-Zeit. Alles Variable (die Einträge,
dein Glossar und Tonfall, die Sprachnamen, die Zielkategorien eines Plurals) reist nur in der
JSON-Nutzlast des User-Turns, nie im Anweisungskanal. Die Antwort muss zu einem festen Schema passen;
eine Antwort mit einem nie angefragten Key lässt ihren ganzen Teil-Batch scheitern.

### Namen von Sprache, Schrift und Region [#namen-von-sprache-schrift-und-region]

<AvailableFrom version="0.12.0" />

Ein Locale-Code allein reicht nicht immer: `sr-Latn` kommt oft auf Kyrillisch zurück, und `pt-BR`
und `pt-PT` verschwimmen. Deshalb bekommt ein LLM-Provider zusätzlich den englischen Namen, die
Schrift und die Region jeder Sprache, aus Node.js für die Locale in deiner Konfiguration, auch wenn
`localeMap` einen anderen Code sendet. Sie reisen als Daten, eine feste Systemregel bittet das
Modell, in der genannten Schrift und Variante zu schreiben, und sie sind nicht Teil des
Fingerprints im [Cache](https://verbatra.kreitz-webdev.de/de/docs/the-cache.md). Ein Code, den Node.js nicht benennen kann, etwa das
für private Nutzung reservierte `qaa`, wird ohne Namen gesendet.

## Das Integritäts-Gate [#das-integritäts-gate]

Eine schema-gültige Übersetzung kann trotzdem kaputt sein, deshalb entscheidet ein Gate anhand des
Kandidatenwerts selbst, ob er angenommen wird; dem eigenen Bericht des Providers wird nie vertraut.
Dasselbe Gate läuft auf jedem Schreibpfad: einer Provider-Antwort, einem Cache-Treffer, einer
Importzeile aus der Arbeitsmappe und einer in Studio getippten Bearbeitung. Ein Wert, den sich
mehrere Keys mit demselben Quelltext teilen, wird pro Key gegen dessen eigene Quelle geprüft.

| Prüfung | Ein Kandidat wird abgelehnt, wenn |
| --- | --- |
| Platzhalter | seine Platzhalter von denen der Quelle abweichen, verglichen als Multimenge (die Anzahl zählt, die Reihenfolge nicht). Was als Platzhalter gilt, legt jedes [Format](https://verbatra.kreitz-webdev.de/de/docs/formats.md) fest. |
| ICU | er sich bei next-intl und ARB nicht mehr als ICU-Nachricht parsen lässt oder seine Plural-Zweige nicht zur Zielsprache passen (unten). Der Vergleich beachtet Zweige, ein in einem Plural-Zweig erfundener Platzhalter wird also erkannt. |
| Erfundene Tokens | er bei i18next, ngx-translate und YAML ein Token der Form `{name}` hinzufügt, das die Quelle nie hatte. Eines wegzulassen ist erlaubt, weil einfache geschweifte Klammern dort wörtlicher Text sind. |
| Inline-Markup | seine HTML- oder XML-Tags von denen der Quelle abweichen oder unausgeglichen sind (unten). |
| Entartung | er eine Wiederholungsschleife ist oder seine Länge sich von der Quelle entfernt hat. |
| Leere | er bei nicht leerer Quelle leer ist oder nur aus Leerraum besteht. Eine Übersetzung zu leeren ist ein bewusster Schritt: Das macht der Platzhalter `[[CLEAR]]` der Arbeitsmappe ([Mit Excel oder CSV übergeben](https://verbatra.kreitz-webdev.de/de/docs/manual-translation.md)). |

Ein Kandidat, der mehrere Prüfungen verletzt, wird mit einem Grund abgelehnt, dem ersten, der in
dieser Reihenfolge zutrifft: `empty`, `icu` (lässt sich nicht parsen), `placeholder`, `markup`,
`icu` (Zweige), `degenerate`. Jeder Grund und die Details, die verbatra dazu ausgibt, stehen unter
[Gründe für Integritätsablehnungen](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#integrity-refusal-reasons).

Was das Gate garantiert: Ein abgelehnter Wert wird nie geschrieben. Er erscheint in der
Lauf-Zusammenfassung als Integritätsabweichung, behält seinen bisherigen Hash in
[der Lock-Datei](https://verbatra.kreitz-webdev.de/de/docs/the-lock-file.md) und wird im nächsten Lauf erneut versucht. Ein Key, für den
gar keine Übersetzung kam (der Provider-Aufruf scheiterte, oder der Key fehlte auch nach einer
Nachfrage noch), wird stattdessen als Provider-Fehlschlag gemeldet und genauso erneut versucht. Auf
der Quellseite wird ein Key, dessen eigener Wert ungültiges ICU ist, vor der Übersetzung
beiseitegelegt.

Was es nicht garantiert: dass die Übersetzung richtig ist. Es beweist Struktur, nicht Bedeutung.

### Plural-Zweige je Zielsprache [#plural-zweige-je-zielsprache]

<AvailableFrom version="0.12.0" />

Ein ICU-Plural hat einen Zweig pro CLDR-Pluralkategorie seiner Sprache
([Beispiele](https://verbatra.kreitz-webdev.de/de/docs/language-support.md#plurale)). Bei next-intl und ARB liest verbatra die Kategorien
der Zielsprache aus den in Node.js eingebauten CLDR-Daten:

- Ein LLM-Provider wird nach genau diesen Zweigen gefragt (Kardinalzahlen für `plural`,
  Ordinalzahlen für `selectordinal`). Die Kategorien reisen als Daten, die Injection-Grenze bleibt
  also unverändert.
- Das Gate lehnt ein `plural` oder `selectordinal` ab, dem ein nötiger Zweig fehlt oder das einen
  behält, den die Sprache nicht hat, auf jedem Schreibpfad, und nennt jeden falschen Zweig.
- Zweige mit exaktem Wert wie `=0` müssen alle erhalten bleiben, und eine Übersetzung darf weitere
  hinzufügen. `select`-Zweige sind die Keys deines Codes, eine Übersetzung behält also genau die der
  Quelle.
- Für eine Locale, für die Node.js keine Pluralregeln hat, ist nur `other` Pflicht.

Ein Wert, der vor dieser Prüfung geschrieben wurde, etwa ein französischer Plural ohne `many`, bleibt,
bis sich seine Quelle ändert; [`verbatra check --qa`](https://verbatra.kreitz-webdev.de/de/docs/cli/check.md#qualitätsprüfung) findet ihn
jetzt schon.

### Inline-Markup-Parität [#inline-markup-parität]

<AvailableFrom version="0.11.0" />

Ein weggelassenes oder kaputtes Inline-Tag (ein `<b>` in einem i18next-`Trans`-Block, ein
maskiertes `&lt;b&gt;` in einem Android-`<string>`, ein `<a href>` in gettext) zerbricht die
Darstellung, deshalb vergleicht das Gate Tag- und Attributnamen als Multimenge:

- **Abgelehnt**: ein weggelassenes, erfundenes oder umbenanntes Tag; eine geänderte Menge von
  Attributnamen (auch ein hinzugefügtes `dir`); unausgeglichenes oder falsch verschachteltes Markup;
  zwei Tags, die zu einem verschmolzen sind.
- **Angenommen**: dieselben Tags in anderer Reihenfolge; ein übersetzter Attributwert wie `title`
  oder `alt`; `<br>` geschrieben als `<br/>`.
- **Geschützte Werte**: Ein URL-Attribut muss Schema und Host behalten (`/docs` darf zu `/de/docs`
  werden, nie zu `https://evil.example/docs`), eine `javascript:`-URL wird abgelehnt, und Werte wie
  `style`, Event-Handler oder ein `<meta content>` müssen genau wie in der Quelle bleiben.
- **Nicht übersetzt**: Der Inhalt von `script`, `style` und ähnlichen Elementen muss unverändert
  zurückkommen. Maskiere so ein Tag im Fließtext (`&lt;script&gt;`), um den Text drumherum zu
  übersetzen.

Markup, das in einen String ohne Markup erfunden wird, wird ebenfalls abgelehnt, während ein
gewöhnliches Wort in spitzen Klammern wie `<Enter>` als Text gelesen wird. Tags, die das Format
schon als Platzhalter meldet, überlässt es der Platzhalterprüfung. Kommentare, Rohtext und
fehlerhafte Quellen: siehe [`markup`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#markup).

## Review-Flags [#review-flags]

Eine zweite, beratende Schicht markiert verdächtige Ergebnisse für einen Menschen. Ein Flag hält nie
einen Key zurück: Der Wert wird geschrieben, gelockt und in die Review-Warteschlange gestellt.

| Grund | Markiert eine Übersetzung, die |
| --- | --- |
| [`LENGTH_RATIO_OUTLIER`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#length_ratio_outlier) | viel kürzer oder länger als ihre Quelle ist, gewichtet nach Schrift |
| [`MAX_LENGTH_EXCEEDED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#max_length_exceeded) | länger als das [`maxLength`](https://verbatra.kreitz-webdev.de/de/docs/config-file.md#maxlength)-Budget des Keys ist <AvailableFrom version="0.11.0" /> |
| [`EQUALS_SOURCE`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#equals_source) | mit ihrem Quelltext identisch ist |
| [`GLOSSARY_TERM_MISSED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#glossary_term_missed) | die Übersetzung eines Glossarbegriffs auslässt oder einen nicht zu übersetzenden Begriff verändert |
| [`GLOSSARY_FORBIDDEN_TERM`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#glossary_forbidden_term) | eine Wiedergabe verwendet, die das Glossar verbietet <AvailableFrom version="0.12.0" /> |
| [`INTEGRITY_REORDERED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#integrity_reordered) | jeden Platzhalter behält, aber in anderer Reihenfolge |
| [`PROVIDER_DEGRADED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#provider_degraded) | aus einem Batch stammt, den der Provider herabgestuft hat, etwa durch eine weggefallene Formalität |
| [`FUZZY_CACHE_REUSE`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#fuzzy_cache_reuse) | [von einer ähnlichen, bearbeiteten Quelle wiederverwendet](https://verbatra.kreitz-webdev.de/de/docs/the-cache.md#unscharfe-wiederverwendung) wurde <AvailableFrom version="0.11.0" /> |
| [`FOREIGN_PLACEHOLDER_CHANGED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#foreign_placeholder_changed) | ein platzhalterartiges Token verliert oder ändert, das ihr [Format](https://verbatra.kreitz-webdev.de/de/docs/formats.md) nicht interpoliert, etwa `{name}` in i18next <AvailableFrom version="0.12.0" /> |
| [`BIDI_CONTROLS_CHANGED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#bidi_controls_changed) | ein Richtungssteuerzeichen offen lässt oder eine Überschreibung hinzufügt, die Text umkehrt <AvailableFrom version="0.12.0" /> |

DeepL, Google und LibreTranslate bekommen so ein Token maskiert und halten einen Wert zurück, den sie
nicht maskieren oder wiederherstellen können ([`PLACEHOLDER_UNSUPPORTED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#placeholder_unsupported)); die Markierung deckt LLMs und Menschen ab.

Cache-Treffer und geteilte Werte werden wie frische Übersetzungen markiert. Markierte Keys
erscheinen als `needsReview` in der Lauf-Zusammenfassung, in den Review-Spalten der Arbeitsmappe
und auf [Studios Review-Seite](https://verbatra.kreitz-webdev.de/de/docs/review-in-studio.md), bis ein Mensch den Wert freigibt, ablehnt
oder neu schreibt ([Review-Entscheidungen](https://verbatra.kreitz-webdev.de/de/docs/the-lock-file.md#review-entscheidungen)).

## Das Token-Budget [#das-token-budget]

`maxTokens` zählt die Tokens eines Laufs über jeden Aufruf und jede Locale; `budgetBehavior: "stop"`
hält den Rest des Laufs zurück, sobald die Obergrenze überschritten würde, für den nächsten Lauf
([Regeln](https://verbatra.kreitz-webdev.de/de/docs/config-file.md#maxtokens-und-budgetbehavior)).

Was das Budget garantiert: Arbeit, die vor der Obergrenze erledigt wurde, bleibt geschrieben und
gelockt, und nichts bleibt halb geschrieben zurück. Was es nicht garantiert: Ein Request unterwegs
kann mehr kosten als geschätzt, die Obergrenze begrenzt einen Lauf (keine `watch`-Sitzung), und
Retranslates aus Studio oder von Agenten sind nicht abgedeckt.

## Fehler geben nichts preis [#fehler-geben-nichts-preis]

Scheitert ein Provider-Aufruf, wird der rohe SDK-Fehler (der Request-Header oder Zugangsdaten tragen
kann) verworfen, nie geloggt oder erneut geworfen. verbatra erzeugt stattdessen einen strukturierten
Fehler mit stabilem [Code](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#provider-error-codes) und fester, geheimnisfreier
Meldung. Das Schwärzen von Keys steht unter [Datenverarbeitung](https://verbatra.kreitz-webdev.de/de/docs/data-handling.md#api-keys).

## Wo du das siehst [#wo-du-das-siehst]

Alles oben ist Daten in der [Lauf-Zusammenfassung](https://verbatra.kreitz-webdev.de/de/docs/sdk/run-summary.md), pro Locale, und das
Scheitern einer Locale hält die anderen nie auf.

### Bereits eingecheckte Werte [#bereits-eingecheckte-werte]

<AvailableFrom version="0.12.0" />

[`verbatra check --qa`](https://verbatra.kreitz-webdev.de/de/docs/cli/check.md#qualitätsprüfung) lässt das Gate und die Review-Flags ohne
Provider-Aufruf über committete Werte laufen.

## Siehe auch [#siehe-auch]

- [Wie ein Lauf funktioniert](https://verbatra.kreitz-webdev.de/de/docs/how-it-works.md): wo jede Prüfung in der Pipeline sitzt.
- [Datenverarbeitung und Datenschutz](https://verbatra.kreitz-webdev.de/de/docs/data-handling.md): was deinen Rechner verlässt.
