The CLI translates seventeen file formats. The format is inferred from the file extension; set format on a files[] entry to override it — or for the two formats that always need it (yaml-openapi, android).
| Format | Extensions | format value | Notes |
|---|---|---|---|
| JSON | .json | json | Key/value. Supports key controls. |
| JSONC | .jsonc | jsonc | JSON with comments. Comments survive and double as translator notes. |
| YAML | .yaml, .yml | yaml | Generic YAML. Every string value is translated; keys and structure preserved. |
| OpenAPI YAML | .yaml, .yml | yaml-openapi | OpenAPI specs. Set format explicitly — plain .yaml auto-detects as yaml. |
| Markdown | .md | md | Prose translated; frontmatter opt-in. |
| MDX | .mdx | mdx | Markdown + JSX. Component props opt-in. |
| Markdoc | .mdoc | markdoc | Markdown + tags. Frontmatter + tag attributes. |
| TypeScript | .ts, .mts, .cts | typescript | Locale modules (export default { … }). String literals translated; code preserved. |
| Gettext PO | .po | po | msgstr translated; msgid, comments, and headers preserved. |
| Flutter ARB | .arb | flutter | String values translated; @-metadata and {placeholders} preserved. |
| Android | .xml | android | strings.xml. Set format explicitly — .xml isn't auto-detected. |
| Xcode strings | .strings | xcode-strings | Values translated; keys preserved. |
| Xcode String Catalog | .xcstrings | xcode-xcstrings | One file holds every locale — targets are written back into the same file (see below). |
| Xcode stringsdict | .stringsdict | xcode-stringsdict | Plural strings translated; format control keys preserved. |
| XLIFF | .xlf, .xliff | xliff | <source> kept, <target> written per unit. Versions 1.2 and 2.0. |
| SubRip | .srt | srt | Subtitle text translated; indices and timecodes untouched. |
| PHP | .php | php | Laravel return [ … ]. String values translated; keys, numbers, and structure preserved. |
See it end to end
The demo project ships one file per format with the exact config each needs. Clone it with npx degit lingodotdev/lingo.dev/demo/new-cli my-lingo-demo and run a push.
npx degit lingodotdev/lingo.dev/demo/new-cli my-lingo-demo
cd my-lingo-demoJSON and JSONC#
Plain key/value translation. Every string value is translated unless a key control says otherwise.
{ "pattern": "content/en/app.json", "lockedKeys": ["meta.version"] }JSONC additionally preserves comments, which the engine reads as context — see translator notes.
{ "pattern": "content/en/settings.jsonc", "preservedKeys": ["featureFlags"] }Markdown, MDX, and Markdoc#
Body prose is translated by default. Frontmatter and embedded components are not translated unless you opt in.
Frontmatter#
List the frontmatter fields to translate with translateFrontmatterFields:
{
"pattern": "content/en/guide.md",
"translateFrontmatterFields": ["title", "description"]
}MDX component props#
For MDX, translate specific props on specific components with translateComponentProps:
{
"pattern": "content/en/landing.mdx",
"translateFrontmatterFields": ["title"],
"translateComponentProps": [{ "component": ["Hero", "Callout"], "props": ["title", "body"] }]
}This translates the title and body props on <Hero> and <Callout> and leaves all other props untouched.
Markdoc#
Markdoc works like Markdown, with frontmatter and tag attributes preserved:
{
"pattern": "content/en/changelog.mdoc",
"translateFrontmatterFields": ["title"]
}YAML#
Generic YAML is auto-detected from .yaml/.yml — every string value is translated, keys and structure preserved:
{ "pattern": "content/en/strings.yaml" }OpenAPI specs are a special case: they share the .yaml extension but need an explicit format so the engine translates only human-facing fields (summaries, descriptions) and leaves schema keys, paths, and operation IDs intact:
{ "pattern": "content/en/api.yaml", "format": "yaml-openapi" }App and framework formats#
PO, Flutter ARB, Xcode .strings, Xcode .stringsdict, TypeScript locale modules, XLIFF, SubRip, and PHP all follow the same rule: translatable text is translated, everything structural is preserved (keys, IDs, metadata, placeholders, timecodes, code, formatting). Each is auto-detected from its extension — just point a pattern at the source file:
{ "pattern": "lib/l10n/app_en.arb" }
{ "pattern": "locales/en.po" }
{ "pattern": "Localizable.strings" }
{ "pattern": "l10n/en.xlf" }One needs an explicit format because its extension is too ambiguous to auto-detect:
{ "pattern": "res/values/strings.xml", "format": "android" }Xcode String Catalogs#
A .xcstrings (String Catalog) file holds all locales in a single file. There's no per-locale output path — the CLI reads the source locale and writes every target back into the same file:
{ "pattern": "Localizable.xcstrings" }Output paths#
For every format except the String Catalog, targets are written by substituting the locale segment of the source pattern — content/en/app.json → content/de/app.json. Keep the source locale in the path so the CLI knows where targets go. See Configuration.
Coming from the legacy CLI?#
Most of the legacy CLI's formats have landed in the current CLI (PO, XLIFF, Android/Xcode strings, Flutter ARB, and more — see the table above). A few less-common formats (e.g. CSV, HTML, MJML, .properties) aren't supported yet; until yours lands, the legacy CLI docs cover it.
