Building blocks
Messages
Messages are the core translation entities in Messagevisor. They hold base translations, optional metadata, examples, and conditional overrides that react to segments or conditions.
Example#
description: Sign in button labeltranslations: en: Sign in nl: AanmeldenA richer message with overrides and examples:
description: Dashboard welcome heading# optional: a shorter one-line description of the messagesummary: Personalized greeting on the dashboard# optional: arbitrary structured metadatameta: tags: - dashboard - personalizationtranslations: en: Welcome back, {name} nl: Welkom terug, {name}overrides: - key: plan-pro segments: plan-pro translations: en: Welcome back, {name}. Your Pro workspace is ready. nl: Welkom terug, {name}. Je Pro-werkruimte staat klaar.examples: - description: Default greeting for English locale: en values: name: AdaMessage keys#
Message keys come from file paths relative to the messages/ directory, with the configured namespaceCharacter replacing path separators.
Default behavior:
messages/auth/signin.yml -> auth.signinmessages/nav/contact.yml -> nav.contactmessages/footer/privacy.yml -> footer.privacyThe separator character is controlled by namespaceCharacter in project's Configuration.
Description#
Human-readable documentation for the message. Shown in the Catalog and used by linting. Not included in runtime datafiles.
description: Sign in button labelSummary#
A shorter one-line description of the message. Used in CSV exports and UIs where the full description is too long.
summary: Sign in button labelTranslations#
The base locale-to-string translation map. This is the primary authored content.
description: Sign in button labeltranslations: en: Sign in en-US: Sign in (US) nl: AanmeldenLocale inheritance applies at build time. A regional locale like en-US can inherit from en through inheritTranslationsFrom, so you only need to author the strings that differ.
Translation workflow state#
Projects with a configured sourceLocale can record workflow state beside base and override translations:
description: Copyright noticetranslations: en: Copyright nl: AuteursrechttranslationStates: nl: status: reviewed sourceHash: 8dfc77ab4089e09d4397e12306086c19767ffd4bbf6e239240bde83fe537a4a7 targetHash: 26feb4304a9e70dcd516470818d30cace58f8dea39572954980330d85584a8b3status is draft, translated, or reviewed. sourceHash is the lowercase SHA-256 hash of the source locale text used for that translation. targetHash records the exact target text that the reviewer approved. Reviewed translations require both hashes so a later edit to either source or target invalidates the recorded approval. Overrides support the same translationStates map.
Upgrade existing reviewed states#
Existing reviewed entries with only sourceHash do not prove that their target text is still the text a reviewer approved. Review the current source and target together before recording a new approval. Do not bulk fill targetHash merely to make lint pass. If review has not happened, move the entry back to an appropriate unreviewed status and keep it out of releases that require review.
Save a preview first, review the source and target copy it contains, then apply that saved preview:
npx messagevisor review --locale=nl --includeMessages='checkout.*' --target=web --status=reviewed --output=review.json# Inspect the source, target, and proposed state changes before approving.npx messagevisor review --apply --input=review.jsonThe preview shows actual source and target text, including the resolved source locale when the source is inherited. Applying it calculates no new approval for unseen copy: the saved text, document versions, selection, and locale inheritance must still match. A conflict requires a fresh preview and human review, not editing hashes in the file.
Only direct translations can be reviewed. Preview locale selection excludes the configured source locale unless explicitly included. --status defaults to reviewed; use draft or translated when approval is not appropriate. Use --override=<key> to select overrides only. Locale, target, and override selectors are repeatable. In projects with sets, --set=<set> selects one set; omitting it visits all sets.
Pass selection and status options when creating the preview, not when applying it. Apply requires --input and rejects a bare --apply or additional set, locale, target, message, override, or status options. Without --output, the command displays a preview but does not save an applicable file. --json changes the report format; redirecting that report is not a substitute for --output.
Keep saved previews private. They contain full message content, not just hashes, and are bound to the project's storage location. Relative input and output paths resolve from the project root, independently of the exports directory. The command creates a new file with owner access only where filesystem permissions support it and refuses to overwrite an existing file. Files are limited to 64 MiB; select fewer messages or one set for a larger project. Treat the token as a content checksum, not a signature, access credential, or proof that someone performed a human review.
All saved sets are checked before the first write. Atomic writes cover one set at a time, not all sets together. Concurrent locale graph edits are outside the write lock guard; coordinate those separately and validate again before treating the result as release approval.
This metadata is authoring workflow state. It is not emitted into runtime datafiles.
Translator context#
Messages and overrides can include translatorContext to explain intent without shipping that information to applications:
translatorContext: notes: Shown after a successful payment. Keep the tone reassuring. contextUrls: - https://example.com/designs/payment-confirmation maxGraphemes: 80 productArea: Billing owner: Payments content team legalClassification: Informational terminology: preferred: [receipt] forbidden: [invoice] doNotTranslate: [Messagevisor] placeholders: name: description: Customer display name examples: [Ada] direction: auto accessibility: visibleAll fields are optional. placeholders entries require a description and may include string examples and direction (ltr, rtl, or auto). accessibility is visible, spoken, or label. maxGraphemes describes a user perceived character budget, not a byte count. These fields are authoring context and are never emitted into runtime datafiles. Review tooling and quality checks separately; a context field alone is not a runtime enforcement mechanism.
Overrides#
Conditional translations that are applied instead of the base translation when runtime conditions match.
Via conditions#
Assuming you already have your desired attributes defined, you can create an override with conditions like this:
description: Sign in button labeltranslations: en: Sign inoverrides: - key: platform-web conditions: - attribute: platform # referencing attributes/platform.yml operator: equals value: web translations: en: Sign in from browserVia segments#
Alternatively, if you are already defined reusable conditions via segments, you can reference them in the override like this:
description: Sign in button labeltranslations: en: Sign inoverrides: - key: platform-web segments: platform-web # referencing segments/platform-web.yml translations: en: Sign in from browserSee Overrides for the full override authoring model, and Conditions for the full operator and composition reference shared by conditions and segment definitions.
Meta#
Arbitrary structured metadata stored in the datafile alongside the translation. It does not affect evaluation logic but is available to the SDK at runtime through getMessageMeta() or the React useSdk() hook.
Useful if you wish to react differently via custom SDK modules.
meta: tags: - auth - onboardingExamples#
Message-scoped examples for documentation and debugging.
They are executable: you can inspect them with npx messagevisor examples --onlyMessages, and they also appear in the generated Catalog so reviewers can see evaluated output next to authored context.
description: Sign in button labelexamples: - description: Default greeting for English locale: en values: name: AdaSee Examples for the full authoring model.
Deprecated#
Mark a message as deprecated so the SDK can signal it at runtime.
deprecated: true# optional warning emitted by SDKdeprecationWarning: Use auth.signIn insteadWhen set, deprecated and deprecationWarning are included in the built datafile and surfaced to the SDK.
Archived#
When set to true it removes the message from normal runtime flows.
Archived messages are excluded from build output, CSV exports, and list commands.
archived: trueThis is the built-in availability switch. There is no separate published/unpublished state for messages.
Promotable#
Controls whether this message participates in sets-based promotion. Defaults to true.
Set promotable: false to prevent a message from being copied when running promote.
promotable: falsetranslations: en: Environment-specific textSee Promotions for how this interacts with set-to-set workflows. Especialy when covering linear promotion flows for multiple environments.
ICU message syntax#
When using the ICU module, translations can contain ICU syntax such as plurals, selects, and named format references:
translations: en: | {count, plural, =0 {No items in cart} one {# item in cart} other {# items in cart} }With named number format:
translations: en-US: "Total: {amount, number, money}"The named format money is defined in the locale's formats.number presets. See Formats and ICU Module.
Namespace-based organization#
Nesting messages in subdirectories creates logical namespaces:
messages/├── auth/│ ├── signin.yml -> auth.signin│ └── signout.yml -> auth.signout├── billing/│ ├── total.yml -> billing.total│ └── invoice.yml -> billing.invoice└── nav/ ├── home.yml -> nav.home └── contact.yml -> nav.contactSee Namespaces for configuration.
Creating messages#
Creating manually#
You can create a message definition file manually in the messages/ directory.
From the CLI#
$ npx messagevisor create --messages --keys=$'auth.signin\nauth.signout'This generates skeleton files with an empty translation for the first locale. You fill in the translations afterwards.
Testing messages#
Message tests validate resolved translations:
message: auth.signinassertions: - locale: en target: web expectedTranslation: Sign in - locale: nl target: web expectedTranslation: AanmeldenSee Testing for the full assertion shape including matrix, context, and values.
Finding duplicate copy#
To find active message keys that resolve to the same translation value, run:
$ npx messagevisor find-duplicates$ npx messagevisor find-duplicates --locale=en-USDuplicate detection is grouped by locale and applies translation inheritance before comparing values. When the catalog is generated with --with-duplicates, it also shows this in each locale's Duplicates tab.
Edge cases and behavior notes#
Override evaluation order matters#
Overrides are evaluated in the order they appear in the file. The first override whose conditions or segments match is the one used.
Changing override order changes runtime behavior.
Raw syntax depends on modules#
Message text is just a string until runtime modules interpret it. ICU plural/select syntax, named formats, and other behaviors depend on the configured modules in application runtime.
Target context can simplify overrides at build time#
When a target defines partially known context upfront, the builder can eliminate impossible override branches before the SDK sees the datafile.
That means the runtime datafile may have fewer override branches than the authored source.
Locale inheritance applies to overrides too#
Each override has its own translations map. Locale inheritance is resolved for override translations the same way it is for base translations.