Use cases
Testing translations before they ship
Translation bugs are invisible until someone reads them. An ICU plural pattern with a syntax error silently falls back to a raw string. An override whose condition is slightly wrong shows the default copy to users who should see a variant. A currency format preset that was correct for one region produces the wrong symbol for another. None of these failures throw an exception. They just silently serve the wrong content.
Messagevisor has a built-in authored test system that catches these problems before they reach production. Tests run in CI, assert exact runtime behavior, and cover the same evaluation engine that your production SDK uses.
What you can test#
Messagevisor supports four test subjects:
- Message tests: assert what translation a specific message key resolves to for a given locale, target, and context
- Segment tests: assert whether a segment matches for a given context
- Locale tests: assert resolved format presets and raw-message translation for a locale
- Target tests: assert message inclusion, format presets, and evaluated translations for a target and locale combination
Each test file has one subject and an assertions array.
Message tests#
The most common test type. Assert the exact string a message resolves to.
message: auth.signinassertions: - description: English base locale: en target: web expectedTranslation: Sign in - description: Dutch locale: nl target: web expectedTranslation: AanmeldenTesting overrides#
Override evaluation is where silent bugs most often hide. Test each override branch explicitly:
message: billing.upgradeassertions: - description: Free user sees default CTA locale: en target: web context: plan: free expectedTranslation: Upgrade your plan - description: Pro user sees add-ons prompt locale: en target: web context: plan: pro expectedTranslation: Explore add-ons for your Pro plan - description: Enterprise user sees account manager prompt locale: en target: web context: plan: enterprise expectedTranslation: Talk to your account managerTesting with interpolation values#
message: cart.itemsassertions: - description: Single item locale: en target: web values: count: 1 expectedTranslation: 1 item in cart - description: Multiple items locale: en target: web values: count: 3 expectedTranslation: 3 items in cart - description: Empty cart locale: en target: web values: count: 0 expectedTranslation: No items in cartICU plural syntax in translations is evaluated by the ICU module. If the plural pattern is malformed, the test catches it at lint or test time rather than at runtime.
Matrix expansion#
When you need to test the same assertion across multiple values, use matrix instead of writing each assertion by hand:
message: dashboard.welcomeassertions: - matrix: name: [Ada, Sam, Eve] description: Greeting for ${{ name }} locale: en target: web values: name: ${{ name }} expectedTranslation: Welcome back, ${{ name }}This expands to three concrete assertions at runtime. Matrix works well for parameterized content and for testing that interpolated values pass through correctly.
Segment tests#
Segment tests validate the conditional logic independently of any message. This is especially valuable for complex conditions that power many overrides.
segment: plan-proassertions: - description: Matches pro plan context: plan: pro expectedToMatch: true - description: Does not match free plan context: plan: free expectedToMatch: false - description: Does not match enterprise plan context: plan: enterprise expectedToMatch: falsesegment: new-userassertions: - matrix: days: [1, 7, 30] description: Matches within ${{ days }} days of signup context: daysSinceSignup: ${{ days }} expectedToMatch: true - description: Does not match at 31 days context: daysSinceSignup: 31 expectedToMatch: falseSegment tests give you a fast feedback loop for debugging conditions before layering message behavior on top.
Locale tests#
Locale tests assert format resolution and raw-message translation without being tied to a specific authored message key.
locale: en-USassertions: - description: USD money format is resolved target: web expectedFormats: number: money: currency: USD currencyDisplay: symbol - description: Date long format is present expectedFormats: date: long: year: numeric month: long day: numericCombining format assertion with raw-message translation in one assertion:
locale: en-USassertions: - description: Currency format translation target: web rawMessage: "Total: {amount, number, money}" values: amount: 19.99 expectedFormats: number: money: currency: USD expectedTranslation: "Total: $19.99"Locale tests are the right place to assert that inheritFormatsFrom is resolving correctly, particularly for regional variants where only some format presets are overridden.
Target tests#
Target tests validate the shape and behavior of a built artifact for a specific target and locale combination.
target: webassertions: - description: Auth messages are included expectedToIncludeMessages: - auth.signin - auth.signout - description: Admin messages are excluded expectedToNotIncludeMessages: - admin.dashboard - admin.users - description: Money format for en-US locale: en-US expectedFormats: number: money: currency: USD - description: Welcome message evaluates for en-US locale: en-US message: dashboard.welcome values: name: Ada expectedTranslation: Welcome back, AdaTarget tests are the best defense against accidental regressions when you change target configuration. If you modify includeMessages in targets/web.yml, a target test will catch any messages that were inadvertently dropped.
Running tests#
$ npx messagevisor testUseful filters:
# Run tests matching a key pattern$ npx messagevisor test --keyPattern=billing# Run only failing assertions$ npx messagevisor test --onlyFailures# Show the built datafile alongside results (useful for target tests)$ npx messagevisor test --showDatafile# Run tests for a specific set$ npx messagevisor test --set=productionRunning in CI#
Add the test step to your pull request workflow so every translation change is validated before merge:
name: Messagevisoron: pull_request: push: branches: [main]jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx messagevisor lint - run: npx messagevisor test - run: npx messagevisor build --showSizeThe combination of lint, test, and build in CI provides a complete quality gate: lint catches structural problems, tests catch behavioral regressions, and build confirms that all target and locale combinations can be generated successfully.
Debugging a failing test#
When a test fails and the cause is not obvious from the assertion output:
# Narrow to the failing key$ npx messagevisor test --keyPattern=billing.upgrade --onlyFailures# Inspect the built datafile shape for a target test$ npx messagevisor test --keyPattern=web --showDatafile# Use evaluate for ad hoc inspection without a committed test$ npx messagevisor evaluate --message=billing.upgrade --locale=en --target=web --context='{"plan":"pro"}'The evaluate command uses the same evaluation engine as tests. If evaluate returns the expected translation, the test assertion itself may have a typo. If evaluate returns something unexpected, the message definition or segment logic needs investigation.

