i18n reference

Config

KeyDefault
supportedLocales["en"]Every locale this project serves
defaultLocale"en"Must be one of the supported ones
messages{}Your catalogs, and your overrides
queryParam"lang"Where an explicit choice arrives, server and browser
cookieThe cookie the server chain’s cookie link reads
storageKey"pithy.locale"The localStorage key the browser’s storage link uses
serverResolversSee belowThe server chain, in order
browserResolversSee belowThe browser chain, in order

The chains

Both are the negotiation half of the layer order — the chains pick a locale, the layers answer a key in it.

Server, in default order:

LinkReads
param?lang=
userpithy_auth_users.locale
cookieThe locale cookie
headerAccept-Language, as the full q-weighted list
defaultThe project default

Browser, in default order:

LinkReads
query?lang=
accountThe signed-in reader’s stored locale
storageThis device’s remembered choice
navigatorThe reader’s own browser languages
serverWhat the server declared
defaultThe project default

Reorder or shorten either. default is the last resort whether or not it is listed.

The translator

Returns
t.t(key, params?)The string, or the key itself on a miss
t.maybe(key, params?)The string, or null on a miss
t.plural(key, count, params?)<key>.<category>, falling back to <key>.other
t.formatNumber(value, options?)
t.formatCurrency(value, currency, options?)
`t.formatDate(Datenumber, options?)`
t.formatList(values, options?)
t.formatRelativeTime(value, unit, options?)
t.catalogLocaleThe locale whose words answered. Falls back
t.formattingLocaleThe locale handed to Intl. Does not fall back
t.direction"ltr" or "rtl"

c.var.locale carries the same three facts, and is null when nothing was negotiated.

Key grammar

<domain>/<path> — domain is a capability’s name, path is lower case with _ inside a word and . between levels.

A tag is at most 64 characters; a key at most 129. Catalog keys has the grammar and the reason for it.

A capability may declare keys only under its own name. Overriding a kit key is always allowed.

Which email templates the kit translates

Translated with the kit (7): magicLink · otp · welcome · securityAlert · invite · passwordChanged · leadCapture

Not (5), because their words arrive as payload: testerNudge · supportReply · operationalNotice · newsletter · marketingCampaign

The shell follows the job’s locale; a payload-carrying template is only as localized as your own copy.

The rendered subject comes back on the enqueue result, so nobody renders it twice — but it is the enqueue-time render. The send path renders once more at the moment the message leaves and rewrites the column from that, so a scheduled job whose catalog was retranslated in between delivers the new sentence while the recorded value keeps the old one.

The locale marker

A catalog file you wrote in a language other than English declares itself within its first 25 lines, exactly:

// LOCALE es — an unreviewed first pass. Not American English by design.

Two facts, both load-bearing: the locale it is written in, and that the copy is an unreviewed first pass. Checking your coverage is what reads them.

It exempts the file’s quoted values, not the file. A catalog is not written in Spanish — its values are. Everything around them is a docblock, an import and an identifier: ordinary English prose, and as subject to a prose rule as any other file’s.

A run is one line’s worth. The census reads line by line, and a quote that never closes on its line opens no value — so the inner lines of a multi-line template literal are read as prose.

ESC