FreeToGenerate.com

Parse the header and run both standard matching algorithms. Nothing is uploaded.

The request and what you have

Separate them with commas or spaces. These are your own files or translations — the tags you would actually respond with.

What each algorithm answers

Lookup — one tag

en

Basic Filtering — a set

fr

The two algorithms disagree on this input.

That is not a bug in either. RFC 4647 defines both, and they search in opposite directions — so the one your framework happens to use decides what this visitor sees.

What Lookup tried

en-us → en

Lookup truncates the requested range from the right until something matches. Note that a trailing single-character subtag is removed together with the one before it, so a step like zh-Hant-CN-x is never tried — that rule comes straight from the specification's own worked example.

The header as parsed

Rangeq
en-us1
fr0.8

A quality value of zero is a refusal, not a weak preference. A header saying one language with q=0 is asking you not to serve it at all, so a tag matched only by a refused range is excluded from both answers here. Ranges with no q at all default to 1, and equal values keep the order the header used, since the specification gives no other tie-break.

How often the two disagree

combinations measured
119
disagreements
14
only Lookup matched
8
only Filtering matched
6

Measured over 7 realistic sets of available tags against 17 realistic ranges: they differ on 14 of 119 combinations. The shape matters more than the rate. In 8 of those only Lookup found anything, because it truncates the request — so en-US reaches your plain en. In 6 only Filtering did, because it expands the request — so en reaches your en-GB. In none of them did both match and pick different things. The two schemes cover opposite directions of mismatch, and neither covers both.

Both algorithms are from RFC 4647; the q-value rules from RFC 9110. Nothing is uploaded — the matching happens in this tab.

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

  1. 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.
  2. 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.
  3. 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.