LangSync

CI Integration

Fail pull requests when locales drift. Recipes for GitHub Actions, GitLab CI, and CircleCI.

langsync validate (and find-missing) exit with code 1 when a locale has missing or extra keys, so they drop directly into any CI system as a quality gate.

Pair with --reporter json if you want machine-readable output for custom annotations or PR comments.

Official GitHub Action

For GitHub, the quickest setup is the composite langsync action. It runs validate, optionally posts a summary comment on the pull request, and fails the job when issues are found.

.github/workflows/i18n.yml
name: i18n
 
on:
  pull_request:
    branches: [main]
 
permissions:
  contents: read
  pull-requests: write
 
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - uses: mariokreitz/langsync/packages/github-action@v2
        with:
          working-directory: .
          fail-on-issues: true
          comment: true

AI translate in CI

The action can optionally run langsync translate before validating, to fill empty values with an AI provider. It is off by default. Enable it with translate: true and pass the API key from a secret (never hard-code it). See AI translate in CI below.

AI translate in CI

Set translate: true and provide the provider, the API key (from a repository secret), and optionally a model. While DeepL, Anthropic, and Gemini are experimental, the action sets LANGSYNC_AI_EXPERIMENTAL=1 for the translate step automatically.

.github/workflows/i18n.yml
name: i18n
 
on:
  pull_request:
    branches: [main]
 
permissions:
  contents: read
  pull-requests: write
 
jobs:
  translate-and-validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - uses: mariokreitz/langsync/packages/github-action@v2
        with:
          translate: true
          ai-provider: openai
          ai-api-key: ${{ secrets.OPENAI_API_KEY }}
          ai-model: gpt-5-mini
          # Recommended on PRs: report only, do not write files or incur a full bill.
          ai-dry-run: true
          fail-on-issues: true
          comment: true
InputDefaultDescription
translatefalseRun langsync translate before validating.
ai-provideropenaiopenai, deepl, anthropic, or gemini.
ai-api-keyProvider API key. Pass via a secret. Required when translate: true.
ai-modelProvider model id. Uses the provider default when empty.
ai-dry-runfalseReport planned translations without writing files.

Translate incurs real API costs

langsync translate calls a paid AI API for every empty key on every run. Use ai-dry-run: true on pull-request checks and reserve a full translate for post-merge or scheduled jobs. Use the --max-keys flag (CLI) to cap how many empty values a single run will translate and bound spend.

GitHub Actions

.github/workflows/i18n.yml
name: i18n
 
on:
  pull_request:
    paths:
      - 'src/i18n/**'
      - 'langsync.config.*'
      - '.github/workflows/i18n.yml'
 
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Enable Corepack
        run: corepack enable
 
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
 
      - run: pnpm install --frozen-lockfile
 
      - name: Validate translations
        run: pnpm langsync validate --reporter json

Why Corepack?

Corepack reads the packageManager field from your package.json and provisions the exact pnpm/yarn version. It's bundled with Node 22, so no third-party setup action is required.

GitLab CI

.gitlab-ci.yml
i18n:validate:
  image: node:22-alpine
  before_script:
    - corepack enable
    - pnpm install --frozen-lockfile
  script:
    - pnpm langsync validate --reporter json
  rules:
    - changes:
        - 'src/i18n/**/*'
        - 'langsync.config.*'

CircleCI

.circleci/config.yml
version: 2.1
 
jobs:
  i18n-validate:
    docker:
      - image: cimg/node:22.0
    steps:
      - checkout
      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - run: pnpm langsync validate --reporter json
 
workflows:
  pr-checks:
    jobs:
      - i18n-validate

Optional: post a PR comment

The JSON reporter's output is small enough to render inline in a PR comment. Combine langsync validate --reporter json with actions/github-script:

- name: Run validate
  id: validate
  continue-on-error: true
  run: |
    REPORT=$(pnpm langsync validate --reporter json)
    echo "report<<EOF" >> $GITHUB_OUTPUT
    echo "$REPORT" >> $GITHUB_OUTPUT
    echo "EOF" >> $GITHUB_OUTPUT
 
- uses: actions/github-script@v7
  if: steps.validate.outcome == 'failure'
  with:
    script: |
      const body = '```json\n' + ${{ toJSON(steps.validate.outputs.report) }} + '\n```';
      github.rest.issues.createComment({
        ...context.repo,
        issue_number: context.issue.number,
        body: `**LangSync validation failed**\n\n${body}`,
      });

Exit codes recap

CommandExit 0Exit 1
validateNo missing or extra keysAny missing / extra key (or runtime error)
find-missingNo missing keysAt least one locale missing keys
syncAlways (use --dry-run to preview)
export excelAlways
import excelAlways

Empty values ("") are reported as warnings by validate — they do not change the exit code.

On this page