@lingo.dev/cli ships your source content to a localization engine, waits while the engine produces translations, and writes the outputs back to disk. It's the replacement for the legacy npx lingo.dev flow — same project, fundamentally different architecture.
What changed vs. the legacy CLI#
The legacy CLI (npx lingo.dev run) extracted strings, called an LLM directly from your machine, and wrote files in one synchronous pass. The new CLI is async-by-design:
lingo pushuploads sources to your engine, kicks off a server-side workflow, and either waits for completion or returns immediately with a run IDlingo pullfetches outputs from the most recent push — works even if you closed the terminal mid-translation, or are pulling from a different machine- A lockfile (
.lingo/lock.json) tracks the last-known-server version of every target so conflict detection can flag local edits before they get overwritten
This unlocks two things the legacy CLI couldn't do: long-running translations without a hanging terminal, and pulling results on a different machine than the one that ran push (or in CI).
Waiting for results#
Today, lingo push uploads sources, starts the server-side workflow, waits for it to finish, and writes the outputs — all in one command. Passing --wait (-w) makes that blocking behavior explicit. You can also re-attach to a finished run later with lingo pull.
lingo push # submit, wait, and write outputs (current default)
lingo push --wait # same thing, made explicit
lingo pull # later: re-attach to the most recent push and download its outputsUpcoming change: a pending release flips the default to async. lingo push will submit the run and exit immediately; you'll run lingo pull to download the finished translations, and --wait (-w) becomes how you opt back into the one-command blocking flow.
--wait(-w) blocks until the workflow finishes and writes outputs in the same command.lingo pullre-attaches to the most recent push for this project and downloads its outputs — works even after you closed the terminal. Run state is per-machine at~/.lingo/runs/<project-hash>.json, sopullresumes on the same machine.
Auth: both commands read LINGO_API_KEY (or --api-key, or a lingo login session). In CI, set LINGO_API_KEY and nothing else is needed.
push modes#
| Command | Mode | When |
|---|---|---|
lingo push | Incremental — diffs source vs .lingo/lock.json, translates only new/changed keys into existing targets, preserves the rest | Every routine run / CI |
lingo push --backfill-missing | Bootstrap — fills target FILES that don't exist yet | First push, or after adding a new locale |
lingo push --force | Full re-translate — overwrites every target (incl. manual edits); --yes/-y skips the prompt | Rarely (e.g. after a glossary/engine change) |
--backfill-missing is a bootstrap flag. It does a scoped fresh request and only adds whole target files that are missing — it does NOT translate newly-added keys into already-translated files (the run reports "already up-to-date" and the key is skipped). For ongoing edits use plain lingo push.
Editing translations by hand#
Plain lingo push preserves manual edits per key:
- Edit a target string (its source unchanged) → that string is kept; other keys keep updating.
- The source behind an edited key changes → a fresh translation is generated for that key, replacing the manual edit.
- A new source key is added → translated and added, even into files with manual edits.
