---
title: "Konfigurationsdatei"
description: "Jeder Konfigurations-Key mit Typ, Default und der Version, die ihn eingeführt hat, dazu wo die Datei liegt und wie eine fehlerhafte Konfiguration scheitert."
type: "reference"
---
# Konfigurationsdatei

Jeder Konfigurations-Key mit Typ, Default und der Version, die ihn eingeführt hat, dazu wo die Datei liegt und wie eine fehlerhafte Konfiguration scheitert.

verbatra liest ein geheimnisfreies Konfigurationsobjekt und validiert es vor jeder Arbeit. Kein API-Schlüssel wird je daraus gelesen ([Provider](https://verbatra.kreitz-webdev.de/de/docs/providers.md#keys-kommen-aus-der-umgebung)), die Konfiguration bleibt also gefahrlos committbar.

## Eine minimale Konfiguration [#eine-minimale-konfiguration]

```ts title="verbatra.config.ts"
import { defineConfig } from "@verbatra/cli";

export default defineConfig({
  // Locales and files: required
  sourceLocale: "en",
  targetLocales: ["de", "fr"],
  format: "i18next-json",
  files: {
    // {locale} is the only token
    pattern: "locales/{locale}.json",
  },

  // Provider: required; the API key comes from the environment, never from here
  provider: {
    id: "gemini",
    options: {
      // example model id; use your provider's current one
      model: "gemini-2.5-flash",
      maxOutputTokens: 4096,
    },
  },

  // Optional groups, each off or at its default until you add it:
  // tone: "informal",
  // glossary: "glossary.json",
  // network: { policy: "local-only" },
  // sensitiveData: { mode: "warn" },
  // maxTokens: 200000,
  // budgetBehavior: "stop",
});
```

Siehe [Locales und Dateien](#locales-und-dateien), [provider](#provider), [glossary](#glossary), [network](#network), [sensible Inhalte](https://verbatra.kreitz-webdev.de/de/docs/data-handling.md#schutz-für-sensible-inhalte), [Budget](#maxtokens-und-budgetbehavior), [rates](#rates) und [extract](#extract).

`defineConfig` typisiert sein Argument nur: `options.model` vervollständigt die Modell-IDs des Providers, zur Laufzeit gilt aber jeder nicht-leere String.

## Jeder Key [#jeder-key]

Unbekannte Keys werden auf jeder Ebene außer `$schema` und Maps, deren Keys du selbst benennst, etwa `maxLength`, `rates.table`, `localeMap` und ein Inline-`glossary`, abgelehnt, also kann kein API-Schlüssel in der Konfiguration leben.

| Key | Typ | Default | Seit | Wirkung |
| --- | --- | --- | --- | --- |
| `$schema` | String | keiner | 0.9.0 | Zeigt einem Editor das [JSON-Schema](#validierung-im-editor); zur Laufzeit ignoriert. |
| `sourceLocale` | Locale-Code | erforderlich | 0.1.0 | Die Locale, in der deine Quellstrings geschrieben sind. |
| `targetLocales` | Array von Locale-Codes | erforderlich | 0.1.0 | Mindestens eine; nie `sourceLocale`, nie zwei, die sich nur in der Groß- und Kleinschreibung unterscheiden. |
| `format` | Format-Id | erforderlich | 0.1.0 | Das [Format](https://verbatra.kreitz-webdev.de/de/docs/formats.md) deiner Locale-Dateien, oder eine `custom:`-Id für einen [Adapter von außerhalb](https://verbatra.kreitz-webdev.de/de/docs/custom-format-adapters.md). |
| `files.pattern` | String | erforderlich | 0.1.0 | Der Pfad zu jeder Locale-Datei, mit einem `{locale}`-Token. |
| `files.localeStyle` | `"literal"`, `"posix"`, `"android"` | `"literal"` | 0.7.0 | Wie `{locale}` im Pfad geschrieben wird. |
| `provider.id` | Provider-Id | erforderlich | 0.1.0 | Der Übersetzungs-Provider, oder `none` für keine maschinelle Übersetzung. |
| `provider.options` | Objekt | erforderlich, außer bei `none` | 0.1.0 | Die [eigenen Optionen](https://verbatra.kreitz-webdev.de/de/docs/providers.md) des Providers. |
| `glossary` | Objekt oder Dateipfad | keines | 0.1.0 | Feste Übersetzungen, verbotene Wiedergaben und Begriffe, die unübersetzt bleiben. |
| `tone` | `"formal"`, `"informal"`, `"neutral"` | keiner | 0.1.0 | Das Register, in dem übersetzt wird, angewendet [pro Provider](https://verbatra.kreitz-webdev.de/de/docs/providers.md#tonfall-und-glossar-über-die-provider-hinweg). |
| `prune` | Boolean | `false` | 0.3.0 | Entfernt verwaiste Keys aus Zieldateien und der Lock-Datei; `translate --prune` überschreibt es. |
| `generatePlurals` | Boolean | `false` | 0.3.0 | Synthetisiert die CLDR-Pluralformen, die eine Zielsprache braucht. |
| `fuzzyCache.enabled` | Boolean | im Block erforderlich | 0.11.0 | Übersetzung einer leicht geänderten Quelle wiederverwenden. |
| `fuzzyCache.threshold` | Zahl, `0.5` bis `1` | `0.9` | 0.11.0 | Wie ähnlich alte und neue Quelle sein müssen. |
| `humanEdits` | `"protect"`, `"suggest"`, `"overwrite"` | `"protect"` | 0.12.0 | Was ein Lauf mit einem veralteten Key macht, den ein Mensch geschrieben hat. |
| `pinnedKeys` | String-Array | keiner | 0.12.0 | Key-Muster (`*` als Platzhalter), die kein maschinelles Schreiben anfasst. |
| `network.policy` | `"any"`, `"local-only"`, `"allowlist"` | `"any"` | 0.12.0 | Zu welchen Hosts sich ein Provider [verbinden darf](https://verbatra.kreitz-webdev.de/de/docs/network-policy.md). |
| `network.allowedHosts` | String-Array | keiner; bei `"allowlist"` erforderlich | 0.12.0 | Zusätzlich erlaubte Hosts: Namen, `*.suffix`-Platzhalter, IPs oder CIDR-Bereiche. |
| `sensitiveData.mode` | `"off"`, `"warn"`, `"block"`, `"redact"` | im Block Pflicht; `"off"` ohne den Block | 0.12.0 | Was ein Lauf mit [sensiblen Inhalten](https://verbatra.kreitz-webdev.de/de/docs/data-handling.md#schutz-für-sensible-inhalte) vor dem Senden macht. |
| `sensitiveData.detectors` | Array von Detektor-IDs | `secret`, `email`, `iban`, `credit-card` | 0.12.0 | Aktive Detektoren; `phone`, `ip`, `private-host` sind optional. |
| `sensitiveData.patterns` | String-Array | keiner | 0.12.0 | Reguläre Ausdrücke für eigene Begriffe, etwa Codenamen. |
| `sensitiveData.allow` | String-Array | keiner | 0.12.0 | Nie gemeldete Treffer; `*` ist ein Platzhalter, Groß- und Kleinschreibung zählt nicht. |
| `maxBatchSize` | positive Ganzzahl | `50` | 0.3.0 | Die meisten Einträge pro Provider-Anfrage. |
| `maxLength` | Zuordnung von Key zu positiver Ganzzahl | keines | 0.11.0 | Längenbudgets pro Key in Graphemclustern. |
| `maxTokens` | positive Ganzzahl | keines | 0.5.0 | Ein Token-Budget für den ganzen Lauf. |
| `budgetBehavior` | `"warn"`, `"stop"` | `"warn"` | 0.5.0 | Ob das Erreichen von `maxTokens` warnt oder das Senden stoppt. |
| `rates.asOf` | Datum `YYYY-MM-DD` | im Block erforderlich | 0.11.0 | Das Datum, an dem du die Raten gelesen hast. |
| `rates.currency` | drei Großbuchstaben | im Block erforderlich | 0.11.0 | Die Währung jeder Rate, etwa `USD`. |
| `rates.table` | Zuordnung von Raten-Key zu Rate | im Block erforderlich | 0.11.0 | Raten pro [Raten-Key](#rates). |
| `extract.framework` | `"i18next"` | im Block erforderlich | 0.11.0 | Nach welchen Aufrufstellen der Quell-Scan sucht. |
| `extract.roots` | String-Array | im Block erforderlich | 0.11.0 | Die zu scannenden Verzeichnisse, mindestens eines. |
| `extract.exclude` | String-Array | keiner | 0.11.0 | Weitere zu überspringende Verzeichnisnamen. |
| `extract.literals.ignore` | String-Array | keiner | 0.11.0 | Exakte Texte, die `doctor --literals` zurückhält. |
| `extract.unused.ignore` | String-Array | keiner | 0.11.0 | Keys oder `*`-Muster, die `diff --unused` nie meldet. |

### Locales und Dateien [#locales-und-dateien]

- **`files.pattern`** muss `{locale}` enthalten, sein einziges Token, und wird gegen die Projektwurzel aufgelöst: `locales/{locale}.json` wird zu `locales/de.json`. Es ist ein wörtlicher Pfad, nie ein Glob, eine Konfiguration adressiert also eine Datei pro Locale ([Namespace-Layouts](https://verbatra.kreitz-webdev.de/de/docs/formats.md#namespace-layouts)); `apple-xcstrings` löst jede Locale zum selben Katalog auf ([Xcode String Catalogs](https://verbatra.kreitz-webdev.de/de/docs/formats.md#xcode-string-catalogs-xcstrings)).
- **`files.localeStyle`** entscheidet, wozu das Token expandiert:
  - `literal` schreibt das Tag wie konfiguriert, wie es die meisten Layouts brauchen.
  - `posix` ersetzt `-` durch `_` für gettext und Javas `messages_{locale}.properties` (aus `pt-BR` wird `pt_BR`); eine Variante wie `de-1996` hat keine posix-Schreibweise. `gettext-po` schreibt Schriften auf die gettext-Art (nächster Abschnitt).
  - `android` expandiert zu einem Ressourcenverzeichnis, geschrieben als `res/{locale}/strings.xml`: `values` für die Quelle, dann `values-de`, `values-pt-rBR`, `values-b+zh+Hans`.

  Ein Style lehnt eine Locale ab, die er nicht schreiben kann.

### gettext-Locale-Namen [#gettext-locale-namen]

<AvailableFrom version="0.12.0" />

Mit `format: "gettext-po"` und `localeStyle: "posix"` tragen Dateien gettext-Namen (`language_REGION@modifier`), während die Konfiguration BCP-47-Codes behält:

| Code in der Konfiguration | Dateiname |
| --- | --- |
| `pt-BR` | `pt_BR` |
| `es-419` | `es_419` |
| `sr-Latn` | `sr@latin` |
| `sr-Latn-RS` | `sr_RS@latin` |
| `uz-Cyrl` | `uz@cyrillic` |
| `ks-Deva-IN` | `ks_IN@devanagari` |
| `zh-Hant-TW` | `zh_TW` |
| `sr-Cyrl-RS` | `sr_RS` |

Eine Schrift, die Sprache und Region vorgeben, fällt weg, deshalb scheitern `zh-TW` und `zh-Hant-TW` zusammen mit `LOCALE_PATH_COLLISION`; eine Schrift ohne gettext-Modifikator, etwa `zh-Hant` allein, scheitert mit `LOCALE_LAYOUT_INVALID`. [`verbatra init`](https://verbatra.kreitz-webdev.de/de/docs/cli/init.md) liest diese Namen zurück. Andere Formate behalten die ICU-Schreibweise (`sr_Latn`).

### Locale-Codes [#locale-codes]

<AvailableFrom version="0.12.0" />

Jeder Locale-Code muss ein Tag sein, den `Intl.getCanonicalLocales` akzeptiert, mit einem Sprach-Subtag aus zwei oder drei Buchstaben, sonst scheitert das Laden mit `CONFIG_INVALID`: `pt_BR` (schreib `pt-BR` mit `localeStyle: "posix"`) und `german` werden abgelehnt. Ein gültiger, nicht kanonischer Code wie `zh-hant-tw` oder das veraltete `iw` funktioniert wie geschrieben.

### provider [#provider]

`provider.id` wählt einen der [Provider](https://verbatra.kreitz-webdev.de/de/docs/providers.md) oder `none`, dessen `options` fehlen darf und sonst `{}` sein muss ([Ohne maschinelle Übersetzung arbeiten](https://verbatra.kreitz-webdev.de/de/docs/human-only-workflow.md)). Jeder andere Provider akzeptiert `options.localeMap` und `options.requestTimeoutMs` ([Provider](https://verbatra.kreitz-webdev.de/de/docs/providers.md#an-den-provider-gesendete-locale-codes)); ab 0.12.0 scheitert ein falsch platzierter Key unter `provider`, etwa `localeMap`, mit `CONFIG_INVALID`.

### glossary [#glossary]

Begriffe, die auf eine feste Weise übersetzt werden, inline oder als Pfad zu einer JSON-Datei mit derselben Form (nie zusammengeführt). Version 1 ordnet jedem Quellbegriff eine Übersetzung für alle Locales zu:

```ts title="verbatra.config.ts"
export default defineConfig({
  // ...
  glossary: {
    "Sign in": "Anmelden",
  },
  // or: glossary: "glossary.json",
});
```

Ein relativer Pfad wird gegen das Verzeichnis der Konfigurationsdatei aufgelöst; die Datei muss UTF-8 und höchstens 1 MiB groß sein und wird einmal beim Laden gelesen. Eine fehlende Datei, ungültiges JSON oder eine ungültige Form scheitert mit `CONFIG_INVALID` und nennt den Pfad sowie bei Version 2 das Feld. Leg einen Begriff `__proto__` in eine Datei; ein JavaScript-Literal macht daraus den Prototyp.

#### Glossar pro Locale (Version 2) [#glossar-pro-locale-version-2]

<AvailableFrom version="0.12.0" />

Version 2 ergänzt Übersetzungen pro Locale, verbotene Wiedergaben und Begriffe, die bleiben, wie sie geschrieben sind:

```json title="glossary.json"
{
  "version": 2,
  "terms": [
    {
      "source": "Dashboard",
      "targets": { "de": "Übersicht", "fr": "Tableau de bord" },
      "forbidden": { "de": ["Instrumententafel"] },
      "note": "The start page after sign-in",
      "partOfSpeech": "noun"
    },
    { "source": "Sign in", "target": "Anmelden", "caseSensitive": true }
  ],
  "doNotTranslate": ["verbatra", { "term": "Acme", "caseSensitive": false }]
}
```

| Feld | Bedeutung |
| --- | --- |
| `version` | Immer `2`; jede andere Zahl scheitert. |
| `terms[].source` | Der Quellbegriff; jeder Begriff kommt einmal vor. |
| `terms[].target` | Die Übersetzung für jede Locale ohne eigenen Eintrag in `targets`. |
| `terms[].targets` | Übersetzungen pro Locale. Eine Locale nimmt ihren eigenen Eintrag, dann den ihrer Basissprache (`de` deckt `de-AT` ab), dann `target`; verglichen werden kanonische Codes ohne Groß- und Kleinschreibung. |
| `terms[].forbidden` | Wiedergaben, die eine Locale nie verwenden darf; eine regionale Locale befolgt auch die Liste ihrer Basissprache. |
| `terms[].caseSensitive` | Begriff, Übersetzungen und verbotene Wiedergaben mit Groß- und Kleinschreibung vergleichen. Default `false`. |
| `terms[].note`, `terms[].partOfSpeech` | Freitext-Kontext, den ein LLM-Provider als Daten erhält. |
| `doNotTranslate` | Begriffe, die unübersetzt übernommen werden. Ein reiner String behält seine Groß- und Kleinschreibung; bei `{ "term", "caseSensitive": false }` darf sie sich ändern. |

Jeder Begriff braucht ein `target`, einen Eintrag in `targets` oder eine `forbidden`-Liste; er darf seine eigene Übersetzung nicht verbieten, keine Locale doppelt nennen und nicht in `doNotTranslate` stehen. Kein Text darf leer sein; ein Begriff hat höchstens 200 Zeichen, eine Übersetzung oder verbotene Wiedergabe 2000, eine `note` 500 und eine `partOfSpeech` 50. Der [Cache](https://verbatra.kreitz-webdev.de/de/docs/the-cache.md)-Fingerprint einer Locale deckt nur ihre eigenen Begriffe ab. Eine Datei der Version 1 behält ihren Fingerprint; [`updateGlossaryTerm`](https://verbatra.kreitz-webdev.de/de/docs/sdk/config.md#updateglossaryterm) schreibt sie nur für eine Änderung, die Version 1 nicht ausdrücken kann, als Version 2 neu.

<AvailableFrom version="0.4.0" pkg="@verbatra/studio" /> [Studio](https://verbatra.kreitz-webdev.de/de/docs/review-in-studio.md) bearbeitet nur die Dateiform, Begriff für Begriff. Welche Provider das Glossar anwenden, steht unter [Provider](https://verbatra.kreitz-webdev.de/de/docs/providers.md#tonfall-und-glossar-über-die-provider-hinweg); jede Ausgabe wird [danach geprüft](https://verbatra.kreitz-webdev.de/de/docs/translation-safety.md).

### generatePlurals [#generateplurals]

Wenn true, synthetisiert verbatra die CLDR-Pluralformen, die ein Ziel braucht, der Quelle aber fehlen, etwa `few` und `many` im Polnischen, und Ordinal-Keys (`<key>_ordinal_<category>`) nach den CLDR-Ordinalregeln. Nur `i18next-json` mit einem LLM-Provider unterstützt das; alles andere, und eine Locale ohne CLDR-Regeln, bekommt die [Plural-Warnung](https://verbatra.kreitz-webdev.de/de/docs/language-support.md#plurale) (Ordinalzahlen nehmen `other` an). Es gibt kein CLI-Flag; `translate()` im SDK nimmt einen Override pro Lauf.

### fuzzyCache [#fuzzycache]

`fuzzyCache: { enabled: true }` lässt eine geänderte Quelle die Übersetzung ihrer früheren Form wiederverwenden, wenn beide mindestens `threshold` ähnlich sind, immer als `FUZZY_CACHE_REUSE` markiert ([Unscharfe Wiederverwendung](https://verbatra.kreitz-webdev.de/de/docs/the-cache.md#unscharfe-wiederverwendung)); `--no-cache` umgeht es.

### humanEdits und pinnedKeys [#humanedits-und-pinnedkeys]

`humanEdits` betrifft einen veralteten Key, dessen Wert ein Mensch geschrieben hat ([Provenance](https://verbatra.kreitz-webdev.de/de/docs/the-lock-file.md#herkunft-wer-welchen-wert-geschrieben-hat) `human`, `import` oder `external`); ein Key, der auf `pinnedKeys` passt (etwa `["legal.*", "brand.name"]`), wird nie maschinell geschrieben, egal was `humanEdits` sagt. `*` passt auf jede Zeichenfolge, Punkte eingeschlossen, und der Abgleich unterscheidet Groß- und Kleinschreibung. Was jeder Modus macht, steht unter [Menschliche Übersetzungen behalten](https://verbatra.kreitz-webdev.de/de/docs/protecting-human-translations.md).

### network [#network]

Siehe [Netzwerkzugriff einschränken](https://verbatra.kreitz-webdev.de/de/docs/network-policy.md).

```ts title="verbatra.config.ts"
network: { policy: "local-only", allowedHosts: ["gpu.lan"] }
```

### maxBatchSize [#maxbatchsize]

Fehlende und geänderte Einträge gehen in sequenziellen [Sub-Batches](https://verbatra.kreitz-webdev.de/de/docs/providers.md#batching) von höchstens dieser Größe hinaus. Kein CLI-Flag überschreibt es.

### maxLength [#maxlength]

Längengrenzen pro Key, für Strings unter einer Layout-Vorgabe:

```ts title="verbatra.config.ts"
maxLength: {
  "nav.dashboard": 18,
  "cta.submit": 12,
}
```

- **Hier oder in `verbatra.limits.json`.** <AvailableFrom version="0.13.0" /> Keys werden wörtlich verglichen, nie als Globs; ein Key, der an keinem der beiden Orte gesetzt ist, wird nie gemessen. `setKeyLengthLimit` nimmt nur Keys der Quelle, also kann Studio für eine erzeugte Pluralform kein Limit setzen; ein von Hand geschriebener Eintrag für ihren eigenen Key gilt. Die eingecheckte Datei neben der Lock-Datei nimmt `{ "version": 1, "maxLength": { ... } }`, die Konfiguration gewinnt pro Key, und [`setKeyLengthLimit`](https://verbatra.kreitz-webdev.de/de/docs/sdk/config.md#setkeylengthlimit) schreibt sie.
- **Graphemcluster.** Die Länge zählt wahrgenommene Zeichen, Leerraum eingeschlossen: Ein Emoji, ein Buchstabe mit Akzent oder ein CJK-Zeichen zählt als eines. Die Grenze ist inklusiv.
- **Wann.** Jeder Wert, den `translate` für den Key erzeugt, Cache-Wiederverwendungen eingeschlossen; ein aktueller Key wird erst gemessen, wenn sich seine Quelle ändert, oder durch [`export --include-unchanged`](https://verbatra.kreitz-webdev.de/de/docs/cli/export.md).
- **Beratend.** Eine Überschreitung wird trotzdem geschrieben und gelockt und als [`MAX_LENGTH_EXCEEDED`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#max_length_exceeded) zum Review markiert.

### maxTokens und budgetBehavior [#maxtokens-und-budgetbehavior]

`maxTokens` ist ein Budget für den ganzen Lauf: Eingabe- plus Ausgabe-Tokens über jeden Provider-Aufruf (Übersetzung und Pluralgenerierung) und jede Ziel-Locale. [`translate --max-tokens`](https://verbatra.kreitz-webdev.de/de/docs/cli/translate.md#token-obergrenze) kann es für einen Lauf verschärfen, nie lockern, und zwar immer als hartes `"stop"`.

`budgetBehavior` entscheidet, was dann passiert:

- `"warn"`, der Default, zählt die Ausgaben und vermerkt die Überschreitung, läuft aber weiter.
- `"stop"` <AvailableFrom version="0.11.0" /> macht es zur Obergrenze für jeden Provider. Vor jeder Anfrage projiziert der Lauf ihre Kosten wie [die Vorab-Schätzung](https://verbatra.kreitz-webdev.de/de/docs/estimating-cost.md) und lehnt eine ab, die `maxTokens` überschreiten würde. Die Keys dieser Anfrage und jeder noch nicht versuchte Key (der Rest der Locale und jede spätere Locale, deren Vergleich und Verwaisten-Meldung trotzdem laufen) werden zurückgehalten, behalten ihren vorherigen Lock-Hash und werden beim nächsten Lauf erneut versucht.

Unter `"stop"`:

- Nach jeder Anfrage wird die Summe auf den vom Provider gemeldeten Verbrauch abgeglichen, ein Lauf kann also knapp über `maxTokens` enden ([das `budget` der Laufzusammenfassung](https://verbatra.kreitz-webdev.de/de/docs/sdk/run-summary.md#die-anatomie-der-runsummary)). Maschinenübersetzungs-Provider melden keine Tokens und werden über die Projektion budgetiert.
- Eine fehlgeschlagene oder abgeschnittene Anfrage wird voll belastet.
- Die Projektion enthält einen festen Aufschlag für den LLM-Prompt, auch für Maschinenübersetzungs-Provider, und umfasst einen ganzen Batch, ein `maxTokens` unterhalb der Projektion eines Batches lehnt ihn also bei jedem Lauf ab; der Hinweis sagt, `maxBatchSize` zu senken oder `maxTokens` zu erhöhen.
- Eine Locale mit zurückgehaltenen Keys ist `partial`, oder `failed`, wenn kein Key ankam, und beide enden mit Exit `1`. Unter `"warn"` ändert sich der Exit-Code nie.

Das Budget begrenzt einen `translate`-Lauf, nie eine Neuübersetzung eines einzelnen Eintrags; `watch` startet pro Lauf ein frisches. Ein Budget schließt [`--concurrency`](https://verbatra.kreitz-webdev.de/de/docs/cli/translate.md#parallele-locales) über `1` bei einem echten Lauf aus; ein Dry-Run ist ausgenommen. `budgetBehavior` ohne `maxTokens` hat keine Wirkung.

<Callout type="warn" title="Zwei verschiedene maxTokens">
Das Top-Level-`maxTokens` ist dieses Budget für den ganzen Lauf; Anthropics davon unabhängiges `provider.options.maxTokens` begrenzt eine Antwort ([Provider](https://verbatra.kreitz-webdev.de/de/docs/providers.md#anthropic)).
</Callout>

### rates [#rates]

Wird nur von [`translate --estimate`](https://verbatra.kreitz-webdev.de/de/docs/estimating-cost.md) gelesen, das aus Raten Geld macht; ein echter Lauf ignoriert es.

```ts title="verbatra.config.ts"
rates: {
  asOf: "2026-01-15",
  currency: "USD",
  table: {
    // placeholders: copy the current rates from each provider's pricing page
    "gemini/gemini-2.5-flash": { inputPerMillionTokens: 1, outputPerMillionTokens: 2 },
    deepl: { perMillionCharacters: 1 },
  },
}
```

Ein `table`-Key ist `provider/model`, oder die blanke Id für einen Provider ohne Modell (`deepl`, `google-translate`). Ein Eintrag wird nach Tokens (`inputPerMillionTokens`, `outputPerMillionTokens`) oder nach Zeichen (`perMillionCharacters`) abgerechnet, nie beides, und eine negative Rate, eine Rate über 1.000.000.000 pro Million oder die falsche Einheit wird abgelehnt. `openai-compatible` und `libretranslate` stellen keine Rechnung, deshalb wird ihre Menge ohne Rate gemeldet.

### extract [#extract]

Wird nur von [`extract`](https://verbatra.kreitz-webdev.de/de/docs/cli/extract.md), [`doctor --literals`](https://verbatra.kreitz-webdev.de/de/docs/cli/doctor.md#nicht-übersetzte-literale) und [`diff --unused`](https://verbatra.kreitz-webdev.de/de/docs/cli/diff.md#ungenutzte-quell-keys) gelesen.

```ts title="verbatra.config.ts"
extract: {
  framework: "i18next",
  roots: ["src", "app"],
  exclude: ["generated"],
  literals: { ignore: ["Acme Inc."] },
  unused: { ignore: ["emails.*"] },
}
```

- **`framework`** muss eines mit Extractor nennen ([Aufrufformen](https://verbatra.kreitz-webdev.de/de/docs/cli/extract.md#was-er-findet)); jedes andere wird abgelehnt.
- **`roots`** werden gegen die Projektwurzel aufgelöst; **`exclude`** ergänzt Verzeichnisnamen (keine Pfade), die überall unterhalb eines Roots passen, zur [immer übersprungenen Menge](https://verbatra.kreitz-webdev.de/de/docs/cli/extract.md#grenzen-des-scans).
- **`literals.ignore`** trifft den ganzen Text eines Literals, Leerraum zusammengefasst und Referenzen aufgelöst ([Nicht übersetzte Literale](https://verbatra.kreitz-webdev.de/de/docs/cli/doctor.md#nicht-übersetzte-literale)).
- **`unused.ignore`** gleicht den dekodierten Key ab ([`diff --unused`](https://verbatra.kreitz-webdev.de/de/docs/cli/diff.md#ungenutzte-quell-keys)).

## Suchreihenfolge [#suchreihenfolge]

verbatra sucht vom Arbeitsverzeichnis aus aufwärts und nimmt den ersten Treffer. Die Suche endet beim nächsten Verzeichnis mit `.git`, sonst bei deinem Home-Verzeichnis (oder beim Arbeitsverzeichnis, wenn Home nicht darüber liegt), greift also nie eine Konfiguration oberhalb deines Repositorys auf:

1. `package.json` (eine `"verbatra"`-Eigenschaft)
2. `.verbatrarc`
3. `.verbatrarc.json`
4. `.verbatrarc.yaml`
5. `.verbatrarc.yml`
6. `.verbatrarc.js`
7. `.verbatrarc.cjs`
8. `.verbatrarc.ts`
9. `verbatra.config.js`
10. `verbatra.config.cjs`
11. `verbatra.config.ts`

`--config <path>` lädt stattdessen genau eine Datei, relativ zum Arbeitsverzeichnis; den Vorrang im SDK beschreibt [`loadConfig`](https://verbatra.kreitz-webdev.de/de/docs/sdk/config.md#loadconfig).

## Validierung im Editor [#validierung-im-editor]

<AvailableFrom version="0.9.0" />

`@verbatra/sdk` liefert ein JSON Schema aus seinem zod-Schema aus, auch unter einer festen URL für jede Installation, `npx`, ein Monorepo mit Hoisting oder einen entfernten Agenten:

```text
https://verbatra.kreitz-webdev.de/schema/v1/config.json
```

Offline, oder um die installierte Version festzulegen, nimmst du die Kopie im Paket, `./node_modules/@verbatra/sdk/dist/config-schema.json` ([alle Schemas](https://verbatra.kreitz-webdev.de/de/docs/cli/output.md#schemas)).

`.verbatrarc.json` und `.verbatrarc` nehmen einen `$schema`-Key für VS Code:

```json title=".verbatrarc.json"
{
  "$schema": "https://verbatra.kreitz-webdev.de/schema/v1/config.json",
  "sourceLocale": "en",
  "targetLocales": ["de", "fr"],
  "format": "i18next-json",
  "files": { "pattern": "locales/{locale}.json" },
  "provider": { "id": "gemini", "options": { "model": "gemini-2.5-flash", "maxOutputTokens": 4096 } }
}
```

Das endungslose `.verbatrarc` braucht zusätzlich eine Workspace-Dateizuordnung:

```json title=".vscode/settings.json"
{
  "json.schemas": [
    {
      "fileMatch": [".verbatrarc", ".verbatrarc.json"],
      "url": "https://verbatra.kreitz-webdev.de/schema/v1/config.json"
    }
  ]
}
```

Eine YAML-Konfiguration nimmt einen YAML-Language-Server-Kommentar:

```yaml title=".verbatrarc.yaml"
# yaml-language-server: $schema=https://verbatra.kreitz-webdev.de/schema/v1/config.json
sourceLocale: en
targetLocales: [de, fr]
format: i18next-json
files:
  pattern: "locales/{locale}.json"
provider:
  id: gemini
  options:
    model: gemini-2.5-flash
    maxOutputTokens: 4096
```

`package.json` nimmt kein Schema; ausführbare Formen typisiert `defineConfig`.

### Was das Dokument nicht prüfen kann [#was-das-dokument-nicht-prüfen-kann]

Nur das Laden der Konfiguration prüft diese Regeln: die Regeln für `targetLocales` und [Locale-Codes](#locale-codes), die Keys von [`localeMap`](https://verbatra.kreitz-webdev.de/de/docs/providers.md#an-den-provider-gesendete-locale-codes) und die Regeln für `baseUrl` und [`apiKeyEnvVar`](https://verbatra.kreitz-webdev.de/de/docs/providers.md#openai-compatible) von `openai-compatible`, gültige Adressen und Bereiche in [`network.allowedHosts`](https://verbatra.kreitz-webdev.de/de/docs/network-policy.md), `sensitiveData.patterns` innerhalb der [Sicherheitsregeln für Muster](https://verbatra.kreitz-webdev.de/de/docs/data-handling.md#schutz-für-sensible-inhalte) und die Begriffsregeln eines Inline-Glossars der [Version 2](#glossar-pro-locale-version-2).

## Das Arbeitsverzeichnis und .env [#das-arbeitsverzeichnis-und-env]

Das Arbeitsverzeichnis (wo du verbatra ausführst, oder `--cwd`) startet die Konfigurationssuche und hält die [Umgebungsdateien](https://verbatra.kreitz-webdev.de/de/docs/cli.md#umgebungsdateien). Locale-Pfade, `verbatra.lock.json`, der Cache, die Provenienzdatei und `.verbatra-local/` werden gegen die Projektwurzel aufgelöst, das Verzeichnis der gefundenen Konfiguration, sodass ein Lauf aus einem Unterverzeichnis dieselben Dateien nutzt. Mit `--config` ist das Arbeitsverzeichnis die Wurzel, und eine Konfiguration an anderer Stelle bekommt einen Hinweis auf stderr, weil ihr Glossarpfad gegen ihr eigenes Verzeichnis aufgelöst wird. Ein Pfad, den du übergibst, etwa bei `--out` oder `check --file`, wird gegen das Arbeitsverzeichnis aufgelöst.

## Wenn das Laden fehlschlägt [#wenn-das-laden-fehlschlägt]

- [`CONFIG_NOT_FOUND`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#config_not_found): an keinem der durchsuchten Orte eine Konfiguration, oder der `--config`-Pfad existiert nicht.
- [`CONFIG_INVALID`](https://verbatra.kreitz-webdev.de/de/docs/error-codes.md#config_invalid): Eine Konfiguration wurde gefunden, ist aber nicht verwendbar. Die Meldung listet jedes Problem mit seinem Key-Pfad; ein unbekannter Key bekommt den Hinweis, dass API-Schlüssel aus der Umgebung kommen. Ein Parse-Fehler nennt die Datei (bei YAML mit Zeile und Spalte), zitiert aber nie ihren Inhalt, und jede Meldung läuft durch die Schlüssel-Redaktion.

Die CLI endet in beiden Fällen mit Exit `2`.
