Docs / Concepts
Languages and locales
Which languages verbatra can translate, how locale codes reach each provider, and how plurals, scripts, regional variants and right-to-left text are handled.
verbatra has no language list of its own: it accepts any valid BCP 47 locale code, so you can use the languages your provider supports, and for a human-only project, all of them. verbatra spells each code the way the provider expects, holds every translation to its language's plural rules, and warns you before a run when a provider cannot serve a locale.
Locale codes
Every locale in your config must be a valid BCP 47 tag, such as fr, pt-BR, sr-Latn or
es-419; for files named pt_, write pt-BR and set files.localeStyle to posix. The
full rules are under Locale codes.
The code from your config is the one verbatra stores everywhere: file paths,
the lock file and the cache. A provider-specific spelling
exists only inside a request. Respelling a code you already translate, such as pt_ to pt-BR,
keeps its history; see A respelled locale code.
Coverage by provider
How well a language translates is the provider's matter; each row links to the vendor's own list.
| Provider | Languages | Code sent | Glossary | Tone |
|---|---|---|---|---|
anthropic | No fixed list. Claude multilingual support | As written, plus English language, script and region names | Locale terms in the prompt | In the prompt |
openai | No fixed list. Using the OpenAI API in different languages | As written, plus names | Locale terms in the prompt | In the prompt |
gemini | Gemini language support | As written, plus names | Locale terms in the prompt | In the prompt |
opencode | Whatever the Zen model you choose covers | As written, plus names | Locale terms in the prompt | In the prompt |
openai-compatible | Whatever the model you run covers | As written, plus names | Locale terms in the prompt | In the prompt |
deepl | DeepL supported languages | Normalized to DeepL's codes | A pre-created DeepL glossary by glossaryId only, for 32 languages | DeepL formality for some languages, not on a free-tier key |
google-translate | Cloud Translation language support (the NMT list applies to the Basic, v2 tier verbatra uses) | Normalized for Chinese and Norwegian, otherwise as written | None | None |
libretranslate | The models installed on your server; the upstream list shows what is available to install | The language alone, except pt-BR, zh-Hans and zh-Hant | None | None |
none | Not applicable: no provider is called | Nothing is sent | Not applicable | Not applicable |
What that means in practice:
- LLM providers have no closed list. They attempt any language you name, and quality falls off where training data is thin, so a lower-resource target deserves a human review. Review flags point at the most suspicious results.
- DeepL and Google Cloud Translation have a closed list, checked before a run.
- LibreTranslate serves whatever models your server installed.
- A
toneor glossary a provider cannot honor is reported as a notice; see Tone and glossary across providers.
How each provider receives the code
DeepL and Google Cloud Translation get their own spelling of some codes, and DeepL refuses a bare
en or pt target before any request. LLM providers get the code plus the
English names of the language, script
and region. localeMap in the provider's options overrides the code per locale; the full mapping
is under Locale codes sent to the provider.
Checking support before a run
Before anything is spent, verbatra judges every configured locale against the provider, and
verbatra doctor --locales prints the verdicts:
- supported: the code verbatra sends is on the provider's table, or the provider is an LLM.
- unverified: only its base language is listed, the code comes from an explicit
localeMapentry, or the provider is a LibreTranslate server whose models are known only from its live list. It is translated, with the warningLOCALE_.UNVERIFIED_ BY_ PROVIDER - unsupported: neither the code nor its base language is listed.
translate,watchand a single-key retranslation refuse the whole run withLOCALE_.UNSUPPORTED_ BY_ PROVIDER
Leave the locale out with --locales, or, if the provider added the language later, map it in
localeMap: an explicit entry is trusted. The shipped tables are copies of the vendors' lists
(DeepL with 125 codes and Google with 198, both of 2026-09-28); verbatra doctor --live checks the
current list.
Nothing is refused for an LLM provider. A target outside the languages LLMs are known to translate
well gets the warning LOCALE_, a nudge to review its output. The list matches the
base language, so pt-BR counts as Portuguese: Arabic (ar),
Chinese (zh), Dutch (nl), English (en), French (fr), German (de), Hindi (hi),
Indonesian (id), Italian (it), Japanese (ja), Korean (ko), Polish (pl), Portuguese (pt),
Russian (ru), Spanish (es), Swedish (sv), Turkish (tr), Ukrainian (uk) and Vietnamese
(vi).
Scripts and regional variants
A script or region subtag changes what the provider is asked for, not how verbatra stores anything:
| Your locale | DeepL | Google Cloud Translation | LibreTranslate | LLM providers |
|---|---|---|---|---|
pt-BR / pt-PT | PT-BR / PT-PT | pt-BR / pt-PT | pt-BR / pt | Portuguese (Brazil) / Portuguese (Portugal) |
es-419, es-MX | ES-419 | es-419, es-MX | es | Spanish (Latin America), Spanish (Mexico) |
zh-Hans / zh-Hant | ZH-HANS / ZH-HANT | zh-CN / zh-TW | zh-Hans / zh-Hant | Chinese (Simplified) / Chinese (Traditional) |
sr-Latn / sr-Cyrl | SR for both | sr-Latn / sr-Cyrl | sr for both | Serbian (Latin) / Serbian (Cyrillic) |
DeepL and LibreTranslate keep a script only for Chinese, so sr-Latn and sr-Cyrl get whatever
script the engine writes for Serbian; an LLM provider, told the script by name, is the dependable
choice. The LENGTH_ flag weights characters by script, so compact Chinese, Japanese
or Korean text is not flagged as short, and a glossary term in a script without spaces between
words is matched by containment.
Plurals
verbatra reads the plural categories a language needs, ordinals included, from the CLDR rules in
Node.js's ICU data: English has two cardinal categories, Russian and Polish four, Arabic and Welsh
six, Japanese only other. For a locale Node.js has no rules for, the run notice assumes one and
other, while the integrity gate, check and doctor require only other; the plural-rules
check of verbatra doctor names such a locale.
How a plural is kept correct depends on the format:
- i18next keys each form with a CLDR suffix (
items_,one items_). When the source lacks a category the target needs, the run reportsfew PLURAL_; withCATEGORIES_ INCOMPLETE generatePlurals: trueand an LLM provider, verbatra writes the missing forms. See Add a language. - next-intl and ARB keep a whole ICU plural in one value, and a value whose arms do not fit the target language is refused; see Plural arms per target language.
- Apple, Android and gettext round-trip exactly the forms each locale has, and a missing form has
to be added by hand. gettext keeps its
Plural-Formsheader as written.
Plural warnings
verbatra check and verbatra doctor warn about every plural
that lacks a category its language uses, in each format listed above except gettext, whose forms
follow its own header; plurals in other formats are not checked. DeepL, Google Cloud Translation and
LibreTranslate translate existing forms but never generate new ones.
Right-to-left languages
Arabic, Hebrew, Persian, Urdu and other right-to-left languages need no special handling: a value is read, sent, checked and written back as it is, with no direction marks added, removed or reordered. What that means for you:
- Set direction in your app, not in the strings, with the
dirattribute, CSS or your platform's layout direction. The inline markup check refuses a translation that adds adirattribute the source does not have. - Direction marks are ordinary text and may be dropped; check the translations that rely on
one. Unclosed embeddings, overrides or isolates (when the source closes its own) or added
overrides raise
BIDI_.CONTROLS_ CHANGED - A changed placeholder order only raises
INTEGRITY_, because another order is often correct in another language.REORDERED - Studio shows values right to left for every locale whose script runs that way, by the
browser's direction or the locale's likely script (
ku-Arabright to left,kuleft to right). Only the display changes. verbatra pseudo --mode bidirenders your source right to left in anar-XBpseudolocale, exposing left-to-right layout assumptions before any translation exists. Mirrored images and icons are yours to check.
Low-resource languages and local models
For a language your hosted provider covers poorly:
- Run a local model through
openai-compatible. It has to be an instruction-tuned chat model that returns one JSON object; see Choosing a local model. - Use a dedicated translation model outside verbatra and bring the result back through the
workbook round-trip, which runs the same integrity checks. None of
these plugs into
openai-compatible, since they are not chat models: MADLAD-400 (Apache-2.0, over 400 languages), TranslateGemma (55 languages, Gemma terms of use), and NLLB-200 (about 200 languages, but CC-BY-NC-4.0, so not for a commercial product). Check each model's license yourself. - Have a person translate. The human-only workflow works for any language.