Skip to content

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
# .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:

terminal
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:

.github/workflows/push-strings.yml
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:

.github/workflows/deploy.yml
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

.gitlab-ci.yml
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

.circleci/config.yml
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:

CodeMeaning
0Success
1Error (including a cancelled confirmation, --require-complete failures)
2Invalid usage — bad flag, unknown language/format, refused without --yes
3Not authenticated, invalid/revoked key, or missing permission (scope)
4Project, key, member or file not found
5Network error, timeout, or the API is unavailable
6Plan limit reached
7pull --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:

json
{
"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:

yaml
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