Ecosystem ↗

6. Build a language-aware feature

Translate a message with a catalog, then keep browser and server locale choices safe.

↗ View Markdown
LESSON 6 OF 818 min + practice · Build
  • Create a typed catalog
  • Keep request locales separate
  • Load browser catalogs explicitly

Your goal#

Create a small catalog, translate a greeting and quantity, and explain why a shared server cannot use one mutable language for all requests.

Prepare a typed catalog#

sh
npm install zlocale@0.0.4
locales/fr.ts
import { defineCatalog } from 'zlocale';
export default defineCatalog({
  locale: 'fr', domain: 'site',
  messages: {
    Welcome: 'Bienvenue',
    '%d item': { one: '%d article', other: '%d articles' },
  },
});

A locale identifies language/region conventions. A domain separates one feature's messages from another. A catalog is data: source keys mapped to translated strings/plural categories. These words are also in the glossary.

Read a prepared message#

An application-owned translator
import { createI18n, sprintf } from 'zlocale';
import french from './locales/fr.js';

const i18n = createI18n({ locale: 'fr', domain: 'site', catalogs: [french] });
const greeting = i18n.__('Welcome');
const count = sprintf(i18n._n('%d item', '%d items', 2), 2);
// Expected: greeting === 'Bienvenue'; count === '2 articles'

No disk or network read occurs in __ or _n. The host has already registered the catalog. Missing messages return source text; plural categories use native Intl/CLDR rules. other is mandatory, and languages may need more categories than English.

Choose the right owner#

On a server, pass i18n.forLocale(validatedLocale) into a handler/module, or use createRequestI18n from zlocale/node around the awaited downstream request. Do not call shared i18n.setLocale independently for simultaneous requests.

In a browser application, setLocale can change the application-owned locale after catalogs are ready. createLocaleController protects rapid language changes from stale async results. createZyroI18n connects reads to Zyro's signal factory so visible text can update. Changing <html lang> alone does not translate the page.

Load only what the host needs#

Use literal, allowlisted import functions for locale chunks. Never concatenate a cookie, header or query value into an import path. A module contributes its catalog; the host chooses supported languages, required domains and preference persistence. SSR can transfer an explicit public catalog subset with serializeCatalogs/parseCatalogs.

For a complete implementation, read site language settings. To publish reusable translated features, read module/plugin authoring. Both use the same package contract with different ownership.

Try it yourself#

Add an English catalog and a second French message. Read each through locale-fixed translators without changing the owner locale. Ask for an unknown message and inspect the fallback. Then explain why a private French response and a public English response should not share an unpartitioned cache key.

You are done when: you can demonstrate translation/fallback and explain when catalogs load, who chooses locale and where request isolation lives.

PUT IT INTO PRACTICE

Check your understanding

Try the exercise above, then answer both questions. Each correct answer earns 10 local learning points, once. You can retry; all lessons stay open.

How should concurrent server requests choose different languages?

Explain the answer

Use a locale-fixed translator or request-local context. Locale-fixed views and AsyncLocalStorage prevent one request from changing another request’s language.

When does __(text) load a catalog file?

Explain the answer

It never loads files; the host explicitly preloads/registers catalogs. Translation reads are synchronous in-memory lookups. Loading is a separate explicit async boundary.

Implementation sources

Implementation files for the APIs described on this page.

ZeruxJS · Open source · MIT OR Apache-2.0

Make yourself at home.

Every shortcut has a visible control too. Reading and navigation work without JavaScript.

Ctrl / ⌘ K
Search all documentation
/
Search, outside editable fields
?
This help, outside editable fields
↑ / ↓
Move through search results
Esc
Close a dialog or version menu
Progress, privacy, offline reading and controls →

Reset this learning record?

This removes course answers, exercise confirmations, bookmarks and recent pages for this docs version in this browser. Export a copy first if you want to keep it.

Search documentation

Type at least two characters.