Locale vs. Language: A Website Localization Audit
Summary
Pixel’s announcement examines a localization audit of a rental-alert website: 38 pages declare English, while pricing supports 24 currencies and 58 country codes. It highlights missing hreflang and direction attributes, showing why locale and language signals matter for international visitors and SEO.
DEV Community
Our site formats money in 24 locales and declares one language, and 14 of those locales are languages we have no words in
Notifio is a desktop app that watches rental search pages and emails you when a new listing appears. The market for that is a person hunting for a room in Amsterdam, Berlin or Madrid, and the site reflects it: fifteen pages about specific rental portals, grouped into six regional sections on /alerts with headings reading Netherlands, United Kingdom, Europe and international, Germany, Spain and southern Europe, Marketplaces. The site also charges each visitor in their own currency, which I have written about from the payments side in three of our 24 currencies are written without decimals and two of our pages tell the browser tab £20 and the visitor €24.99. What I had never done is look at the two halves together: what the site knows about the visitor's locale, against what it tells the browser about its own language. The markup side of the census 38 pages fetched from the live site: $ grep -o ']lang="[^"]"' pages/.html | grep -o 'lang="[^"]"' | sort | uniq -c
38 lang="en"
$ grep -c 'hreflang=' pages/.html | grep -v ':0'
$ grep -c 'dir="' pages/.html | grep -v ':0'
38 of 38 pages declare lang="en" 0 hreflang links 0 dir attributes, anywhere, on any element 0 inline lang attributes on any element other than 38 of 38 pages carry exactly one canonical link Nothing wrong with any of that for a monolingual site. The site is monolingual. Hold that thought. The data side of the census // server/src/lib/pricing/display-currency.ts
eur: { code: "EUR", locale: "en-IE", rate: 1.1696 },
chf: { code: "CHF", locale: "de-CH", rate: 1.0981 },
isk: { code: "ISK", locale: "is-IS", rate: 166.32 },
huf: { code: "HUF", locale: "hu-HU", rate: 423.31 },
ils: { code: "ILS", locale: "he-IL", rate: 4.0016 },
24 currencies 58 country codes mapped onto them, resolved per request from x-vercel-ip-country 24 distinct BCP 47 locales, one per currency of those, 10 are English variants: en-GB, en-IE, en-US, en-CA, en-AU, en-NZ, en-IN, en-SG, en-HK, en-ZA and 14 are not: de-CH, sv-SE, nb-NO, da-DK, is-IS, pl-PL, cs-CZ, hu-HU, ro-RO, tr-TR, es-MX, pt-BR, ja-JP, he-IL So the site is carrying enough locale knowledge to write a price the way a reader in Reykjavik, Budapest or Tel Aviv writes it, on pages that have never contained a word of Icelandic, Hungarian or Hebrew. The locale is passed to Intl.NumberFormat and nowhere else. It never reaches the document. The euro is formatted in English This is the detail that made me want to write this up. 27 of the 58 country codes map to eur. That bucket is the Netherlands, Germany, Spain, Italy, France, Ireland and twenty-one others, and it includes every country whose rental portals the site has pages about. Its locale is en-IE. en-IE €24.99
nl-NL € 24,99
de-DE 24,99 €
es-ES 24,99 €
A Dutch visitor to /pricing is shown €24.99. Every Dutch site they have ever bought anything from wrote it € 24,99: symbol, space, comma decimal. The number is right, the currency is right, the punctuation is Irish. I want to be fair to the decision, because there is one. Picking en-IE for the euro means one entry in the table instead of twenty-seven, it keeps the decimal separator the same as every other price on an English-language page, and a page that writes "€24.99 once" in one sentence and "24,99 €" in the next looks broken rather than localised. A single euro locale is the only option that does not require deciding which of twenty-seven conventions wins. But it is still a choice nobody wrote down, and the reason I know it was not deliberate is the locale list it sits in. If formatting natively mattered, en-IE would be wrong. If it did not matter, is-IS and hu-HU would not be there. Two invisible characters The Hebrew entry is the one that goes furthest past what the document can support: $ node -e 'console.log(JSON.stringify(new Intl.NumberFormat("he-IL",
{style:"currency",currency:"ILS"}).format(109)))'
"109.00 ₪"
Those two marks before the digits and before the shekel sign are U+200F RIGHT...

Powered by Readybot.io