Repository navigation
Install and first push
This page takes a repository that already has message catalogues and ends with a translated string in your files. It takes about ten minutes.
You need Node 22 or later and a repository with one JSON catalogue per language, such as src/i18n/en.json beside src/i18n/de.json.
npm install --save-dev @corpus-tool/cli @corpus-tool/workbenchThe two packages always share a version. The CLI is what you run; the workbench is the instance it starts.
If your package manager refuses the version because it was published today, that is a release-age policy; yarn 4.18 has one by default. A team instance says how each manager allows a package through it, what pnpm's verifyDepsBeforeRun does to pnpm corpus, and how to run every command through npx without installing into a repository whose tree is heavy.
The examples below are a repository with six strings in src/i18n/en.json, all six translated into German and two into Portuguese. The config and the output of init and build are recorded from a real run in that repository, so they are exactly what you will see. The workbench banner and the push and pull output are written by hand, because reproducing them needs a running instance; they show the shape, and your numbers will be your own.
npx corpus init --project acme-app --source en --messages "src/i18n/{lang}.json" --type uiwrote corpus.config.ts
created .gitignore with .corpus/
Next:
1. corpus workbench (needs @corpus-tool/workbench) starts an instance, creates the project "acme-app" and writes its token to .corpus/token.
The config's server is http://localhost:3000: corpus workbench listens there by default; for another port, edit the config or run init with --server.
For another instance at http://localhost:3000: CORPUS_INVITE_SECRET=<its secret> corpus project create prints the token, for CORPUS_TOKEN or .corpus/token.
2. corpus push
When @corpus-tool/cli is not installed in the repository, because you run the CLI through npx without adding it, init writes corpus.config.mjs as a plain object instead and says so; the typed file's import would not load there. Both files are read the same way.
--type names what kind of string the catalogue holds. It is yours to choose: it groups strings in the catalogue, carries a note to translators, and decides which metadata a string may have. ui, interface text, is the default if you leave it out.
init read the directory to find the languages, looked at the source file to see which i18n library wrote it, and wrote:
import { defineCorpus } from "@corpus-tool/cli";
export default defineCorpus({
project: "acme-app",
server: "http://localhost:3000",
sourceLanguage: "en",
languages: ["en", "de", "pt-PT"],
sources: [
{ adapter: "messages", type: "ui", path: "src/i18n/{lang}.json" },
],
});It also added .corpus/ to your .gitignore. That directory holds the local database, the instance secret and the project token, and none of them belong in git.
Check what it will send before starting anything:
npx corpus buildbuilt acme-app: 6 string(s) (ui 6), 0 entity(ies) (none)
build needs no server and no token, so it is the fastest way to see whether your config is right. If a string will not parse, it names the string with its file and key, leaves it out, and exits 1. The rest still push, so one malformed string does not stop you. When something is wrong covers what it can say.
npx corpus workbenchCorpus workbench 0.16.0 is running at http://localhost:3000
database .corpus/corpus.db
secret a101b74f13e7b92de77912d782256645adbd6713f5d43d9d (join with it once; it is in .corpus/secret)
token written to .corpus/token (project acme-app created)
stop Ctrl-C
It created the project your config names and wrote its token. Leave it running.
In another shell:
npx corpus pushpushed acme-app: 6 added, 0 changed, 0 stale, 0 archived, 8 translation(s) seeded from the repository
The seeds are the translations your repository already had: Corpus imports them as translated rather than asking anyone to redo them. If a row's text is the same as the English, it stays untranslated, because an exporter that fills a missing translation with the source is not a translation.
Open http://localhost:3000, join with the secret from the workbench output, and pick a name and a password. The first person to join is the maintainer.
The dashboard shows what to work on. Click Untranslated, and you are in the editor on the first string: the source on the left with its placeholders as chips, your language on the right. Type a translation, insert placeholders from the chips rather than typing braces, and save. Then click Verify, which only a maintainer can do.
npx corpus pullsrc/i18n/pt-PT.json
pulled acme-app at verified: 1 file(s) changed
git diff shows one key changed in that file. That is the loop: translations arrive as a reviewable commit.
By default pull takes only verified rows. --min-state translated takes translated ones too, which is what you want if nobody verifies.
- The daily loop is what this looks like once it is not the first time.
- The config file explains every field, including what to do when your catalogues are not one file per language.
- Corpus in CI keeps a broken translation or a hard-coded string out of your build.
Start here
Configuration
Running an instance
In CI
Agents
Translating
Reference