|Labs
Book a DemoPlatform
React (Lingo Compiler)
Alpha
React (MCP)React (i18n)CLI

Overview

  • @lingo.dev/cli

Getting started

  • Quickstart
  • Configuration

Reference

  • lingo push
  • lingo pull
  • Other commands

Configuration

Max PrilutskiyMax Prilutskiy·Updated 8 days ago·3 min read

The CLI's persistent state lives in three places: two inside the project (committed), one in your home directory (per-machine).

.lingo/config.json — committed#

Created by lingo init (localization section) and lingo link (org/engine binding). Commit it.

json
{
  "orgId": "org_a8c...",
  "engineId": "eng_b9d...",
  "sourceLocale": "en",
  "targetLocales": ["de", "fr", "es"],
  "files": [
    { "pattern": "locales/en.json" },
    { "pattern": "docs/en/**/*.md" }
  ]
}

Field reference#

FieldRequiredDescription
orgIdyes (after link)Organization that owns the engine.
engineIdyes (after link)Engine that performs the translation. Holds model config, glossary, brand voice.
sourceLocaleyesLocale code of your source files (e.g. "en"). Source files are read; never written.
targetLocalesyesLocales to translate into. Outputs are written to the same directory as the source, with the locale code substituted into the pattern (locales/en.json → locales/de.json).
filesyesArray of source patterns. pattern is a glob (forward slashes, ** recursion, * wildcards). The CLI substitutes locale codes when resolving target paths.
githubnoSettings for the GitHub App — ignored by the CLI itself.

Pattern → target mapping#

The CLI replaces the source locale in the pattern with each target locale:

  • locales/en.json → locales/de.json, locales/fr.json, ...
  • docs/en/**/*.md → docs/de/**/*.md (mirrored subtree)
  • copy/en/marketing.md → copy/de/marketing.md

If your source layout doesn't include the locale code in the path, restructure it. The CLI can't infer where target files should go without it.

.lingo/lock.json — committed#

Tracks the last-known-server hash of every source and target file. Used for two things:

  1. lingo push consults it to decide whether a source has changed since the last successful run. Unchanged files become a no-op without a server round-trip.
  2. lingo pull consults it to detect local target edits — if a local target's hash differs from what the lockfile says, pulling would overwrite local work, so pull errors unless you pass --force.

Source hashes are committed to the lockfile only after a fully successful push, so a half-completed run can be retried without manual cleanup.

Commit the lockfile alongside translated outputs. Treat conflicts the same way you'd treat package-lock.json conflicts: regenerate by running lingo push again.

~/.lingo/runs/<hash>.json — per-machine#

Records the most recently submitted push so that lingo pull knows which run's outputs to fetch — works across closed terminals and across machines that share the same checkout.

json
{
  "runId": "run_a8c...",
  "engineId": "eng_b9d...",
  "organizationId": "org_a8c...",
  "sourceLocale": "en",
  "createdAt": "2026-05-22T14:32:01.000Z"
}

The filename hash is derived from the absolute project root path, not from file contents, so:

  • Editing en.json doesn't invalidate the file — the same pull will still find the run.
  • Two checkouts of the same repo on the same machine get distinct files (different absolute paths).
  • Moving the project (mv ~/Projects/foo ~/Projects/bar) between push and pull invalidates the lookup, since the hash changes. The JSON is still in ~/.lingo/runs/ if you need to recover the run ID manually.

This file is per-machine state, not project state — it isn't gitignored because it lives outside the repo entirely.

Authentication credentials — ~/.lingo/auth.json#

Stored by lingo login (OTP flow stores a Supabase session; --api-key flow stores the key). Not committed, never read by anything except the CLI itself.

bash
lingo logout         # clear credentials
lingo whoami         # check what's stored and which org/engine the cwd resolves to

Resolution from subdirectories#

Every command walks up from cwd looking for the nearest .lingo/config.json. Running lingo push from src/components/ writes its lockfile back to the project root, not a fresh .lingo/ in components/. So you don't have to cd to root before each command.

Was this page helpful?