Skip to content
Ship every weekUI and contentinternationalization

Two languages built in

Your site speaks English and Dutch from day one, with no missing lines.

The public site ships complete in English and Dutch. If a translation is missing, the build fails before anyone sees it. Pages load in the right language instantly, with no flash of the wrong one.

  • 2 complete languages out of the box
  • 0 translation downloads at first load
  • 1 checked cookie that remembers the choice

How it works

The mechanism end to end, as implemented in the repository.

  1. 1

    The URL decides first

    A /nl prefix means Dutch. Without one, the app falls back to the account preference, then a validated cookie, then the default.

  2. 2

    Pages render from built-in catalogs

    All copy reads from typed catalogs compiled into the app, so the server and the first client render already speak the right language.

  3. 3

    Switching keeps your place

    The language switcher rewrites the current URL, keeping the path, query, and hash, and updates the page language immediately.

  4. 4

    The choice sticks

    The pick is saved in a checked cookie and, for signed-in users, on the account, so the next visit opens in the same language.

Interactive example

See it working

This walkthrough demonstrates the implemented backend contract without using an account, credentials, or provider calls.

Interactive example

Explore the implemented decisions

Choose a sample state
Start building

Deterministic sample data only. No account, provider, or network connection is used by the interaction.

The problem this solves

Without a shared foundation, every team rebuilds these edge cases — differently.

The raw key on your live site

You add a label in English and forget the Dutch file. Nothing complains. A Dutch customer finds nav.settings.title on your settings page. Here, that gap fails the build, not the demo.

The shared link that switches languages

A Dutch reader shares your pricing page. Their colleague opens it and lands on the English version, because the language only lived in a cookie. Here, the language is in the URL, so it travels with the link.

The flash of the wrong language

Provider-based i18n loads translations after the page. Every first visit flickers from keys or English to the real text. Here, translations are compiled in, so there's nothing to wait for.

Highlights

Why it matters

The build catches missing lines

Both languages live side by side in one file. Add a phrase in one and forget the other, and the build fails instead of shipping a gap.

The language travels with the link

Dutch pages live under /nl, so a shared link opens in the same language the sender saw.

No loading flicker

Translations are compiled into the app, not fetched at runtime, so the first paint is already in the right language.

Engineering guarantees

What this does and does not promise

Safety boundary

This translates your app's own words, in English and Dutch. It doesn't machine-translate anything, generate hreflang tags for you, or translate what your customers write.

Implementation evidence

The boundary above is code, not a claim. These files carry the contract:

  • src/modules/i18n/messages.ts
  • src/modules/i18n/locale-navigation.ts
  • src/modules/i18n/MODULE.md
Questions

Frequently asked questions

Build on the two languages built in foundation

The docs describe the same contracts this page demonstrates.