Skip to content
Skip to Content

Reference / Configuration

Providers

Every AI and machine translation provider verbatra supports, with its options and API key variable, plus the behavior all of them share.

Select a provider by id in the provider block of your config. Anthropic, OpenAI, Gemini, OpenCode and openai-compatible are language models; DeepL, Google Cloud Translation and LibreTranslate are machine-translation services; none turns machine translation off. Language coverage is on Languages and locales, and what each provider receives on Data handling and privacy.

Which one should I pick?

Every provider works with every format and command, and switching later means editing one id.

If you wantUse
Free, for a small project with non-confidential stringsGemini: a free API tier, whose requests Google may have humans review outside the EEA, Switzerland and the UK (terms).
The best quality for a production appAnthropic or OpenAI, both paid.
High volume, a native glossary, no LLMDeepL, free or paid tier.
The widest coverage from an API key on Google CloudGoogle Cloud Translation (Basic, v2).
Offline, no cost, nothing leaving your networkopenai-compatible with LM Studio, Ollama or vLLM.
No language model, but self-hostedLibreTranslate on your own server.
No machine translation at allnone: memory and humans only.

Keys come from the environment

verbatra never reads a key from the config. Each hosted provider reads one variable:

Provider idEnvironment variable
anthropicANTHROPIC_API_KEY
openaiOPENAI_API_KEY
geminiGEMINI_API_KEY
deeplDEEPL_API_KEY
google-translateGOOGLE_TRANSLATE_API_KEY
opencodeOPENCODE_API_KEY

Set it in .env (which verbatra init gitignores), your shell, or your CI secret store, and never commit it. An unset or empty variable fails with MISSING_API_KEY, naming the variable but never a value. openai-compatible and libretranslate usually need no key.

Anthropic

verbatra.config.ts
provider: {
  id: "anthropic",
  options: {
    model: "claude-sonnet-4-6", // example model id
    maxTokens: 4096,
  },
}
OptionTypeDefaultEffect
modelstringrequiredThe model id.
maxTokenspositive integerrequiredCaps one response's output tokens (limits); unrelated to the top-level budget.
requestTimeoutMspositive integer120000Bounds each request attempt (Request timeout).
localeMapmap of locale to codenoneOverrides the code sent per locale (Locale codes).

Reads ANTHROPIC_API_KEY.

OpenAI

verbatra.config.ts
provider: {
  id: "openai",
  options: {
    model: "gpt-5.4-mini", // example model id
    maxOutputTokens: 4096,
  },
}
OptionTypeDefaultEffect
modelstringrequiredThe model id.
maxOutputTokenspositive integerrequiredCaps one response's output tokens.
requestTimeoutMspositive integer120000Bounds each request attempt.
localeMapmap of locale to codenoneOverrides the code sent per locale.

Reads OPENAI_API_KEY.

Gemini

verbatra.config.ts
provider: {
  id: "gemini",
  options: {
    model: "gemini-2.5-flash", // example model id
    maxOutputTokens: 4096,
  },
}
OptionTypeDefaultEffect
modelstringrequiredThe model id.
maxOutputTokenspositive integerrequiredCaps one response's output tokens.
requestTimeoutMspositive integer120000Bounds each request attempt.
localeMapmap of locale to codenoneOverrides the code sent per locale.

Reads GEMINI_API_KEY.

The free tier and its limits

A key from Google AI Studio needs no billing, but its per-minute and per-day caps can stop a first run over many locales: translate one locale at a time or lower maxBatchSize, and re-run for the rest.

DeepL

verbatra.config.ts
provider: {
  id: "deepl",
  options: {
    glossaryId: "<your-glossary-id>",
  },
}
OptionTypeDefaultEffect
glossaryIdstringnoneA glossary you created in DeepL, applied natively.
requestTimeoutMspositive integer120000The minimum wait per attempt, up to six attempts.
localeMapmap of locale to codenoneOverrides the code sent per locale.

Reads DEEPL_API_KEY; options may be {}. DeepL has no model and no output-token limit, and degrades with a notice rather than failing:

  • Placeholders travel as numbered markers in an ignored tag (Hello {{name}} as Hello <x>{0}</x>) and are restored. PLACEHOLDER_UNSUPPORTED withholds a value with a non-literal placeholder or other text holding <, >, { or }, and a result with a lost, repeated, changed or bare marker, an unknown entity or a stray tag. Markup alone goes as plain text.
  • Tone maps to formality (formal more, informal less, neutral omitted). A free-tier key sends no formality at all and reports FORMALITY_DOWNGRADED. A paid key requests prefer_more or prefer_less, so a language without formality, such as Swedish, falls back with the same notice instead of failing. The glossary is not applied.

verbatra ships DeepL's dated language table and refuses an unlisted language with LOCALE_UNSUPPORTED_BY_PROVIDER before anything is spent. A glossaryId works only when both languages are on DeepL's glossary list (such as DE, EN, FR, JA, ZH).

Google Cloud Translation

verbatra.config.ts
provider: {
  id: "google-translate",
  options: {},
}
OptionTypeDefaultEffect
requestTimeoutMspositive integer120000Bounds the whole call (no retries).
localeMapmap of locale to codenoneOverrides the code sent per locale.

Reads GOOGLE_TRANSLATE_API_KEY, an API key for a Google Cloud project with the Cloud Translation API enabled. Only Basic (v2) works: Advanced (v3) needs a service account.

  • Placeholders are masked as for DeepL, inside <span translate="no">; line breaks, tabs and double spaces are withheld too.
  • Codes. Each code sent is checked as well-formed BCP 47 before any call (en_US fails with INVALID_REQUEST). Regional codes are allowed; a pair Google rejects fails with INVALID_REQUEST from the API.

verbatra ships Google's dated language table and refuses an unlisted language before anything is spent. A regional code whose language is listed only as a whole, such as de-AT, is sent as written with a warning.

OpenCode

Only OpenCode Zen models served over /chat/completions work here (the model table lists each model's endpoint). OpenCode keeps Go and the free Zen models for its own clients.

verbatra.config.ts
provider: {
  id: "opencode",
  options: {
    model: "glm-5.3", // example: a Zen model served over Chat Completions
    maxOutputTokens: 4096,
  },
}
OptionTypeDefaultEffect
modelstringrequiredThe model id.
maxOutputTokenspositive integerrequiredCaps one response's output tokens.
requestTimeoutMspositive integer120000Bounds each request attempt.
localeMapmap of locale to codenoneOverrides the code sent per locale.

Fixed endpoint, no baseUrl. Reads OPENCODE_API_KEY.

openai-compatible

Any server speaking the OpenAI Chat Completions API: LM Studio, Ollama, vLLM, or a hosted API.

The server must serve Chat Completions at <baseUrl>/chat/completions with strict JSON schema output. Gateways serving only their own client apps do not work.

verbatra.config.ts
provider: {
  id: "openai-compatible",
  options: {
    baseUrl: "http://192.168.178.74:1234/v1",
    model: "qwen2.5-14b-instruct", // example: whatever your server exposes
    maxOutputTokens: 1024,
  },
}
OptionTypeDefaultEffect
baseUrlhttp or https URLrequiredThe API root, usually ending in /v1. A URL ending in /chat/completions or /completions is rejected; one missing /v1 passes validation, then fails at request time.
modelstringrequiredWhatever model your server exposes.
maxOutputTokenspositive integerrequiredCaps one response's output tokens.
apiKeyEnvVarvariable namenoneThe variable holding this server's key.
requestTimeoutMspositive integer120000Bounds each request attempt.
localeMapmap of locale to codenoneOverrides the code sent per locale.

For a hosted API such as Mistral:

verbatra.config.ts
provider: {
  id: "openai-compatible",
  options: {
    baseUrl: "https://api.mistral.ai/v1",
    model: "mistral-large-latest", // example: check Mistral's current model list
    maxOutputTokens: 4096,
    apiKeyEnvVar: "MISTRAL_API_KEY",
  },
}

The response uses the same strict schema as openai; the first balanced JSON object is taken from any surrounding prose or code fence and validated. verbatra init --provider openai-compatible --base-url <url> --model <name> writes this block (init), and --api-key-env-var <NAME> adds the variable's name.

Keys are optional

The key resolves in order:

  1. apiKeyEnvVar: if set, its variable must hold a key, or the run fails with MISSING_API_KEY.
  2. OPENAI_COMPATIBLE_API_KEY: used when set and non-empty.
  3. "local": a fixed, non-secret placeholder, which is what makes a keyless server work.

apiKeyEnvVar cannot name a hosted provider's variable, and the client never reads OPENAI_API_KEY, so a hosted key never reaches a custom baseUrl.

A real key over plaintext http travels in cleartext

With a key and a non-loopback http:// URL, the key crosses your network unencrypted, without a warning, so use https:// for a server that needs a key.

Choosing a local model

Prefer an instruction-tuned, non-reasoning, multilingual model as large as your hardware runs well, such as qwen2.5-14b-instruct. Reasoning models and heavily quantized builds tend to wrap or truncate the answer, which fails as INVALID_RESPONSE or OUTPUT_TRUNCATED.

LibreTranslate

A self-hosted LibreTranslate server (Argos Translate, no language model). verbatra only calls its HTTP API, so the server's AGPL-3.0 license does not extend to your project.

verbatra.config.ts
provider: {
  id: "libretranslate",
  options: {
    baseUrl: "http://localhost:5000",
  },
}
OptionTypeDefaultEffect
baseUrlhttp or https URLrequiredThe API root, without /translate.
requestTimeoutMspositive integer120000Bounds the whole call (no retries).
localeMapmap of locale to codenoneOverrides the code sent per locale.

Its languages are the models installed on your server:

docker run -p 5000:5000 libretranslate/libretranslate --load-only en,de,fr
  • Key. LIBRETRANSLATE_API_KEY is sent as api_key only when set. A server started with --api-keys fails the run with MISSING_API_KEY when it is unset, and with AUTH_FAILED when it rejects it.
  • Placeholders are masked as for DeepL, as bare {0} markers; markup is kept, so only braces beside a placeholder count.
  • Markup. A value with HTML-style tags is sent in html format: text inside tags is translated, the tags stay (phrasing across a tag may suffer), and changed tags are refused.
  • Language check. Every target warns LOCALE_UNVERIFIED_BY_PROVIDER on every run; verbatra doctor --locales --live checks them against the server.

verbatra init --provider libretranslate --base-url <url> writes this block.

none

Disables machine translation by policy, for a rule that forbids sending strings to any service.

verbatra.config.ts
provider: { id: "none" }

options may be omitted or {}. No provider is constructed, no key is read and no request is made; what each command does instead is on Run without machine translation.

Tone and glossary across providers

A request carries only its target locale's glossary terms, and every output is checked against them.

Providertoneglossary
Anthropic, OpenAI, Gemini, OpenCode, openai-compatiblesent and honoredtranslations, forbidden renderings, do-not-translate terms and notes sent as binding data
DeepLmapped to formalityGLOSSARY_IGNORED; only glossaryId applies
Google Cloud Translation, LibreTranslateFORMALITY_DOWNGRADED for formal or informalGLOSSARY_IGNORED

Behavior every provider shares

Batching

A locale's work goes out in sequential sub-batches of at most maxBatchSize entries. A failed sub-batch is withheld and retried next run while the rest proceed. DeepL and Google Cloud Translation also split a sub-batch to fit their own request caps.

Retries

Rate limits and server errors are retried with backoff, by the client for OpenAI, OpenCode, openai-compatible, Anthropic and DeepL and by verbatra for Gemini. Google Cloud Translation and LibreTranslate do not retry. Gemini honours a stated wait up to 5 seconds and fails a longer one (an exhausted daily quota) at once with RATE_LIMITED naming the wait.

Request timeout

requestTimeoutMs (default 120000 ms) keeps a server that never answers from stalling a run and holding a locale's write lock. Raise it for a slow local model or a large maxBatchSize.

verbatra.config.ts
provider: {
  id: "openai-compatible",
  options: {
    baseUrl: "http://192.168.178.74:1234/v1",
    model: "qwen2.5-14b-instruct",
    maxOutputTokens: 1024,
    requestTimeoutMs: 300000, // 5 minutes
  },
}
  • Per attempt. Each retry gets the full timeout. The LLM providers also retry a timed-out attempt, and all but Gemini a connection error, so a hung server takes up to three timeouts plus backoff.
  • The error names the cause. TIMEOUT means one attempt got no answer in time; repeated refused connections fail as PROVIDER_ERROR.
  • DeepL cannot abort a request in flight, so an interrupted run leaves it to settle at DeepL.

Locale codes sent to the provider

Files, the lock and the memory use your config's codes. Only the request is translated to what the provider expects:

ProviderSource localeTarget locale
DeepLThe language alone: en-US as EN, zh-Hant-TW as ZH.zh-Hant, zh-TW, zh-HK, zh-MO as ZH-HANT; zh-Hans, zh-CN, zh-SG as ZH-HANS. DeepL variants stay (EN-GB, EN-US, PT-BR, PT-PT, ES-419, FR-CA, FR-FR, DE-CH, DE-DE). Spanish with a Latin American or Caribbean region (MX, AR, CO, CL, PE, VE, EC, GT, CU, BO, DO, HN, PY, SV, NI, CR, PA, UY, PR, US, 419) as ES-419. Other regions and scripts are dropped: de-AT as DE.
Google Cloud Translationzh-Hant (zh-HK, zh-MO) as zh-TW, zh-Hans (zh-SG) as zh-CN, nb as no; everything else as written.Same as the source.
LibreTranslateThe language alone (de-AT as de), except pt-BR stays and Chinese goes as zh-Hans or zh-Hant (zh-TW as zh-Hant).Same as the source.
Anthropic, OpenAI, Gemini, OpenCode, openai-compatibleAs written.Same as the source.

An LLM also gets the language, script and region names. DeepL has no generic English or Portuguese target, so a target ending up as bare en or pt (such as en-AU) is refused before the run: map it to EN-GB, EN-US, PT-BR or PT-PT.

localeMap overrides the code per locale:

verbatra.config.ts
provider: {
  id: "deepl",
  options: {
    localeMap: {
      "en-AU": "EN-GB",
      "fr-BE": "FR-FR",
    },
  },
}
  • Keys must be sourceLocale or a target locale as configured, or the config fails with CONFIG_INVALID. The map goes under options, for every provider except none.
  • Values are non-empty strings of at most 64 characters, sent unchanged: as the code to a machine-translation provider, in place of the code to an LLM ("pt-BR (Brazilian Portuguese)" works), whose language names still come from the key. A bare EN or PT for DeepL fails with INVALID_REQUEST; a value the provider rejects fails its sub-batch.
  • Effect on memory. Only new and changed strings go out with a new code. A non-empty map is part of the memory fingerprint, so changing it starts a separate bucket.

Checking a provider's language support

Every configured locale is judged before anything is spent:

ProviderJudged againstRefused before the runWarned about
DeepLIts shipped language table, with glossary and formality support per languageAn unlisted source or target languageA glossary for a pair outside the glossary languages, a tone without formality, a partly listed code
Google Cloud TranslationIts shipped language tableAn unlisted languageAny glossary, a formal or informal tone, a regional code listed only as a language
LibreTranslateNothing before a run; the server's /languages with doctor --liveNeverEvery target locale on every run, any glossary, a formal or informal tone
Anthropic, OpenAI, Gemini, OpenCode, openai-compatibleNothing: an LLM accepts any languageNeverA language outside the well-tested list
noneNot applicableNeverNever

An unsupported locale is refused with LOCALE_UNSUPPORTED_BY_PROVIDER (exit 2) before any provider is built; a warning never refuses and appears as a notice. verbatra doctor reports each verdict, and its --live flag checks the provider's current list, but translate always uses the shipped table. For a language added since, map the locale in localeMap: an explicit code is sent with a warning, not refused.

Structured errors

A provider failure surfaces as a structured, secret-free error affecting only its own sub-batch. A PROVIDER_ERROR names only the host and port of an openai-compatible or LibreTranslate baseUrl, and only translation.googleapis.com for Google, never the query carrying its key.

Output-token limits

The LLM providers cap each response's tokens; a model that stops at the cap raises OUTPUT_TRUNCATED. verbatra re-splits a truncated sub-batch into halves, down toward one entry, and withholds only an entry that still overflows. To fix it for good, lower maxBatchSize or raise maxTokens (Anthropic) or maxOutputTokens. A reasoning model spends hidden reasoning tokens from the same budget, so give it generous headroom.

Review flags

A suspicious translation from any provider is flagged for review, not rejected. See review reasons and Review in Studio.

Edit on GitHub

On this page