Monorepos

Updated: 4 days ago · 1 min read

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:

json
{
  "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:

text
apps/web/.lingo/config.json      # de, fr
apps/mobile/.lingo/config.json   # de, fr, ja, ko

Run commands from inside a package (or cd in) — the CLI finds that package's config:

bash
cd apps/mobile && lingo push

Each 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:

yaml
- 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.