|
Documentation
Book a DemoPlatform
PlatformMCPCLIAPIWorkflows
Guides
Changelog

Localization

  • Overview
  • Translation API
  • Web App Localization
  • Mobile App Localization
  • iOS with String Catalogs
  • Android with strings.xml
  • Emails Localization
  • Static Content (e.g. .md, .json)
  • Next.js with Markdoc
  • Rails with i18n

Workflows

  • Engine Setup with MCP
  • Jira Triage
  • CI/CD

Static Content Localization

The Lingo.dev CLI translates static files in your repository - Markdown, MDX, Markdoc, JSON, YAML, subtitles, and more - through a configured localization engine. Point it at your content, run once, and get translated files alongside your source.

Supported Content Types#

The CLI detects each file's format from its extension - there is no bucket type to configure. Locale lives in the path (content/en/x.md becomes content/de/x.md), so no [locale] placeholder is needed.

Content typeFormatExample path
DocumentationMarkdowndocs/en/getting-started.md
DocumentationMDXdocs/en/getting-started.mdx
DocumentationMarkdocdocs/en/getting-started.mdoc
Structured dataJSONdata/en.json
Structured dataYAMLdata/en.yaml
Blog postsMarkdown / MDXblog/en/post-slug.md
LocalizationGettext POlocale/en/messages.po
LocalizationXLIFFlocale/en.xliff
SubtitlesSRTsubs/en/intro.srt

See the formats reference for the full list of supported file types.

Not yet supported in the new CLI

CSV (csv-per-locale), VTT subtitles, plain-text .txt, Java .properties, and locale-root-key YAML are not supported by the new CLI yet. Keep those files on the legacy CLI for now and follow the changelog for updates.

Prerequisites#

Every run sends content through a localization engine - the configuration that determines which LLM model, glossary, brand voice, and instructions apply. Create one in the Lingo.dev dashboard, then set up the CLI (Node 22+):

bash
npm install -g @lingo.dev/cli
lingo login
lingo init
lingo link

lingo init and lingo link create .lingo/config.json, wiring the CLI to your organization and engine. Commit this file so every environment shares the same configuration.

json
{
  "orgId": "org_abc123",
  "engineId": "eng_abc123",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de", "ja"],
  "files": [{ "pattern": "docs/en/getting-started.md" }]
}

In CI, skip lingo login and provide LINGO_API_KEY as an environment variable instead. Generate one from API keys.

Documentation Sites#

Most documentation frameworks organize translated content in per-locale directories. Add a pattern per source file (or a glob) to files. The CLI preserves frontmatter, code blocks, and component syntax while translating Markdown, MDX, and Markdoc.

json
{
  "orgId": "org_abc123",
  "engineId": "eng_abc123",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de", "ja"],
  "files": [
    { "pattern": "docs/en/getting-started.md" },
    { "pattern": "docs/en/setup.mdx" }
  ]
}

Run the first translation, backfilling every target locale:

bash
lingo push --backfill-missing

On later runs, lingo push translates only what changed. Use lingo pull to fetch translations produced elsewhere.

Adjust the source path to match your framework's directory convention:

FrameworkLocale directory conventionReference
Docusaurusi18n/[locale]/docusaurus-plugin-content-docs/current/Docusaurus i18n guide
NextraPer-locale pages or JSON dictionariesNextra documentation
Hugocontent/[locale]/Hugo multilingual guide
Astrosrc/content/[locale]/ or JSON dictionariesAstro i18n guide
VitePress[locale]/ directory prefixVitePress i18n
MkDocsPer-locale docs/ with i18n pluginMkDocs i18n plugin

MDX components

MDX translation preserves JSX component syntax. Custom components like <Callout>, <Tabs>, and <CodeBlock> pass through unchanged - only the text content inside them is translated.

Structured Data#

JSON and YAML files translate automatically from their extension. Use key controls to prevent non-translatable values (IDs, URLs, configuration flags) from being modified.

json
{
  "orgId": "org_abc123",
  "engineId": "eng_abc123",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de", "ja"],
  "files": [
    { "pattern": "content/en.json" },
    { "pattern": "data/en.yaml" }
  ]
}

Generic YAML needs no format field. Only yaml-openapi and android require an explicit "format" on the file entry.

Locale-root-key YAML not supported yet

YAML files that use the locale code as the root key (common in Rails and Hugo) are not supported by the new CLI yet. Keep those on the legacy CLI and follow the changelog for updates.

Subtitles#

SRT subtitle files translate from their extension. The CLI preserves all timing data, cue indices, and formatting tags - only the text content is translated.

json
{
  "orgId": "org_abc123",
  "engineId": "eng_abc123",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de", "ja"],
  "files": [{ "pattern": "subs/en/intro.srt" }]
}

VTT not supported yet

WebVTT (.vtt) subtitles are not supported by the new CLI yet. Keep VTT files on the legacy CLI and follow the changelog for updates.

Working with Large Content#

Static content repositories can contain thousands of files. The CLI handles this efficiently:

MechanismHow it helps
Run state.lingo/lock.json tracks fingerprints of source content, so lingo push only translates new or modified files. Commit it; it is regenerated on each push.
Server-side parallelismThe engine parallelizes translation for you - there is no concurrency flag to tune.
Targeted runsScope a run to specific files with a glob: lingo push "docs/en/**".

To verify translations are up to date without writing files - useful as a CI gate - run lingo check.

Next Steps#

Supported Formats
Full reference for every file format the CLI can translate
Key Controls
Prevent specific values from being translated
GitHub App
Automate static content translation on every push
Run State
How incremental translation tracking works with .lingo/lock.json

Was this page helpful?

Max PrilutskiyMax Prilutskiy·Updated 4 days ago·4 min read