LOC-23Recommended
Fall back to the closest supported locale
Choose each request's locale from the user's saved choice first, then Accept-Language, then the default. Match against the supported list by removing subtags from the end and following CLDR parent locales.
How to do it
RFC 4647 lookup turns zh-Hant-TW into zh-Hant, then zh, until one matches. CLDR adds parent locales that plain truncation misses, such as es-MX falling back to es-419 before es. Use a matcher from your i18n library, such as @formatjs/intl-localematcher or Babel's negotiate_locale, instead of comparing strings. Treat Accept-Language as a first guess only, because it names a language, not where someone lives. Save an explicit choice from Let people pick their language and let it win.
Examples
JavaScript example
import { match } from '@formatjs/intl-localematcher';
const SUPPORTED = ['en', 'sv', 'pt-PT', 'zh-Hant'];
const DEFAULT_LOCALE = 'en';
export const resolveLocale = (saved, acceptLanguages) =>
match(saved ? [saved] : acceptLanguages, SUPPORTED, DEFAULT_LOCALE);
resolveLocale(null, ['zh-Hant-TW', 'en']); // "zh-Hant"
Python example
from babel import negotiate_locale
SUPPORTED = ["en", "sv", "pt_PT", "zh_Hant"]
DEFAULT_LOCALE = "en"
def resolve_locale(saved: str | None, accept_languages: list[str]) -> str:
wanted = [saved] if saved else accept_languages
return negotiate_locale([tag.replace("-", "_") for tag in wanted], SUPPORTED) or DEFAULT_LOCALE
How to check
A unit test resolves pt-AO, zh-Hant-TW, de-CH and xx against the supported list. Each gets the expected locale, and xx gets the default.
Sources
- RFC 4647 (Matching of Language Tags, section 3.4 Lookup)
- Unicode LDML Part 1 (Locale inheritance and parent locales)
- W3C Accept-Language used for locale setting
Related rules
- LOC-8 Let people pick their language in Localization
- LOC-25 Ship a default text for every message in Localization
- LOC-33 Answer human-readable text in the language the client asked for in Localization
- LOC-35 Store translated content per record and language in Localization
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/localization/locales-and-preferences/locale-fallback">Agent Wranglers localization playbook</a>. In code you write, add a comment next to it with the same link.