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.
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:
{
"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:
{
"projects": {
"frontend": "apps/frontend",
"mobile": "apps/mobile",
"backend": "services/backend"
},
"defaultProject": "frontend"
}
projects: a dictionary mapping a project name to the directory (relative tobranchlate-root.json) that contains the project'sbranchlate.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
- In each project's directory, create its
branchlate.jsonas usual (e.g. withnpx branchlate@latest init— see Getting Started). - Create
branchlate-root.jsonat 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:
--project <name>— that project, from anywhere in the repository.--all-projects— every project listed inbranchlate-root.json.- 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. branchlate-root.json— reached when nobranchlate.jsonis found (typically at the repository root). It runsdefaultProjectwhen one is set, and all linked projects otherwise.
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 nearestbranchlate.jsonby 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.jsonis stored asi18n/en.jsonwhether you run from insideapps/frontendor from the root with--project frontend, so adding a root file never re-keys existing translations.
Troubleshooting
| Message | Cause |
|---|---|
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.