Référence / Configuration
Page traduite automatiquement
Fichier de configuration
Chaque clé de configuration avec son type, son défaut et la version qui l'a ajoutée, plus l'emplacement du fichier et l'échec d'une mauvaise configuration.
verbatra lit un objet de configuration sans secret et le valide avant tout travail. Aucune clé d'API n'est jamais lue depuis ce fichier (Fournisseurs), donc la configuration peut être commitée sans risque.
Une configuration minimale
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",
});Voir locales et fichiers, provider, glossary, network, contenu sensible, budget, rates et extract.
defineConfig ne fait que typer son argument : options.model propose les identifiants de modèle du fournisseur, mais à l'exécution toute chaîne non vide est acceptée.
Chaque clé
Les clés inconnues sont rejetées à chaque niveau sauf $schema et les maps dont tu nommes les clés, comme maxLength, rates.table, localeMap et un glossary en ligne, donc une clé d'API ne peut pas se trouver dans la configuration.
| Clé | Type | Défaut | Depuis | Rôle |
|---|---|---|---|---|
$schema | chaîne | aucun | 0.9.0 | Pointe un éditeur vers le JSON Schema ; ignoré à l'exécution. |
sourceLocale | code de locale | requis | 0.1.0 | La locale dans laquelle tes chaînes source sont écrites. |
targetLocales | tableau de codes de locale | requis | 0.1.0 | Au moins une ; jamais sourceLocale, jamais deux égales sans tenir compte de la casse. |
format | id de format | requis | 0.1.0 | Le format de tes fichiers de locale, ou un id custom: pour un adaptateur extérieur. |
files.pattern | chaîne | requis | 0.1.0 | Le chemin de chaque fichier de locale, avec un jeton {locale}. |
files.localeStyle | "literal", "posix", "android" | "literal" | 0.7.0 | Comment {locale} est écrit dans le chemin. |
provider.id | id de fournisseur | requis | 0.1.0 | Le fournisseur de traduction, ou none pour aucune traduction automatique. |
provider.options | objet | requis, sauf pour none | 0.1.0 | Les options propres au fournisseur. |
glossary | objet ou chemin de fichier | aucun | 0.1.0 | Traductions fixes, traductions interdites et termes à ne pas traduire. |
tone | "formal", "informal", "neutral" | aucun | 0.1.0 | Le registre des traductions, appliqué selon le fournisseur. |
prune | booléen | false | 0.3.0 | Supprime les clés orphelines des fichiers cibles et du fichier de verrouillage ; translate --prune prime. |
generatePlurals | booléen | false | 0.3.0 | Synthétise les formes plurielles CLDR dont une langue cible a besoin. |
fuzzyCache.enabled | booléen | requis dans le bloc | 0.11.0 | Réutilise la traduction d'une source peu modifiée. |
fuzzyCache.threshold | nombre, de 0.5 à 1 | 0.9 | 0.11.0 | À quel point ancienne et nouvelle source doivent se ressembler. |
humanEdits | "protect", "suggest", "overwrite" | "protect" | 0.12.0 | Ce que fait une exécution d'une clé obsolète écrite par une personne. |
pinnedKeys | tableau de chaînes | aucun | 0.12.0 | Des motifs de clés (* en joker) qu'aucune écriture automatique ne touche. |
network.policy | "any", "local-only", "allowlist" | "any" | 0.12.0 | Les hôtes auxquels un fournisseur peut se connecter. |
network.allowedHosts | tableau de chaînes | aucun ; requis pour "allowlist" | 0.12.0 | Hôtes autorisés en plus : noms, jokers *.suffix, IP ou plages CIDR. |
sensitiveData.mode | "off", "warn", "block", "redact" | obligatoire dans le bloc ; "off" sans le bloc | 0.12.0 | Ce qu'une exécution fait du contenu sensible avant l'envoi. |
sensitiveData.detectors | tableau d'ID de détecteurs | secret, email, iban, credit-card | 0.12.0 | Détecteurs actifs ; phone, ip, private-host sont optionnels. |
sensitiveData.patterns | tableau de chaînes | aucun | 0.12.0 | Expressions régulières pour tes propres termes, comme des noms de code. |
sensitiveData.allow | tableau de chaînes | aucun | 0.12.0 | Correspondances jamais signalées ; * est un joker et la casse est ignorée. |
maxBatchSize | entier positif | 50 | 0.3.0 | Le maximum d'entrées par requête fournisseur. |
maxLength | table de clé vers entier positif | aucun | 0.11.0 | Longueurs maximales par clé, en groupes de graphèmes. |
maxTokens | entier positif | aucun | 0.5.0 | Un budget de tokens pour toute l'exécution. |
budgetBehavior | "warn", "stop" | "warn" | 0.5.0 | Si atteindre maxTokens avertit ou arrête les envois. |
rates.asOf | date YYYY-MM-DD | requis dans le bloc | 0.11.0 | La date à laquelle tu as lu les tarifs. |
rates.currency | trois lettres majuscules | requis dans le bloc | 0.11.0 | La devise de chaque tarif, comme USD. |
rates.table | table de clé de tarif vers tarif | requis dans le bloc | 0.11.0 | Les tarifs par clé de tarif. |
extract.framework | "i18next" | requis dans le bloc | 0.11.0 | Les appels que l'analyse du code source recherche. |
extract.roots | tableau de chaînes | requis dans le bloc | 0.11.0 | Les répertoires à analyser, au moins un. |
extract.exclude | tableau de chaînes | aucun | 0.11.0 | Des noms de répertoire supplémentaires à ignorer. |
extract.literals.ignore | tableau de chaînes | aucun | 0.11.0 | Des textes exacts que doctor --literals retient. |
extract.unused.ignore | tableau de chaînes | aucun | 0.11.0 | Des clés ou motifs * que diff --unused ne signale jamais. |
Locales et fichiers
-
files.patterndoit contenir{locale}, son seul jeton, et se résout par rapport à la racine du projet :locales/{locale}.jsondevientlocales/de.json. C'est un chemin littéral, jamais un glob, donc une configuration adresse un fichier par locale (Dispositions avec espaces de noms) ;apple-xcstringsrésout chaque locale vers le même catalogue (Xcode String Catalogs). -
files.localeStyledécide en quoi le jeton se développe :literalécrit l'étiquette telle qu'elle est configurée, comme le veulent la plupart des dispositions.posixremplace-par_pour gettext et la disposition Javamessages_({locale}.properties pt-BRdevientpt_) ; une variante commeBR de-1996n'a pas d'écriture posix.gettext-poécrit les écritures à la manière de gettext (section suivante).androidse développe en un répertoire de ressources, écrit sous la formeres/{locale}/strings.xml:valuespour la source, puisvalues-de,values-pt-rBR,values-b+zh+Hans.
Un style refuse une locale qu'il ne sait pas écrire.
Noms de locale gettext
Avec format: "gettext-po" et localeStyle: "posix", les fichiers portent des noms gettext (language_) tandis que la configuration garde des codes BCP 47 :
| Code dans la configuration | Nom de fichier |
|---|---|
pt-BR | pt_ |
es-419 | es_ |
sr-Latn | sr@latin |
sr-Latn-RS | sr_ |
uz-Cyrl | uz@cyrillic |
ks-Deva-IN | ks_ |
zh-Hant-TW | zh_ |
sr-Cyrl-RS | sr_ |
Une écriture que la langue et la région impliquent déjà est omise, donc zh-TW et zh-Hant-TW ensemble échouent avec LOCALE_ ; une écriture sans modificateur gettext, comme zh-Hant seul, échoue avec LOCALE_. verbatra init relit ces noms. Les autres formats gardent l'écriture ICU (sr_).
Codes de locale
Chaque code de locale doit être une étiquette qu'accepte Intl.getCanonicalLocales, avec une sous-étiquette de langue de deux ou trois lettres, sinon le chargement échoue avec CONFIG_ : pt_ (écris pt-BR avec localeStyle: "posix") et german sont rejetés. Un code valide non canonique, comme zh-hant-tw ou l'obsolète iw, fonctionne tel qu'il est écrit.
provider
provider.id sélectionne l'un des fournisseurs ou none, dont options peut être omis et doit sinon valoir {} (Travailler sans traduction automatique). Les autres fournisseurs acceptent options.localeMap et options.requestTimeoutMs (Fournisseurs) ; depuis 0.12.0, une clé mal placée sous provider, comme localeMap, échoue avec CONFIG_.
glossary
Des termes traduits d'une façon fixe, en ligne ou comme chemin vers un fichier JSON de la même forme (jamais fusionnées). La version 1 associe chaque terme source à une traduction pour toutes les locales :
export default defineConfig({
// ...
glossary: {
"Sign in": "Anmelden",
},
// or: glossary: "glossary.json",
});Un chemin relatif se résout par rapport au répertoire du fichier de configuration ; le fichier doit être en UTF-8, ne pas dépasser 1 MiB, et il est lu une fois au chargement. Un fichier absent, un JSON invalide ou une forme invalide échoue avec CONFIG_, en nommant le chemin et, pour la version 2, le champ. Mets un terme _ dans un fichier ; un littéral JavaScript le transforme en prototype.
Glossaire par locale (version 2)
La version 2 ajoute des traductions par locale, des traductions interdites et des termes à garder tels qu'ils sont écrits :
{
"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 }]
}| Champ | Signification |
|---|---|
version | Toujours 2 ; tout autre nombre échoue. |
terms[].source | Le terme source ; chaque terme apparaît une seule fois. |
terms[].target | La traduction pour chaque locale sans entrée propre dans targets. |
terms[].targets | Les traductions par locale. Une locale prend sa propre entrée, puis celle de sa langue de base (de couvre de-AT), puis target, en comparant les codes canoniques sans tenir compte de la casse. |
terms[].forbidden | Les traductions qu'une locale ne doit jamais utiliser ; une locale régionale respecte aussi la liste de sa langue de base. |
terms[].caseSensitive | Comparer le terme, les traductions et les traductions interdites en tenant compte de la casse. false par défaut. |
terms[].note, terms[].partOfSpeech | Un contexte en texte libre, envoyé comme données à un fournisseur LLM. |
doNotTranslate | Les termes recopiés sans traduction. Une simple chaîne garde sa casse ; { "term", "caseSensitive": false } peut la changer. |
Chaque terme a besoin d'un target, d'une entrée dans targets ou d'une liste forbidden ; il ne peut pas interdire sa propre traduction, nommer deux fois une locale ni figurer dans doNotTranslate. Aucun texte ne peut être vide ; un terme compte au plus 200 caractères, une traduction ou une forme interdite 2000, une note 500 et une partOfSpeech 50. L'empreinte de cache d'une locale ne couvre que ses propres termes. Un fichier version 1 garde son empreinte ; updateGlossaryTerm ne le réécrit en version 2 que pour une modification que la version 1 ne sait pas exprimer.
Studio ne modifie que la forme fichier, un terme à la fois. Les fournisseurs qui appliquent le glossaire sont indiqués dans Fournisseurs ; chaque sortie est vérifiée ensuite.
generatePlurals
Quand il vaut true, verbatra synthétise les formes plurielles CLDR dont une cible a besoin mais que la source n'a pas, comme few et many pour le polonais, et les clés ordinales (<key>_) selon les règles ordinales CLDR. Seul i18next-json avec un fournisseur LLM le prend en charge ; tout le reste, et une locale sans règles CLDR, reçoit l'avertissement de pluriel (les ordinaux supposent other). Il n'y a pas d'option CLI ; translate() du SDK accepte une surcharge par exécution.
fuzzyCache
fuzzyCache: { enabled: true } laisse une source modifiée réutiliser la traduction de sa forme antérieure quand les deux se ressemblent au moins à hauteur de threshold, toujours signalée avec FUZZY_ (Réutilisation approximative) ; --no-cache le contourne.
humanEdits et pinnedKeys
humanEdits couvre une clé obsolète dont la valeur a été écrite par une personne (provenance human, import ou external) ; une clé qui correspond à pinnedKeys (comme ["legal.*", "brand.name"]) n'est jamais écrite par une machine, quoi que dise humanEdits. * correspond à toute suite de caractères, points compris, et la comparaison est sensible à la casse. Ce que fait chaque mode est décrit dans Garder les traductions humaines.
network
Voir Restreindre l'accès réseau.
network: { policy: "local-only", allowedHosts: ["gpu.lan"] }maxBatchSize
Les entrées manquantes et modifiées partent en sous-lots séquentiels d'au plus cette taille. Aucune option CLI ne le remplace.
maxLength
Des limites de longueur par clé, pour les chaînes soumises à une contrainte de mise en page :
maxLength: {
"nav.dashboard": 18,
"cta.submit": 12,
}- Ici ou dans
verbatra.limits.json. nouveau Les clés sont comparées littéralement, jamais comme des globs ; une clé fixée dans aucun des deux n'est jamais mesurée.setKeyLengthLimitn'accepte que des clés de la source, donc Studio ne peut pas limiter une forme plurielle générée ; une entrée écrite à la main pour sa propre clé s'applique. Le fichier versionné à côté du fichier de verrou accepte{ "version": 1, "maxLength": { ... } }, la config l'emporte par clé etsetKeyLengthLimitl'écrit. - Groupes de graphèmes. La longueur compte les caractères perçus, espaces compris : un emoji, une lettre accentuée ou un caractère CJK compte pour un. La limite est inclusive.
- Quand. Chaque valeur que
translateproduit pour la clé, réutilisations du cache comprises ; une clé à jour n'est mesurée que quand sa source change, ou parexport --include-unchanged. - Consultatif. Un dépassement est quand même écrit et verrouillé, et signalé pour relecture comme
MAX_.LENGTH_ EXCEEDED
maxTokens et budgetBehavior
maxTokens est un budget pour toute l'exécution : tokens d'entrée plus de sortie sur chaque appel fournisseur (traduction et génération de pluriels) et chaque locale cible. translate --max-tokens peut le resserrer pour une exécution, jamais le relâcher, et toujours comme un "stop" ferme.
budgetBehavior décide de ce qui se passe alors :
"warn", la valeur par défaut, compte la dépense et signale le dépassement, mais continue."stop"en fait un plafond pour chaque fournisseur. Avant chaque requête, l'exécution projette son coût comme l'estimation préalable et refuse une requête qui dépasseraitmaxTokens. Les clés de cette requête et chaque clé pas encore tentée (le reste de la locale et chaque locale suivante, dont la comparaison et le rapport d'orphelines tournent quand même) sont retenues, gardent leur hash de verrouillage précédent et sont retentées à l'exécution suivante.
Sous "stop" :
- Après chaque requête, le total est réconcilié avec l'usage remonté par le fournisseur : une exécution peut donc finir légèrement au-dessus de
maxTokens(lebudgetdu résumé d'exécution). Les fournisseurs de traduction automatique ne remontent aucun token et sont budgétés à partir de la projection. - Une requête échouée ou tronquée est entièrement comptée.
- La projection ajoute une allocation fixe pour le prompt LLM, aussi pour les fournisseurs de traduction automatique, et couvre un lot entier, donc un
maxTokensinférieur à la projection d'un lot le refuse à chaque exécution ; l'avis dit de baissermaxBatchSizeou d'augmentermaxTokens. - Une locale dont des clés ont été retenues est
partial, oufailedsi aucune clé n'est arrivée, et les deux sortent avec1. Sous"warn", le code de sortie ne change jamais.
Le budget borne une exécution de translate, jamais la retraduction d'une seule entrée ; watch en démarre un neuf par exécution. Un budget exclut --concurrency supérieur à 1 sur une exécution réelle ; un dry run est exempté. budgetBehavior sans maxTokens n'a aucun effet.
Deux maxTokens différents
Le maxTokens de premier niveau est ce budget de toute l'exécution ; le provider.options.maxTokens d'Anthropic, sans lien avec lui, plafonne une réponse (Fournisseurs).
rates
Lu uniquement par translate --estimate, qui transforme les tarifs en argent ; une exécution réelle l'ignore.
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 },
},
}Une clé de table est provider/model, ou l'id seul pour un fournisseur sans modèle (deepl, google-translate). Une entrée est facturée aux tokens (inputPerMillionTokens, outputPerMillionTokens) ou aux caractères (perMillionCharacters), jamais les deux, et un tarif négatif, un tarif au-delà de 1 000 000 000 par million ou la mauvaise unité est refusé. openai-compatible et libretranslate n'envoient aucune facture, donc leur quantité est indiquée sans tarif.
extract
Lu uniquement par extract, doctor --literals et diff --unused.
extract: {
framework: "i18next",
roots: ["src", "app"],
exclude: ["generated"],
literals: { ignore: ["Acme Inc."] },
unused: { ignore: ["emails.*"] },
}frameworkdoit en nommer un doté d'un extracteur (formes d'appel) ; tout autre est refusé.rootsse résolvent par rapport à la racine du projet ;excludeajoute des noms de répertoire (pas des chemins), reconnus n'importe où sous une racine, à l'ensemble toujours ignoré.literals.ignorecorrespond au texte complet d'un littéral, espaces regroupés et références décodées (Littéraux non traduits).unused.ignoreest comparé à la clé décodée (diff --unused).
Ordre de découverte
verbatra cherche vers le haut depuis le répertoire de travail et prend la première correspondance, en s'arrêtant au répertoire le plus proche qui contient .git, sinon à ton répertoire personnel (ou au répertoire de travail quand le personnel n'est pas au-dessus), donc il ne prend jamais une configuration située au-dessus de ton dépôt :
package.json(une propriété"verbatra").verbatrarc.verbatrarc.json.verbatrarc.yaml.verbatrarc.yml.verbatrarc.js.verbatrarc.cjs.verbatrarc.tsverbatra.config.jsverbatra.config.cjsverbatra.config.ts
--config <path> charge un seul fichier à la place, relatif au répertoire de travail ; la priorité côté SDK est décrite sous loadConfig.
Validation dans l'éditeur
@verbatra/sdk livre un JSON Schema généré à partir de son schéma zod, servi aussi à une URL stable pour toute installation, npx, un monorepo avec hoisting ou un agent distant :
https://verbatra.kreitz-webdev.de/schema/v1/config.jsonHors ligne, ou pour figer la version installée, utilise la copie du paquet, ./node_ (tous les schémas).
.verbatrarc.json et .verbatrarc prennent une clé $schema pour VS Code :
{
"$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 } }
}Le .verbatrarc sans extension a aussi besoin d'une association de fichiers dans l'espace de travail :
{
"json.schemas": [
{
"fileMatch": [".verbatrarc", ".verbatrarc.json"],
"url": "https://verbatra.kreitz-webdev.de/schema/v1/config.json"
}
]
}Une configuration YAML prend un commentaire YAML Language Server :
# 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: 4096package.json ne prend pas de schéma ; les formes exécutables sont typées par defineConfig.
Ce que le document ne peut pas vérifier
Seul le chargement de la configuration vérifie ces règles : les règles de targetLocales et des codes de locale, les clés de localeMap et les règles de baseUrl et apiKeyEnvVar d'openai-compatible, des adresses et plages valides dans network.allowedHosts, sensitiveData.patterns dans les règles de sécurité des motifs et les règles des termes d'un glossaire en ligne en version 2.
Le répertoire de travail et .env
Le répertoire de travail (là où tu lances verbatra, ou --cwd) lance la recherche de configuration et contient les fichiers d'environnement. Les chemins de locale, verbatra.lock.json, le cache, le fichier de provenance et .verbatra-local/ sont résolus par rapport à la racine du projet, le répertoire de la configuration trouvée, donc une exécution depuis un sous-répertoire utilise les mêmes fichiers. Avec --config, le répertoire de travail est la racine, et une configuration située ailleurs reçoit une note sur stderr, car le chemin de son glossaire est résolu par rapport à son propre répertoire. Un chemin que tu passes, comme --out ou check --file, est résolu par rapport au répertoire de travail.
Quand le chargement échoue
CONFIG_: aucune configuration à aucun des endroits cherchés, ou le cheminNOT_ FOUND --confign'existe pas.CONFIG_: une configuration a été trouvée mais n'est pas utilisable. Le message liste chaque problème avec son chemin de clé ; une clé inconnue ajoute l'indice que les clés d'API viennent de l'environnement. Un échec d'analyse nomme le fichier (avec la ligne et la colonne pour YAML) mais ne cite jamais son contenu, et chaque message passe par la rédaction des clés.INVALID
La CLI sort avec 2 dans les deux cas.