Home/Blog/How to Build a Continuous Localization Pipeline That Ships With Every Release
Playbooks

How to Build a Continuous Localization Pipeline That Ships With Every Release

Your German enterprise deal closed yesterday. This morning your PM ships a new checkout flow. The English string reads "Confirm and pay". The German build shipped it in English too, because the translator was on holiday and nobody caught it.

That is not a translation problem. It is a pipeline problem. A continuous localization pipeline treats language as a first-class part of your release process, not a batched project you run every quarter.

What is a continuous localization pipeline?

A continuous localization pipeline is a build system for language. New source strings flow out to translation as soon as they land on main. Approved translations flow back as pull requests. Every release branch is gated on locale completeness before merge, and post-release fixes ship over the air without cutting a new build.

The three properties that separate a real pipeline from a decorated spreadsheet:

  1. Strings are source code. They live in the repo, next to the components that render them, in the platform-native format.
  2. Translation is an event, not a project. Every merged commit that touches a string file triggers a job. There is no weekly export.
  3. CI enforces completeness. Missing keys, unresolved placeholders, and overflow bugs fail the check the same way a failing test does.

If any one of those is missing, you have a translation workflow. Not a pipeline.

Where should source strings live in the repo?

One file per locale, per surface, in a predictable path. This is the boring part that people get wrong.

Layer Path pattern Format
Web app src/locales/{locale}.json Nested JSON with ICU plurals
iOS Resources/{locale}.lproj/Localizable.strings Apple strings + stringsdict
Android res/values-{locale}/strings.xml Android XML with plurals
Marketing site content/{locale}/*.md Markdown frontmatter with slug map
Emails emails/templates/{locale}/*.mjml MJML with token references

The rule: your pipeline reads and writes the platform's native format. It does not invent a lingua franca. If your CMS or vendor demands XLIFF, generate it as an artifact, do not adopt it as the source.

How should string sync work between the repo and translation?

Sync in both directions, on every commit that touches locale files.

  • Push. When source-language keys change on main, the pipeline creates or updates translation tasks within five minutes. New keys enter drafts. Deleted keys close their tasks. Modified keys invalidate prior translations and re-enter the queue with the old string attached as context.
  • Pull. When a translator approves a string, the pipeline updates a single release-scoped pull request per language. Not one PR per string. Not one PR per day. One PR per release branch, force-updated as approvals land.

The one-PR-per-release rule is what keeps your git history reviewable. Otherwise localization noise drowns out the diffs that matter.

What should the CI gate check on every release?

Six checks, running on every pull request against a release branch:

  1. Completeness. Every key in the source file has a translation in every enabled locale, or an explicit fallback declaration.
  2. Placeholder integrity. {userName} in English is {userName} in Japanese. Not {userName-san}. Not missing.
  3. Plural coverage. Every ICU plural has all the plural forms the target locale requires. Russian has four forms. Arabic has six. Japanese has one. Your linter knows.
  4. Layout overflow. Rendered strings fit their containers at 320px and 1280px. German is 30 percent longer than English on average. Finnish and Thai break assumptions.
  5. Glossary compliance. Terms flagged as do-not-translate are unchanged. Terms with a canonical translation match the glossary. Fail loudly on drift.
  6. Encoding sanity. No mojibake. No BOM in the middle of a file. No surrogate pair splits.

If your CI does not fail on all six, your customers will find the seventh check for you.

How do you route which languages go to MT vs human review?

Not every string deserves the same treatment. The routing rule that works:

  • Tier 1 languages (top 3 to 5 by revenue). Every string routes through human review. MT drafts land instantly, humans approve or edit within 48 hours. This is where your CS tickets come from, so it is where you pay the money.
  • Tier 2 languages (next 5 to 10 by revenue). Human review on billing, legal, onboarding, and error strings. MT ships for everything else with a confidence threshold and a monitoring loop.
  • Tier 3 languages (long tail). MT ships. A human reviews the strings that generate the top 20 CS tickets in that locale, once a month. That is the entire process.

The mistake is treating all 30 languages as tier 1. You end up either paying 10x more than needed or missing SLAs on the languages that actually generate revenue.

What does the release day look like once the pipeline is running?

Boring, ideally.

  • Engineer merges a feature. New strings appear as draft translations in the translation UI within five minutes.
  • Reviewers work through their queues async. Tier 1 languages complete within 48 hours. Tier 2 and 3 ship as drafts.
  • CI on the release branch shows a locale readiness dashboard. Green for complete, yellow for MT-only, red for missing.
  • Release is cut. Web deploys immediately. Mobile ships to app stores. Any string fix in the next 30 days publishes over the air, no new build required.

The dashboard is the artifact your PM references in the standup. The pipeline is the reason the dashboard is trustworthy.

What is the fastest way to migrate an existing project?

Do not boil the ocean. Migrate in four steps, one week each.

  • Week one. Consolidate strings into one file per locale per surface. Delete duplicate keys. Delete abandoned keys. Commit the diff.
  • Week two. Wire the sync. Push existing strings to your translation service. Import back into a scratch branch. Diff to catch encoding and placeholder issues.
  • Week three. Add CI checks in warning mode. Let engineers see what fails without blocking merges.
  • Week four. Flip CI to blocking. Turn on over-the-air delivery for approved locales. Publish the readiness dashboard.

Teams that try to do all four in one sprint burn out. Teams that budget four weeks ship, and then keep shipping.

The mistake to avoid

Most teams treat localization as a launch project that ends when the German site goes live. That framing produces the exact failure mode this pipeline exists to prevent: strings drift, layouts break, translators go stale, and a year later half the app is quietly English again. Treat localization as a permanent property of your build system instead. Every commit is a localization event. Every release is a locale audit. Every fix ships over the air within hours. Everything else is a spreadsheet in a nice jacket.

continuous-localizationi18n-pipelinedeveloper-workflowci-cdtranslation-management

Frequently asked questions

How is continuous localization different from traditional translation management?

Traditional TMS treats translation as a project you kick off at feature freeze. Continuous localization treats it as a build step that runs on every commit. Strings never leave the repo, drafts land within minutes of a merge, and human review happens in parallel with QA rather than after it. The result is that language completeness is a CI signal, not a launch checklist item.

Which file formats should we use for source strings?

Pick one canonical format per platform and never mix. Web apps usually settle on nested JSON or ICU MessageFormat. iOS uses .strings and .stringsdict for plurals. Android uses strings.xml. Flutter uses ARB. The mistake is exporting to XLIFF for the vendor and treating XLIFF as the source of truth. Keep the platform-native file as the source and let the pipeline generate XLIFF on demand.

How do we handle strings that need context for translators?

Attach context at the key level, not in a separate document. Add a description field next to the string in your source file, link to a Figma frame or screenshot, and expose that context in the translator UI. A translator with the screenshot produces 40 to 60 percent fewer defects than one working from a bare string. This is the single highest-leverage change in a localization workflow.

What happens when a translation is late for release?

The pipeline should not block your English release, but it should visibly mark the locale as incomplete. Ship the release with a fallback to English for missing keys, tag the locale as degraded in your monitoring, and page the owner of that language. Over-the-air delivery then lets you push the missing strings within hours of approval, without a new build.

Do we still need human translators if we use machine translation?

For any surface that touches revenue, billing, legal, or safety, yes. Machine translation is now good enough to draft, terrible enough to embarrass you on the strings that matter. Route your top 3 to 5 languages through human review and let long-tail languages ship on MT with a monitoring loop. The economics only work if you resist the temptation to review everything.

Ship every language the day you ship English

Thalarum syncs strings from your repo, drafts translations with your glossary, routes review, and blocks broken releases in CI.

Request early access