Integrations
CI/CD Integration
Check translation files on every pull request, push new source strings automatically, and pull up-to-date translations in every deployment.
In CI, there’s no config file to write and no login step: set your API key as a secret environment variable called LANGCTL_API_KEY, and the project, format and paths come from the langctl.json committed in your repository (create it locally with langctl init).
GitHub Actions
Check Translations on Pull Requests
Fail the build when committed translation files are out of date:
# .github/workflows/i18n.yml
name: translations
on: [pull_request]
jobs:
i18n:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx langctl@0 pull --check # fail if committed files are out of date
env:
LANGCTL_API_KEY: ${{ secrets.LANGCTL_API_KEY }} pull --check doesn’t write anything. It exits with code 7 if any file would change, so the fix is to run langctl pull locally and commit the result.
Other useful CI commands:
langctl push --dry-run --json # preview what a push would change
langctl push --publish # upload and publish new source strings (e.g. on main)
langctl pull --require-complete # fail if any language is missing translations
langctl whoami --json # verify the key, org and scopes Push New Strings from Main
Upload new keys from your source-language file whenever main changes:
name: push strings
on:
push:
branches: [main]
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx langctl@0 push --publish
env:
LANGCTL_API_KEY: ${{ secrets.LANGCTL_API_KEY }} By default push uploads only the source language and only adds keys that don’t exist yet; existing translations are left alone unless you pass --overwrite. Drop --publish if new keys should stay drafts until someone reviews them. This job needs a key with the translations:write scope.
Pull at Build Time
If you’d rather not commit translation files, pull them right before building:
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Pull Translations
env:
LANGCTL_API_KEY: ${{ secrets.LANGCTL_API_KEY }}
run: npx langctl@0 pull
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy
run: npm run deploy GitLab CI
check_translations:
image: node:22
script:
- npx langctl@0 pull --check
# LANGCTL_API_KEY is set as a masked CI/CD variable in project settings
build_and_deploy:
image: node:22
script:
- npx langctl@0 pull
- npm ci
- npm run build
- npm run deploy
only:
- main CircleCI
version: 2.1
jobs:
build_and_deploy:
docker:
- image: cimg/node:22.0
steps:
- checkout
- run:
name: Pull Translations
command: npx langctl@0 pull
- run:
name: Build
command: |
npm ci
npm run build
- run:
name: Deploy
command: npm run deploy
workflows:
version: 2
build_deploy:
jobs:
- build_and_deploy Exit Codes
Every failure has its own exit code, so scripts can react to it:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (including a cancelled confirmation, --require-complete failures) |
| 2 | Invalid usage — bad flag, unknown language/format, refused without --yes |
| 3 | Not authenticated, invalid/revoked key, or missing permission (scope) |
| 4 | Project, key, member or file not found |
| 5 | Network error, timeout, or the API is unavailable |
| 6 | Plan limit reached |
| 7 | pull --check: files are out of date |
With --json, errors are also printed to stdout as JSON with a message, exitCode and hint.
Best Practices
Use Secrets and Minimal Scopes
Never commit API keys to git. Use CI/CD secrets:
- GitHub: Repository Settings > Secrets
- GitLab: Settings > CI/CD > Variables
- CircleCI: Project Settings > Environment Variables
Give each key only the scopes the job needs — translations:read is enough for pull.
Non-Interactive by Default
In CI, langctl never prompts and never animates: spinners are off when CI is set or output isn’t a terminal, and destructive commands (such as keys delete or projects delete) refuse to run without --yes. Requests time out after 30 seconds by default (LANGCTL_TIMEOUT or --timeout to change it), and idempotent requests are retried with backoff on network errors, 429 and 5xx responses.
Pull in Build Scripts
Add a pull to your build process:
{
"scripts": {
"build": "vite build",
"prebuild": "langctl pull"
}
} Pin the CLI Version
npx langctl@0 always runs the latest 0.x release without a global install. Pin an exact version (for example npx langctl@0.3.0) if you want CI to change only when you upgrade.
Scheduled Syncs
Update translations daily without manual deploys:
on:
schedule:
- cron: '0 2 * * *' # 2 AM daily Tip
Scheduled workflows can keep translations updated without manual intervention. Because output is sorted and has no timestamps, a scheduled langctl pull only changes files when translations actually change.
Next Steps
- Version Control Integration - Git workflows
- CLI Export & Import - Pull, push, export and import reference
- Platform Integrations - Platform guides