@intlpullhq/cli
v0.2.0
Published
The official CLI for IntlPull - intelligent i18n for modern apps. Manage translations, sync with cloud, and automate localization workflows.
Maintainers
Readme
Table of Contents
| Section | Description | |---------|-------------| | Features | Key capabilities of the CLI | | Installation | How to install the CLI | | Quick Start | Get started in 4 commands | | Authentication | Login, API keys, and auth methods | | Commands Reference | All available commands | | ↳ Initialize | Set up IntlPull in your project | | ↳ Upload | Upload translation keys | | ↳ Download | Download translations | | ↳ Sync | One-time sync for CI/CD | | ↳ Listen / Watch | Real-time sync during dev | | ↳ Import / Export | Bulk import/export translations | | ↳ OTA Releases | Over-the-air updates | | ↳ Migration | Migrate from other platforms | | ↳ Project Management & Tools | Additional CLI features (keys, tm, etc.) | | CI/CD Integration | GitHub Actions, GitLab CI, Docker | | Configuration | Project config and output patterns | | ↳ Output Patterns | Namespace file structures | | ↳ iOS String Tables | Multiple .strings files (Localizable, Settings, etc.) | | ↳ Android Multi-XML | Multiple XML files (strings.xml, strings_settings.xml) | | ↳ Advanced Config | sources/targets mappings for complex setups | | Tips & Best Practices | Recommended usage patterns | | Support | Get help |
Features
- Parallel Downloads - Fetch translations by namespace concurrently for blazing fast syncs
- Real-time Sync - Watch for changes and auto-update local files
- Auto-detect Project - Automatically finds
.intlpull.jsonin parent directories - Multiple Output Formats - JSON, YAML, TypeScript, Android XML, iOS Strings
- CI/CD Ready - Full non-interactive mode support for automation pipelines (no TTY required)
- OTA Releases - Publish over-the-air updates for mobile/web SDKs
- Enterprise Workflows - Approval workflows for translation review
- Migration Tools - Migrate from Lokalise, Crowdin, Phrase, or local files
Installation
# Use with npx (recommended)
npx @intlpullhq/cli <command>
# Or install globally
npm install -g @intlpullhq/cli
intlpull <command>Quick Start
# 1. Authenticate
npx @intlpullhq/cli login
# 2. Initialize project
npx @intlpullhq/cli init
# 3. Push your translation keys
npx @intlpullhq/cli upload
# 4. Download translations
npx @intlpullhq/cli downloadAuthentication
# Interactive login (prompts for token)
npx @intlpullhq/cli login
# With API token (warns about shell history)
npx @intlpullhq/cli login --token <your-api-key>
# Or use environment variable (recommended for CI/CD)
export INTLPULL_API_KEY=your-api-key
# Check current auth status
npx @intlpullhq/cli whoami
# Logout
npx @intlpullhq/cli logoutAuth priority: Environment variable (INTLPULL_API_KEY) > Config file (~/.intlpull/auth.json)
Commands Reference
Global Options
| Option | Description |
|--------|-------------|
| --env-file <path> | Path to custom env file (e.g., .env.production) |
| -V, --version | Output version number |
| -h, --help | Display help |
Initialize Project
Set up IntlPull in your project with auto-detection.
# Interactive setup
npx @intlpullhq/cli init
# With options
npx @intlpullhq/cli init --framework next --library next-intl --output ./messages
# Non-interactive (auto-detect everything)
npx @intlpullhq/cli init -y
# Link to existing project
npx @intlpullhq/cli init --project <project-id>Options:
| Option | Description |
|--------|-------------|
| --framework <framework> | Framework: next, react, vue, svelte, astro |
| --library <library> | i18n library: next-intl, react-i18next, vue-i18n |
| --output <dir> | Output directory for translation files |
| --project <id> | Project ID to link |
| -y, --yes | Auto-detect and initialize without prompts |
Upload
Upload translation keys to IntlPull. Automatically detects project, files, and git branch.
# Auto-detect and upload
npx @intlpullhq/cli upload
# Upload specific file
npx @intlpullhq/cli upload --file messages/admin.json
# Upload only source language
npx @intlpullhq/cli upload --source-only
# Preview without uploading
npx @intlpullhq/cli upload --dry-run
Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID (auto-detected with project-scoped API key) |
| --file <path> | Specific file to upload (e.g., messages/admin.json) |
| --branch <name> | Branch name (auto-detected from git, pushes to IntlPull branch) |
| --platform <platform> | Platform to tag keys with (ios, android, web) |
| --all-languages | Upload translations for ALL languages (default: true) (default: true) |
| --source-only | Upload only source language keys (default: false) |
| --dry-run | Preview without uploading (default: false) |
| -v, --verbose | Show detailed detection info (default: false) |
| -q, --quiet | Suppress output except errors (CI mode) |
| --translation-key-prefix <prefix> | Prepend prefix to all keys (e.g., "checkout" makes "submit" → "checkout.submit") |
| --apply-tm | Apply Translation Memory matches after pushing (default: false) |
| --tm-threshold <n> | Minimum TM match score (default: 85) (default: "85") |
| --tm-dry-run | Preview TM matches without applying (default: false) |
| -h, --help | display help for command |
Download
Download translations from IntlPull. Uses parallel fetching by default.
# Auto-detect and download
npx @intlpullhq/cli download
# Specify format and output
npx @intlpullhq/cli download --format json --output ./messages
# Download specific languages
npx @intlpullhq/cli download --languages en,es,fr
# Download specific namespaces
npx @intlpullhq/cli download --namespace common,auth
# Download only approved translations (for production builds)
npx @intlpullhq/cli download --status approved
# Download translations pending review
npx @intlpullhq/cli download --status in_review
# Download only keys tagged with specific tags
npx @intlpullhq/cli download --tags ui,forms
# Combine workflow status and tags filters
npx @intlpullhq/cli download --status approved --tags marketing
# Quiet mode for CI
npx @intlpullhq/cli download --quiet
Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID (auto-detected with project-scoped API key) |
| --format <format> | Output format (json|yaml|ts|ios|xcstrings|stringsdict|android|arb) |
| --output <dir> | Output directory (auto-detected from framework) |
| --languages <langs> | Languages to download (comma-separated, defaults to all) |
| --namespace <namespaces> | Namespaces to download (comma-separated, defaults to all) |
| --branch <branch> | Translation branch to download from |
| --platform <platform> | Platform variant (default|ios|android|web) |
| --status <status> | Filter by workflow status (draft|in_review|approved|rejected|published) |
| --tags <tags> | Filter by tags (comma-separated). Only keys with matching tags are included |
| --no-parallel | Disable parallel fetching (use single request) |
| -i, --interactive | Run in interactive mode with prompts |
| -q, --quiet | Suppress output except errors |
| --clean-orphans [mode] | Remove orphaned files (files not in remote). Use "dry-run" to preview |
| -f, --force | Skip confirmation prompts for destructive operations |
| --translation-key-prefix <prefix> | Strip this prefix from keys (e.g., "checkout" makes "checkout.submit" → "submit") |
| --filter-by-prefix <prefix> | Only download keys starting with this prefix |
| --default-namespace <ns> | Namespace that maps to platform default file (Localizable.strings/strings.xml) |
| --namespace-prefix <prefix> | Prefix for non-default namespace files (Android: "strings_") |
| --table <mapping> | iOS String Table mapping (e.g., "common:Localizable,settings:Settings") |
| --xml-file <mapping> | Android XML filename mapping (e.g., "common:strings,settings:strings_settings") |
| --target <nameOrId> | Export to a specific GitHub target (alias for `export targets --target`) |
| --all-targets | Export to all configured GitHub targets (alias for `export targets --all-targets`) |
| -h, --help | display help for command |
Sync
One-time sync (equivalent to listen --once). Perfect for CI/CD.
# Quick sync
npx @intlpullhq/cli sync
# With options
npx @intlpullhq/cli sync --output ./locales --format json --languages en,es
# Quiet mode for CI
npx @intlpullhq/cli sync --quietOptions:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| --output <dir> | Output directory |
| --format <format> | Output format |
| --languages <langs> | Languages to sync (comma-separated) |
| --platform <platform> | Platform variant |
| --quiet | Minimal output |
| --no-parallel | Disable parallel fetching (use single request) |
| -h, --help | display help for command |
Listen / Watch
Watch for translation updates and auto-sync to local files. Great for development.
# Start watching
npx @intlpullhq/cli listen
# Or use alias
npx @intlpullhq/cli watch
# With custom settings
npx @intlpullhq/cli listen \
--output ./messages \
--format json \
--interval 5 \
--timeout 300
# Sync once and exit
npx @intlpullhq/cli listen --onceOptions:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| --output <dir> | Output directory (auto-detected from .intlpull.json) |
| --format <format> | Output format |
| --languages <langs> | Languages to sync (comma-separated) |
| --branch <branch> | Translation branch |
| --platform <platform> | Platform variant |
| --interval <seconds> | Polling interval in seconds (default: "5") |
| --timeout <seconds> | Auto-stop after N seconds (default: 300 = 5 min) |
| --once | Sync once and exit (no watching) |
| --quiet | Minimal output |
| --no-parallel | Disable parallel fetching (use single request) |
| -h, --help | display help for command |
Status
Show project status and translation progress.
npx @intlpullhq/cli statusCheck & Fix
Check for and fix missing translations.
# Check for missing translations
npx @intlpullhq/cli check --source en --target es,fr,de
# Output report to file
npx @intlpullhq/cli check --output report.json
# Check and auto-fix
npx @intlpullhq/cli check --fix
# Auto-fix with preview
npx @intlpullhq/cli fix --source en --dry-run
# Fix specific languages
npx @intlpullhq/cli fix --target es,frCheck Options:
| Option | Description |
|--------|-------------|
| --source <lang> | Source language (default: en) |
| --target <langs> | Target languages (comma-separated) |
| --output <file> | Output report file |
| --fix | Auto-fix missing translations |
| --fail-on-missing | Exit with code 2 if translations are missing |
| --output-format <format> | Output format: text or json (default: "text") |
Fix Options:
| Option | Description |
|--------|-------------|
| --source <lang> | Source language (default: en) |
| --target <langs> | Target languages (comma-separated) |
| --dry-run | Preview without fixing |
Diff
Show what would change on upload/download.
npx @intlpullhq/cli diff --source en --target esOptions:
| Option | Description |
|--------|-------------|
| --source <lang> | Source language |
| --target <lang> | Target language to compare |
Drift
Reconcile three sources of truth — source code (keys referenced via t('…'),
<FormattedMessage>, $t('…'), etc.), local translation files, and the TMS —
and report (or clean up) keys that have drifted apart. Unlike diff (files ↔ TMS only),
drift also scans your source code.
# Report only (read-only)
npx @intlpullhq/cli drift
# Machine-readable + CI gate (fail if code references keys that don't exist in the TMS)
npx @intlpullhq/cli drift --json --fail-on code-missing-tms
# Clean up truly-dead keys (asks you to type the count to confirm)
npx @intlpullhq/cli drift --prune-tms # delete TMS keys absent from BOTH code and files
npx @intlpullhq/cli drift --prune-local # delete local keys absent from BOTH TMS and code
npx @intlpullhq/cli drift --prune-tms=unused # delete TMS keys not referenced in code
npx @intlpullhq/cli drift --prune-tms --dry-run # preview onlyBuckets:
| Bucket (--fail-on id) | Meaning | Action |
|-------------------------|---------|--------|
| code-missing-tms | Referenced in code, missing from TMS | Define/push the key (often a typo) |
| tms-unused | In TMS, not referenced in code | Possibly dead → --prune-tms |
| files-ahead | In local files, not in TMS | intlpull upload |
| tms-ahead | In TMS, not in local files | intlpull download |
Options:
| Option | Description |
|--------|-------------|
| --source <lang> | Source language (default: config or en) |
| --scan-paths <globs> | Comma-separated globs to scan for key usage |
| --exclude <globs> | Comma-separated globs to exclude from scanning |
| --key-prefix <prefix> | Prefix to resolve namespace-relative code keys |
| --ignore <regexes> | Comma-separated regexes of keys to ignore (e.g. dynamic keys) |
| --strict-match | Exact namespace.key matching only (no fuzzy bare-key match) |
| --project <id> | Project ID (auto-detected with a project-scoped API key) |
| --branch <name> | Branch (default: git branch or main) |
| --json | Machine-readable JSON output (report only) |
| --fail-on <buckets> | Exit 2 if listed buckets are non-empty (comma list or any) |
| --prune-tms [mode] | Delete drifted TMS keys: dead (default) or unused |
| --prune-local [mode] | Delete drifted local keys: dead (default) or unused |
| --dry-run | Preview deletions without applying |
| --yes | Skip the interactive typed confirmation (required in CI) |
Cleanup is destructive.
driftis read-only by default. A--prune-*flag shows the deletion list and asks you to type the exact count to confirm; in CI/non-TTY it refuses unless--yesis passed.deadmode (the default) only deletes keys absent from both other sources, so it never removes un-pushed-but-used keys.
Note: code scanning is regex-based and cannot resolve dynamic keys (
t(`x.${id}`)) oruseTranslations('ns')scoping. Use--key-prefix,--ignore <regex>, or--strict-matchto tune results.
Import
Import existing translation files into IntlPull.
# Import from directory
npx @intlpullhq/cli import --path ./messages --format json
# Import with glob pattern
npx @intlpullhq/cli import --path ./locales --pattern "*.json"
# Preview import
npx @intlpullhq/cli import --path ./messages --dry-run
# Import to specific namespace
npx @intlpullhq/cli import --path ./messages --namespace adminOptions:
| Option | Description |
|--------|-------------|
| --path <path> | Path to file or directory |
| --pattern <pattern> | Glob pattern (e.g., *.json) |
| --format <format> | File format: json, yaml, ts, ios, android (default: json) |
| --project <id> | Project ID |
| --language <code> | Target language (auto-detect if omitted) |
| --namespace <name> | Target namespace (default: common) |
| --branch <name> | Branch to import to |
| --platform <platform> | Platform to tag keys: ios, android, web |
| --dry-run | Preview without importing |
| --update-existing | Update existing translations (default: true) |
| --skip-existing | Skip if key already exists |
| --detect-icu-plurals | Detect ICU plural syntax (default: true) |
Export
Export translations as a ZIP archive or directly to GitHub targets.
Export to File
# Export all as JSON
npx @intlpullhq/cli export file --format json --output ./translations.zip
# Export specific languages
npx @intlpullhq/cli export file --languages en,es,fr
# Export for mobile platforms
npx @intlpullhq/cli export file --format android --output ./android-strings.zip
npx @intlpullhq/cli export file --format ios --output ./ios-strings.zipOptions:
| Option | Description |
|--------|-------------|
| --format <format> | Format: json, yaml, ts, android, ios, xliff, po, arb, stringsdict, xcstrings, csv |
| --languages <langs> | Languages to export (comma-separated) |
| --output <file> | Output file path |
| --version <version> | Version to export |
| --branch <branch> | Translation branch |
| --platform <platform> | Platform variant (auto-sets --format) |
| --project <id> | Project ID |
Export to GitHub Targets
# Export to a specific target
npx @intlpullhq/cli export targets --target web-frontend
# Export to all configured targets
npx @intlpullhq/cli export targets --all-targets
# Preview without pushing
npx @intlpullhq/cli export targets --all-targets --dry-runOptions:
| Option | Description |
|--------|-------------|
| --target <nameOrId> | Specific target to export to |
| --all-targets | Export to all configured targets |
| --platform <platform> | Export to all targets for a platform (ios/android/web/flutter) |
| --project <id> | Project ID |
| --languages <langs> | Languages to export (comma-separated) |
| --include-drafts | Include draft translations |
| --dry-run | Preview without pushing to GitHub |
| --json | Output results as JSON for CI/CD |
| --concurrency <n> | Max parallel exports (default: 3) |
| --commit-message <msg> | Custom commit message |
| --pr-title <title> | Custom PR title |
| -q, --quiet | Suppress output except errors |
OTA Releases
Publish and manage over-the-air releases for mobile/web SDKs.
# Publish new release
npx @intlpullhq/cli publish 1.2.0
# Auto-version
npx @intlpullhq/cli publish
# Preview without publishing
npx @intlpullhq/cli publish --dry-run
# List releases
npx @intlpullhq/cli releases list
# Delete a release
npx @intlpullhq/cli releases delete <releaseId>Publish Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| --branch <branch> | Translation branch (auto-detected from git) |
| --dry-run | Preview without publishing |
| -q, --quiet | Only output version on success |
Releases Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| --limit <n> | Number of releases to show (default: 10) |
| -q, --quiet | Machine-readable output |
Projects
Manage IntlPull projects.
# List all projects
npx @intlpullhq/cli projects list
# Create new project
npx @intlpullhq/cli projects create "My App" --languages en,es,fr
# Link directory to project
npx @intlpullhq/cli projects link <project-id>Create Options:
| Option | Description |
|--------|-------------|
| --source-language <lang> | Source language (default: en) |
| --languages <langs> | Supported languages (comma-separated) |
Migration
Migrate from other platforms or local files.
Migrate Local Files
# Migrate from current directory
npx @intlpullhq/cli migrate files
# Migrate from specific path
npx @intlpullhq/cli migrate files ./locales
# With options
npx @intlpullhq/cli migrate files ./messages \
--project "My App" \
--source en \
--pattern "*.json" \
--dry-runOptions:
| Option | Description |
|--------|-------------|
| --project <name> | Project name (prompts if not provided) |
| --source <lang> | Source/base language (default: en) |
| --namespace <name> | Namespace for keys (default: common) |
| --pattern <glob> | File pattern (default: *.json) |
| --concurrency <n> | Max parallel uploads (default: 5) |
| --dry-run | Preview without importing |
| -y, --yes | Skip confirmation prompts |
Migrate from Competitors
# Migrate from Lokalise
npx @intlpullhq/cli migrate from lokalise --api-key <key>
# Migrate from Crowdin
npx @intlpullhq/cli migrate from crowdin --api-key <key> --project-id <id>
# Migrate from Phrase
npx @intlpullhq/cli migrate from phrase --api-key <key>
# Preview migration
npx @intlpullhq/cli migrate from lokalise --api-key <key> --dry-runOptions:
| Option | Description |
|--------|-------------|
| --api-key <key> | API key for source platform (required in CI/CD) |
| --project-id <id> | Specific project to migrate |
| --to <id> | Target IntlPull project ID |
| --dry-run | Preview without importing |
Note: In non-interactive environments (CI/CD), the
--api-keyflag is required.
Workflows (Enterprise)
Manage approval workflows for translation review.
# Check workflow status
npx @intlpullhq/cli workflow status
# List pending approvals
npx @intlpullhq/cli workflow pending
# Approve a translation
npx @intlpullhq/cli workflow approve <translationId>
npx @intlpullhq/cli workflow approve <translationId> -m "Looks good!"
# Reject a translation (reason required)
npx @intlpullhq/cli workflow reject <translationId> -m "Needs revision"Options:
| Command | Options |
|---------|---------|
| status | --project <id> |
| pending | --project <id> |
| approve | --project <id>, -m, --comment <message> |
| reject | --project <id>, -m, --reason <message> (required) |
Email Templates
Manage React Email template translations.
# Upload HTML email templates to IntlPull
npx @intlpullhq/cli emails upload --dir ./emails
# Download translations for email templates
npx @intlpullhq/cli emails download --output ./emails/translations --format json
# List templates
npx @intlpullhq/cli emails list
# Check status
npx @intlpullhq/cli emails statusUpload Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| -d, --dir <path> | Emails directory (default: ./emails) |
| --dry-run | Preview without uploading |
Download Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| -o, --output <dir> | Output directory |
| --languages <langs> | Languages to download (comma-separated) |
| --format <format> | Output format: json, ts (default: json) |
Documents
Manage document translations (PDF, DOCX, etc.).
# List documents
npx @intlpullhq/cli documents list
# Upload document for translation
npx @intlpullhq/cli documents upload --file ./manual.pdf --target es,fr,de
# Download translated document
npx @intlpullhq/cli documents download --id <docId> --lang es --output ./manual-es.pdfUpload Options:
| Option | Description |
|--------|-------------|
| --file <path> | Path to file (required) |
| --project <id> | Project ID |
| --source <lang> | Source language |
| --target <langs> | Target languages (comma-separated) |
Download Options:
| Option | Description |
|--------|-------------|
| --id <docId> | Document ID (required) |
| --lang <lang> | Target language (required) |
| --project <id> | Project ID |
| --output <path> | Output file path |
Zendesk Integration
Sync Help Center articles with Zendesk.
# Check integration status
npx @intlpullhq/cli zendesk status
# Connect to Zendesk
npx @intlpullhq/cli zendesk connect \
--subdomain mycompany \
--email [email protected] \
--token <api-token>
# Pull articles from Zendesk
npx @intlpullhq/cli zendesk sync --direction pull
# Push translations to Zendesk
npx @intlpullhq/cli zendesk sync --direction push --locale es
# Disconnect
npx @intlpullhq/cli zendesk disconnectConnect Options:
| Option | Description |
|--------|-------------|
| --subdomain <subdomain> | Zendesk subdomain (required) |
| --email <email> | Admin email address (required) |
| --token <token> | Zendesk API token (required) |
Sync Options:
| Option | Description |
|--------|-------------|
| --direction <direction> | Sync direction: pull, push (default: pull) |
| --locale <locale> | Target locale (required for push) |
| --dry-run | Preview without syncing |
Project Management & Tools
IntlPull CLI includes several commands for managing various aspects of your i18n workflow:
keys
Manage translation keys.
# List keys
npx @intlpullhq/cli keys list --search "welcome" --format table
# List keys filtered by workflow status
npx @intlpullhq/cli keys list --status approved
# List keys filtered by tags
npx @intlpullhq/cli keys list --tags "ui,forms"
# Combine status and tags filters
npx @intlpullhq/cli keys list --status in_review --tags "v2"
# Create a key
npx @intlpullhq/cli keys create "auth.login.title" --value "Log In" --namespace auth
# Update a key
npx @intlpullhq/cli keys update "auth.login.title" --tags "v2,auth"
# Delete a key
npx @intlpullhq/cli keys delete "auth.login.title"
# Bulk delete keys
npx @intlpullhq/cli keys bulk-delete --query "deprecated_*" --dry-runOptions:
| Subcommand | Options |
|------------|---------|
| list | --project <id>, --namespace <ns>, --search <query>, --tags <tags>, --status <status>, --limit <n>, --format <format> |
| create | --project <id>, --value <text>, --namespace <ns>, --context <text>, --tags <tags> |
| update | --project <id>, --context <text>, --tags <tags> |
| delete | --project <id>, --force |
| bulk-delete | --project <id>, --query <search>, --dry-run, --force |
tm
Manage Translation Memory (TM).
# Apply TM matches to project translations
npx @intlpullhq/cli tm apply --language es --threshold 85
# Search TM
npx @intlpullhq/cli tm search "Hello World" --source en --target es
# Add entry to TM
npx @intlpullhq/cli tm add "Hello World" "Hola Mundo" --source-lang en --target-lang es
# Show stats
npx @intlpullhq/cli tm statsOptions:
| Subcommand | Options |
|------------|---------|
| apply | --project <id>, --language <code>, --namespace <ns>, --threshold <n>, --dry-run |
| search | --source <lang>, --target <lang>, --min-quality <n> |
| add | --source-lang <lang>, --target-lang <lang>, --quality <n>, --context <text> |
snapshots
Capture and restore project state snapshots.
# Create a snapshot
npx @intlpullhq/cli snapshots create --name "v1.0.0-release" --description "Before major refactor"
# List snapshots
npx @intlpullhq/cli snapshots list
# Restore from a snapshot
npx @intlpullhq/cli snapshots restore <snapshotId>Options:
| Subcommand | Options |
|------------|---------|
| create | --project <id>, --name <name>, --description <desc> |
| list | --project <id> |
| restore | --project <id>, --force |
| delete | --project <id>, --force |
webhooks
Manage project webhooks.
# List webhooks
npx @intlpullhq/cli webhooks list
# Create a webhook
npx @intlpullhq/cli webhooks create --url "https://api.example.com/webhook" --events "translation.created,key.updated"
# Test a webhook
npx @intlpullhq/cli webhooks test <webhookId>Options:
| Subcommand | Options |
|------------|---------|
| list | --project <id>, --format <format> |
| create | --project <id>, --url <url>, --events <events>, --secret <secret>, --enabled |
| test | --project <id> |
| delete | --project <id>, --force |
contributors
Manage team contributors.
# List contributors
npx @intlpullhq/cli contributors list
# Invite a new team member
npx @intlpullhq/cli contributors add --email "[email protected]" --role "admin"
# Update role
npx @intlpullhq/cli contributors update <userId> --role "viewer"Options:
| Subcommand | Options |
|------------|---------|
| list | --project <id>, --format <format> |
| add | --project <id>, --email <email>, --role <role>, --languages <langs>, --message <msg> |
| update | --project <id>, --role <role>, --languages <langs> |
| remove | --project <id>, --force |
cleanup
Detect and delete orphaned translation keys that are no longer used in your codebase.
# Preview orphans (dry run)
npx @intlpullhq/cli cleanup --scan-codebase --dry-run
# Delete orphans older than 30 days
npx @intlpullhq/cli cleanup --days 30Options:
| Option | Description |
|--------|-------------|
| --project <id> | Project ID |
| --scan-codebase | Scan local files for key usage patterns |
| --dry-run | Preview orphans without deleting |
| --days <n> | Only consider keys not updated in N days |
| -f, --force | Skip confirmation prompt |
CI/CD Integration
All CLI commands support non-interactive mode and work without a TTY. The CLI automatically detects when running in a non-interactive environment (CI/CD pipelines, Docker, scripts) and adjusts its behavior accordingly.
Non-Interactive Mode
Commands automatically work in non-interactive environments:
| Command | Non-Interactive Behavior |
|---------|-------------------------|
| upload | Auto-confirms plan limit warnings |
| download | Works with --quiet flag |
| init | Auto-detects framework (use -y for explicit) |
| sync | Full support |
| check / fix | Full support |
| diff | Full support |
| import / export | Full support |
| migrate files | Use --yes flag to skip prompts |
| migrate from | Requires --api-key flag |
Docker
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npx @intlpullhq/cli sync --quiet
RUN npm run buildGitHub Actions
name: Sync Translations
on: [push]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Sync translations
env:
INTLPULL_API_KEY: ${{ secrets.INTLPULL_API_KEY }}
run: npx @intlpullhq/cli sync --quietGitLab CI
sync-translations:
stage: build
image: node:20-alpine
script:
- npx @intlpullhq/cli sync --quiet
variables:
INTLPULL_API_KEY: $INTLPULL_API_KEY
only:
- mainVercel / Netlify
Add INTLPULL_API_KEY to your environment variables and update your build command:
npx @intlpullhq/cli sync --quiet && next buildPre-commit Hook (.husky/pre-commit)
npx @intlpullhq/cli check --source enRecommended CI/CD Flags
| Flag | Description |
|------|-------------|
| --quiet or -q | Suppress output except errors |
| --dry-run | Preview changes without applying |
| --yes or -y | Skip confirmation prompts |
| --no-parallel | Disable parallel fetching (if rate limited) |
Configuration
Project Configuration (.intlpull.json)
Created by intlpull init, this file configures your project:
{
"projectId": "proj_xxxx",
"framework": "next",
"library": "next-intl",
"outputDir": "./messages",
"output": "messages/[locale]/[namespace].json",
"sourceLanguage": "en",
"namespaces": ["common", "admin"]
}Output Patterns (Namespace Support)
The CLI supports flexible output patterns with placeholders to match any i18n library structure.
Placeholders:
[locale]or{locale}- Language code (e.g.,en,es,zh-CN)[namespace]or{namespace}- Namespace name (e.g.,common,auth)
Namespaces in Native Mobile Formats (ios / android)
When exporting namespaces for mobile platforms using --platform ios or --platform android:
- iOS (
.strings): Namespaces are preserved using dot-notation. For example, a keywelcomein theauthnamespace becomes"auth.welcome" = "Welcome";. This flat representation perfectly maps to Xcode localization dictionaries and matches standards in platforms like Phrase and Lokalise. - Android (
.xml): Namespaces are prefixed using underscores. For example, a keywelcomein theauthnamespace becomes<string name="auth_welcome">Welcome</string>, fully compliant with Android Resource XML ID requirements.
Note on --platform vs --format:
The --platform flag sets the target variant when querying the backend (e.g., fetching platform-specific override keys) and auto-sets the corresponding local file --format. You only need to provide --format manually if you wish to override the default platform file type (e.g. download web keys as TS using --platform web --format ts).
Common Patterns by Framework:
| Framework/Library | Pattern | Example Output |
|------------------|---------|----------------|
| next-intl | messages/[locale].json | messages/en.json |
| next-i18next | public/locales/[locale]/[namespace].json | public/locales/en/common.json |
| react-i18next | public/locales/[locale]/[namespace].json | public/locales/en/auth.json |
| i18next | locales/[locale]/[namespace].json | locales/en/common.json |
| vue-i18n | src/locales/[locale].json | src/locales/en.json |
| Chrome Extension | _locales/[locale]/messages.json | _locales/en/messages.json |
| React Native | assets/locales/[locale]/[namespace].json | assets/locales/en/common.json |
Configure via CLI flag:
# Namespaced output (creates separate files per namespace)
npx @intlpullhq/cli download --output "public/locales/[locale]/[namespace].json"
# Flat output (merges all namespaces into one file)
npx @intlpullhq/cli download --output "messages/[locale].json"
# Namespace-first structure
npx @intlpullhq/cli download --output "locales/[namespace]/[locale].json"Configure in .intlpull.json:
{
"projectId": "proj_xxxx",
"output": "public/locales/[locale]/[namespace].json"
}Important: Ensure your i18n library's configuration matches the output path:
| Library | Config Update Required |
|---------|----------------------|
| next-intl | Update src/i18n/request.ts to load from the correct path |
| i18next / react-i18next | Update backend.loadPath in your i18n config |
| Chrome Extension | Must use _locales/[locale]/messages.json (Chrome requirement) |
iOS String Tables
iOS uses .strings files organized by "String Tables" (separate files for different modules). The CLI supports this natively:
Default Behavior:
- The
commonnamespace maps toLocalizable.strings(accessed withouttableName:in Swift) - Other namespaces become their own String Table files (e.g.,
settings→Settings.strings)
# Download with iOS String Tables
npx @intlpullhq/cli download --format ios --default-namespace common
# Custom namespace-to-table mapping
npx @intlpullhq/cli download --format ios --table "common:Localizable,settings:UserSettings,auth:Authentication"
# Output structure:
# ios/en.lproj/Localizable.strings (common namespace)
# ios/en.lproj/UserSettings.strings (settings namespace)
# ios/en.lproj/Authentication.strings (auth namespace)Swift Usage:
// From Localizable.strings (default table)
NSLocalizedString("welcome", comment: "")
// From Settings.strings (custom table)
NSLocalizedString("theme_dark", tableName: "UserSettings", comment: "")
// SwiftUI
Text("welcome") // Localizable.strings
Text("theme_dark", tableName: "UserSettings") // UserSettings.stringsCLI Options for iOS:
| Option | Description |
|--------|-------------|
| --default-namespace <ns> | Namespace that maps to Localizable.strings (default: common) |
| --table <mapping> | Custom namespace→table mapping (e.g., common:Localizable,settings:Settings) |
Android Multi-XML Files
Android commonly splits translations into multiple XML files to reduce merge conflicts and organize code by feature:
Default Behavior:
- The
commonnamespace maps tostrings.xml(the main resource file) - Other namespaces use a prefix (default:
strings_) →strings_settings.xml,strings_auth.xml
# Download with Android multi-XML
npx @intlpullhq/cli download --format android --default-namespace common
# Custom prefix for non-default namespaces
npx @intlpullhq/cli download --format android --namespace-prefix "app_"
# Output: strings.xml, app_settings.xml, app_auth.xml
# Custom namespace-to-filename mapping
npx @intlpullhq/cli download --format android --xml-file "common:strings,settings:strings_settings,billing:billing_strings"
# Output structure:
# res/values/strings.xml (default locale, common namespace)
# res/values/strings_settings.xml (default locale, settings namespace)
# res/values-es/strings.xml (Spanish, common namespace)
# res/values-es/strings_settings.xmlKey Collision Warning:
Android requires globally unique keys across ALL XML files (they compile to a single R.string.* namespace). The CLI automatically detects and warns about key collisions:
Warning: 3 key collision(s) detected across namespaces.
Android requires globally unique keys across all XML files.
Colliding keys:
- "title" in: common, settings
- "submit" in: common, auth
- "cancel" in: settings, auth
Consider prefixing keys with namespace (e.g., "settings_title" instead of "title").CLI Options for Android:
| Option | Description |
|--------|-------------|
| --default-namespace <ns> | Namespace that maps to strings.xml (default: common) |
| --namespace-prefix <prefix> | Prefix for non-default namespace files (default: strings_) |
| --xml-file <mapping> | Custom namespace→filename mapping |
Advanced Config (.intlpull.json)
For complex setups, configure namespace mappings in your project config:
{
"projectId": "proj_xxxx",
"platform": ["ios", "android"],
"sources": [
{ "file": "ios/en.lproj/Localizable.strings", "namespace": "common", "format": "ios" },
{ "file": "ios/en.lproj/Settings.strings", "namespace": "settings", "format": "ios" },
{ "file": "android/app/src/main/res/values/strings.xml", "namespace": "common", "format": "android" }
],
"targets": [
{ "file": "ios/[locale].lproj/Localizable.strings", "namespace": "common", "format": "ios" },
{ "file": "ios/[locale].lproj/Settings.strings", "namespace": "settings", "format": "ios" },
{ "file": "android/app/src/main/res/values-[locale]/strings.xml", "namespace": "common", "format": "android" },
{ "file": "android/app/src/main/res/values-[locale]/strings_settings.xml", "namespace": "settings", "format": "android" }
],
"namespaceRename": {
"common": { "ios": "Localizable", "android": "strings" },
"settings": { "ios": "Settings", "android": "strings_settings" }
},
"platformDefaults": {
"ios": { "defaultTable": "Localizable", "extension": ".strings" },
"android": { "defaultFile": "strings", "prefix": "strings_" }
}
}Environment Variables
| Variable | Description |
|----------|-------------|
| INTLPULL_API_KEY | API key for authentication (required) |
| INTLPULL_PROJECT_ID | Default project ID |
| INTLPULL_DEBUG | Enable debug logging (true/false) |
Tips & Best Practices
| Practice | Recommendation |
|----------|----------------|
| 🔑 API Keys | Use project-scoped keys for auto-detection (no --project flag needed) |
| ⚡ Parallel Mode | Enabled by default — downloads namespaces concurrently |
| 🤫 CI/CD Output | Use --quiet for minimal, machine-friendly logs |
| 🔍 Preview Changes | Use --dry-run before applying destructive operations |
| 🌿 Branch Workflows | CLI auto-detects git branch for branch-based translations |
| 📦 Namespace Files | Use output patterns like [locale]/[namespace].json for organized files |
Support
| Resource | Link | |:---------|:-----| | 📚 Documentation | https://intlpull.com/docs | | 📧 Email Support | [email protected] |
