Status: Stable.
Normative home:
i18n.
Why this exists
A host renders some text for humans: interrupt prompts, error messages, extension UI strings. This document states how a client asks for a locale and how a host answers. Machine-readable identifiers stay ASCII, dates and times stay ISO 8601, and number, currency and text-direction formatting are out of scope.
Language tags
Every locale identifier — Accept-Language, Content-Language and every locale field — is a BCP 47 tag.
- Tags compare case-insensitively (RFC 5646 §2.1.1, RFC 4647 §2).
- A host SHOULD advertise and emit tags in the case RFC 5646 §2.1.1 recommends: lowercase language, titlecase script, uppercase region (
zh-Hant-TW,es-419).
Accept-Language
A client MAY send Accept-Language (RFC 9110 §12.5.4) on any request. A host MAY honor it to localize human-facing text in the response.
A host:
- MUST parse the header without failing the request. A malformed value MUST NOT cause
400; the host uses its default locale and proceeds. - MUST NOT reject a request because it does not support the requested locale.
- MUST honor q-values: the highest-q locale it supports wins, and ties go to request order.
- MUST NOT infer a locale from the request body to override the header, which is authoritative.
- SHOULD set
Content-Languageto the canonical tag of the locale used when it localized the response. When it localized nothing, it SHOULD omitContent-Languageor set it to its default locale. - MAY cache localized text. When localization is expensive, caching by
(localeTag, sourceText)is RECOMMENDED. - MAY return default-locale text at once and emit
node.completedwhen the localized version is ready. This is discouraged for an interrupt prompt, which a human needs localized now.
Fallback
When a host cannot localize to the requested locale, it:
- walks the list in q-value order and uses the first locale it supports;
- otherwise tries the language family: asked for
ja-JPand holding onlyja, it usesjaand setsContent-Language: ja; - otherwise uses its default locale, which it SHOULD advertise as
i18n.defaultLocale; - sets
Content-Languageto the locale it actually used, never another.
Error envelopes
A host MAY localize an error message, and MAY then add details.locale.
- The
errorcode MUST be the same registered identifier in every locale (errors.md); it is never translated. detailskeys are schema field names and are not localized.- A human-facing
detailsvalue MAY be localized, and SHOULD carry alocalesibling on the same object.
The i18n record
A host that negotiates locale SHOULD advertise i18n. A host advertising it honors Accept-Language on every protected route; a host that omits it serves one locale.
defaultLocale— the locale returned when noAccept-Languageentry matchessupportedLocales;"en"when omitted.supportedLocales— the locales the host can return for human-facing text. It MUST containdefaultLocale, and its order carries no meaning.- A host that translates by machine SHOULD list only the locales it has validated end to end.
Replay and fork
Locale is chosen at request time. Content-Language is request-scoped and not recorded in the event log, so a replay re-projects the recorded text and does not re-render it. A fork localizes by its own request's Accept-Language, not the parent run's.
Clients
A client that wants localized content MUST check discovery for i18n before sending Accept-Language. A host that does not advertise it ignores the header and returns its default locale.
Sources: RFC 0206.