Auditable compliance and legal copy
Legal and compliance copy occupies a special category in a product: the text matters in a way that most copy does not. A GDPR consent label, a financial disclaimer, a terms acceptance checkbox - getting these wrong is not a UX problem, it is a legal liability. The teams responsible for this content need to answer questions like "What exact text was displayed on this date?", "Who approved this change?", and "Is the live production text identical to what legal signed off on?"
Traditional translation workflows cannot answer these questions reliably. A spreadsheet with a last-modified date does not tell you who changed it. A hosted localization platform might have activity logs, but they are separate from your code review and deployment records. Messagevisor's Git-based model makes every change a commit, every approval a pull request review, and every deployment a CI artifact - giving you an audit trail that is accurate, immutable, and queryable.
The audit trail by default#
When all translation changes go through pull requests, your audit trail is Git history. For any message in the repository:
$ git log --follow -p messages/legal/gdprConsent.ymlThis shows every change ever made to that file: who made it, when, what the commit message said, and the exact text before and after each edit. git blame identifies the author of each current line.
For compliance purposes, this is often sufficient. The record is tamper-evident (a Git history cannot be silently rewritten on a hosted repository with branch protection enabled), attributable to specific people, and permanent.
Structuring legal copy in its own namespace#
Isolating legal copy in a dedicated namespace makes it easy to apply CODEOWNERS rules and to navigate to the right files during an audit:
messages/├── legal/│ ├── gdprConsent.yml│ ├── termsOfService.yml│ ├── privacyPolicy.yml│ └── cookieConsent.yml├── compliance/│ ├── financialDisclaimer.yml│ ├── investmentWarning.yml│ └── ageVerification.yml└── billing/ └── ...Marking legal messages with compliance metadata#
Use meta to record compliance-relevant context directly on the message:
description: GDPR consent checkbox text shown during registrationsummary: GDPR consent - requires legal approval for any changesmeta: compliance: gdpr legalApprovedBy: ada@yourcompany.com legalApprovedAt: "2025-09-03" jurisdiction: - EU - EEA changeRequiresLegalReview: truetranslations: en: I have read and agree to the Privacy Policy and Terms of Service nl: Ik heb het Privacybeleid en de Servicevoorwaarden gelezen en ga hiermee akkoord de: Ich habe die Datenschutzrichtlinie und die Nutzungsbedingungen gelesen und stimme zu fr: J'ai lu et j'accepte la Politique de confidentialité et les Conditions d'utilisationMeta does not affect evaluation, but it is included in the built datafile. Internal tooling, the catalog, and the SDK's getMessageMeta() method can surface this metadata to reviewers and support tools.
Requiring legal review via CODEOWNERS#
Enforce that legal or compliance teams must approve every pull request that touches this namespace:
# Legal team must approve any changes to legal copymessages/legal/ @legal-teammessages/compliance/ @legal-team @compliance-team# Financial disclaimers require both legal and finance sign-offmessages/compliance/financialDisclaimer.yml @legal-team @finance-teamWith branch protection enabled and "Require review from Code Owners" checked, a pull request modifying messages/legal/gdprConsent.yml cannot be merged until someone from @legal-team approves it. This is enforced by GitHub - it cannot be bypassed by the pull request author.
Testing that compliance copy is not accidentally changed#
Write explicit tests that lock the exact text of critical messages. A test that asserts expectedTranslation: I have read and agree to the Privacy Policy and Terms of Service will fail if anyone changes the text without also updating the test - which means updating the test itself becomes evidence that the change was intentional and reviewed.
message: legal.gdprConsentassertions: - description: English GDPR consent text locale: en target: web expectedTranslation: I have read and agree to the Privacy Policy and Terms of Service - description: Dutch GDPR consent text locale: nl target: web expectedTranslation: Ik heb het Privacybeleid en de Servicevoorwaarden gelezen en ga hiermee akkoord - description: German GDPR consent text locale: de target: web expectedTranslation: Ich habe die Datenschutzrichtlinie und die Nutzungsbedingungen gelesen und stimme zuThese tests act as a second pair of eyes on compliance content: any modification to the translation file that does not also update the test will fail CI, flagging the inconsistency before merge.
Promotion audit for compliance releases#
When compliance copy moves from staging to production, preview exactly what would change:
$ npx messagevisor promote --from=staging --to=production \ --includeMessages="legal*"Review the preview output with legal, then apply the promotion with an audit record:
$ npx messagevisor promote --from=staging --to=production \ --includeMessages="legal*" \ --apply \ --audit=markdownThe markdown audit file lists every message that was added or modified. Commit it to your repository or attach it to your change management ticket as evidence of what changed in a specific release.
Environment-specific legal copy#
Some compliance text differs per environment or per region. The production legal copy may have been signed off by counsel while staging carries a draft version. Using sets keeps these separate without risk of the draft leaking into production:
description: DRAFT - pending legal reviewmeta: status: draft draftedBy: product-teamtranslations: en: "DRAFT: I have read and agree to the Privacy Policy and Terms of Service"description: GDPR consent checkbox text - legally approved 2025-09-03meta: status: approved legalApprovedBy: ada@yourcompany.com legalApprovedAt: "2025-09-03"translations: en: I have read and agree to the Privacy Policy and Terms of ServiceThe staging draft is marked promotable: false to prevent it from accidentally overwriting the approved production copy during a bulk promotion:
description: DRAFT - pending legal reviewpromotable: falsetranslations: en: "DRAFT: I have read and agree to the Privacy Policy and Terms of Service"Answering the "what was live on this date" question#
For compliance investigations, you need to know the exact text that was displayed to users on a specific date. The build artifact corresponds to a specific commit. If your deployment pipeline records which Git commit each build came from, you can reconstruct the exact translations for any point in time:
# Check out the repository at the date in question$ git checkout $(git rev-list -n 1 --before="2025-10-01 12:00" main)# Build the same artifact that was live at that time$ npx messagevisor build --set=production --target=web --locale=en# Inspect the output$ cat datafiles/messagevisor-web-en.json | jq '.messages["legal.gdprConsent"]'This gives you the exact translation that was in the production artifact on that date, derived from the same definitions that were checked in.
Regional compliance variants#
Compliance requirements differ by jurisdiction. The same product may need "VAT" in the EU and "Tax" in the US, or a financial disclaimer that is only required in certain markets. Use overrides with region-based conditions to serve jurisdiction-specific compliance copy:
description: Tax line item label on invoices and checkout summariestranslations: en: Taxoverrides: - key: eu-vat conditions: attribute: region operator: in value: [DE, AT, CH, FR, NL, BE, SE, FI, DK, NO] translations: en: VAT de: MwSt. nl: BTW fr: TVA - key: uk-vat conditions: attribute: region operator: equals value: GB translations: en: VATTest each jurisdiction explicitly to ensure the right term is shown:
message: billing.taxLineItemassertions: - description: US default is Tax locale: en target: web context: region: US expectedTranslation: Tax - description: Germany shows MwSt locale: de target: web context: region: DE expectedTranslation: MwSt. - description: Netherlands shows BTW locale: nl target: web context: region: NL expectedTranslation: BTW - description: UK shows VAT in English locale: en target: web context: region: GB expectedTranslation: VATThese tests are compliance assertions. A failing test means the wrong tax terminology is showing in a specific jurisdiction - which could be a regulatory problem. Run them in CI on every pull request.

