The Lingo.dev CLI translates native mobile resource files - Xcode .strings, Android XML, Flutter ARB, and React Native JSON - through a configured localization engine. The CLI auto-detects each file format from its extension, preserves structure, and handles plurals natively.
Platform Overview#
| Platform | Native format | Typical source file path |
|---|---|---|
| iOS (Xcode) | .strings | en.lproj/Localizable.strings |
| iOS (Xcode) | .stringsdict | en.lproj/Localizable.stringsdict |
| iOS (Xcode) | .xcstrings | Localizable.xcstrings |
| Android | strings.xml | app/src/main/res/values-en/strings.xml |
| Flutter | .arb | lib/l10n/app_en.arb |
| React Native | .json | src/locales/en.json |
Prerequisites#
Every CLI 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.
Install the CLI (Node.js 22+) and authenticate:
npm install -g @lingo.dev/cli
lingo loginlingo login signs you in with a one-time code. For CI, skip the interactive login and pass --api-key (or set LINGO_API_KEY).
Configure Your Platform#
Run lingo init to create .lingo/config.json (source/target locales plus your file patterns), then lingo link to attach your orgId and engineId. Commit .lingo/config.json to your repo. The examples below show the resulting config for each platform - the source path always contains the source locale code, and the CLI substitutes it for each target locale (en.lproj -> de.lproj, values-en -> values-de, app_en.arb -> app_de.arb).
Xcode supports three localization formats. Use the one that matches your project setup.
String Catalogs (.xcstrings) - the modern Xcode format introduced in Xcode 15. A single JSON file contains all locales, and Xcode updates it automatically when you add new strings. The CLI mutates this file in place, so the pattern points at the single catalog with no locale segment.
{
"orgId": "org_...",
"engineId": "eng_...",
"sourceLocale": "en",
"targetLocales": ["es", "fr", "de", "ja"],
"files": [{ "pattern": "MyApp/Localizable.xcstrings" }]
}Legacy .strings files - one file per locale in [code].lproj/ directories. The source locale lives in the path (en.lproj) and the CLI writes each target into its own .lproj directory. If your project also uses .stringsdict for plurals, add a second files entry.
{
"orgId": "org_...",
"engineId": "eng_...",
"sourceLocale": "en",
"targetLocales": ["es", "fr", "de", "ja"],
"files": [
{ "pattern": "MyApp/en.lproj/Localizable.strings" },
{ "pattern": "MyApp/en.lproj/Localizable.stringsdict" }
]
}See Apple's localization documentation for setting up Xcode's i18n infrastructure.
Running Translations#
Translate all resource files in one command:
lingo pushThe CLI reads your source locale files, computes what changed since the last run using the lockfile (.lingo/lock.json, which you commit), translates only the delta, and writes results to target locale files.
On the first run - or whenever you add a new target locale - translate everything from scratch:
lingo push --backfill-missingTarget a specific platform when your project contains multiple resource types by passing a glob (there are no --bucket or --target-locale flags):
lingo push "app/src/main/res/values-en/strings.xml"
lingo push "MyApp/Localizable.xcstrings"To fetch the latest translations elsewhere (for example on another machine), run lingo pull. To verify translations are up to date without writing changes - useful as a deploy gate - run lingo check.
Plurals and Platform Conventions#
Each mobile platform handles plural forms differently - iOS uses .stringsdict or String Catalog rules, Android uses <plurals> XML elements, and Flutter uses ICU MessageFormat in ARB files. The CLI preserves each platform's native plural structure during translation and generates the correct plural categories for each target locale.
Translator notes
Mobile strings are often short and context-dependent. Use translator notes in Xcode .xcstrings files to give the localization engine context about where a string appears - "button label in checkout flow" translates differently than "navigation menu item."
Automating in CI#
The recommended way to keep translations current is the Lingo.dev GitHub App. It runs server-side, reads your committed .lingo/config.json and engineId, and opens translation updates automatically - no runner, secret, or lockfile management on your side. If you prefer to run translations in your own pipeline, execute lingo push in your CI runner and commit the results.
