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
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@v4first. 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: writeandpull-requests: write. The defaultGITHUB_TOKENis read-only in many repositories. The action pushes a branch and callsgh pr createwith 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--layerto 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. Runthe-i18n-cli statuslocally to see the names your project resolves, or read Layers. Projects with one locale directory usually have one layer namedroot.
Pin the action to a tag rather than @main once you have a version you trust.
What the Action Runs
Five steps, in order:
- Node.js setup via
actions/setup-node@v4, pinned to Node 22. - Install.
npm install -g the-i18n-cli@<cli_version>plus the SDK for the chosen provider —openai,@anthropic-ai/sdkor@google/genai. The SDKs are optional peer dependencies of the CLI, so only the one you use gets installed. An unrecognizedproviderfails here. Setcli_version: skipto skip this step when a build earlier in the job already put the CLI on thePATH. - Translate.
the-i18n-cli translate --jsoninworking_directory, withapi_keyrouted into the provider's own environment variable (OPENAI_API_KEY,ANTHROPIC_API_KEYorGEMINI_API_KEY). The optional inputs map onto flags:localesto--targets,source_localeto--ref,keysto--keys,batch_sizeto--batch-size, and the two booleans to--dry-runand--fail-on-failed. - Pull request, unless
dry_runistrueorcreate_prisfalse. - 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:
| Part | Default |
|---|---|
| Branch | i18n/translate-missing-<unix-timestamp> |
| Base | base_branch, else the pull request's base ref, else the branch that triggered the workflow |
| Commit message | chore(i18n): translate missing keys via <provider> |
| Title | chore(i18n): translate missing keys (<layer>) |
| Body | A table naming the provider, the model, the layer and the number of keys translated |
| Author | The 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.
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.
| Situation | Exit | What the action does |
|---|---|---|
| Everything translated, no gate requested | 0 | Opens the pull request, job green |
Some keys failed, fail_on_failed off (the default) | 0 | Opens the pull request, emits a warning annotation naming the count, job green |
Some keys failed, fail_on_failed: true | 2 | Records gate_tripped, still opens the pull request, then fails the job in the final step |
| Nothing translated at all — unusable API key, unreadable project | 1 | Fails the translate step immediately, no pull request |
Unknown provider | 1 | Fails during install or translate with an error annotation |
Branch push or gh pr create rejected | non-zero | Fails 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.