Translation deployment strategies: in-app, CDN, hybrid
Bundled, CDN-loaded or hybrid: how to ship and update your translations, with caching, versioning, offline support and rollback covered.
Translating a string is one thing; shipping it is another. Once your keys are translated (see what i18n is), an architecture question remains — one that’s rarely asked early enough: how do those translations reach the user, and what happens when you fix one?
The answer decides how fast you can fix a typo, how robust you are offline, your bandwidth bill, and whether you can roll back. Three strategies dominate. Let’s walk through them, then how to choose.
Three ways to deliver
1. In-app bundle (embedded translations)
The language files are included in the app at build time. It’s the default for most frameworks: translations ship as part of the deliverable, just like code.
Pros: maximum simplicity, no network dependency, guaranteed offline, optimal performance (nothing to load).
Cons: any text fix needs a new build and a redeploy — even a store review on mobile. Adding a language follows the same cycle. For a team that iterates fast, that’s the main brake.
2. CDN-loaded
Translations live on a server and are distributed by a CDN. The app fetches them at runtime. You publish a fix on the server; clients pick it up on their next load, with no app redeploy.
Pros: update translations without a rebuild or store resubmission, add a language on the fly, full decoupling between the code’s release cycle and the content’s.
Cons: a network dependency. If the CDN is unreachable or the device is offline on first launch, the user can end up with no text. You also have to manage caching and accept a little startup latency.
One honest note: “no redeploy” doesn’t mean “instant on screen.” Clients apply the new version on the next load or refresh, not mid-session. That’s plenty for fixing fast, but it isn’t live-to-the-second in production.
3. Hybrid (bundled fallback + CDN overlay)
The hybrid combines the two: a baseline is bundled in the build, and a fresh layer is fetched from the CDN when possible. On launch, the app shows the bundled translations immediately, then swaps in the CDN version once it arrives.
Pros: you get the best of both — instant, offline-safe startup from the bundle; freshness and agility from the CDN. A network outage degrades gracefully instead of breaking.
Cons: a bit more logic to set up (source priority, merging, caching). That’s the only real cost — and you pay it once.
The comparison table
| Criterion | In-app (bundle) | CDN | Hybrid |
|---|---|---|---|
| Fix without redeploying | ❌ | ✅ | ✅ |
| Works offline out of the box | ✅ | ⚠️ (needs cache) | ✅ |
| Add a language on the fly | ❌ | ✅ | ✅ |
| Network dependency | None | High | Low (fallback) |
| Setup complexity | Low | Medium | Medium+ |
| Startup performance | Optimal | Slight latency | Optimal |
Caching, versioning and bandwidth
The moment you load translations remotely, three technical topics become unavoidable.
Version your bundles
Never serve one “live” translation file that you overwrite in place: you’d lose track of which version is running where. Publish immutable releases instead — each publish produces a versioned bundle (by date, number, or content hash). Clients point at the latest, and you keep the history.
Invalidation and caching
A CDN caches files by nature — that’s what makes it fast. The flip side is that a
fix can stay hidden behind a stale cache. The classic fix is cache-busting: the
bundle URL changes on each release (/v1/en/common.json?v=42 or a dated path),
which forces a fetch of the new version while keeping an aggressive cache on the old
ones.
Bandwidth cost
Loading translations on every launch has a cost. You keep it down by splitting per
namespace (load only the current page’s keys), enabling compression
(gzip/brotli), and leaning on HTTP headers (ETag, Cache-Control) so a client only
re-downloads a bundle when it has actually changed.
Offline-first
For any app that can lose the network — that is, every mobile app — the rule is simple: never depend on the network to render text on first paint. The hybrid pattern is built for exactly this: bundled baseline, opportunistic CDN refresh, and a local cache of the last bundle for subsequent offline starts. The user always has something to read, and the freshest version as soon as a connection returns.
Rollback: undoing fast
A good deployment strategy is also judged on how it undoes. With bundled translations, reverting a bad translation means redeploying — slow. With immutable, versioned CDN releases, rollback is trivial: you republish the previous version (or point clients back at it), and the fix propagates on the next load. It’s one of the most underrated arguments for the versioned CDN.
Decoupling content from code: who gets to publish?
Beyond the technical side, your deployment strategy decides who can fix a translation. With a bundled file, changing a word means a pull request, a review, a build, a deploy: only developers publish, and every comma burns engineering time. By decoupling content from code through a CDN, a translator or product owner can fix a string and publish a release without touching the repo or pulling in the engineering team.
That decoupling is often more valuable than the speed itself: it makes the organization self-sufficient on its own content. Developers keep their hands on code and infrastructure; language owners keep their hands on the words. Everyone ships at their own pace.
The life of a fix in hybrid
Concretely, here’s what happens when you fix a typo with a well-tuned hybrid setup:
- Edit. The text is fixed in the source of truth (editor, CLI, or in-context on the page itself).
- Release. You publish a new CDN release, immutable and versioned — the old one stays available for a possible rollback.
- Fetch. On next launch, each client compares its local version to the latest
published one and downloads the bundle only if it changed (thanks to the
ETag). - Render. The fix replaces the old value; on network failure, the bundled baseline takes over.
// Startup: bundled baseline first, CDN overlay second
i18n.addResourceBundle("en", "common", bundledEn); // always present
const fresh = await fetchCdn("en", "common").catch(() => null);
if (fresh) i18n.addResourceBundle("en", "common", fresh, true, true); // overwrites
No rebuild, no store review, no deployment window to book.
Which one for which case?
- Small static site, one or two languages, stable content: the in-app bundle is enough. Don’t add complexity you don’t need.
- Fast-moving product, several markets, frequent fixes: the CDN (or hybrid) gets indispensable fast, so you’re not redeploying for every comma.
- Mobile app, or anything that has to work offline: the hybrid is almost always the right compromise — the bundle’s robustness, the CDN’s agility.
Our take: hybrid
Let’s be blunt: for most serious products, hybrid wins. You ship fast without giving up any robustness. It’s the architecture Sonenta settled on — a bundled fallback on the app side, a CDN-distributed overlay with versioned releases (so rollback is immediate), and offline-first by default. You fix from a single source of truth, you publish a release, your apps pick it up. No rebuild, no store resubmission.
And if you’re starting from an existing setup, moving to this model is no big-bang — that’s the whole point of migrating from i18next.