Guide & Best Practices

Use this guide to keep localized UIs predictable at scale. The package is most effective when locale scope and content remain explicit.

1) Start with one top-level <intl-locale>

html
1<intl-locale locale="en-US" fallback-locale="en" src-dir="/locales" update-document>2    <h1><intl-msg key="checkout.title">Checkout</intl-msg></h1>3    <intl-number type="currency" currency="USD">1299.99</intl-number>4</intl-locale>

Use one top-level provider for the page when possible. It centralizes:

2) Prefer readable fallback text

html
1<intl-msg key="cta.primary">Get started</intl-msg>2<intl-number type="currency" currency="USD">1299.99</intl-number>3<intl-datetime date-style="short">2026-01-01T10:00:00Z</intl-datetime>4<intl-list type="and">shipping tax discounts</intl-list>

Keep fallback text meaningful. It helps SEO, JS-disabled rendering, and loading states.

3) Build nested locale boundaries intentionally

html
1<intl-locale locale="en-US" src-dir="/locales">2    <h1><intl-msg key="product.title">Product</intl-msg></h1>3 4    <section>5        <intl-locale locale="fr-FR">6            <h2><intl-msg key="product.title">Produit</intl-msg></h2>7        </intl-locale>8    </section>9</intl-locale>

Nested scopes inherit message state from parent and can override values where needed.

4) Use API helpers where component markup is not ideal

ts
1import { createIntl, intlMsg, intlNumber, intlDateTime } from '@beforesemicolon/intl'2 3const preview = createIntl({4  locale: 'ja-JP',5  fallbackLocale: 'en',6  messages: { invoice: { total: 'Total: {amount}' } },7})8 9intlMsg('invoice.total', { amount: '¥1,000' }, { scope: preview })10intlNumber(1000, { locale: 'ja-JP', style: 'currency', currency: 'JPY' })11intlDateTime('2026-01-01T10:00:00Z', { locale: 'ja-JP', dateStyle: 'full' })

Use helpers for server-rendered content, labels in background jobs, and non-DOM workflows.

5) Language switching without a page reload

ts
1import { setLocale } from '@beforesemicolon/intl'2 3const selector = document.querySelector('select#locale')4selector?.addEventListener('change', async (event) => {5  const locale = (event.target as HTMLSelectElement).value6  const snapshot = await setLocale(locale)7  document.documentElement.lang = snapshot.locale8  document.documentElement.dir = snapshot.direction9})

Language switching works when components are subscribed to the active runtime.

6) Keep translation bundles small

At build time, combine shared keys and page-specific keys into scoped bundles:

text
1locales/common.json2locales/landing-page.json3locales/en.landing-page.json

Use src="/locales/en.landing-page.json" for the landing page runtime. This avoids loading unrelated pages.

7) Prefer SEO-safe content structure

Use clear visible text in HTML and keep formatting decisions close to output:

html
1<h1><intl-msg key="hero.title">Internationalization in plain HTML.</intl-msg></h1>2<intl-datetime date-style="full" time-style="short">2026-01-01T10:00:00Z</intl-datetime>

Your parser and crawler both benefit from predictable, localized output in the DOM.

Production checklist

edit this doc