GitHub Actions
Automatic translations in your pull requests. Every PR gets translated, committed, and ready to merge.
Just looking for the action? localheroai/localhero-action on GitHub Marketplace →
Prefer a one-click install instead of editing a workflow file? Try the Localhero.ai GitHub App on the Marketplace →
Quick Start
Add this workflow to your repository. That's it.
name: Translate
on:
pull_request:
paths:
- "locales/**" # Adjust to your path
- "localhero.json"
repository_dispatch:
types: [localhero-sync]
workflow_dispatch:
jobs:
translate:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
# Checkout PR with history so we only translate what changed
- uses: actions/checkout@v4
with:
ref: ${{ github.event.client_payload.branch || github.head_ref }}
fetch-depth: 0
- uses: localheroai/localhero-action@v1
with:
api-key: ${{ secrets.LOCALHERO_API_KEY }}
Setup checklist
1. Create the workflow file above at .github/workflows/localhero.yml
2. Add your API key to repository secrets as LOCALHERO_API_KEY
Repository → Settings → Secrets and variables → Actions → New secret
3. Update the paths: to match where your translation files live
Get your API key from your account.
How it works
When it runs
The workflow triggers when a PR changes your translation files. It compares your branch to the base branch, finds new or modified keys, translates them, and commits the results directly to your PR.
What gets skipped
The action automatically skips translation when:
- • PR has a
skip-translationlabel - • PR is a draft
- • Commit was made by the LocalHero bot (prevents loops)
Note that marking a draft PR as ready for review does not re-run the workflow on its own:
GitHub only triggers pull_request workflows on
opened,
synchronize and
reopened by default,
and re-running a skipped run reuses the original event where the PR was still a draft.
Push a new commit after marking the PR ready, or add
ready_for_review to your workflow triggers:
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
Permissions
contents: write lets the action commit translations.
pull-requests: write allows adding PR comments.
Configuration
The action accepts these inputs:
| Input | Default | Description |
|---|---|---|
api-key |
required | Your LocalHero API key |
command |
ci |
CLI command: ci, translate, push, pull |
verbose |
false |
Show detailed output |
cli-version |
latest |
Pin to a specific CLI version |
skip-labels |
skip-translation |
PR labels that skip translation (comma-separated) |
- uses: localheroai/localhero-action@v1
with:
api-key: ${{ secrets.LOCALHERO_API_KEY }}
verbose: true
skip-labels: 'skip-translation,wip,no-i18n'
Common patterns
Gettext / PO files
For projects using gettext, run your message extraction before the action:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Extract messages
run: |
pip install -r requirements.txt
python manage.py makemessages -a
- uses: localheroai/localhero-action@v1
with:
api-key: ${{ secrets.LOCALHERO_API_KEY }}
Adjust the extraction command for your framework: xgettext for C/C++, pybabel extract for Flask, etc.
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx lingui extract
- uses: localheroai/localhero-action@v1
with:
api-key: ${{ secrets.LOCALHERO_API_KEY }}
Manual trigger
The workflow includes workflow_dispatch, so you can run it manually from the Actions tab.
Alternative: Using the CLI directly
For advanced use cases, you can call the CLI directly instead of using the action. This gives you full control but requires more setup.
name: Translate
on:
pull_request:
paths:
- "config/locales/**"
workflow_dispatch:
concurrency:
group: translate-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
translate:
if: |
!contains(github.event.pull_request.labels.*.name, 'skip-translation') &&
github.event.pull_request.draft == false &&
!(github.actor == 'localhero-ai[bot]' && github.event.action == 'synchronize')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- name: Fetch base branch
if: github.event_name == 'pull_request'
run: git fetch --no-tags origin ${{ github.base_ref }}
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Translate
env:
LOCALHERO_API_KEY: ${{ secrets.LOCALHERO_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_BASE_REF: ${{ github.base_ref }}
run: npx -y @localheroai/cli ci
The init command can generate this file for you: npx @localheroai/cli init
Related reading
- Pairing this with Claude Code, Cursor, or Codex? How to make your AI coding agent translation-aware covers the context to hand the agent so it stops at source strings and lets this action handle the rest.
- Building the CI step yourself first? Translating JSON and YAML locale files in CI walks through the DIY pattern and where the trade-off shifts.
- Not sure which approach fits your team? How to pick a translation setup for your stack compares DIY, code-native, and full TMS by stack and team shape.
- Weighing this action against the alternatives? The best GitHub Actions for i18n translation, compared surveys the open-source actions, hosted tools, and TMS integrations side by side.
Klar til å prøve?
Kom i gang på noen minutter. Du trenger ikke kredittkort.