Translatize CLI
The command-line client for Translatize. It downloads a branch into per-language JSON files with pull, sends local source keys back with push, and reports drift with status – so a pull request can be gated on translation completeness. It also gates on translation quality with qa, uploads screenshot captures, publishes over-the-air releases, and writes the Translatize rules for coding agents into AGENTS.md.
Install
Add the CLI as a dev dependency. The binary is named translatize – run it with npx translatize <command> or from an npm script. It is built on @translatize/core and requires Node.js 18.17 or newer. This page describes version 0.2.0.
npm install -D @translatize/cli@translatize scope, so the install above and npx @translatize/cli@latest resolve from the public registry. The qa, screenshots upload and release commands and init --agents need 0.2.0 or newer.Quickstart
# 1. Scaffold translatize.config.json (records your project + file layout)
npx translatize init
# 2. Provide a branch-bound API token
export TRANSLATIZE_API_TOKEN=mcni_xxx
# 3. Download translations into locales/<lang>.json
npx translatize pullinit writes a translatize.config.json you commit to your repo. The token is never written to that file – it is read from the environment or the --token flag at run time.
Authentication
The CLI authenticates with an API token – a string beginning with mcni_, not your login. Create one under Project Settings → Integrations in the app. A token is bound to a single project and a base branch, and by default translatize acts on that bound branch.
A token with the Agent workflow branch scope (create-own) may also target branches it created: pass --branch <name> (or set branch in the config) to operate on one of them. Naming a branch the token cannot touch fails with a branch_not_allowed error that lists the branches the token may target.
The token is resolved in this order:
- the
--token <mcni_...>flag, then - the
TRANSLATIZE_API_TOKENenvironment variable.
If neither is set, every command except init stops with a usage error (exit code 2).
translatize.config.json or any file. In CI, store it as a secret and expose it as the TRANSLATIZE_API_TOKEN environment variable.What each command needs
A token has a role – translator, developer or admin – and some commands need a paid plan:
| Command | Token role | Plan |
|---|---|---|
init | Any, or no token at all | Any |
pull, push, status | Translator or higher | Any |
qa | Translator or higher | Any |
screenshots upload | Developer or higher | Starter, Professional or Agency |
release | Developer or higher | Publishing works on any plan; apps get the release from our CDN once the account has delivery blocks, an add-on from Starter up (see Delivery) |
Configuration
translatize.config.json lives at the root of your project, or wherever you point --config. File patterns are resolved relative to the directory that holds the config file.
{
"apiUrl": "https://api.translatize.com/v1",
"projectId": "your-project-id",
"format": "json-nested",
"files": "locales/{lang}.json",
"namespace": "app",
"branch": "feature-x"
}| Field | Required | Description |
|---|---|---|
apiUrl | no | API base URL including the /v1 segment. Defaults to https://api.translatize.com/v1. |
projectId | no | The project id. May be left empty – the CLI then takes the project from the token. Set it to pin the project; a mismatch with the token is reported as a warning and the token wins. |
format | yes | On-disk shape: json-nested (dotted keys become nested objects) or json-flat (dotted keys stay flat). |
files | yes | Path pattern for each language file. Must contain the {lang} placeholder, e.g. locales/{lang}.json. |
namespace | no | Restrict pull, push and status to keys that start with <namespace>.; pull then writes only those keys. |
branch | no | Default branch for pull, push, status, qa and screenshots upload. Must be in the token's allowed set and is overridden by --branch. When omitted, commands use the token's bound branch. |
github | no | Settings for the GitHub App (sourceLanguage, autoTranslate, commitTranslations, requireComplete, syncDefaultBranch). The CLI ignores this block, so one file serves both. |
Other top-level keys are accepted and kept as they are, so init --force keeps another tool's settings. qa, screenshots upload and release only talk to the API, so they also run without a config file; the API URL then comes from --api-url or the default.
json-nested vs json-flat
The API stores keys in flat, dotted form (app.nav.home). format decides how they are written to disk:
// json-nested
{
"app": {
"nav": { "home": "Home" },
"title": "Hello"
}
}
// json-flat
{
"app.nav.home": "Home",
"app.title": "Hello"
}Both are read back losslessly, and files are written with keys sorted and a trailing newline so re-running pull stays diff-friendly. If a key is both a value and a parent (both a and a.b exist), json-nested can't represent it and the CLI asks you to switch to json-flat.
Commands
Every command accepts these global flags:
| Flag | Description |
|---|---|
-c, --config <path> | Path to the config file. Default translatize.config.json. |
--token <token> | API token, overriding TRANSLATIZE_API_TOKEN. |
--api-url <url> | API base URL, overriding the config value. |
translatize -v (or --version) prints the version, and translatize <command> --help lists a command's flags. Running translatize with no arguments prints the help.
translatize init
Create translatize.config.json (or the file named with --config). It refuses to overwrite an existing file unless --force is passed. If a token is available, init checks it, prints the project name, bound branch, role and configured languages, and records the token's project id when you don't pass one. If the check fails, it writes the config anyway and warns. A URL given with --api-url is saved as apiUrl.
| Flag | Description |
|---|---|
--project-id <id> | Project id to record in the config. |
--files <pattern> | File pattern with a {lang} placeholder. Default locales/{lang}.json. |
--format <format> | json-nested (default) or json-flat. |
--force | Overwrite an existing config file. |
--agents | Write the coding-agent rules into AGENTS.md instead (see below). Leaves the config alone. |
translatize init --agents
npx translatize init --agentsWrites the Translatize rules for coding agents into a marked section of AGENTS.md (created when missing) and of CLAUDE.md when that file exists, in the config file's directory. The rules tell an agent to:
- never hardcode user-visible strings and use the project's i18n lookup instead;
- create keys through the Translatize MCP server with the source text, a description, a character limit only when the UI clearly limits the length, and the code location;
- use dotted key names grouped by feature, and reuse a key only when the meaning matches;
- work on a Translatize branch, never directly on
main; - run the quality checks and the merge gate before asking for a merge, and never merge into
mainwithout your approval; - never put secrets or personal data in translations.
The section sits between <!-- translatize:agents:start --> and <!-- translatize:agents:end -->. Re-running replaces only that section and leaves the rest of the file alone, so run it again after upgrading the CLI. To give the agent the tools those rules name, connect it to the MCP server.
translatize pull
Download the branch and write one file per project language at the files pattern, creating parent directories as needed. A key whose value is empty or missing for a language is omitted from that file. When namespace is set, only keys under it are pulled.
| Flag | Description |
|---|---|
--branch <name> | Branch to operate on. Overrides the config branch field and the token's bound branch. Must be in the token's allowed set. |
--dry-run | Report the per-file changes (new file / keys added / changed / removed locally) without writing anything. |
--json | Emit machine-readable JSON. |
translatize push
Read each language file, compare it with the branch, and upload only new and changed keys. Values are assembled per key across all language files, and large uploads are split into several requests for you. A missing language file is skipped with a warning. push never deletes keys: keys that exist on the branch but not locally are reported and left untouched. It exits 1 if the server rejects any key, and lists each one with its error.
| Flag | Description |
|---|---|
--branch <name> | Branch to operate on. Overrides the config branch field and the token's bound branch. Must be in the token's allowed set. |
--dry-run | Print the pending create/update list without sending anything. |
--json | Emit machine-readable JSON. |
translatize status
Compare local files against the branch without changing either side – this is the CI gate. It prints per-language completeness (non-empty translations over total branch keys) and the counts of keys that are only local, only remote, or whose values differ, with up to 10 example keys each. Without a --fail-* flag it reports differences but still exits 0.
| Flag | Description |
|---|---|
--branch <name> | Branch to operate on. Overrides the config branch field and the token's bound branch. Must be in the token's allowed set. |
--fail-on-missing | Exit 1 if any language has missing translations. |
--fail-on-diff | Exit 1 if local differs from the branch in any way. |
--json | Emit machine-readable JSON. |
translatize qa
Run the quality checks on the branch – placeholders, HTML tags, ICU syntax, ICU plural categories, glossary terms, length against the key's character limit, numbers, punctuation, untranslated values and whitespace – and print the counts per severity with the first 20 issues. Checks switched off for the project and findings someone ignored in the app are not counted.
On a branch other than the token's base branch (for example an agent's own branch), qa also reads the merge gate for merging it into the base and prints whether the merge would be blocked.
It exits 1 on issues at the --fail-on severity or above, and whenever the merge gate would block the merge, whatever --fail-on says. Info-level issues never fail it. AI review findings are at most warnings, so they can only fail it with --fail-on warning. A very large branch is checked in the background on the server; qa then reports the issues already stored for the branch, which are kept up to date as labels change.
| Flag | Description |
|---|---|
--branch <name> | Branch to check. Overrides the config branch field and the token's bound branch. |
--fail-on <severity> | error (default) or warning (warnings and errors). Any other value is a usage error. |
--json | Emit the full report as JSON. |
translatize screenshots upload
Upload the captures written by @translatize/playwright (<name>.json + <name>.png pairs, by default in .translatize/captures) as one capture run, so Translatize can link each screen to the keys shown on it. The CLI asks the server which images it already has and uploads only the rest; the details of every screen, such as the texts and their positions, are always sent. It then completes the run and prints how many images went up and how many key links the run made.
A JSON file without its PNG is skipped with a warning. A screen the server rejects is listed and the others still go up; a full storage quota stops the upload, and the screens already sent are kept. A run holds at most 500 screens – with more captures the command stops before uploading and asks you to split them into several directories. It exits 1 when any screen failed or the upload stopped.
| Argument / flag | Description |
|---|---|
[dir] | Capture directory. Default .translatize/captures. |
--branch <name> | Branch whose keys the screens are matched against. Overrides the config branch field; must be in the token's allowed set. |
--run-name <name> | Name of the run. Defaults to the CI branch or tag name and the time. |
--commit-sha <sha> | Commit the screens come from. Defaults to GITHUB_SHA or CI_COMMIT_SHA. |
--json | Emit machine-readable JSON. |
translatize release
Publish an over-the-air distribution as a new version on our CDN, and print the version, the number of keys and languages, and the manifest URL. The token sees only distributions whose branch it may use: if it sees one, that one is published; otherwise name it with --distribution or narrow the choice with --branch. A disabled distribution can still be published – the CDN serves it again once it is enabled in the app.
If the branch has nothing to publish yet – no translated values, or no approved labels for a distribution that publishes approved labels only – the command says so and exits 1.
| Flag | Description |
|---|---|
--distribution <id> | Distribution id, public id or name. Needed when the token sees more than one. |
--branch <name> | Pick the distribution that publishes this branch. The config branch field is not used here. |
--json | Emit machine-readable JSON. |
Selecting a branch
pull, push, status, qa and screenshots upload all accept --branch <name>. The branch is resolved in this order:
- the
--branch <name>flag, then - the
branchfield intranslatize.config.json, then - the token's bound (base) branch.
A standard token can only ever act on its bound branch, so --branch must name that same branch. A create-own agent token may additionally target any branch it created. Targeting a branch outside the token's allowed set fails with a branch_not_allowed error listing the branches the token may use. release uses --branch only to pick a distribution.
create-own scope lets an AI agent open and manage its own translation branches – it can act on its base branch and the branches it creates, never on any other. See the MCP Server and Integration API docs for the full agent workflow.Machine-readable output
Every command except init accepts --json and prints one JSON document to stdout. Warnings and errors go to stderr. Colours are off when stdout is not a terminal or when NO_COLOR is set, so CI logs stay plain.
Environment variables
| Variable | Used for |
|---|---|
TRANSLATIZE_API_TOKEN | The API token when --token is not given. |
NO_COLOR | Any non-empty value turns coloured output off. |
GITHUB_SHA, CI_COMMIT_SHA | Default commit for screenshots upload (GitHub Actions, GitLab CI). |
GITHUB_REF_NAME, CI_COMMIT_REF_NAME | Part of the default run name for screenshots upload. |
Exit codes
| Code | Meaning |
|---|---|
0 | Success (including status differences when no --fail-* gate is set), and --help or --version. |
1 | A gate tripped (status --fail-on-*, qa issues or a blocked merge gate), a push or screenshots upload had server-side failures, or an API request failed (for example an expired token or a missing plan feature). |
2 | Usage or configuration error: an unknown command or flag, a bad value such as --fail-on info, a missing or invalid config, or no token. |
Use in CI
status is designed to fail a build when translations fall behind. This GitHub Actions job blocks merges when any language is missing translations:
name: translations
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm ci
- run: npx translatize status --fail-on-missing
env:
TRANSLATIZE_API_TOKEN: ${{ secrets.TRANSLATIZE_API_TOKEN }}Add a QA step to fail the build on translation quality errors too:
- run: npx translatize qa --fail-on error
env:
TRANSLATIZE_API_TOKEN: ${{ secrets.TRANSLATIZE_API_TOKEN }}After your Playwright tests have written their captures, upload them in the same job. The token needs the developer role:
- run: npx playwright test
- run: npx translatize screenshots upload
env:
TRANSLATIZE_API_TOKEN: ${{ secrets.TRANSLATIZE_API_TOKEN }}release needs no checkout and no config file. This workflow publishes a distribution named "Web app" when you start it by hand:
name: Publish translations
on: workflow_dispatch
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npx --yes @translatize/cli@0.2.0 release --distribution "Web app"
env:
TRANSLATIZE_API_TOKEN: ${{ secrets.TRANSLATIZE_API_TOKEN }}To push newly added source keys after a merge to main, run npx translatize push in a job with a token bound to that branch. Rather than script npx by hand, you can use the ready-made GitHub Action or GitLab CI component for pull, push, status and qa.
