GitHub Actions Guide
Set up automatic translations in your pull requests. Every PR gets complete translations, automatically committed and ready to merge.
How it works
The init command automatically creates a GitHub Actions workflow file. Here's what happens when you open a pull request:
When it runs
The workflow triggers automatically when someone opens or updates a pull request that changes your translation files. It only watches the specific paths you configured in localhero.json.
Concurrency: If you push new changes while the workflow is running, it automatically cancels the old run and starts fresh with your latest changes.
What it does
1. Checks out your PR branch
2. Sets up Node.js
3. Runs npx @localheroai/cli ci using your API key from repository secrets
4. Compares your branch with the base branch to find changed keys
5. Translates the keys that were added or modified in your branch
6. Commits the translated files directly to your PR
skip-translation label, and commits by the Localhero bot.
Permissions needed
contents: write - Allows the workflow to commit translated files back to your PR
pull-requests: write - Lets the workflow add information to the PR
These are standard GitHub permissions. All changes are made as separate commits in the PR for you to review.
Setting it up
Quick setup
1. Run npx @localheroai/cli init — if your project is already set up, running init again will only prompt you to create the workflow file
2. Add your API key to GitHub repository secrets as LOCALHERO_API_KEY
On GitHub: Repository → Settings → Secrets and variables → Actions → New repository secret.
Get your key from your account.
3. Commit the workflow file at .github/workflows/localhero-translate.yml
4. That's it! The next PR that changes translation files will trigger automatic translations.
View the generated workflow file
The auto-generated .github/workflows/localhero-translate.yml:
name: Localhero.ai - Automatic I18n translation
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:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- name: Fetch base branch for comparison
if: github.event_name == 'pull_request'
run: |
git fetch --no-tags origin ${{ github.base_ref }}
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: 22
- name: Translate strings
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
Customization
Skip translation for a PR
Add the skip-translation label to any PR to prevent the workflow from running. Draft PRs are also skipped automatically — translations run when you mark the PR as ready for review.
Run manually
The workflow includes workflow_dispatch trigger, so you can run it manually from the GitHub Actions tab.
Change trigger paths
Edit the paths: section in the workflow file to watch different directories.
on:
pull_request:
paths:
- "src/locales/**" # Your custom path
- "public/i18n/**" # Additional path
Gettext / PO file projects
If your project uses gettext (PO files), you should run your message extraction command before the LocalHero CLI. This ensures new translation keys are properly extracted from your source code with correct source references.
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
sudo apt-get update && sudo apt-get install -y gettext
pip install -r requirements.txt
- name: Translate strings
env:
LOCALHERO_API_KEY: ${{ secrets.LOCALHERO_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_BASE_REF: ${{ github.base_ref }}
run: |
python manage.py makemessages -a
npx -y @localheroai/cli ci
Adjust the extraction command for your framework (e.g., xgettext for C/C++, pybabel extract for Flask).