
אין צורך לתרגם הכל מחדש: איך תרגום דלתא עובד ב-CI
By Robert M
You don't need to re-translate everything: how delta translation works in CI
Once you have automated translation wired into your CI pipeline, a natural question comes up quickly: does this re-translate the entire locale file every time someone pushes a change?
Without delta mode, yes. Every push that touches your source locale file sends the whole thing to the API, every key, every string, whether it changed or not. For a small project early on that is fine. For a mature project with hundreds of translation keys across 10 or 20 languages, you are burning tokens on strings that have not changed since the last run and getting back identical output for the privilege.
Delta translation solves this. Instead of sending the full file, the Action compares your current source file against a stored baseline, identifies only the keys that were added or modified, and sends just those to the API. The output for unchanged keys is taken from the existing translated files. Token usage drops to match the actual work being done.
How the baseline works
When delta mode runs for the first time, or when you force a full refresh, the Action translates the complete source file and stores a flat JSON representation of it as a baseline file in your repository. On subsequent runs, the Action loads that baseline, diffs it against the current source file, and builds a payload containing only the changed keys.
For JSON locale files the baseline is stored as .polylingo-baseline.json in your messages directory. For YAML locale files it is .polylingo-yaml-baseline.json in your locales directory. For Laravel PHP lang files it is .polylingo-laravel-php-baseline.json in your lang directory.
The baseline is committed alongside your translated files so it travels with the repository. Any developer who clones the repo gets the same baseline the pipeline is working from.
What counts as a change
The diff operates on the flattened key representation of your source file. Nested structures are flattened to dot-notation keys before comparison:
{
"nav.home": "Home",
"nav.about": "About us",
"hero.title": "Welcome to our platform",
"hero.cta": "Get started"
}
A key is included in the delta payload if:
- It exists in the current source file but not in the baseline (new key)
- It exists in both but the value has changed (modified key) Keys that exist in the baseline but not in the current source file (deleted keys) are removed from the translated output files automatically. Keys that are identical in both are skipped entirely and their existing translations are left in place.
What this looks like in practice
Say you have a Next.js project with 200 translation keys in messages/en.json, already translated into 12 languages. A developer updates the hero section copy and adds two new keys for a feature announcement. That is 4 changed keys out of 200.
Without delta mode, the pipeline sends all 200 keys multiplied across 12 languages on every push. With delta mode it sends 4 keys. The token usage for that run is roughly 2% of what a full translation would cost. The pipeline is also faster because there is less to send and less to wait for.
Over a month of regular development, the saving compounds significantly. Most pushes touch a handful of strings. Full retranslation only happens when you add a new language or explicitly reset the baseline.
The three PolyLingo GitHub Actions
Delta mode is available across all three PolyLingo translation Actions. Each one is built for a specific content type and project structure.
translateAction — JSON and Markdown
The original Action. Handles flat JSON locale files in the next-intl and i18next style, with an optional Markdown documentation pass via the async jobs API for larger files.
- uses: UsePolyLingo/translate-action@v1
with:
api_key: ${{ secrets.POLYLINGO_API_KEY }}
source_file: messages/en.json
locales: fr,de,es,ja,zh
delta: true
commit: true
commit_message: "chore(i18n): sync translations"
Delta baseline: messages/.polylingo-baseline.json
github.com/UsePolyLingo/translateAction — View on Marketplace
translate-action-yaml — YAML locale files
For projects using nested YAML locale files: Rails i18n, Vue i18n, and any other framework that uses the YAML format. The Action handles the Rails convention of a root locale key automatically — en.yml has an en: root key, and each output file gets the correct target locale key (fr:, de: etc).
Since the PolyLingo API works with JSON natively, the Action flattens nested YAML to dot-notation JSON before sending, translates, then reconstructs the nested structure and writes valid YAML output. One v1 caveat worth knowing: keys that contain dots naturally are not supported, as they conflict with the dot-notation flattening.
- uses: UsePolyLingo/translate-action-yaml@v1
with:
api_key: ${{ secrets.POLYLINGO_API_KEY }}
locales_dir: config/locales
source_file: config/locales/en.yml
locales: fr,de,es,ja
delta: true
commit: true
Delta baseline: config/locales/.polylingo-yaml-baseline.json
github.com/UsePolyLingo/translate-action-yaml — View on Marketplace
translate-action-laravel — Laravel lang files
For Laravel projects using either JSON translation files (lang/en.json, supporting both nested and flat structures in Laravel 9+ style) or PHP array lang files (lang/en/*.php). The Action auto-detects which format your project uses via php_format: auto — it checks for lang/en.json first and falls back to PHP array files if not found.
For PHP files it shells out to the PHP CLI to read source files and serializes translated output back to valid PHP array syntax in JavaScript, with no additional dependencies. GitHub-hosted ubuntu-latest runners include PHP 8.x by default so no extra setup step is needed. PHP 7.4 or later is required.
One v1 caveat: keys containing dots are not supported in PHP mode due to the dot-notation flattening strategy.
- uses: UsePolyLingo/translate-action-laravel@v1
with:
api_key: ${{ secrets.POLYLINGO_API_KEY }}
lang_dir: lang
source_locale: en
locales: fr,de,es,pt,nl
php_format: auto
delta: true
open_pr: true
Delta baseline: lang/.polylingo-laravel-json-baseline.json or lang/.polylingo-laravel-php-baseline.json depending on format.
github.com/UsePolyLingo/translate-action-laravel — View on Marketplace
PR mode vs commit mode
All three Actions support two output modes.
Commit mode (commit: true) writes the translated files and commits them directly to the current branch. Simple setup, good for teams that treat translation as an automated process that does not need review.
PR mode (open_pr: true) creates a new branch (polylingo/yaml-<sha>, polylingo/laravel-<sha> etc), writes the translated files there, and opens a pull request against your base branch. Better for teams that want a human review step before translated content merges, or for projects where translation quality directly affects the user experience.
When both are set, PR mode wins.
PR mode requires pull-requests: write in your workflow permissions:
permissions:
contents: write
pull-requests: write
Forcing a full refresh
Delta mode compares against the stored baseline. If you want to retranslate everything regardless of what the baseline shows, set delta: false. This also updates the baseline to match the current source file, so subsequent delta runs start from the new state.
A full refresh is useful when you add a new target language, when you want to pick up translation quality improvements in a new model version, or when the baseline has drifted from reality for any reason.
- uses: UsePolyLingo/translate-action-yaml@v1
with:
api_key: ${{ secrets.POLYLINGO_API_KEY }}
locales_dir: config/locales
locales: fr,de,es,ja,zh,ko,ar
delta: false # full refresh, updates baseline
commit: true
Outputs for downstream steps
All three Actions expose the same outputs so you can use them in subsequent workflow steps:
- uses: UsePolyLingo/translate-action@v1
id: translate
with:
api_key: ${{ secrets.POLYLINGO_API_KEY }}
source_file: messages/en.json
locales: fr,de,es
delta: true
commit: true
- name: Log translation stats
run: |
echo "Locales translated: ${{ steps.translate.outputs.locales_translated }}"
echo "Files changed: ${{ steps.translate.outputs.files_changed }}"
echo "Tokens used: ${{ steps.translate.outputs.tokens_used }}"
Each run also writes a summary table to the GitHub Actions step summary so you can see token usage and translation results without digging through logs.
Getting started
All three Actions are available on the GitHub Marketplace. You will need a PolyLingo API key, available free at usepolylingo.com. The free tier includes 50,000 tokens per month. With delta mode enabled, most projects will stay well within that on routine development pushes.
Add your API key as a repository secret (POLYLINGO_API_KEY) and drop the relevant Action into your workflow. The first run does a full translation and sets the baseline. Every run after that only translates what changed.