og:locale declares which language and region your Open Graph tags are written in. We fetched 130 URLs on November 2, 2026, parsed the raw server HTML of the 91 that answered, and counted: 74 of those pages ship an og:image, and 31 declare a locale. Forty-three send a share card with no language stated at all.
The spec marks the property optional and gives it a default of en_US, which is precisely why English-only sites can stay silent and never notice. Localized sites do not have that luxury, and the sample shows they are not consistently claiming it either.
What the Tag Actually Declares
The wording on ogp.me, fetched the same day as the sample, is one sentence: og:locale — The locale these tags are marked up in. Of the format language_TERRITORY. Default is en_US. The companion property is separate: og:locale:alternate — An array of other locales this page is available in.
Two arguments live inside that one value. The language half says which language the strings are in. The territory half says which regional variant, and for some pairs the difference is not cosmetic: pt_BR against pt_PT, en_US against en_GB, zh_CN against zh_TW. A card labeled en_US for a page written in British English is a small lie, and it is the kind of lie something downstream may act on.
The format detail is where implementations slip. The spec wants an underscore between language and territory — en_US. HTML's lang attribute uses a hyphen — en-US. Two conventions for the same idea, four pixels apart, and five of the 31 pages in our sample merged them.
Real talk: this tag renders nothing. Nobody has ever looked at a share card and seen its locale. We also cannot show you a platform rejecting a hyphenated or uppercase value, because that happens inside code we cannot read from outside. What the tag buys is a truthful declaration in the one place a consumer asks for it — and on a site actually serving several languages, that is the only place the answer exists.
What 91 Pages Ship
Method: plain GET with a desktop Safari user agent, JavaScript never executed, every meta property read from the bytes the server sent. Thirty-nine of the 130 targets never made it into the count — twenty-one 403s (nytimes, reddit, medium, stackoverflow, etsy, ebay, apnews and others), six that timed out or dropped the connection (washingtonpost, asahi, rakuten, zalando, ozon, canada.ca), four 404s from localized Wikipedia article titles that do not exist under those names, three empty 202s (amazon.com, amazon.de, booking.com), two 401s (wsj, reuters), one 451 (bangkokpost), one 429 (expedia) and one 200 that arrived with no body (cnn).
| Checked | Result | What it tells you |
|---|---|---|
| Pages parsed | 91 of 130 | Blocked and empty responses excluded |
| Ship any og:* property | 81 of 91 | Ten pages had no Open Graph block at all |
| Ship og:image | 74 of 91 (81%) | The card itself is the part people get right |
| Ship og:locale | 31 of 91 (34%) | 38% of the pages that ship any OG block |
| Ship an image but no locale | 43 of 74 | Declared picture, undeclared language |
| Value matches language_TERRITORY | 26 of 31 | Five used a hyphen, a bare language, or caps |
| Region that is a real ISO code | 25 of 31 | Clarín shipped es_LA, which is not a country |
| Locale contradicts html lang | 0 of 31 | Where the tag exists, the language is right |
| Ship og:locale:alternate | 1 of 91 | And that one repeats its primary value |
| Ship any Twitter-side locale | 0 of 91 | Language declaration has no Twitter twin |
Read the table by column, not by row. The values themselves are disciplined: not one of the 31 pages contradicted its own html lang, which means nobody made up a language. What varies is whether the tag exists and whether the territory half survived contact with a template.
| Value | Pages | Who |
|---|---|---|
| en_US | 10 | apple, airbnb, harvard, vercel, gitlab, mdn, news.google, webmd, lemonde.fr |
| fr_FR | 4 | apple/fr, airbnb.fr, lefigaro, 20minutes |
| ja_JP | 3 | apple/jp, nhk twice |
| pt_BR | 2 | folha, globo |
| Singles | 12 | es_ES rtve, es_CO eltiempo, es_LA clarín, en_GB nhs.uk, de_DE heise, fi_FI iltalehti, ko_KR yna, plus five malformed values counted below |
The spread is the finding. en_GB appears once in 91 pages, on nhs.uk, a site whose entire audience is British. Meanwhile bbc.com served lang="en-GB" twice with no locale on either page, and theguardian.com shipped no Open Graph block in server HTML at all.
Six Values the Spec Would Reject
| Site | Shipped | Problem |
|---|---|---|
| salesforce.com | EN_US | Uppercased language; parsers comparing strings miss it |
| notion.so | en-US | Hyphen, copied from the html lang convention |
| nike.com | en-US | Hyphen, and its html lang says en-US too |
| mozilla.org/de | de | Bare language, no territory |
| qualtrics.com | en | Bare language, no territory |
| clarin.com | es_LA | LA is not an ISO 3166-1 code; ISO calls it es-419 |
Two of these are copy-paste, two are a CMS field doing half its job, and two are a deliberate extension. The copy-paste pair is worth flagging for anyone building templates: when the same variable feeds both html lang and og:locale, the hyphen form ships to both, and only one of them wants it. The bare-language pair looks like a language picker that never had a region dropdown. Clarín's es_LA is the interesting one — an intentional, readable, technically invalid value that describes an audience rather than a country.
I cannot tell you which of these six broke a preview. What I can tell you is that a parser doing string equality against en_US sees six pages it cannot match, and a parser doing a naive split on the underscore sees five of them fail before it reaches the language.
Localized Paths Are Where It Disappears
Nine pages in the sample were requested on a locale-specific path. Only three of those declared a locale, and all three belonged to Apple.
| URL | html lang | og:locale |
|---|---|---|
| apple.com/fr/ | fr-FR | fr_FR |
| apple.com/jp/ | ja-JP | ja_JP |
| ikea.com/de/de/ | de-DE | none |
| ikea.com/us/en/ | en-US | none |
| spotify.com/de/ | en | none |
| dw.com/en/top-stories/s-9097 | en | none |
| mozilla.org/de/ | de | de |
| un.org/en/ | en | none |
Apple is the model to copy, because the localization is per path rather than per site: share /fr/ and the card is labeled French, share /jp/ and it is labeled Japanese, with the language and region halves both correct. IKEA ran two fully localized paths in the same fetch with no locale on either. Spotify's /de/ page is the strangest row in the table — the region is in the URL, the markup says lang="en", and no locale resolves the contradiction.
Then there is the split nobody expects. Of the 34 pages whose html lang was not English, 16 declared og:locale (47%). Of the 53 English pages, 15 did (28%). Non-English pages are nearly twice as likely to carry the tag, which is rational: if your page is written in English, the spec default already tells the truth, so thousands of English-only sites are compliant by saying nothing.
And the alternate array is effectively dead on the live web. One page out of 91 declared an og:locale:alternate, and the value was byte-identical to its own primary — eltiempo.com listing es_CO twice. Big multilingual newsrooms in the sample (spiegel.de, corriere.it, cna.com.tw, zaobao.com.sg) shipped no locale at all, so they had nothing to alternate.
Setting It So It Sticks
Two lines in static HTML, and the order matters only in that the primary must come first:
<meta property="og:locale" content="en_GB" />
<meta property="og:locale:alternate" content="fr_FR" />
<meta property="og:locale:alternate" content="de_DE" />
In Next.js the field sits inside openGraph as locale, and alternate versions go in alternateLocale. The Metadata API emits the primary tag when you set it — the resolver in the installed version writes og:locale at lib/metadata/metadata.js around line 701 and og:locale:alternate around line 865, with alternateLocale typed as a string or array in lib/metadata/types/opengraph-types.d.ts. It does not choose a locale for you:
// app/layout.tsx
export const metadata = {
openGraph: {
locale: 'en_GB',
alternateLocale: ['fr_FR', 'de_DE'],
type: 'website',
},
};
// output
// <meta property="og:locale" content="en_GB" />
// <meta property="og:locale:alternate" content="fr_FR" />
Omit the field and no tag appears, which is exactly what happened to us. Our own homepage, fetched today, carries og:title, og:description, og:image, og:image:width and og:image:height, and no og:locale — the root layout never declared one. The same shallow merge that erased our og:site_name and og:type last month applies here: a page that declares its own openGraph object replaces the layout's object rather than extending it, so any tag the layout set disappears unless the page repeats it. If you fix one tag in this position, write the object once and spread it into page-level overrides.
On a CMS, the value almost always comes from the same language field that feeds html lang. Check the output rather than the setting, because the two malformed shapes we found in the wild — en-US and a bare de — both look like a template that read a language code and never asked for a region.
og:locale Checklist
- Use an underscore, not a hyphen. en_US is the spec form; en-US is html lang's form. Two pages in the sample shipped the hyphen and both of them already had en-US in their html tag.
- Decide the region on purpose. British English is en_GB. Brazilian Portuguese is pt_BR. If the region genuinely does not matter, an English-only site can stay silent and inherit the en_US default — say it out loud anyway if you serve a regional audience.
- Do not invent a territory. Clarín's es_LA reads well and fails ISO 3166-1. If you need a language-wide code the closest valid option is the M.49 form, es-419 — still not the language_TERRITORY shape the spec asks for.
- Set it per path on localized routes. Apple labels /fr/ as fr_FR and /jp/ as ja_JP, so every share of every path is correct. One global value on a localized site labels every card with the default locale.
- Add alternates only for URLs that exist. One page in 91 declared alternates, and its value matched its own primary. A list of translations you cannot link to is worse than silence.
- Assert it in CI on one localized path per language. Every failure in this article is invisible in a browser tab and obvious to a fetcher. One curl and one grep would have caught all six malformed values before they shipped.
One Line
og:locale is the property the web treats as optional and its own spec page skips — ogp.me ships nine Open Graph properties and no locale of its own. Thirty-four percent coverage is not a crisis while the default is en_US, and it becomes one the moment your share card needs to say which language it is in. Six of the 31 pages that tried got the format wrong; the cost of getting it right is one underscore.
Paste your URL into the free OG checker to see every Open Graph property a scraper reads from your server HTML — locale, alternates and all — then render the card itself at ogimgen.com. Related reading: the full Open Graph meta tag list covers each property and which are required, og:site_name explains the page-override failure that swallows locale too, and og:description and og:title apply the same measured treatment to the tags inside the locale you are declaring.