Also available in: Español · Português · Français · العربية
Accept-Language Header Parser
See exactly which of your languages a browser's Accept-Language header selects — and why the two standard algorithms can pick different ones.
What is the Accept-Language header?
Accept-Language is the field a browser sends with every request to say which languages its user would like to read. It carries a list of language ranges, each optionally weighted with a quality value between 0 and 1, most preferred first. A typical one looks like en-US,fr;q=0.8 — this reader wants American English, and will take French at a lower preference.
The header is a request, not an instruction. It tells you what the visitor would like; it cannot tell you what you have. Turning it into an actual response means matching those ranges against the set of languages you can genuinely serve, and that matching is where the interesting part lives, because there is more than one standard way to do it and they do not always agree.
This tool does both halves. It parses the header the way RFC 9110 defines the quality values, then runs the two matching schemes RFC 4647 defines — Lookup and Basic Filtering — against the tags you say you can serve, and shows you each answer side by side.
How to use it
- Paste the header. Copy the Accept-Language value out of your server logs, your browser's developer tools, or a request you are debugging. The parsed ranges appear in a table, sorted by quality value with the default of 1 filled in where the header omits it.
- List the languages you can serve. Type the tags you actually have files or translations for, separated by commas or spaces — en, fr, de. These are the tags you would respond with, not the ones the visitor asked for.
- Read both answers. Lookup gives you one tag; Basic Filtering gives you a set. Where they differ the page says so, and the fallback chain underneath shows every step Lookup tried on its way to the answer.
The two algorithms fail in opposite directions
RFC 4647 defines both schemes over the same header, and the difference is not a matter of strictness. Lookup truncates the request from the right until something matches, so a visitor asking for en-US will be served your plain en. Basic Filtering goes the other way: a range matches any tag it is a prefix of at a subtag boundary, so a visitor asking for en will be matched to your en-GB.
Measured over 7 realistic sets of available tags against 17 realistic ranges — 119 combinations — the two disagree on 14 of them, or 11.8%. The rate is less interesting than the shape. In 8 of those 14 only Lookup found anything at all, and in 6 only Filtering did. There was not a single case where both matched and chose different tags.
So the two schemes are not rivals over the same answers. Each covers a direction of mismatch that the other cannot see, and neither covers both. If your framework uses Lookup you will quietly fail the visitors whose request is less specific than your files; if it uses Filtering you will fail the ones whose request is more specific. Knowing which one you are running is the whole point, and most stacks do not tell you.
The truncation rule also has a trap, and the specification prints it. Reducing zh-Hant-CN-x-private1-private2 goes to zh-Hant-CN-x-private1 and then straight to zh-Hant-CN — never to zh-Hant-CN-x — because a single-character subtag left at the end is removed together with the subtag before it. The fallback chain on this page reproduces that worked example step for step.
What the header cannot tell you
A quality value of zero is a refusal, not a weak preference. A header that names a language with q=0 is asking you not to serve it at all, which is a different statement from simply leaving it out. This tool excludes a tag matched only by a refused range from both answers; a matcher that treats q=0 as merely the lowest score will happily serve the one language the visitor explicitly declined.
Equal quality values have no tie-break in the specification, so the header's own order is the only signal left and that is what this page uses. Be aware that it is a convention rather than a rule, and another implementation may reasonably order them differently.
More broadly, the header describes a preference and not a fact. It is usually the operating system's language list rather than a deliberate choice, it says nothing about the language of the content the visitor arrived looking for, and it is trivially spoofed. It is a good default and a poor override: if a visitor has clicked a language switcher, that click should win, and it should be remembered somewhere more durable than a guess about their browser.
One thing this page does not do is compare itself against a reference implementation, and the reason is worth stating. The Intl API performs Lookup with localeMatcher set to lookup, but against the runtime's own available locales rather than a set you supply, so it answers a different question. The specification's printed worked example is used as the test vector instead.
Why is it free?
The whole thing runs in your browser. Parsing a header and comparing strings costs nothing to do on your own machine, so there is no server to pay for and no reason to ask you for anything.
Nothing you type is uploaded, stored or logged — headers out of production logs can identify a person, and the safe way to handle that is not to receive it in the first place. There is no account, no sign-up and no limit on how many you check.