@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 pass on your machine. The current CLI splits the work into push and pull:
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 changes the default so lingo push submits the run and exits 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.
