Every example below is a real repository with .lingo/config.json committed and translations already in place, so you can read the config next to the output it produced. Most are runnable applications; a couple exist to show a file format on its own. Clone or fork one, run lingo link to bind your own engine, and push.
Pick an approach first#
There are two ways to localize with the CLI, and it decides which examples are relevant to you.
Translate the files you already have. Your framework keeps translations in its own format — Rails YAML, Android XML, Laravel PHP, ARB, Markdown — and the CLI translates those files in place. Nothing about your code changes. This is nine of the eleven examples below.
Author without keys. You wrap strings in l.text(...) where they appear, lingo extract builds a hash-keyed catalog for you, and there are no translation keys to name or maintain. That costs a build step and a runtime package, and it is what the two web app examples show.
Mobile apps#
| Example | Format | Why this one |
|---|---|---|
| iOS | xcode-xcstrings | A String Catalog holds every locale, so the target path is the source path |
| Android | android | Bare values/ as the source, and Android's own qualifiers (values-pt-rBR/) |
| Flutter | flutter | @-metadata and ICU placeholders kept, @@locale rewritten per file |
Web apps#
The two keyless examples. Both wrap strings with l.text(...) and generate their catalog with lingo extract, so the middle column names the runtime package rather than a file format.
| Example | Package | Why this one |
|---|---|---|
| React + Vite | @lingo.dev/react | Keyless authoring; generated declarations narrow l.text() to extracted strings |
| Next.js | @lingo.dev/react-next | Keyless authoring plus locale routing, hreflang, and a switcher. Pages Router |
hreflang in production
LingoHead builds its hreflang URLs from a baseUrl prop that defaults to empty, so out of the box the tags are relative. Search engines expect absolute URLs — pass your site's origin (<LingoHead baseUrl="https://example.com" />) before relying on them.
Content and specs#
| Example | Format | Why this one |
|---|---|---|
| Markdown docs | md, mdx | Prose by default, with frontmatter fields and MDX props opted in |
| Markdoc | markdoc, json | Next.js content and UI strings in one push — three entries, different options each |
| OpenAPI | yaml-openapi | Summaries and descriptions only; paths, operation IDs and enums untouched |
Framework catalogs#
| Example | Format | Why this one |
|---|---|---|
| Rails | yaml-root-key | The locale is the YAML root key, so the root key itself is rewritten |
| Laravel | php, po | Laravel catalogs plus a gettext file; :name placeholders preserved in both |
| TypeScript modules | typescript | Catalogs as TypeScript modules instead of JSON. The format only — no app consumes them |
TypeScript catalogs need a default export
The typescript format reads a default export — export default { … }, with or without as const. A named export produces no translatable content, and the run completes having copied the source verbatim. So if a push reports files localized but roughly zero output tokens, check the export shape first.
Using one of these#
npm install -g @lingo.dev/cli
lingo login
lingo link # writes your own orgId and engineId into .lingo/config.json
lingo push --waitNone of the examples commit orgId or engineId — that keeps a fork from pushing through someone else's engine. lingo link fills both in locally.
If you want the GitHub App instead of the CLI
The GitHub App reads engineId from the .lingo/config.json committed in your repository — it resolves the organization from the App installation itself, but the engine has to be in the file. After forking, run lingo link and commit the updated config before installing the App.
Not every format has an example#
These eleven cover the frameworks people ask about most, but the CLI translates eighteen formats. xliff, srt, Xcode .strings and .stringsdict, generic yaml, and standalone JSON/JSONC all work without having a repository here — see Formats for the full list and the config each one needs.
