Monorepos
Every command walks up from the current directory to the nearest .lingo/config.json. That single rule is all you need to make the CLI work in a monorepo — you just decide where the config files live.
Option 1: one config at the root#
If your packages share a source locale and target locales, put one .lingo/config.json at the repo root and let its files patterns reach into each package:
{
"sourceLocale": "en",
"targetLocales": ["de", "fr"],
"files": [
{ "pattern": "apps/web/locales/en.json" },
{ "pattern": "packages/docs/content/en/**/*.md" }
]
}One lingo push from the root covers everything. The lockfile lives at the root too.
Option 2: a config per package#
When packages need different locales or engines, give each its own .lingo/config.json:
apps/web/.lingo/config.json # de, fr
apps/mobile/.lingo/config.json # de, fr, ja, koRun commands from inside a package (or cd in) — the CLI finds that package's config:
cd apps/mobile && lingo pushEach package has its own lockfile and links to its own engine independently.
CI per package#
In CI, set each step's working-directory to the package so lingo push finds that package's config:
- run: npx @lingo.dev/cli@latest push --wait
working-directory: apps/web
- run: npx @lingo.dev/cli@latest push --wait
working-directory: apps/mobile--wait keeps each step running until the translations are written, so a later step can commit them. See CI/CD.
Running from a subdirectory
You don't have to cd to the package root by hand for local commands — running lingo push from anywhere inside a package walks up to that package's config and writes the lockfile back beside it.