---
title: "Use the GitHub Action"
description: "Run verbatra in GitHub Actions with the composite action: translate on a push or gate a pull request, with annotations and a job summary."
type: "how-to"
---
# Use the GitHub Action

Run verbatra in GitHub Actions with the composite action: translate on a push or gate a pull request, with annotations and a job summary.

Use the action to run verbatra in a GitHub Actions job and see what happened. It runs `translate`, `check` or `diff` with `--json`; for any other command or flag, such as `--prune`, run the [CLI directly](https://verbatra.kreitz-webdev.de/docs/ci-and-exit-codes.md).

Before you start, you need:

- A verbatra config directly inside the directory the action runs in (see [Config discovery](#config-discovery)).
- An exact `@verbatra/cli` version to pin, `0.9.3` or newer.
- For `translate` only: your provider's API key as a repository secret.
- Actions Runner v2.327.1 or newer, and `verbatra/action` `v1.3.0` or newer, which this page describes and `qa`, `require-reviewed`, `sensitive`, `locales` and `needs-human` need.

## Add the action to a workflow [#add-the-action-to-a-workflow]

The action lives in [verbatra/action](https://github.com/verbatra/action) and on the [GitHub Marketplace](https://github.com/marketplace/actions/verbatra); there is no npm package. Pin it to a commit SHA, as below, or use the moving `verbatra/action@v1` tag. The old `mariokreitz/verbatra/packages/github-action` path no longer resolves.

```yaml title=".github/workflows/translate.yml"
name: translate
on:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  verbatra:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<commit-sha>
      - uses: verbatra/action@<commit-sha> # a commit on the v1 line
        with:
          version: 0.12.0 # an exact @verbatra/cli release, 0.9.3 or newer
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
```

The action installs `@verbatra/cli` and `@verbatra/sdk` at exactly that version into a scratch directory, leaving your dependencies untouched, so the job needs no install step.

### Pass the provider key [#pass-the-provider-key]

Map the key from your repository secrets under the variable your provider reads; [Providers](https://verbatra.kreitz-webdev.de/docs/providers.md#keys-come-from-the-environment) lists them. The action has no key input, so a key never travels as an input or a CLI argument.

## Gate a pull request [#gate-a-pull-request]

Set `command: check` to fail a pull request whose locale files drifted, or `command: diff` to list the pending keys as well. Both are read-only and call no provider, so they need no key and run on fork pull requests:

```yaml
      - uses: verbatra/action@<commit-sha> # a commit on the v1 line
        with:
          version: 0.12.0
          command: check
```

## Checking translation quality [#checking-translation-quality]

<AvailableFrom version="0.12.0" />

Add `qa: "true"` to `command: check` to run [`verbatra check --qa`](https://verbatra.kreitz-webdev.de/docs/cli/check.md#quality-check) over every committed translation, including values typed by hand.

```yaml
      - uses: verbatra/action@<commit-sha> # a commit on the v1 line
        with:
          version: 0.12.0
          command: check
          qa: "true"
```

- Each finding becomes one annotation titled `verbatra qa: <locale>`: an error for a value the integrity gate would refuse, a warning for a review reason.
- The step fails on any error finding. Add `qa-strict: "true"` to fail on warnings and incomplete plurals too, or `qa-severity: error` to report errors only. The CLI rejects both together.

## Require reviewed translations [#require-reviewed-translations]

<AvailableFrom version="0.12.0" />

Add `require-reviewed: "true"` to `command: check` to run [`verbatra check --require-reviewed`](https://verbatra.kreitz-webdev.de/docs/cli/check.md#require-reviewed-translations). The step fails while a machine-written translation is not approved in the committed `verbatra.provenance.json`, where [Studio](https://verbatra.kreitz-webdev.de/docs/review-in-studio.md) records review decisions. It stays read-only and keyless and combines with `qa`.

```yaml
      - uses: verbatra/action@<commit-sha> # a commit on the v1 line
        with:
          version: 0.12.0
          command: check
          require-reviewed: "true"
```

- Each locale with unapproved keys gets one [`REVIEW_REQUIRED`](https://verbatra.kreitz-webdev.de/docs/error-codes.md#review_required) error annotation naming them, and the job summary adds an `unreviewed` column.
- A `verbatra.provenance.json` that is corrupt or from a newer verbatra fails the gate with one `REVIEW_STATE_UNREADABLE` annotation.

## Pick locales and scan sources [#pick-locales-and-scan-sources]

Set `locales` to a comma-separated list such as `de,fr` to limit any command to those locales.

```yaml
      - uses: verbatra/action@<commit-sha> # a commit on the v1 line
        with:
          version: 0.12.0
          command: check
          locales: "de,fr"
```

### Sensitive content [#sensitive-content]

<AvailableFrom version="0.12.0" />

Add `sensitive: "true"` to `command: check` to run `verbatra check --sensitive`. The step fails when the source file or glossary holds content a `sensitiveData` detector or pattern matches, such as emails, keys or card numbers, and annotates each affected key without printing the match. An older `version`, or a `0.12.0-next.*` prerelease, fails the step before anything is installed.

## Human-only mode [#human-only-mode]

<AvailableFrom version="0.12.0" />

With `provider: { id: "none" }` (see [Human-only workflow](https://verbatra.kreitz-webdev.de/docs/human-only-workflow.md)), `translate` exits `3` when it leaves keys for a person. The action treats that as work to hand off, not a failure: the step passes, each affected locale gets a `NEEDS_HUMAN` warning, the job summary lists the keys, and the `needs-human` output is `"true"`. Branch on it:

```yaml
      - uses: verbatra/action@<commit-sha> # a commit on the v1 line
        id: verbatra
        with:
          version: 0.12.0
      - if: steps.verbatra.outputs.needs-human == 'true'
        run: echo "Some keys need a human translation; see the job summary."
```

Only `translate` gets this; `check` and `diff` never exit `3`, so a failure from them still fails the step.

## Config discovery [#config-discovery]

With `config-path` empty, the action requires a recognized config file directly inside `working-directory` (the repository root by default). It never looks in a parent directory, so in a monorepo point `working-directory` at the app and keep its config there:

```yaml
      with:
        version: 0.12.0
        working-directory: apps/docs
```

If no config is found, the step fails before installing the CLI and names the directory it checked. To load a config from elsewhere, set `config-path`: a relative path resolves against `working-directory`.

## Persist translations [#persist-translations]

`translate` writes the updated locale files into the runner's checkout; the action never commits. Add your own step that commits and pushes, or opens a pull request, and grant `contents: write` or `pull-requests: write` to that job only.

## Inputs [#inputs]

| Input | Default | Effect |
| --- | --- | --- |
| `version` | required | exact `@verbatra/cli` release to run, `0.9.3` or newer; a dist-tag, range or `^`/`~` prefix fails the step |
| `command` | `translate` | `translate`, `check` or `diff`; anything else fails the step |
| `config-path` | `""` | config file to load (`--config`); relative to `working-directory` |
| `working-directory` | `""` | directory to run in (`--cwd`); the repository root when empty |
| `dry-run` | `"false"` | `"true"` previews without calling a provider or writing (`--dry-run`); `translate` only |
| `qa` | `"false"` | `"true"` adds the quality check (`--qa`); `check` only |
| `qa-severity` | `""` | lowest severity to report, `error` or `warning` (`--severity`); requires `qa` |
| `qa-strict` | `"false"` | `"true"` also fails on warnings (`--strict`); requires `qa` |
| `require-reviewed` | `"false"` | `"true"` also fails on unapproved machine-written translations (`--require-reviewed`); `check` only |
| `sensitive` | `"false"` | `"true"` adds the sensitive-content scan (`--sensitive`); `check` only |
| `locales` | `""` | comma-separated target locales to limit the run to (`--locales`) |
| `node-version` | `"24"` | Node.js version to set up. CLI 0.13.0+: `22.22.0`+ on 22, 24+ (action v1.4.0: even-numbered majors only); CLI 0.12.x: `^22.18.0 || >=24`; older: `>=22.14.0` |

The boolean inputs accept only `"true"` or `"false"`. A bad boolean, or an input without the command or `qa` it needs, fails the step with exit `1` before the CLI install. The CLI rejects an unknown `qa-severity`, and `qa-severity: error` with `qa-strict`, with exit `2` after it.

## Outputs [#outputs]

| Output | Value |
| --- | --- |
| `needs-human` | `"true"` when `translate` exited `3` and left keys for a person, otherwise `"false"` |

## What a run shows you [#what-a-run-shows-you]

- **Annotations.** Under `translate` with exit `1`, each failed or partial locale becomes one error annotation titled `verbatra: <locale>`. Under `check` or `diff`, each drifted locale gets one titled `verbatra check: <locale>` or `verbatra diff: <locale>`. All of these attach to that locale's file when the action can find it. A partial locale's `LOCALE_PARTIAL` annotation names the withheld keys, grouped by cause. Every `check` adds one `PLURAL_CATEGORIES_INCOMPLETE` warning per locale whose plurals lack a CLDR category the language uses. With `sensitive`, each flagged key gets a `SENSITIVE_CONTENT` error annotation and a line in the summary. A run that fails as a whole gets one `verbatra` annotation with the error code, its cause as `(cause: X)` and a `Next step:` hint when the CLI gives them. GitHub shows at most 10 annotations per severity, so the action orders them drift, review, quality errors, warnings, and adds one notice counting the rest.
- **Job summary.** One row per locale with the counts for the command that ran, an aggregate line, and the partial and failed locales. With `qa` it adds a findings table of up to 1000 findings; incomplete plurals get their own section.
- **Exit status.** The action emits the annotations and summary first, then exits with the CLI's own [exit code](https://verbatra.kreitz-webdev.de/docs/cli/output.md#exit-codes), except that a `translate` exit `3` passes and a failure before the CLI runs (no working directory, no config, a failed install) exits `1`; a refused input gets one `::error::`, no summary.

## Security [#security]

Pin both references: `uses:` to a commit SHA and `version` to an exact release ([current one on npm](https://www.npmjs.com/package/@verbatra/cli)); the action rejects anything that could resolve `latest`. Inputs reach the CLI as a quoted argument array, never spliced into shell text.

## Related [#related]

- [Run verbatra in CI](https://verbatra.kreitz-webdev.de/docs/ci-and-exit-codes.md): the same gates in any CI system.
- [verbatra/action](https://github.com/verbatra/action): the release notes and the input reference generated from `action.yml`.
