CI/CD

GitHub Actions

The action is a composite action: it installs @the-i18n-kit/cli on the runner, runs the-i18n-cli translate, and opens a pull request with whatever the run wrote. Every input and output is generated from action.yml in the action reference.

A Minimal Workflow

.github/workflows/i18n.yml
name: i18n

on:
  pull_request:
    paths:
      - 'i18n/locales/en.json'

permissions:
  contents: write
  pull-requests: write

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: fabkho/the-i18n-kit@main
        with:
          provider: google
          model: gemini-2.5-flash
          api_key: ${{ secrets.GEMINI_API_KEY }}
          layer: common

Three things in that workflow are load-bearing:

  • actions/checkout@v4 first. The action runs the CLI against the working directory and commits from it. Without a checkout there is no repository to scan and no repository to commit to.
  • contents: write and pull-requests: write. The default GITHUB_TOKEN is read-only in many repositories. The action pushes a branch and calls gh pr create with that token, and both calls need those scopes. Repositories and organizations can also disable pull request creation by Actions entirely, under Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests"; with it off, the pull request step fails however the token is scoped.
  • layer. The action requires a layer name and always passes --layer to the CLI. A layer is a scoped locale directory together with the code that owns it — a Nuxt layer, a workspace package, a shared UI library, or an app in a monorepo. Run the-i18n-cli status locally to see the names your project resolves, or read Layers. Projects with one locale directory usually have one layer named root.

Pin the action to a tag rather than @main once you have a version you trust.

What the Action Runs

Five steps, in order:

  1. Node.js setup via actions/setup-node@v4, pinned to Node 22.
  2. Install. npm install -g the-i18n-cli@<cli_version> plus the SDK for the chosen provider — openai, @anthropic-ai/sdk or @google/genai. The SDKs are optional peer dependencies of the CLI, so only the one you use gets installed. An unrecognized provider fails here. Set cli_version: skip to skip this step when a build earlier in the job already put the CLI on the PATH.
  3. Translate. the-i18n-cli translate --json in working_directory, with api_key routed into the provider's own environment variable (OPENAI_API_KEY, ANTHROPIC_API_KEY or GEMINI_API_KEY). The optional inputs map onto flags: locales to --targets, source_locale to --ref, keys to --keys, batch_size to --batch-size, and the two booleans to --dry-run and --fail-on-failed.
  4. Pull request, unless dry_run is true or create_pr is false.
  5. Gate check, last, which fails the job if step 3 tripped a gate.

The step outputs translated_count and failed_count are read out of the JSON result for the log and for downstream steps. They never decide whether the job passes — that comes from the CLI's exit code.

The Pull Request It Opens

The action commits on a new branch and opens the pull request with the gh CLI. Defaults, all overridable through the inputs:

PartDefault
Branchi18n/translate-missing-<unix-timestamp>
Basebase_branch, else the pull request's base ref, else the branch that triggered the workflow
Commit messagechore(i18n): translate missing keys via <provider>
Titlechore(i18n): translate missing keys (<layer>)
BodyA table naming the provider, the model, the layer and the number of keys translated
AuthorThe workflow actor, with their users.noreply.github.com address

When the run wrote nothing — every key already translated — the step reports "No translation changes to commit", leaves the pr_url output empty, and exits 0. No empty pull request is opened.

The commit stages the whole working directory (git add .), not only locale files. If an earlier step in the same job builds, formats or otherwise writes to the checkout, those changes land in the translation pull request too. Give the action its own job, or its own checkout via working_directory.

With dry_run: true no files are written and no pull request is opened; the run reports what it would have translated as a workflow notice.

When the Job Fails

The CLI reports three outcomes as exit codes, and the action treats them differently. A gate tripping means the run did its job and found something you asked to be told about; a failed run means the tool fell over and its counts say nothing about your project.

SituationExitWhat the action does
Everything translated, no gate requested0Opens the pull request, job green
Some keys failed, fail_on_failed off (the default)0Opens the pull request, emits a warning annotation naming the count, job green
Some keys failed, fail_on_failed: true2Records gate_tripped, still opens the pull request, then fails the job in the final step
Nothing translated at all — unusable API key, unreadable project1Fails the translate step immediately, no pull request
Unknown provider1Fails during install or translate with an error annotation
Branch push or gh pr create rejectednon-zeroFails the pull request step — check the token permissions above

A tripped gate does not cost you the translations. The gate is recorded in the gate_tripped output, the pull request is opened, and only then does a final step exit 2.

Partial failures are not a gate by default. A run that wrote 795 keys and lost 141 opens the pull request with the 795; the 141 stay missing, so the next run retries them. Set fail_on_failed: true when you would rather see that as a red job than as a warning in a log nobody reads.

Inputs and Outputs

The full table — every input with its required status and default, and every output — is generated from action.yml and lives in the action reference. It is regenerated in CI, so an input listed there is an input the action accepts.

Next

  • GitLab CI for the equivalent on GitLab, including the two scanning jobs the action has no counterpart for.
  • Exit codes and CI gates for the full gate list across all commands.
Copyright © 2026