Configuration reference
sourceLocale
BCP-47 code for the source language (e.g. "en-GB", "en", "pt-BR"). No translation file is generated for this locale — the key string itself is the source text.
Must match SOURCE_LOCALE exported from your runtime i18n setup file (src/i18n.ts / src/i18n.js).
targetLocales
Array of BCP-47 locale codes to translate to (e.g. ["de", "fr", "es", "pt-BR"]).
targetLocales is the primary locale list for UI translation and the default locale list for documentation blocks. Use generate-ui-languages to build the ui-languages.json manifest from sourceLocale + targetLocales.
uiLanguage (optional)
BCP-47 code for the tool's own UI language (CLI help, logs/summaries, and the Translation Dashboard). It is independent of sourceLocale / targetLocales, and is overridden by the -L / --ui-lang flag and the AI_I18N_LANG environment variable. Unknown values degrade gracefully to the source locale (en-GB) — there is no strict validation. See Tool UI language.
languagesManifestPath (optional)
Root-level optional string (not nested under ui). Path where extract and generate-ui-languages write the ui-languages.json manifest, and where the CLI reads it for display names and language-list post-processing. When omitted, defaults to ui.flatOutputDir/ui-languages.json at config load.
Use this when:
- The manifest should live outside
ui.flatOutputDir(for example next to app helpers undersrc/i18n/). - You want language switcher post-processing (
languageListBlock) to build locale labels from the project manifest rather than only the bundled master catalog.
includeUiLanguageEnglishNames does not read this file — it uses the bundled master catalog (see ui.uiExtractor below).
Legacy: root-level uiLanguagesPath is still accepted when loading a config file and is rewritten automatically to languagesManifestPath.
concurrency (optional)
Maximum target locales translated at the same time (translate-ui, translate-docs, translate-svg, and the matching steps inside sync). If omitted, the CLI uses 4 for UI translation and 3 for documentation translation (built-in defaults). Override per run with -j / --concurrency.
batchConcurrency (optional)
translate-docs, translate-svg, and translate-json (and the matching steps inside sync): maximum parallel LLM batch requests per file (each batch can contain many segments). Default 4 when omitted. Ignored by translate-ui. Override with -b / --batch-concurrency.
fileConcurrency (optional)
Maximum number of files processed concurrently within a single locale during translate-docs and sync. When set to a value greater than 1, files within the same locale are processed in parallel using a semaphore to control memory usage. Default 1 (sequential processing) when omitted. Higher values can significantly improve throughput for I/O-bound operations, especially when all segments are already cached (no API calls needed).
Example:
{
"fileConcurrency": 4
}Use case: Set this to 2-4 when running sync --force-update with 100% cache hits to reduce total processing time. The improvement is most noticeable with many small files.
batchSize / maxBatchChars (optional)
Segment batching for translate-docs, translate-svg, and translate-json: how many segments per API request, and a character ceiling. Defaults: 20 segments, 4096 characters (when omitted).
provider and providers
provider (top-level, optional) selects the active provider key from providers. It is optional when exactly one provider is configured; required when more than one is configured.
providers (top-level) maps a provider key to its block. Built-in keys (see the preset table below) need only translationModels; any other key defines a custom OpenAI-compatible endpoint and requires baseUrl (plus apiKeyEnv unless the endpoint needs no key).
Each providers.<name> block accepts:
translationModelsPreferred ordered list of model IDs (plain upstream ids, noprovider/prefix; OpenRouter ids keep their nativevendor/modelform). The first is tried first; later entries are fallbacks on error. This is the global default chain for every pipeline when no more specific tier applies.uiModels(optional) Ordered UI-only model list fortranslate-ui, plural generation (Step 0 and Pass B), andproofread-ui. Tried after any matchinglocaleModelsentry for the target locale, beforetranslationModels.localeModels(optional) Per-locale overrides for all translation pipelines. Array of{ "locale": "<BCP-47>", "models": ["…"] }objects. Locale tags are matched case-insensitively (pt-br=pt-BR). Each locale's list is tried first for that locale only, then pipeline-specific tiers (uiModelsfor UI) andtranslationModels. Duplicate normalized locale keys are rejected at config load.baseUrlOpenAI-compatible base URL. Overrides the preset base URL; required for a non-preset provider.apiKeyEnvEnvironment variable holding the API key. Overrides the preset env var.headersExtra HTTP headers sent with every request to this provider.maxTokensMax completion tokens per request. Default:8192.temperatureSampling temperature. Default:0.2.requestTimeoutMsMaximum time in milliseconds to wait for each request. Default:30000(30 seconds).
Built-in provider presets (key — base URL — API-key env var):
| Provider | Base URL | API-key env var |
|---|---|---|
openrouter | https://openrouter.ai/api/v1 | OPENROUTER_API_KEY |
openai | https://api.openai.com/v1 | OPENAI_API_KEY |
anthropic | https://api.anthropic.com/v1 | ANTHROPIC_API_KEY |
gemini | https://generativelanguage.googleapis.com/v1beta/openai | GOOGLE_API_KEY |
deepseek | https://api.deepseek.com | DEEPSEEK_API_KEY |
cerebras | https://api.cerebras.ai/v1 | CEREBRAS_API_KEY |
groq | https://api.groq.com/openai/v1 | GROQ_API_KEY |
mistral | https://api.mistral.ai/v1 | MISTRAL_API_KEY |
xai | https://api.x.ai/v1 | XAI_API_KEY |
nvidia | https://integrate.api.nvidia.com/v1 | NVIDIA_API_KEY |
alibaba | https://dashscope-intl.aliyuncs.com/compatible-mode/v1 | ALIBABA_API_KEY |
apifun | https://api.apikey.fun/v1 | APIFUN_API_KEY |
ollama | http://localhost:11434/v1 | (none) |
A legacy top-level openrouter block (with baseUrl, translationModels, defaultModel, fallbackModel, maxTokens, temperature, requestTimeoutMs) is still accepted and is auto-migrated to providers.openrouter (with provider: "openrouter") on load; defaultModel / fallbackModel fold into translationModels.
For a runnable example that configures several providers in one config and switches between them with -P, see examples/multi-provider (openai, anthropic, nvidia, and deepseek on the same document).
Why use multiple models: Different providers and models have varying costs and offer different levels of quality across languages and locales. Configure translationModels as an ordered fallback chain (rather than a single model) so the CLI can attempt the next model if a request fails.
Treat the list below as a baseline that you can expand: if translation for a specific locale is poor or unsuccessful, research which models support that language or script effectively (refer to online resources or your provider’s documentation), and add those model ids as further alternatives.
These model ids match ai-i18n-tools init [-P <provider>] when -P openrouter (the default). Other presets get native model ids from init -P <provider> — see Built-in providers.
This list was tested for broad locale coverage across a large documentation project with 36 target locales; it serves as a practical default, but is not guaranteed to perform well for every locale.
Example translationModels (same defaults as ai-i18n-tools init [-P <provider>]):
Default translationModels fallback list
"translationModels": [
"google/gemini-2.5-flash",
"meta-llama/llama-3.3-70b-instruct",
"openai/gpt-4o-mini",
"google/gemma-4-26b-a4b-it",
"~anthropic/claude-haiku-latest",
"z-ai/glm-5.2",
"google/gemini-3.5-flash",
"~anthropic/claude-sonnet-latest"
// … add more fallback models as needed
]Recommended uiModels: UI strings are short but highly visible — a premium model often improves tone, plurals, and consistency. Optional uiModels is tried after any matching localeModels entry and before translationModels (see the field list above). Example:
Recommended uiModels for UI translation
"uiModels": [
"~anthropic/claude-sonnet-latest",
"z-ai/glm-5.2"
]Recommended localeModels for Asian languages: Japanese, Korean, and Chinese locales often benefit from models tuned for those scripts. Add per-locale overrides that are tried first (before uiModels / translationModels) when the target locale matches:
Recommended localeModels for ja, ko, zh-Hans, zh-Hant
"localeModels": [
{ "locale": "ja", "models": [ "z-ai/glm-5.2", "minimax/minimax-m2.7" ] },
{ "locale": "ko", "models": [ "z-ai/glm-5.2", "minimax/minimax-m2.7" ] },
{ "locale": "zh-Hans", "models": [ "z-ai/glm-5.2", "minimax/minimax-m2.7" ] },
{ "locale": "zh-Hant", "models": [ "z-ai/glm-5.2", "minimax/minimax-m2.7" ] }
]Set the active provider's API-key env var (see the preset table) in your environment or .env file.
Before changing model lists, run ai-i18n-tools check-models. For any provider it verifies each configured model id (translationModels, uiModels, and all localeModels entries) against that provider's live model list (GET /models), reports ids that are missing or past expiration_date, lists the valid models, and exits non-zero when any configured id is invalid. When the provider returns pricing (e.g. OpenRouter) it also shows estimated input/output pricing (USD per 1M tokens).
To compare the configured models on real translation work, run ai-i18n-tools bench-models. It benchmarks every unique model id from translationModels, uiModels, and localeModels by translating one sample through each in isolation (in parallel, bounded by concurrency) and prints per-model input/output tokens, wall-clock time, and USD cost, so you can weigh speed against price before settling on model lists.
features
| Field | Pipeline | Description |
|---|---|---|
translateUIStrings | 1 | Extract t("…") / i18n.t("…") into strings.json, then translate entries and write per-locale flat JSON (extract runs automatically; use standalone extract to refresh the catalog only). |
translateDocs | 2 | Translate .md / .mdx / .astro pages; Docusaurus shell JSON when docs[].docusaurusCatalogDir is set; Nextra _meta / dictionary when configured; VitePress theme when docsOutput.vitepressThemeCatalog is set; Fumadocs meta.json / UI catalog when docsOutput.style is "fumadocs". |
translateJson | 3 | Arbitrary nested JSON under json[] (translate-json). |
translateSVG | — | Translate .svg files (requires the top-level svg block). |
Translate SVG files with translate-svg when features.translateSVG is true and a top-level svg block is configured. The sync command runs that step when both are set (unless --no-svg).
ui
sourceRoots
Directories or glob patterns (relative to cwd) scanned fort("…")calls. Supports patterns likesrc/or["src/**/*.ts"].stringsJson
Path to the master catalog file. Updated byextract.flatOutputDir
Directory where per-locale JSON files are written (de.json, etc.).uiExtractor.funcNames(or legacyreactExtractor.funcNames)
Additional function names to scan (default:["t", "i18n.t"]).uiExtractor.extensions(or legacyreactExtractor.extensions)
File extensions to include (default:[".js", ".jsx", ".ts", ".tsx"]). Add.astrofor Astro frontmatter and template expressions.uiExtractor.includePackageDescription(or legacyreactExtractor.includePackageDescription)
Whentrue(default),extractalso includespackage.jsondescriptionas a UI string when present.uiExtractor.packageJsonPath(or legacyreactExtractor.packageJsonPath)
Custom path to thepackage.jsonfile used for that optional description extraction.uiExtractor.includeUiLanguageEnglishNames(or legacyreactExtractor.includeUiLanguageEnglishNames)When
true(defaultfalse),extractalso adds eachenglishNamefrom the bundled ui-languages master catalog (built fromsourceLocale+targetLocales) tostrings.jsonwhen not already present from the source scan (same hash keys). Does not readlanguagesManifestPath.
cacheDir
cacheDirSQLite cache directory (shared by alldocsblocks). Default.translation-cache. Reuse across runs. If you are migrating from a custom doc translation cache, archive or delete it —cacheDircreates its own SQLite database and is not compatible with other schemas.
Best practice for git exclusions:
- Exclude the contents of the translation cache folder (for example, using
.gitignoreor.git/info/exclude) to prevent committing temporary cache artefacts. - Retain
cache.db(do not routinely delete it), as preserving the SQLite cache prevents re-translating unchanged segments. This saves both runtime and API costs when updating or modifying software that usesai-i18n-tools. - Exclude temporary and log files to avoid committing backup and debug-related files.
Example:
# Translation cache directory
.translation-cache/*
# Keep SQLite cache for reuse
!.translation-cache/cache.db
# Temporary and log files
*.tmp
*.logdocs
Array of documentation pipeline blocks. translate-docs and the docs phase of sync process each block in order. Legacy keys are still accepted at load time and rewritten when the config file is writable; prefer current names in new configs.
| Legacy key | Current key / behaviour |
|---|---|
documentations | docs |
markdownOutput | docs[].docsOutput |
jsonSource | docs[].docusaurusCatalogDir |
top-level openrouter | providers.openrouter + provider: "openrouter" |
features.translateMarkdown | features.translateDocs |
features.translateJSON | removed (use docs[].docusaurusCatalogDir or json[]) |
features.extractUIStrings | removed (extract runs before UI translation) |
glossary.uiGlossaryFromStringsJson | glossary.uiGlossary |
ui.reactExtractor | ui.uiExtractor (alias still accepted) |
svg.svgExtractor.forceLowercase | svg.forceLowercase |
Content sources
descriptionOptional human-readable note for this block (not used for translation). Prefixed in thetranslate-docs🌐headline when set; also shown instatussection headers.contentPathsMarkdown/MDX page bodies and.astrotemplates to translate (translate-docsscans these for.md,.mdx, and.astro). Supports directory paths or glob patterns (e.g."docs/**/*.md","guides/*.mdx","src/pages/index.astro"). That is where localised documentation prose comes from.sourceFilesOptional alias merged intocontentPathsat load.targetLocalesOptional subset of locales for this block only (otherwise roottargetLocales). Effective documentation locales are the union across blocks.docusaurusCatalogDirOptional. Source directory for Docusaurus JSON label catalogs for this block (e.g."i18n/en"fromdocusaurus write-translations). Page bodies always come fromcontentPaths;docusaurusCatalogDironly supplies shell/UI JSON, not MDX.nextraMetaGlobOptional glob(s) for Nextra_meta.ts/_meta.tsx/_meta.jsunderdocsRoot. WhendocsOutput.styleis"nextra"and this is omitted, all_metafiles underdocsRootare collected automatically.nextraMetaTranslatableKeysOptional property names whose string values are translated in Nextra_metaobjects (default:title,display,breadcrumb).nextraDictionaryPathOptional English Nextra theme dictionary module (e.g."app/_dictionaries/en.ts"). Translated to{dir}/{locale}.tsduringtranslate-docs.nextraDictionaryOutputTemplateOptional output template for locale dictionary modules (default:{dir}/{locale}.tsrelative to the dictionary directory).
Output layout
outputDirRoot directory for translated output for this block.docsOutput.style"nested"(default),"flat","doc-system", or aliases"docusaurus"/"astro-starlight"/"vitepress"/"nextra".docsOutput.localeSubpathPath segment between{locale}/and{relativeToDocsRoot}fordoc-system(required when usingstyle: "doc-system"directly; preset when using an alias). Use""for Starlight-style locale folders.docsOutput.docsRootSource docs root for Docusaurus layout (e.g."docs"). Default"docs"when omitted.docsOutput.pathTemplateCustom markdown output path. Placeholders:"{outputDir}","{locale}","{LOCALE}","{llocale}","{relPath}","{stem}","{basename}","{extension}","{docsRoot}","{relativeToDocsRoot}".docsOutput.jsonPathTemplateCustom JSON output path for label files. Supports the same placeholders aspathTemplate.docsOutput.localePathLowercaseWhentrue, built-in output layouts (nested,flat,doc-systemwithoutpathTemplate) use lowercased locale segments in paths. Defaultfalse;astro-starlightanddoc-systemwith emptylocaleSubpathdefault totrueat config load.docsOutput.flatPreserveRelativeDirWhendocsOutput.style = "flat", keep source subdirectories so files with the same basename do not collide. Defaultfalse.docsOutput.rewriteRelativeLinksRewrite relative links after translation (auto-enabled whendocsOutput.style = "flat"and no custompathTemplate).docsOutput.linkRewriteDocsRootRepo root used when computing flat-link rewrite prefixes. Usually leave this as"."unless your translated docs live under a different project root.docsOutput.rewriteVitepressLinksWhentrue, run the VitePress link normalizer after translation. Defaults to enabled whendocsOutput.styleis"vitepress". Use with anydoc-systemlayout where locale folders sit beside English underdocsRoot. Rewrites README-styledocs/guide/…paths to site routes (/guide/…) and locale-relative../guide/…links. For links to repo files outside the VitePress tree (LICENSE,examples/), use full URLs in English source — see VitePress integration — README as the docs homepage.docsOutput.rewriteNextraLinksWhentrue, run the Nextra link normalizer after translation. Defaults to enabled whendocsOutput.styleis"nextra". Rewritescontent/en/…and relative.mdxpaths to locale-neutral site routes (/guide/…) for Next.jsi18n. See Nextra integration — Link conventions.docsOutput.fumadocsParser"dot"(default) or"dir". Dot writesstem.{locale}.mdxbeside English sources; dir writes locale folders like Nextra. See Fumadocs integration — Page layout.docsOutput.rewriteFumadocsLinksWhentrue, run the Fumadocs link normalizer after translation. Defaults to enabled whendocsOutput.styleis"fumadocs". Rewrites content paths and relative.mdxlinks to/docs/…routes.docsOutput.fumadocsUiCatalogOptional. Fumadocs UI override catalog bootstrap + translation insidetranslate-docs. Fields:sourcePath(e.g.lib/layout.shared.ts),catalogPath(generated English JSON), optionaloutputPathTemplate(default:ui.{locale}.jsonbesidecatalogPath).docs[].fumadocsMetaGlobOptional glob(s) formeta.jsoncollection whendocsOutput.styleis"fumadocs". Default: recursivemeta.jsonunderdocsOutput.docsRoot.docs[].fumadocsMetaTranslatableKeysProperty names whose string values are translated in Fumadocsmeta.json(default:title,description).docsOutput.vitepressThemeCatalogOptional. VitePress theme/nav/sidebar catalog bootstrap + translation insidetranslate-docs. Fields:configPath(VitePress config with theme strings),catalogPath(generated English nested JSON), optionaloutputPathTemplate(default:theme.{locale}.jsonbesidecatalogPath).
Post-processing
docsOutput.postProcessingOptional transforms on the translated markdown body (YAML keys and non-prose front matter values are preserved). Runs after segment reassembly and link rewriting (flat or VitePress), and beforeaddFrontmatter.docsOutput.postProcessing.regexAdjustmentsOrdered list of{ "description"?, "search", "replace" }.searchis a regex pattern (plain string uses flagg, or/pattern/flags).replacesupports placeholders such as${translatedLocale},${sourceLocale},${sourceFullPath},${translatedFullPath},${sourceFilename},${translatedFilename},${sourceBasedir},${translatedBasedir}.docsOutput.postProcessing.languageListBlock{ "start", "end", "separator", "label"? }— regenerates a bounded "read in other languages" link row in source and translated markdown. RequireslanguagesManifestPath(or a manifest atui.flatOutputDir/ui-languages.json) for endonym labels whenlabel: "local".
Behaviour and metadata
translateFrontmatterFieldsSame level asdocsOutput(perdocs[]block). Defaulttrue: translate user-facing YAML prose for Starlight/Docusaurus (title,description,sidebar.label,sidebar_label,keywords,hero.title,hero.tagline,hero.image.alt,hero.actions[].text,pagination_label,prev/nextlabels). Setfalseto keep the entire front matter block unchanged; pass a string array to restrict to specific dot-paths.segmentSplittingSame level asdocsOutput(perdocs[]block). Optional finer-grained segments fortranslate-docsextraction:{ "enabled", "maxCharsPerSegment"?, "splitPipeTables"?, "splitDenseParagraphs"?, "maxLinesPerParagraphChunk"?, "splitLongLists"?, "maxListItemsPerChunk"?, "qualityRetrySplit"?, "maxQualityRetrySplitDepth"? }. Whenenabledistrue(default whensegmentSplittingis omitted), dense paragraphs, GFM pipe tables (first chunk includes header, separator, and first data row), and long lists are split; sub-parts rejoin with single newlines (tightJoinPrevious). Set"enabled": falseto use one segment per blank-line-delimited body block only. WhenqualityRetrySplitistrue(default), markdown segments that fail AST validation after all models are exhausted are split progressively and retried from the first model;maxQualityRetrySplitDepth(default3) caps recursive splits.warnMarkdownSourceIssuesWhentrue(default when omitted), eachtranslate-docsrun rescans markdown segments for risky delimiters / unclosed inline code, prints terminal warnings, and replacesmarkdown_source_issuesrows for that file's cache filepath. Setfalseto skip warnings and SQLite updates for this block.addFrontmatterWhentrue(default when omitted), translated markdown files include YAML keys:translation_last_updated,source_file_mtime,source_file_hash,translation_language,source_file_path, and when at least one segment has model metadata,translation_models(sorted list of model ids from the active provider). Set tofalseto skip.emphasisPlaceholdersPerdocs[]block. Whentrue, mask markdown emphasis delimiters as placeholders before translation. Defaults totruefor CJK locales (zh,ja,ko) and for locales listed inrtlLocales; otherwise defaults tofalse. Overridable via CLI--emphasis-placeholders/--no-emphasis-placeholders.rtlLocalesOptional array of BCP-47 codes treated as RTL for emphasis-placeholder defaults (merged with built-in RTL detection).
protectAttributesOptional. Extra JSX/HTML attribute names whose quoted string values must not be sent to the translator. Merged with built-in defaults (class,id,style,src,href,type,data-*, mostaria-*, etc.). Case-insensitive. Applies to:.astroparse-and-replace extraction (static HTML tags and string literals afterattr=inside{expression}blocks).- MDX placeholder extraction during markdown/Astro segment translation (
label,tooltip, andaria-labelon capitalised JSX tags, plusTabItemvaluewhen applicable).
Example:
"protectAttributes": ["variant", "size"]keepsvariant="primary"inside{items.map(...)}unchanged across locales.You can also list normally translatable attributes (for example
"title"or"aria-label") when you want those values copied verbatim from English.protectKeysOptional. Extra object property names whose quoted string values must not be translated inside template{expression}blocks and MDX object literals (for examplelabel:inside<Tabs values={[ … ]}>). Merged with built-in defaults (class,key,id,href,src, etc.). Case-insensitive.Example:
"protectKeys": ["slug", "code"]skips{ slug: 'getting-started', title: 'Getting started' }→ onlytitleis translated whenslugis protected.
Example (docsOutput.style = "flat" — screenshot paths + optional language list wrapper):
Flat layout postProcessing example (screenshots + languageListBlock)
"docsOutput": {
"style": "flat",
"postProcessing": {
"regexAdjustments": [
{
"description": "Per-locale screenshot folders",
"search": "images/screenshots/[^/]+/",
"replace": "images/screenshots/${translatedLocale}/"
}
],
"languageListBlock": {
"start": "<small id=\"lang-list\">",
"end": "</small>",
"separator": " · ",
"label": "local"
}
}
}json
Top-level array of nested JSON translation pipelines. Used only when features.translateJson is true (translate-json or the JSON stage of sync). See JSON.
| Field | Description |
|---|---|
description | Optional note for CLI / status (not translated). |
contentPaths | Source .json files, directories, or globs under the project root. |
outputPathTemplate | Required output path per target locale. Placeholders: {locale}, {LOCALE}, {llocale}, {stem}, {basename}, {extension}, {relativeToSourceRoot}. |
targetLocales | Optional subset for this block; otherwise root targetLocales. |
keyPolicy.mode | allowlist, denylist, or both. |
keyPolicy.translateKeys | Dot paths / globs to include when mode is allowlist or both. |
keyPolicy.skipKeys | Dot paths / globs to exclude (default denylist includes id, slug, href, url, key, code). |
svg
Top-level paths and layout for SVG files. Translation runs only when features.translateSVG is true (via translate-svg or the SVG stage of sync).
| Field | Description |
|---|---|
sourcePath | One or more directories or glob patterns (e.g. "images/*.svg", "**/icons/*.svg"). The patterns are resolved relative to the project root and scanned recursively for .svg files. |
outputDir | Root directory for translated SVG output. |
style | "flat" or "nested" when pathTemplate is unset. |
pathTemplate | Custom SVG output path. Placeholders: "{outputDir}", "{locale}", "{LOCALE}", "{llocale}", "{relPath}", "{stem}", "{basename}", "{extension}", "{relativeToSourceRoot}". |
localePathLowercase | When true, built-in flat / nested SVG layouts use lowercased locale segments. Custom pathTemplate values are unchanged; use {llocale} for lowercase segments. |
forceLowercase | Lower-case translated text on SVG reassembly. Useful for designs that rely on all-lowercase labels. |
glossary
| Field | Description |
|---|---|
uiGlossary | Path to strings.json - auto-builds a glossary from existing translations. |
userGlossary | Path to a CSV with columns Original language string (or en), locale, Translation - one row per source term and target locale (locale may be * for all targets). |
autoAddUserEditedToGlossary | When true, dashboard edits to UI strings can be appended to the user glossary automatically. |
Generate an empty glossary CSV:
ai-i18n-tools glossary-generate