translato-cli
v1.1.7
Published
Translato CLI — push & pull translation keys from CI/CD pipelines
Maintainers
Readme
translato-cli
CLI for Translato — push and pull translation keys between your local i18n files and the Translato TMS.
Install
npm install -g translato-cliQuick Start
# 1. Log in via your browser
translato login
# 2. Create config files interactively
translato init
# 3. Push source keys AND values to the TMS
translato push
# 4. Pull all translations (including source language) to local files
translato pull
# 5. Or do both in one step
translato syncCommands
| Command | Description |
|---------|-------------|
| translato init | Interactive setup — creates .translato.yml config and .env for credentials |
| translato login | Authenticate with Translato via browser (device authorization flow) |
| translato logout | Remove stored credentials from .env |
| translato push | Upload source language keys and values to the TMS |
| translato pull | Download translations for all languages (including source) to local files |
| translato sync | Two-way sync: push keys then pull translations |
| translato status | Show project info and per-language translation progress bars |
| translato validate | Check files for missing keys, extra keys, empty values, and placeholder mismatches |
| translato diff | Compare local translation files with remote TMS translations |
| translato watch | Watch source files for changes and auto-push on save |
Login
translato loginAuthenticates using a device authorization flow. The CLI displays a one-time code and opens your browser. Log in to Translato in the browser, enter the code, and the CLI receives an API token automatically. The token is saved to .env for future use.
🔑 Logging in to Translato...
Your authorization code: ABCD-EFGH
A browser window will open. Log in to Translato and enter the code above.
If the browser doesn't open, visit:
http://translato.app/auth/cli?code=ABCD-EFGH
Waiting for authorization... (expires in 10 minutes)
✅ Login successful!
Your API key has been saved to .env.
Run translato status to verify your connection.Logout
translato logoutRemoves TRANSLATO_API_KEY, TRANSLATO_PROJECT, and TRANSLATO_HOST from your .env file. If .env becomes empty after removal, the file is deleted. Other environment variables in .env are preserved.
Init
translato initInteractive setup wizard that creates a .translato.yml config file and optionally saves credentials to .env. The command:
- Auto-detects your locales directory — scans for
src/locales,src/i18n,src/lang,locales,i18n,lang,translations,public/locales,public/i18n,assets/locales,assets/i18n, andresources/lang. - Auto-detects languages — reads filenames (
en.json,de.yml) and subdirectories (en/,fr/) from the locales directory. - Auto-detects namespaces — finds namespace files like
common.json,auth.jsonin the source language directory. - Prompts for configuration — project name, locales directory, source language, target languages (if not detected), API key, and project ID.
- Verifies credentials — if both API key and project ID are provided, calls the API to confirm they work before writing config.
- Writes
.translato.yml— safe to commit (no secrets). - Writes credentials to
.env— with0600permissions on Unix (owner-only). - Updates
.gitignore— adds.env,.env.lock,.translato.state, and.translato.lockif missing.
🚀 Initializing Translato project...
📁 Detected locales directory: ./src/locales
🌐 Detected languages: en, de, fr, es
Project name (my-app):
Locales directory (src/locales):
Source language (en):
✓ Languages detected from files: en, de, fr, es
API key (or press Enter to set later in .env):
Project ID (or press Enter to set later in .env):
✅ Created .translato.yml (safe to commit)
✅ Added .env, .env.lock, .translato.state, .translato.lock to .gitignore
Then run: translato pushIn non-interactive environments (CI, piped input), the command skips prompts and uses auto-detected values or defaults.
Tip: If you already ran
translato login, your API key is already in.env—initwill pick it up automatically.
Push
translato push reads your source language files, extracts all keys, and uploads them to the Translato API:
- Terms — every key in the source files is registered as a term in the project.
- Source translations — the actual values (e.g.
"Hello","Active API Access Tokens") are stored as translations for the source language. This ensures the TMS shows the real text instead of "click to translate".
The CLI tracks file hashes locally (.translato.state) and skips the push when nothing has changed since the last push. Use --force to push anyway.
Pushing keys to Translato...
📄 ./src/i18n/en.json — 5 keys
Total unique keys: 5
Terms — parsed: 5, added: 2
Translations (en) — parsed: 5, added: 2, updated: 3
✅ Push complete.Before pushing, the CLI performs two safety checks:
- Pull-before-push guard — if the remote project has translations you haven't pulled yet (or you've never pulled), the push is blocked. Run
translato pullfirst, or use--forceto skip. - Orphan key detection — remote keys that no longer exist in your local source files are flagged. The push is aborted unless
--forceis used.
Pull
translato pull downloads translations for every language in the project (including the source language) and writes them to local files. On success, it records a pull timestamp in .translato.state so the push guard knows your workspace is current. If any language fails to pull, the timestamp is not updated.
Pulling translations from Translato...
Languages: en, fr, es, de
✅ en — 5 ungrouped keys → ./src/i18n/en.json
✅ fr — 5 ungrouped keys → ./src/i18n/fr.json
✅ es — 5 ungrouped keys → ./src/i18n/es.json
⏭ de — no translations, skipping
✅ Pull complete.Languages are downloaded in parallel (up to --concurrency at a time, default: 5). Per-language errors are handled gracefully — one failed language won't stop the rest from being pulled.
Sync
translato syncTwo-way sync: runs push followed by pull in a single command. Useful as a one-liner for CI or when you want to upload new keys and download the latest translations in one step.
Status
translato statusShows project information and per-language translation progress:
📊 Translato Project Status
Project: My App
ID: cmnee05bm0001fw1uv7jo5f6k
Terms: 248
Created: 2026-03-31
Languages:
en ████████████████████ 100%
de ██████████████░░░░░░ 71%
fr ████████████░░░░░░░░ 60%
es ████░░░░░░░░░░░░░░░░ 20%Validate
translato validate
translato validate --lang es,frChecks all translation files against the source language for:
- Missing keys — keys present in source but absent in a target language
- Extra keys — keys in a target file that don't exist in source
- Empty values — keys that exist but have no translation text
- Placeholder mismatches —
{name},{{count}},%{user},%s,%dpatterns that differ between source and target
Exit code is non-zero when issues are found — useful as a CI gate.
Diff
translato diff
translato diff --lang de,frCompares your local translation files against what is stored in the remote TMS. Shows keys that exist only locally, keys only in remote, and keys with different values. Languages are compared in parallel (up to --concurrency).
🔄 Comparing local files with remote translations...
Languages: en, fr, de
✅ en — in sync
⚠️ fr — 3 difference(s):
+ 1 key(s) only in local files
+ nav.settings: Settings
~ 2 key(s) with different values
~ greeting:
local: Bonjour le monde
remote: Bonjour
✅ de — in sync
──────────────────────────────────────────────────
⚠️ 3 total difference(s) found.Watch
translato watchWatches source language files for changes and auto-pushes when a file is saved. Changes to target language files are ignored. Useful during active development. The CLI debounces rapid saves (500ms) and prevents overlapping pushes.
Configuration
.translato.yml
Running translato init creates this file in your project root. It is safe to commit — no secrets are stored here.
# Translato CLI Configuration
project: my-app
paths:
locales: ./src/locales
source: en
format: json # json | yaml | po | properties
languages:
- en
- de
- fr
- es
namespaces:
- name: common
file: common.json
hooks:
pre-push: npm run lint:translations
post-pull: npm run format:translations
timeout: 30 # seconds (default: 60, minimum: 5).env (secrets)
Credentials are stored in .env — never commit this file.
TRANSLATO_API_KEY=your-api-key
TRANSLATO_PROJECT=your-project-idThe CLI loads .env automatically on startup. You can also set these as real environment variables (e.g. in CI).
Environment Variable References
Use ${VAR_NAME} syntax inside .translato.yml to reference environment variables. They are resolved at load time. The CLI warns when a referenced variable is not set.
paths:
locales: ${MY_LOCALES_DIR}Config Priority
CLI flags are saved to .env and take effect immediately. Priority:
CLI flags > environment variables > .env file > .translato.yml valuesYou can set credentials with any command — they are saved for future use:
translato status --api-key sk-my-key --project proj-123Global Options
These can be passed to any command:
| Option | Env Variable | Description |
|--------|-------------|-------------|
| --api-key <key> | TRANSLATO_API_KEY | API key — saved to .env for future use |
| --project <id> | TRANSLATO_PROJECT | Project ID — saved to .env for future use |
| --host <url> | TRANSLATO_HOST | API server URL (optional — only for self-hosted instances) |
| --files <glob> | — | File glob pattern (default: from config or src/locales/*.json) |
| --format <fmt> | — | File format: json / yaml / po / properties |
| --out-dir <path> | — | Output directory for pulled translations |
| --concurrency <n> | — | Max parallel language operations (default: 5, max: 20) |
| --force | — | Skip orphan key warnings and change detection on push |
| --lang <codes> | — | Only process these languages (comma-separated, e.g. de,fr) |
| --exclude-lang <codes> | — | Skip these languages (comma-separated, e.g. en,ja) |
| --skip-existing | — | Skip files that already exist locally (pull only) |
| --json | — | Output results as JSON (for CI pipelines) |
| --dry-run | — | Preview changes without API calls or file writes |
| --verbose | — | Show detailed output including HTTP requests |
| -v, --version | — | Print CLI version |
File Formats
| Format | Extensions | Notes |
|--------|-----------|-------|
| JSON | .json | Nested objects flattened to dot notation on push, unflattened on pull |
| YAML | .yml, .yaml | Full YAML support via js-yaml |
| PO/Gettext | .po, .pot | msgid/msgstr, multiline, msgctxt, plural forms |
| Properties | .properties | key=value pairs, \uXXXX Unicode escapes, line continuation |
JSON nesting
Nested JSON objects are flattened to dot-separated keys for the TMS:
{ "common": { "hello": "Hello" } }Becomes the term key common.hello with value Hello. On pull, it is unflattened back to the original nested structure.
Namespaces
When namespaces are configured in .translato.yml, the CLI groups keys by namespace:
- Push: Keys from files matching a namespace (e.g.
auth.json) are prefixed asauth:login.titlewhen uploaded. - Pull: Keys with a namespace prefix (e.g.
auth:) are split back into the correct namespace file. - The CLI auto-detects directory-based (
en/common.json) vs flat (en.json) locale structures.
Files that don't match any namespace entry are handled as ungrouped keys.
Hooks
| Hook | Runs | Triggered By |
|------|------|-------------|
| pre-push | Before uploading keys | push, sync, watch |
| post-pull | After downloading translations | pull, sync |
Hooks run in a shell and abort the command if they exit with a non-zero code. The timeout is configurable (default: 60 seconds, minimum: 5 seconds):
hooks:
pre-push: npm run lint:translations
post-pull: npm run format:translations
timeout: 30 # secondsSecurity: Dangerous patterns (eval, backtick subshells, curl | sh, source, xargs, find -exec, etc.) are blocked. Commands starting with known-safe prefixes (npm, npx, yarn, pnpm, bun, node, prettier, eslint, biome, echo, true) bypass the blocklist check. Commands longer than 500 characters are rejected.
Note: Hooks execute shell commands. Always audit hook commands in
.translato.ymlbefore running the CLI in a new project, especially from untrusted sources.
Language Filtering
Use --lang and --exclude-lang to limit which languages are processed. These flags work with pull, validate, and diff.
# Only pull German and French
translato pull --lang de,fr
# Pull everything except English and Japanese
translato pull --exclude-lang en,ja
# Validate only Spanish translations
translato validate --lang es
# Compare only specific languages with remote
translato diff --lang de,frWhen both flags are used together, --lang is applied first (allowlist), then --exclude-lang removes from the result.
Dry Run
Preview what would happen without making changes:
translato push --dry-run
translato pull --dry-runJSON Output
Use --json to get structured JSON output on stdout (human-readable output goes to stderr). Useful for CI pipelines and scripting:
translato status --json
translato validate --json
translato diff --jsonUpdate Notifications
The CLI checks npm for newer versions once every 24 hours and displays a warning if an update is available. To disable (e.g. in CI), set:
TRANSLATO_NO_UPDATE_CHECK=1The check is also automatically skipped when CI=true.
Proxy Support
The CLI respects HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables for corporate environments. Requires undici to be available (built-in with Node 18.7+).
HTTPS_PROXY=http://proxy.corp:8080 translato pullFiles to .gitignore
Add these to your .gitignore (automatically added by translato init):
.env
.env.lock
.translato.state
.translato.lockCI/CD
GitHub Actions
name: Translation Sync
on:
push:
paths: ['src/locales/en.json']
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npx translato-cli push
env:
TRANSLATO_API_KEY: ${{ secrets.TRANSLATO_API_KEY }}
TRANSLATO_PROJECT: ${{ secrets.TRANSLATO_PROJECT }}GitLab CI
translate:
script:
- npx translato-cli push
variables:
TRANSLATO_API_KEY: $TRANSLATO_API_KEY
TRANSLATO_PROJECT: $TRANSLATO_PROJECTPR Validation
Add a validation step to catch translation issues before merge:
- run: npx translato-cli validate --jsonTroubleshooting
Output path fallback
When pulling translations, if the CLI cannot determine an output path from a {lang} placeholder, wildcard, or source language directory in your file glob, it falls back to inserting the language code before the file extension. For example, messages.json becomes messages.de.json. To avoid this fallback, use a {lang} placeholder in your glob (e.g. locales/{lang}.json) or structure files with a source language directory (e.g. locales/en/messages.json). Run with --verbose to see when the fallback is used.
"click to translate" after push
If the TMS web UI shows "click to translate" instead of your source text, run translato push again. The CLI pushes both terms and source language translations. Check the output for the Translations (en) line — added or updated should be non-zero.
Pull skips a language
If pull shows ⏭ xx — no translations, skipping, it means no translation records exist on the server for that language. Push first (translato push), then translate in the TMS, then pull.
Missing API key or project ID
❌ Missing API key. Use --api-key or set TRANSLATO_API_KEY.Set credentials with translato login (recommended), or pass --api-key and --project flags (saved to .env automatically), or add them manually to .env in your project root.
"No changes since last push"
The CLI tracks a SHA-256 hash of your source translations. If nothing changed, push is skipped to avoid unnecessary API calls. Use --force to push anyway.
Another process is running
If you see a warning about another translato process, it means a .translato.lock file exists from a previous run. The CLI checks if the PID is still alive — stale locks from crashed processes are cleaned up automatically.
Development
# Run in dev mode (without building)
npm run dev -- push
# Run tests
npm test
# Build for distribution
npm run buildLicense
MIT
