|
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

Mobile App Localization

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#

PlatformNative formatTypical source file path
iOS (Xcode).stringsen.lproj/Localizable.strings
iOS (Xcode).stringsdicten.lproj/Localizable.stringsdict
iOS (Xcode).xcstringsLocalizable.xcstrings
Androidstrings.xmlapp/src/main/res/values/strings.xml
Flutter.arblib/l10n/app_en.arb
React Native.jsonsrc/locales/en.json

Prerequisites#

Every CLI run sends content through a localization engine - the configuration that determines which LLM model, glossary, brand voice, and rules apply. Create one in the Lingo.dev dashboard.

Install the CLI (Node.js 22+) and authenticate:

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

lingo 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 pattern always points at your source file, and the CLI derives each target path from it. Usually that means swapping the locale it finds in the path (en.lproj → de.lproj, app_en.arb → app_de.arb). Two platforms are different, and both are handled for you: a String Catalog holds every locale in one file, so the target path is the source path; and Android keeps its default strings in an unqualified values/ with no locale in the path at all, so the CLI appends the target qualifier to it (values/ → values-de/).

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.

json
{
  "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.

json
{
  "orgId": "org_...",
  "engineId": "eng_...",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de", "ja"],
  "files": [
    { "pattern": "MyApp/en.lproj/Localizable.strings" },
    { "pattern": "MyApp/en.lproj/Localizable.stringsdict" }
  ]
}

A project whose development language bundle is Base.lproj rather than en.lproj works too - the CLI recognises Base.lproj as the source locale.

See Apple's localization documentation for setting up Xcode's i18n infrastructure.

Running Translations#

Translate all resource files in one command:

bash
lingo push

The 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:

bash
lingo push --backfill-missing

Target a specific platform when your project contains multiple resource types by passing a glob (there are no --bucket or --target-locale flags). Patterns are matched against source paths, so scope by the source file rather than a target:

bash
lingo push "app/src/main/res/values/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.

Platform Deep Dives#

iOS with String Catalogs
End-to-end guide for Xcode .xcstrings with the CLI and GitHub App
Android with strings.xml
End-to-end guide for Android XML resources with the CLI and GitHub App
Example projects
Working iOS, Android and Flutter repositories with the config and translations committed

Next Steps#

Supported Formats
Full reference for all mobile file formats
Glossaries
Lock brand names and technical terms from translation
GitHub App
Automate mobile translations on every push
Key Locking
Copy specific values without translating them

Was this page helpful?

Max PrilutskiyMax Prilutskiy·Updated 12 days ago·5 min read