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>
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:
- message loading
- locale fallback behavior
- document direction updates
2) Prefer readable fallback text
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
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
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
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:
1locales/common.json2locales/landing-page.json3locales/en.landing-page.jsonUse 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:
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
- one explicit locale provider for each major page boundary
srcfor exact page bundles,src-dirfor broad locale bundles- use child text for simple values
- include
fallback-locale - keep
update-documentonly on the top-most scope - keep
intl-msgfallback text readable