| Component name | languages |
|---|---|
| In the visitorId | yes |
| structuredId group | locale |
| Hashing | plain value; a digest only when longer than 128 characters or not printable ASCII |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 1 |
Browsers keep a list of the languages a person prefers, in order, for example English (United States) first, then English. Websites use it to pick a language. WhiskerJS reads that list as it is. Many people have only one or two languages, but the exact list and its order still separate groups of users.
What it reads
navigator.languages, the string entries joined with commas. If the list is missing or empty, navigator.language alone. An empty result is unavailable / no_api.
How the technique works
The list reflects the browser’s language settings, which by default follow the operating system’s languages and region. Joining the entries in order keeps both the choice and the priority. Values are short ASCII language tags in practice, so they are reported as plain text; a value longer than 128 characters or outside printable ASCII would be hashed.
Why it helps
Language lists differ between regions and between people who add languages, and the order matters. It is in the locale group together with time_zone and intl_locale. It is also a strong consistency check against the request’s Accept-Language header; see Recommended server checks.
Stability
It changes when the user adds, removes or reorders languages in the browser or the operating system. A change of only the locale part of the structuredId often has this cause.
Browser support and what blocks it
ok in all browsers; not in the exclusion table. Some privacy modes report a reduced list (for example only the first language); WhiskerJS reports whatever the browser gives.
Integrity checks that look at the same properties: iframe_mismatch (language and languages against a clean frame), navigator_own_property and native_tampering (the languages getter).
Privacy
It reveals language preferences, which can hint at origin or region. Read without permission or prompt, never sent by WhiskerJS.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
none | for example en-US,en |
unavailable |
no_api |
no languages and no language |
unavailable |
no_digest |
a long or non-ASCII value needed hashing without Web Crypto |
blocked |
security_error, threw |
the getter threw |
excluded |
caller_excluded |
your exclude list |
timed_out |
timeout, aborted |
1000 ms inner timeout, budget, abort |
Example value
"languages": { "status": "ok", "value": "en-US,en", "hashed": false }
Questions and answers
Is languages the same as the Accept-Language header?
They come from the same browser setting and usually match. Comparing them on your server is a useful check; a mismatch can point to a script that changed one but not the other.
Why did my headless browser report en-US@posix?
Headless browsers take the language from the operating system environment. In the container test run used for these pages, headless Chromium inherited a POSIX locale and reported en-US@posix.