Skip to content

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.

Available from 0.12.0

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_BR.json, 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_BR 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.

ProviderLanguagesCode sentGlossaryTone
anthropicNo fixed list. Claude multilingual supportAs written, plus English language, script and region namesLocale terms in the promptIn the prompt
openaiNo fixed list. Using the OpenAI API in different languagesAs written, plus namesLocale terms in the promptIn the prompt
geminiGemini language supportAs written, plus namesLocale terms in the promptIn the prompt
opencodeWhatever the Zen model you choose coversAs written, plus namesLocale terms in the promptIn the prompt
openai-compatibleWhatever the model you run coversAs written, plus namesLocale terms in the promptIn the prompt
deeplDeepL supported languagesNormalized to DeepL's codesA pre-created DeepL glossary by glossaryId only, for 32 languagesDeepL formality for some languages, not on a free-tier key
google-translateCloud Translation language support (the NMT list applies to the Basic, v2 tier verbatra uses)Normalized for Chinese and Norwegian, otherwise as writtenNoneNone
libretranslateThe models installed on your server; the upstream list shows what is available to installThe language alone, except pt-BR, zh-Hans and zh-HantNoneNone
noneNot applicable: no provider is calledNothing is sentNot applicableNot 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 tone or 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 localeMap entry, or the provider is a LibreTranslate server whose models are known only from its live list. It is translated, with the warning LOCALE_UNVERIFIED_BY_PROVIDER.
  • unsupported: neither the code nor its base language is listed. translate, watch and a single-key retranslation refuse the whole run with LOCALE_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_NOT_WELL_TESTED, 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 localeDeepLGoogle Cloud TranslationLibreTranslateLLM providers
pt-BR / pt-PTPT-BR / PT-PTpt-BR / pt-PTpt-BR / ptPortuguese (Brazil) / Portuguese (Portugal)
es-419, es-MXES-419es-419, es-MXesSpanish (Latin America), Spanish (Mexico)
zh-Hans / zh-HantZH-HANS / ZH-HANTzh-CN / zh-TWzh-Hans / zh-HantChinese (Simplified) / Chinese (Traditional)
sr-Latn / sr-CyrlSR for bothsr-Latn / sr-Cyrlsr for bothSerbian (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_RATIO_OUTLIER 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_few). When the source lacks a category the target needs, the run reports PLURAL_CATEGORIES_INCOMPLETE; with generatePlurals: true and 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-Forms header 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 dir attribute, CSS or your platform's layout direction. The inline markup check refuses a translation that adds a dir attribute 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_REORDERED, because another order is often correct in another language.
  • 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-Arab right to left, ku left to right). Only the display changes.
  • verbatra pseudo --mode bidi renders your source right to left in an ar-XB pseudolocale, 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.
Edit on GitHub

On this page