Use cases
Decoupling translation releases from code deployments
Translation edits happen constantly. A button label gets sharper, an error message needs rewording, a new locale launches in a regional market. In most organizations, each of those updates still follows the same path as a code release: branch, build, pipeline, and a coordinated rollout window.
When copy is bound to application releases, text that could go live in minutes waits days for the next deploy. Product and engineering teams run into that wall every sprint. Messagevisor separates translation releases from code deployments so content can ship on its own schedule.
The conventional problem#
In a typical application, translations live inside the application bundle. When you build and ship the app, you ship the translations. This means:
- A legal team that needs an emergency disclaimer update must wait for the next release window.
- A product manager who wants to soften error copy before a big campaign cannot act independently.
- A single mistranslation that slips through to production requires a hotfix deployment to fix.
- Different platforms - web, iOS, Android - each carry their own translation snapshots, so the same product speaks differently across surfaces.
Some teams move translations to a hosted platform to solve the immediacy problem, but they trade coupling for loss of review discipline: changes apply directly to production with no PR, no test run, no rollback path.
How Messagevisor decouples translations from deployments#
Messagevisor's model is: translations are authored as YAML files in a Git repository, built into JSON datafiles as a CI step, and served from a CDN or object storage that your applications fetch at runtime. The application bundle has no translations baked in. It only has an SDK and a URL.
┌──────────────────────────────┐│ Messagevisor repository ││ (YAML definitions + tests) │└────────────┬─────────────────┘ │ PR merged → CI builds ▼┌──────────────────────────────┐│ CDN / object storage ││ messagevisor-web-en-US.json ││ messagevisor-web-nl-NL.json │└────────────┬─────────────────┘ │ SDK fetches at startup + polls for updates ▼┌──────────────────────────────┐│ Running application ││ (no translations baked in) │└──────────────────────────────┘With this architecture, updating a translation is:
- Open a pull request against the Messagevisor repository.
- CI runs lint and tests.
- Reviewer approves and merges.
- CI builds new datafiles and publishes them to the CDN.
- Running applications pick up the new files at their next poll interval.
No application deployment required. No release window. No coordinated rollout.
Setting up the pipeline#
See examples in:
- GitHub Actions
- AWS CloudFront with S3
- Cloudflare Workers Static Assets
- Cloudflare Workers KV
- Cloudflare Workers with Hono
Loading datafiles at runtime#
On the application side, the SDK fetches the datafile from its published location:
import { createMessagevisor } from "@messagevisor/sdk";const datafile = await fetch( "https://cdn.yoursite.com/messagevisor/messagevisor-web-en-US.json").then((res) => res.json());const m = createMessagevisor({ datafile, context: { platform: "web" },});When you want to pick up updates without requiring a page reload, poll for a fresh datafile and call setDatafile():
// Refresh the datafile every 5 minutessetInterval(async () => { const fresh = await fetch( "https://cdn.yoursite.com/messagevisor/messagevisor-web-en-US.json" ).then((res) => res.json()); m.setDatafile(fresh, true);}, 5 * 60 * 1000);You can check getRevision() before calling setDatafile() to skip unnecessary updates if the revision has not changed. Passing true replaces the previously loaded datafile for that locale instead of merging into it.
The rollback story#
Because every state of the Messagevisor repository corresponds to a Git commit, rolling back a bad translation is as simple as rolling back any code change:
$ git revert <commit-sha>After the revert is merged and CI publishes the updated datafiles, applications pick up the corrected content on their next poll. There is no emergency database edit, no hotfix branch for the application, no release window coordination. The fix ships as fast as any other translation change.
Separation of release timelines#
This model means your translation repository operates on its own release timeline, completely separate from your application release cycles:
- Product teams can update copy in the morning and see it live before lunch.
- Legal can push compliance copy updates independently.
- Marketing can push campaign copy for a specific launch time by merging at the right moment.
- Engineers deploying a new application version do not need to coordinate with the translations team because the app always fetches the latest datafile.
Multiple applications - web, mobile, backend - can all point at the same generated datafiles. When translations update, every surface reflects the change simultaneously.
What validation the pull request provides#
The separation only works well if the pull request workflow provides meaningful validation. Messagevisor's CI step gives you:
- Lint: validates YAML structure, field types, required fields, and cross-references between messages, segments, and locales.
- Tests: runs authored assertions against the actual evaluation engine, so a broken ICU pattern or a wrong override condition is caught before merge.
- Build: confirms that all target and locale combinations produce valid datafiles, and
--showSizeflags unexpected size growth.
This means the speed of decoupled releases is not purchased by giving up quality gates.
Keeping datafiles small with targets#
Each application only needs a slice of the total translation set. Use targets to ensure each application loads only the messages it uses:
description: Web applicationincludeMessages: - auth* - nav* - billing* - common*excludeMessages: - admin* - internal*locales: - en - en-US - nl-NLcontext: platform: webA target that includes 300 messages instead of 3000 means a significantly smaller datafile that loads faster and parses faster on every page load. The --showSize flag on build shows the impact of your target configuration.

