Workflow
Linting
Linting checks authoring files before you build or deploy datafiles.
Run lint#
$ npx messagevisor lint$ npx messagevisor lint --set=staging$ npx messagevisor lint --json --prettyWhat lint checks#
Entity schema validity#
- required properties are present
- types match (string, boolean, integer, etc.)
- unknown properties are flagged
Attribute and condition validity#
- condition
attributekeys reference defined attribute files - operators are valid for the attribute type
- values are compatible with the attribute type and any other additional constraints
conditions: - attribute: plan # must exist in attributes/plan.yml operator: equals value: pro # must be in plan's enum if enum is definedLocale format preset shape#
- format presets are valid for their format family
dateStyleandtimeStyleare not combined with granular component fields in the same preset- currency codes are plausible ISO 4217 values
Message override structure#
- override
keyvalues are unique within a message - referenced segment keys exist
- condition shapes are valid
Test definitions#
- message test
messagekeys resolve to real message files - segment test
segmentkeys resolve to real segment files - locale test
localekeys resolve to real locale files - target test
targetkeys resolve to real target files
ICU syntax and format references#
- ICU message syntax is structurally valid
- named format references in ICU syntax (e.g.
{amount, number, money}) resolve to actual presets in the active locale's formats
ICU linting is enabled by default. You can disable only this ICU-specific validation pass with lintIcu: false in configuration:
module.exports = { lintIcu: false,};When lintIcu is disabled, schema, reference, condition, format shape, and test definition checks still run.
Source translation contracts#
When sourceLocale is configured, lint also checks that:
- every base and override translation group resolves the source locale, directly or through locale inheritance
- translated ICU arguments preserve the source value contract and required rich text tags
translationStatesentries refer to real translations- reviewed translations carry both source and target hashes
- recorded hashes still match the current source and approved target text
lintIcu: false skips ICU parsing and argument/tag comparison, but it does not disable workflow-state or stale-hash validation.
The value contract is not a requirement to copy the source grammar. A numeric value can be used as a number or in a plural expression, and a date value can be formatted as a date or time. Translations can reorder or repeat arguments and choose plural categories appropriate to their locale. Preserve argument names, compatible value types, and required rich text tags. Keep semantic select keys and deliberate exact selectors meaningful; they are application values, not words to translate. See locale grammar.
Target linting also validates target context against declared attributes, rejects direct references to archived entities, and reports include/exclude patterns that match nothing when matching entities exist.
Translation readiness#
Without --locale, readiness excludes the configured source locale. You can include it explicitly to check resolution, but source copy does not require translation review. Without a configured sourceLocale, resolution can still be reported, but --requireReviewed cannot certify translations.
Use readiness to check translation coverage and review gates for the set, locale, and target you intend to publish:
npx messagevisor readiness --set=production --locale=nl --target=web --requireReviewed --requireDirect --maxMissing=0 --maxStale=0 --jsonIt reports direct, inherited, missing, reviewed, unreviewed, and stale translations. --requireReviewed requires current review evidence; --requireDirect rejects reliance on inherited copy. Missing and stale limits both default to zero when omitted. Use --includeMessages and --excludeMessages to narrow the report when necessary, and inspect the selected scope before treating a passing gate as release evidence.
Readiness, quality, and review previews accept one --set value; omitting it runs across all sets. Locale and target selectors can be repeated; review previews also accept repeated override selectors. A passing filtered report only covers that selection, not the whole project.
Readiness does not replace schema linting, runtime tests, or human language review. Existing reviewed entries without targetHash need a fresh review, not automatic hash acceptance.
To record approval, save a review preview, inspect its source and target copy, then apply that file. If copy or its review context changes meanwhile, create and inspect a new preview before retrying. Rerun readiness after applying it.
Quality previews#
Preview pseudo translations and run optional content checks without changing authored files:
npx messagevisor quality --set=staging --target=web --locale=nl --pseudo=accent --expansion=0.3 --jsonnpx messagevisor quality --set=staging --target=web --locale=ar --pseudo=rtl --bidi --json--pseudo accepts accent or rtl; --expansion controls the requested text expansion. Set, target, locale, --includeMessages, and --excludeMessages filters narrow the preview. This command never writes translation files and has no apply step.
Quality checks use authored translator context, including maxGraphemes budgets and forbidden terminology. Length checks use the longest literal path through ICU branches, not the size of values supplied at runtime. Test real interpolated values and rendered layouts separately.
Forbidden terms are matched as exact, case sensitive substrings of possible literal output. A term cannot evade the check by being split across rich text tags or an ICU branch boundary: a<b>b</b>c matches a forbidden abc. Alternatives are checked separately, never concatenated into a sentence that cannot be produced by that branch. Runtime argument values are unknown boundaries, so checks cannot certify terminology inside or spanning those values. The preferred and doNotTranslate lists remain guidance for translators, not automatic replacements or enforcement rules.
The opt in --bidi check reports unsafe embedding and override controls, unbalanced isolates, and dynamic arguments without isolation in RTL copy. These checks do not change ordinary builds or silently rewrite translations. Review flagged copy and test the intended display before publishing.
An explicit locale direction takes precedence. When it is omitted, quality uses the host's Intl.Locale direction data. If direction cannot be determined, the report includes bidi_unknown_direction and fails instead of silently claiming complete coverage. Set direction: rtl or direction: ltr on that locale and rerun the check. For RTL copy, use LRI or FSI isolation around values that are not explicitly documented as RTL in placeholder context, and balance isolates within each ICU branch.
Lint in CI#
Lint is designed to be fast and informative. Running it before build in CI catches authoring mistakes early and produces machine-readable output for integration with other tools:
$ npx messagevisor lintLint vs test#
lintvalidates structure and cross-referencestestvalidates runtime behavior
Lint does not evaluate messages. A project can pass lint but have failing tests if authored translations or format presets produce unexpected runtime results.
With sets#
When sets: true, lint runs across all sets by default.
To focus on one set:
$ npx messagevisor lint --set=staging