Skip to main content

Multi-Project Repositories (Monorepos)

In a monorepo containing several applications, each application is its own Branchlate project with its own branchlate.json. A branchlate-root.json file at the root of the repository links them together, so you can run commands for any project by name without changing directories.

info

branchlate-root.json, --project and --all-projects require CLI 1.3.0 or later. Commands that use npx branchlate@latest always get it.

A typical layout looks like this:

my-monorepo/
├── branchlate-root.json ← links the projects below
├── apps/
│ ├── frontend/
│ │ ├── branchlate.json
│ │ └── i18n/ ← en.json, fr.json, …
│ └── mobile/
│ ├── branchlate.json
│ └── i18n/
└── services/
└── backend/
├── branchlate.json
└── i18n/

Configuration

Each project keeps a regular branchlate.json in its own directory — nothing changes there:

apps/frontend/branchlate.json
{
"projectId": "frontend",
"organisationId": "my-organisation",
"languageFilePattern": "i18n/{languageCode}.json"
}

The branchlate-root.json at the repository root only maps project names to the directories containing them:

branchlate-root.json
{
"projects": {
"frontend": "apps/frontend",
"mobile": "apps/mobile",
"backend": "services/backend"
},
"defaultProject": "frontend"
}
  • projects: a dictionary mapping a project name to the directory (relative to branchlate-root.json) that contains the project's branchlate.json. A path to the configuration file itself also works (e.g. "apps/frontend/branchlate.json").
  • defaultProject (optional): the project used when a command is run from the repository root without --project.

All settings (languages, patterns, sub-projects, …) live in each project's own branchlate.json — the root file is only an index.

Setting up

  1. In each project's directory, create its branchlate.json as usual (e.g. with npx branchlate@latest init — see Getting Started).
  2. Create branchlate-root.json at the repository root listing the project directories.

Existing per-project setups don't need any change: adding the root file is enough.


Running commands

The sync, status, and format commands accept a --project option to target a project by name from anywhere in the repository:

npx branchlate@latest sync --project frontend
npx branchlate@latest status --project backend
npx branchlate@latest format --project mobile --dry-run

How a command picks its project

The first rule that applies wins:

  1. --project <name> — that project, from anywhere in the repository.
  2. --all-projects — every project listed in branchlate-root.json.
  3. The nearest branchlate.json, searching the current directory and then its parents. Running from inside a project therefore behaves exactly as it did before, with or without a root file.
  4. branchlate-root.json — reached when no branchlate.json is found (typically at the repository root). It runs defaultProject when one is set, and all linked projects otherwise.
note

If a branchlate.json and a branchlate-root.json are both found while walking up from the current directory, the one closest to that directory wins.

To explicitly run on every project (ignoring defaultProject), use --all-projects (or its shorter alias --all) — handy in CI:

npx branchlate@latest sync --all-projects
npx branchlate@latest status --all --min-coverage=100

branch delete and project delete also accept --project <name> to select which project's configuration provides their default --project-id/--organisation-id values.


Backward compatibility

branchlate-root.json is purely additive:

  • Single-project repositories need no root file — everything works as before.
  • In a monorepo without a root file, the per-project workflow still applies: cd apps/frontend && npx branchlate@latest sync (the CLI finds the nearest branchlate.json by walking up parent directories, so any subfolder of the project works too).
  • Adding a root file later doesn't change any project's configuration — it only enables --project, --all-projects, and running from the repository root.
  • Translation file names are recorded relative to each project's own branchlate.json, never to the repository root. apps/frontend/i18n/en.json is stored as i18n/en.json whether you run from inside apps/frontend or from the root with --project frontend, so adding a root file never re-keys existing translations.

Troubleshooting

MessageCause
Project "x" not found in branchlate-root.json. Available projects: …--project doesn't match any key under projects. Names are the keys of that dictionary, not the projectId inside each branchlate.json.
Multiple projects are defined in branchlate-root.json. Use --project <name> or set "defaultProject".A command that can only act on one project (branch delete, project delete) was run from the repository root with no defaultProject.
Project "x": no configuration found at … (linked from branchlate-root.json).The mapped directory has no branchlate.json. Run init there, or point the entry at the configuration file directly.
defaultProject "x" does not exist in "projects" of branchlate-root.json.defaultProject must be one of the keys under projects.
--project requires a branchlate-root.json in the current directory or one of its parents.--project and --all-projects only work once a root file links the projects.

Multi-project vs sub-projects

  • Sub-projects split one Branchlate project into logical sets of translations (per feature, per file family). They share the same parent project and appear together on the project's page. See Sub-Projects.
  • A multi-project repository contains several independent, top-level Branchlate projects — each with its own branches, languages, and page on Branchlate — linked by branchlate-root.json.

Use sub-projects to organize translations within an application, and branchlate-root.json to manage several applications in one repository. Both combine naturally: each linked project can define its own subProjects.