Promoting translations through environments
Moving translation content through a dev → staging → production pipeline is harder than it sounds. A naive file copy clobbers environment-specific content. Manual cherry-picking is error-prone and leaves no record of what moved. A bulk overwrite from one environment to another works until the staging team makes a legitimate edit that should be preserved, and then it silently disappears the next time someone runs the copy.
Messagevisor's promote command solves this with a structured, merge-aware promotion workflow: it advances authored definitions between sets, respects promotable: false flags, resolves conflicts explicitly, and produces an optional audit record of every change that moved.
The promotion model#
Promotion works between sets. Each set is a fully independent authoring tree. The promote command reads from a source set, previews the destination changes by default, and writes only when --apply is passed. Applied promotions merge at the level of individual fields and override keys rather than doing a blunt file overwrite.
Configure the allowed flows in messagevisor.config.js:
module.exports = { sets: true, promotionFlows: [ { from: "dev", to: "staging" }, { from: "staging", to: "production" }, ],};promotionFlows restricts which directions are allowed. Attempting to promote in an unlisted direction fails clearly rather than silently doing something unexpected.
A complete promotion sequence#
The recommended pattern for any promotion is: preview, then execute, then validate.
Step 1: Preview#
$ npx messagevisor promote --from=dev --to=stagingThe preview output shows exactly what would be created, updated, or unchanged in the destination set. No files are written.
Add --showUnchanged to see the full picture, not just what would change:
$ npx messagevisor promote --from=dev --to=staging --showUnchangedStep 2: Execute the promotion#
$ npx messagevisor promote --from=dev --to=staging --applyThis writes the merged result to the staging set files. The changes are now file system modifications. Commit them to your branch and open a pull request.
Step 3: Validate the destination#
After promotion, confirm the destination set is valid and all tests pass:
$ npx messagevisor lint --set=staging$ npx messagevisor test --set=staging$ npx messagevisor build --set=stagingThis sequence ensures that merged content is valid, tests pass, and build succeeds before the staging datafiles are published.
Promoting only a subset of content#
You rarely want to promote everything at once. Use filters to move only what is ready:
# Promote only checkout-related messages$ npx messagevisor promote --from=dev --to=staging \ --includeMessages="checkout*" \ --apply# Promote only a specific locale$ npx messagevisor promote --from=dev --to=staging \ --locale=nl-NL \ --apply# Promote only a specific target's messages$ npx messagevisor promote --from=staging --to=production \ --target=web \ --apply# Promote everything except overrides (useful when staging has experiment overrides# that should not reach production yet)$ npx messagevisor promote --from=staging --to=production \ --excludeOverrides \ --applyConflict resolution#
When both source and destination sets have content for the same message key, you have a conflict. Messagevisor gives you explicit policy choices:
# Source wins - incoming values overwrite destination$ npx messagevisor promote --from=dev --to=staging --conflicts=source --apply# Destination wins - existing values in destination are preserved$ npx messagevisor promote --from=dev --to=staging --conflicts=destination --apply# Stop on conflict - do not make any change, report the conflict$ npx messagevisor promote --from=dev --to=staging --conflicts=fail --applyUse --conflicts=fail in automated pipelines where a silent merge decision would be hard to notice. If the pipeline stops on conflict, a human reviews the divergence and decides which value is correct.
Protecting environment-specific content from promotion#
Some messages should exist in a specific environment but should never be promoted forward. Mark them promotable: false:
description: Staging environment banner - should never reach productionpromotable: falsetranslations: en: You are viewing the staging environmentdescription: Sign-in button - dev placeholder, not ready to promotepromotable: falsetranslations: en: "[DEV] Sign in"The promote command skips any entity with promotable: false. These messages stay in their set and are never written to the destination set.
Generating an audit trail#
For regulated changes or change management workflows, use --audit to produce a record of what moved:
$ npx messagevisor promote --from=staging --to=production \ --apply \ --audit=markdownThe audit file lists every message and override that was added, updated, or unchanged in the destination. Commit it to the promotion branch or attach it to your change management ticket.
This is especially valuable for compliance-sensitive content (legal copy, financial disclaimers) where you need a durable record of what changed and when.
Checking whether two sets are in sync#
Before a production release, quickly preview whether staging has diverged from production:
$ npx messagevisor promote --from=staging --to=productionThe preview reports whether promotion would result in any changes. Use it as a pre-release gate: if the output shows changes, decide whether they should be promoted before the release or after.
Running promotions in CI#
For automated promotion workflows, a GitHub Actions job that promotes and opens a pull request with the changes:
name: Promote to stagingon: workflow_dispatch: schedule: # Run daily at 09:00 UTC to pick up overnight dev changes - cron: "0 9 * * 1-5"jobs: promote: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: | npx messagevisor promote \ --from=dev \ --to=staging \ --apply \ --conflicts=source \ --audit=markdown - run: npx messagevisor lint --set=staging - run: npx messagevisor test --set=staging - uses: peter-evans/create-pull-request@v6 with: title: "chore: promote dev translations to staging" branch: "promote/dev-to-staging" commit-message: "chore: promote translations from dev to staging" body: | Automated promotion of dev translations to staging. See attached audit file for details of what changed.This workflow runs the promotion on a schedule, validates the result, and opens a pull request for human review and merge. The merge step publishes the updated staging datafiles via the main CI workflow.

