skip to content

How would you order locale sources - URL marker, stored preference, language header - for a product with public pages and signed-in users?

level: principalimportance: should knowfreq 44%

answer

  1. explicit beats ambient
  2. links carry a URL locale, cookies do not
  3. public pages and app surfaces differ
  4. the header seeds, it does not steer
  5. one resolver, measured fallback rate

basics

~20 s

Order by how explicit and how durable each signal is: an in-URL locale for public linkable pages, a signed-in user's stored preference for application surfaces, the client's language header only to seed a first visit, and the configured default last.

solid answer

~50 s

The decision is a product one, not a framework one, and it turns on three properties: **explicitness** (a choice the user made must outrank a setting they never touched), **shareability** (a locale in the URL travels with a pasted or indexed link; a cookie does not), and **cost** (every locale in the path multiplies URLs, needs translation coverage, and adds redirect paths that can fight each other). A policy that holds up in most products: the URL marker wins on public, linkable pages; the stored preference wins on authenticated surfaces; the client's language header seeds a first visit but never overrides an existing preference; the configured default ends the chain. What teams get wrong is rarely the order but the **singularity**: one resolver everywhere, a switcher that updates both URL and preference, and a measured fallback rate.

go deeper

for a junior

Recall that the order of locale sources is a deliberate choice, not a framework default, and that a language a user picked should outrank a setting their browser sent.

for a middle

Explain what each source buys and costs: a URL locale travels with the link, a stored preference is explicit but per-device, and the client header is a guess that is often unset or inherited.

for a senior

Show the operational failures - redirect loops, split authority between URL and preference, a switcher that updates one source - and insist on one resolver plus a measured fallback rate.

for a principal

Frame it as a product tradeoff across surfaces, write the policy down as one invariant, and name the signals and the triggers that would make you revisit it.

## What the decision actually trades Every source in the chain buys something and costs something. Ordering them is choosing which cost you would rather pay. | source | what it buys | what it costs | |---|---|---| | locale in the URL | a link that renders the same for everyone; distinct, indexable, cacheable addresses | one URL per locale for every page; redirect paths; a switcher that must rewrite links | | stored preference | respects an explicit decision; invisible in the address | not shareable; per-device; needs a writable store and a place to manage it | | client language header | a sensible first guess with zero user effort | often unset or inherited from a shared device; not something the user knowingly chose | | configured default | a chain that cannot fail | silently serves the wrong language when everything above it missed | The axis that usually decides it is **who the page is for**. A public marketing or documentation page is read by people arriving from links and search, where the address must be self-describing. An authenticated application surface is read by one known person who already told you what they want, where the address should be stable across languages. ## A default policy that holds up 1. **Public, linkable, indexable pages: the URL marker wins.** The locale is part of the address, so a shared link, a bookmark and a crawler all see the same page. Nothing else may override it - if a stored preference silently redirects, no link ever renders what its sender saw. 2. **Authenticated surfaces: the stored preference wins.** The user made an explicit choice; a device-level header must not quietly overrule it, least of all on a borrowed or shared machine. 3. **The client language header seeds, it does not steer.** On a first visit with no preference and no marker, it is the best available guess - use it, and persist the result as the preference so the guess becomes a decision the user can correct. 4. **The configured default terminates the chain**, and its use is measured rather than assumed. The part that decides whether the policy survives contact with the product is that **one resolver implements it**. If each surface reaches for whichever source is nearest, the language flips as the user navigates, and no one can state what the rule is. ## Failure modes to design against - **Split brain between URL and preference.** Both claim authority: the user opens a link in one locale, the stored preference says another, and each request fights the previous one. Decide per surface which is authoritative and make the other read-only there. - **Redirect loops.** A rule that redirects to the preferred locale, combined with a rule that writes the preference from the URL, will bounce. At most one of those two rules may exist on a given surface. - **A switcher that updates only one source.** Changing the language must both persist the preference and move the user to the corresponding URL, or the next navigation reverts. - **Assuming uniform coverage.** Translation completeness differs per surface and per release; a locale offered in the switcher but thinly covered produces pages that fall back key by key and look broken rather than untranslated. - **Ignoring URL multiplication.** Every supported locale multiplies addresses, which affects link maintenance, sitemap size and how much of the site any shared cache has to hold. ## Making it a decision you can revisit Because resolution happens in one place, it is also the one place to measure. Three signals tell you whether the order is right: - the **share of requests that fall through to the configured default** - high means the chain is missing the signal that is actually available; - the **rate of manual language switches immediately after landing** - the resolver guessed wrong and the user corrected it; - **redirect depth on entry URLs** - more than one hop means two rules are negotiating in production. Revisit the policy when the product's shape changes: adding a locale with partial coverage, adding a surface that is both public and personalized, or moving rendering to the client, each of which changes which source is genuinely authoritative. What should not change casually is the invariant underneath all of it - explicit beats ambient, the chain ends in a default that cannot fail, and exactly one component decides.

  • Why can a redirect-to-preferred-locale rule fight a locale in the URL?
    Both claim to be authoritative. A shared link arrives in one locale, the stored preference redirects to another, and the recipient never sees what the sender saw. If a second rule also writes the preference from the URL, the two bounce. Pick one winner per surface and make the other read-only there.
  • What tells you in production that the precedence order is wrong?
    Three signals, all measurable at the resolver: the share of requests resolving to the configured default, the rate of manual language switches right after landing, and redirect depth on entry URLs. Each points at a different fault - a missing signal, a bad guess, or two rules negotiating.
  • Should a language offered in the switcher always be in the supported set?
    Only if its coverage justifies it. Offering a locale whose bundles are thin produces pages that fall back key by key, which reads as broken rather than untranslated. Gate the switcher on a coverage threshold measured at release, and let partially covered locales resolve only when explicitly requested.

saying these in an interview costs you the question

  • Lets the client's language header override a choice the user made explicitly.
  • Implements the precedence rule separately in each surface or handler.
  • Redirects to the preferred locale on every request, looping with the URL marker.
  • Ships a switcher that changes the URL without persisting the preference.
  • Treats extra locale variants as free, ignoring URL and coverage costs.
  • Never measures how often resolution falls through to the configured default.