Skip to content
October 10, 202612 min readGuides

Git-synced translations vs runtime delivery: which one fits your CI/CD pipeline?

Git-synced translations are translation files that live in your repository and are kept current by your CI pipeline: new keys go out to a translation platform, finished translations come back as a commit or pull request, and users see them after the next deploy. Runtime delivery means the application loads its translations from a CDN while it runs, so a published change reaches users without a commit or a redeploy. The two are not exclusive: many production setups bundle a synced copy into the build and fetch updates from the CDN on top.

This post is a decision guide for the question "how do we sync translations with our CI/CD pipeline?": what each architecture looks like end to end, a decision table across the criteria that actually decide it, how teams run it today, and what neither architecture solves.

Key facts
  • Git sync makes translations part of the release: they are versioned, reviewed and rolled back with the code, and every change waits for the next deploy.
  • Runtime delivery decouples translations from the release: a published fix goes live without a deploy, at the price of a network request, a cache to tune and CDN traffic to pay for.
  • The deciding questions: who edits translations, how often you deploy, whether the build needs the files (SSR, static builds, offline, native mobile), and whether review happens in pull requests or in a translation editor.
  • Both is a valid answer: bundled files as the baseline, the CDN for changes between releases. It only works with one source of truth for translation values.

Architecture 1: translation files in git, synced through CI

The files are the deliverable. The flow, end to end:

  1. A developer adds a key in code, for example t('checkout.title'), with a default value in the source language.
  2. On push or pull request, CI extracts the keys from the source code (or reads the source-language files) and pushes new and changed keys to the translation platform.
  3. Translators, machine translation or AI fill the target languages in the platform; reviewers approve.
  4. A CI step downloads the translations into the locale files in the repository.
  5. The change lands as a commit or a pull request and ships with the next build and deploy.

What it gives you: the repository is the complete picture of a release, including its text. A translation change is a diff that can be reviewed next to the code that caused it, reverted with git revert, and reproduced by checking out an old tag. The build has no runtime dependency on anything outside your infrastructure, which is exactly what static site generation, server-side rendering, offline apps and native mobile apps want.

What it costs: every translation change is a code change. A typo in Italian waits for the next deploy or gets a hotfix release of its own. Translation is asynchronous, so CI cannot wait for it: a key pushed today is usually translated after the pipeline that pushed it has finished, and the result arrives in a later run or in a bot pull request. Locale files that every feature branch touches are a classic source of merge conflicts, and a sync job that rewrites whole files produces diffs nobody can review.

Architecture 2: translations delivered at runtime from a CDN

The files are an implementation detail of the platform. The flow:

  1. A developer adds a key in code. It reaches the translation platform either from the running application during development (in i18next, the saveMissing option) or from a CI sync step.
  2. Translators, machine translation or AI fill the target languages; reviewers approve.
  3. The platform publishes the approved state to a CDN.
  4. The running application fetches the namespaces it needs for the current language from the CDN, typically on load.
  5. Users see the new text once the cache expires, with no commit, build or deploy.

What it gives you: translation stops being a release dependency. Product, marketing and translators fix text on their own schedule, a new language can go live without a release, and the repository holds no locale files to conflict on. This is the delivery model behind continuous localization; the definition and its stages are in what is continuous localization.

What it costs: a network request on the critical path, tuned with cache lifetimes (a long cache means fast loads and slower updates, a short one the opposite). A runtime dependency on the CDN, so production setups need a fallback for blocked or offline networks. Traffic that scales with your visitors rather than with your builds. Files published on a public CDN are readable by anyone who knows the URL. And language review happens in the translation platform, not in your pull requests.

Architecture 3: both

The hybrid uses each architecture for what it is good at:

  1. Keys flow to the platform through a CI sync (or saveMissing during development).
  2. At build time, CI downloads the published translations and bundles them: the server renders with them, static pages are generated from them, an offline app ships with them.
  3. At runtime, the application loads the same namespaces from the CDN where it can, and falls back to the bundled copy where it cannot.
  4. A fix published between releases reaches users through the CDN; the next build picks it up into the bundle.

The condition is one source of truth for translation values. The code owns the keys, the platform owns the translations, and the files in git are a generated snapshot of a published state. Once someone edits a value in the repository while someone else edits it in the platform, the two outputs diverge and every sync turns into conflict resolution.

Decision table

CriterionGit sync through CIRuntime delivery from a CDNBoth
Who edits translationsDevelopers, or translators whose work returns through a sync commitTranslators, product and marketing in a translation editor, without git accessTranslators in the editor; developers see generated files
Release cadenceText ships with each deploy: fine if you deploy often or copy rarely changesText ships when it is published, independent of deploysBaseline ships with each deploy, fixes in between
SSR/SSG and offlineStrong: the files are part of the buildNeeds a fetch at render or load time, a cache and an offline fallbackStrong: bundled baseline, CDN on top
Review in pull requestsYes: translation diffs sit next to the code changeNo: review happens in the translation platformCode review sees the generated snapshot; language review stays in the platform
Rollbackgit revert and redeploy: text rolls back with the codeRepublish an earlier state, no deploy; depends on the platform's history and versionsEither level: revert the bundle with the code, or republish the CDN state
Cost of CDN trafficNone at runtime; downloads happen per build, not per visitorScales with visitors and page loads; caching reduces itSame as runtime delivery for whatever the app fetches from the CDN
Confidential stringsStay in a private repository until they ship (a shipped web bundle is public anyway)Published files are readable by URL: unreleased text needs authenticated delivery or must stay unpublishedKeep unreleased text out of the published state until launch
Native mobileNatural fit: resource files are compiled into the appRequires an over-the-air SDK inside the appBundle in the binary; runtime updates only with such an SDK

How to read it:

  • Choose git sync when developers write most of the text, "with the next release" is fast enough for a fix, the build must be self-contained (SSR, static export, offline, native mobile), or your team's review culture runs on pull requests.
  • Choose runtime delivery when people outside engineering own the text, translation fixes should not wait for a release, or text changes far more often than you deploy.
  • Choose both when you need a self-contained build and text that changes between releases. Server-rendered web apps that also want fixable copy are the typical case.

How teams handle it today

  • Files edited in the repository, no sync. Developers edit JSON or YAML next to the code. The cheapest correct setup for one developer and languages they can read; it stops working when a translator needs access. The failure modes are in managing translation files without manual JSON editing.
  • A scheduled sync job. A nightly or post-merge job pushes new keys and opens a bot pull request with whatever was translated since the last run. It works, and it separates translation from the feature that needed it: the feature merges in the source language, and its translations arrive later in a pull request of their own.
  • Pull-request scoped sync. The sync runs on the pull request itself and only for the keys that pull request added or changed, so translations can land in the same branch before merge. It needs a diff against the base branch (a full git history in the CI checkout), and it still depends on translations finishing before someone clicks merge.
  • CDN delivery for the web app. The application loads translations at runtime; the repository holds no locale files, or only the source language. Translators publish fixes without asking engineering. The mechanics, caching included, are in how to update translations without a redeploy.
  • The hybrid. Builds download the published translations for server rendering and as an offline fallback; the client loads the CDN version on top.
  • Native apps in mobile CI. The mobile build downloads the strings in the platform's resource format before compiling, and text changes ship with app releases.

What neither architecture solves

  • Quality. Where the files live has no effect on whether a translation is right. Terminology, tone and approval are handled by glossaries, a review workflow and automated QA checks, in either architecture.
  • Asynchronous translation. Neither CI nor a CDN makes translators faster. Decide what happens while a key is not translated yet: falling back to the source language is a deliberate product decision, not a bug.
  • Two sources of truth. Bidirectional sync is a tool, not a governance model. If values can change in both places, decide which one wins before the first conflict, not during it.
  • Instant updates. Runtime delivery means "without a deploy", not "instantly": cached files stay stale until the cache expires, and a very short cache costs latency.
  • Key design. Naming, namespaces and ownership are yours in both models; neither a sync job nor a CDN designs them.
  • Small setups. One developer, two languages they can read, rare text changes: files in git and no platform at all is the cheapest correct answer. The upgrade signal is the second person who needs to change text.

How this works in Locize

Locize supports both architectures from one project, so the choice is a delivery setting, not a migration. The project is the single source of truth for translation values; the files in your repository and the files on the CDN are both outputs of its published state.

  • Into git, through CI. Three GitHub Actions: locize/translate extracts new and changed keys, syncs them, triggers AI translation and downloads the results (it wraps i18next-cli localize --ci and never modifies your source); locize/sync reconciles existing locale files in both directions, with optional auto-translate; locize/download fetches published translations at build time for SSR and static builds. None of them commits: you pair them with a commit or pull-request step. Outside GitHub, the CLI runs the same flows (locize sync, locize download, npx i18next-cli locize-sync). A new free project can auto-translate a limited starter amount, see free CI translation for i18next.
  • At runtime, from the CDN. Translations are published to the CDN by default: the Standard CDN is public with a fixed one-hour cache. The Pro CDN, from the Growth plan, adds a configurable cache max-age per version and private publishing that requires an API key. With i18next, i18next-locize-backend loads them, and i18next-chained-backend adds a bundled fallback for the hybrid. Build-time downloads count as CDN downloads too, but per build rather than per visitor.
  • Releases and rollback. Versions separate stages such as latest and production (locize copy-version, locize publish-version), and the history of each key shows who changed what and lets you restore earlier values.
  • Native iOS and Android. Build-time sync only: locize download or locize sync with the android, strings or xcstrings format writes the resource files before the app is compiled. Locize has no native over-the-air SDK, so new text reaches native apps with the next app release.

To translate only what a pull request changed: on pull_request events, command: sync defaults to changed-only mode, so only the keys the PR added or modified are synced and AI-translated, and nothing is deleted. The base branch must be in the checkout:

name: Translate PR
on:
  pull_request:

permissions:
  contents: write

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0
      - uses: locize/translate@v2
        with:
          command: sync
          project-id: ${{ secrets.LOCIZE_PROJECTID }}
          api-key: ${{ secrets.LOCIZE_API_KEY }}
      - uses: stefanzweifel/git-auto-commit-action@v7
        with:
          commit_message: 'chore(i18n): translate PR changes'

Pull requests from forks do not receive repository secrets, so this workflow only works for branches in the same repository. A commit pushed with the default GITHUB_TOKEN does not start new workflow runs, so CI does not run again on the translation commit.

The same diff scoping exists as changed-only: true on locize/sync, as locize sync --changed-only true (locize-cli 12.7 or newer) and as npx i18next-cli locize-sync --changed-only (i18next-cli 1.72 or newer).

If you want to compare both on your own code: create a project, add the workflow above with your secrets, and load the same project from the CDN in a preview build. Then decide with your own diffs and load times in front of you.

Frequently asked questions

What is the difference between git-synced translations and runtime delivery? With git sync, translation files live in the repository and a CI step keeps them current, so translations ship with the next build and deploy. With runtime delivery, the application loads translations from a CDN while it runs, so a published change reaches users without a commit or a deploy, once the cache expires.

How do we sync translations with our CI/CD pipeline? Extract keys from the code and push new or changed keys to your translation platform (ideally only the pull request's diff), then download the finished translations into your locale files and commit them or open a pull request. Configure a fallback language for keys that are not translated yet, and add CDN delivery if text should change between releases.

Which translation tools sync with CI/CD pipelines automatically? Look for a CLI or a ready-made CI action that extracts keys or reads your existing files, can limit a sync to a pull request's diff, writes your file format without losing structure or plural forms, leaves commits to your own git workflow, and can optionally deliver over a CDN. Locize provides three GitHub Actions and a CLI that runs in any CI system.

We keep translations in a git repository. Can a localization platform sync the files automatically? Yes. A CI sync step pushes new source-language keys up and writes translations back into the same files and folders, recorded as a commit or pull request. Keep one direction per concern: the code owns the keys, the platform owns the translation values.

Should translation files be committed to the repository? When the build or the review needs them: SSR and static builds, offline apps, native mobile apps, or teams that review translation changes in pull requests. If translations come from a CDN and the build does not read them, the files are a generated copy you can keep out of git. Nobody should edit them by hand either way.

Can we use git sync and CDN delivery at the same time? Yes. The build bundles a downloaded copy of the published translations as the baseline, and the running application loads newer translations from the CDN when it can reach it. It works as long as both outputs come from one published state, and breaks once values are edited in two places.

Tired of managing translations by hand?

Locize is the translation management backend by the i18next team: CDN delivery, AI translation, in-context editing, no redeploys.

Start your free 14-day trial