Localization (l10n)
Localization (commonly abbreviated as l10n, because there are 10 letters between the leading l and trailing n) is the ongoing work of adapting an internationalized application to a specific language, region, and cultural context.
What l10n actually means#
Where internationalization is the structural work that makes adaptation possible, localization is the continuous act of doing the adaptation:
- translating message strings into a target language
- choosing the right currency display, number grouping, and date formatting for the region
- adjusting examples and copy so they read naturally to a native speaker
- handling region-specific behavior (VAT-inclusive prices, regional disclaimers, alternate plan names)
- maintaining all of the above as the product evolves
A locale is never "finished". Product strings change, new features ship, regulations shift, and translations need to keep up.
Why l10n is usually painful#
The traditional localization loop looks like this:
- Engineers add new strings to the source code
- A script extracts them into a translation file or pushes them to an external platform
- Translators work in a separate tool, often without seeing where the strings appear
- Translated values come back as a JSON or YAML export
- Someone hand-merges the result into the repository
- Repeat for every locale, every release
This loop tends to produce:
- Drift between the source strings and the translated values, because the round-trip is slow.
- Invisible context. Translators see a key like
dashboard.empty.ctabut not what it does, who sees it, or what the surrounding UI says. - No tests. Whether a translated string interpolates correctly, pluralizes correctly, or even fits its UI slot is verified manually, if at all.
- Conditional logic stuck in code. "Use this string in Germany" lives as an
ifbranch in the application, not in the translation definition. Translators cannot see it and reviewers cannot audit it. - Audit trail in two systems. Some history lives in Git, some lives in the translation platform, and they rarely agree.
How Messagevisor approaches l10n#
Messagevisor keeps localization in the same repository as the rest of the product, reviewed through the same pull requests and validated by the same CI.
Translations live next to their context#
Each message file holds the description, the base translations, runtime examples, and any conditional overrides in one place. A translator reading the file sees what the string is for, how it interpolates, and which variants exist.
description: Dashboard welcome headingtranslations: en: Welcome back, {name} nl: Welkom terug, {name} de: Willkommen zurück, {name}examples: - description: Default greeting for English locale: en values: name: AdaRegion-specific behavior is data, not code#
Need a different string when the user is in Germany, on the Pro plan, or visiting on mobile? That belongs in the message as an override, driven by attributes and segments, not in application code.
overrides: - key: vat-included conditions: attribute: region operator: in value: [DE, AT, CH] translations: en: "Total (incl. VAT): {amount, number, money}"Reviewers and translators see the conditional behavior in the same file as the base translation. Application code stays free of localization branches.
Regional variants reuse a base#
Most regional locales (en-GB, en-US, nl-BE, nl-NL) differ from their base in a handful of strings and a few format presets. Messagevisor's inheritance lets a regional locale inherit translations and formats from a parent and only author the deltas.
The prune command removes redundant child translations and formats that have drifted back to matching the parent, keeping the locale file honest over time.
Format presets capture conventions per locale#
Format presets let each locale own its number, date, time, relative-time, and date-time-range formatting. Currency display, decimal grouping, time zone defaults, and date pattern all live with the locale and are inherited where useful.
inheritFormatsFrom: enformats: number: money: style: currency currency: GBPLocalization is testable#
Tests assert that a given message resolves to the expected translation for a given locale, target, and runtime context, including interpolated values.
message: billing.totalassertions: - locale: en-GB target: web context: region: DE values: amount: 149.99 expectedTranslation: "Total (incl. VAT): £149.99"A bad translation, a broken override, or a missing format preset shows up in CI, not in production.
Non-engineers can still participate#
Messagevisor is built around engineers and engineering-adjacent contributors editing YAML through pull requests. For translation teams that prefer a different surface, the project supports an export ▸ translate ▸ import workflow: extract a working set of strings, hand them off to translators in a familiar format, then import the results back into the project.
The catalog provides a generated, read-only UI for browsing messages, locales, overrides, examples, duplicate values, and history. It is useful for translators, product managers, and reviewers who do not work in Git directly.
What you get from doing l10n this way#
- Translation changes are pull requests. Every change has a diff, an author, a reviewer, and CI checks.
- Behavioral logic is auditable. Overrides driven by region, plan, or experiment are visible alongside the strings they modify.
- Regressions are caught early. Tests cover not just key presence but actual resolved output per locale and context.
- Translators see context. Descriptions, examples, and surrounding overrides give translators the information they need to make good choices.
- History stays in one place. Git answers "who changed this translation, when, and why" without a separate platform.
l10n vs i18n#
The boundary is worth restating:
- Internationalization (i18n) is the structural work that makes localization possible.
- Localization (l10n) is the ongoing act of adapting the system to a specific locale.
Messagevisor is designed to make both visible, reviewable, and testable, on the same Git timeline as the rest of the product.

