ScreenshotNeo

BlogHow-to

How to add Open Graph tags to a multilingual website

Set Open Graph metadata for each localized URL, and use hreflang separately to connect language versions for Google Search.

By the ScreenshotNeo team4 October 202611 min read

For every localized URL, put page-specific Open Graph tags in the HTML <head>: the title, type, image, canonical page URL, and that page’s locale. Add one og:locale:alternate tag for each other locale in which the same page is available. Separately add reciprocal hreflang links that map those locale versions to their URLs for Google Search. Open Graph locale tags do not replace hreflang.

This guide shows the HTML pattern, explains how to generate it from a locale map, and gives a checklist for validating each version. For social preview images and page rendering, a screenshot can help you inspect what a crawler sees; ScreenshotNeo is a website screenshot API and MCP server for developers at ScreenshotNeo.

1. Understand the two kinds of localization metadata

Metadata What it describes Value format Where it belongs
og:locale The locale of the current Open Graph object Language and territory separated by underscore, such as en_GB Open Graph meta element in the page head
og:locale:alternate Other locales in which the page is available Same underscore format One repeated meta element per alternate locale
hreflang The URL of an alternate language or regional version for Search Language and optional region separated by hyphen, such as en-GB Alternate link elements, sitemap, or HTTP headers

These values are deliberately different: Open Graph uses an underscore in its locale value, while Google’s hreflang examples use a hyphen. og:locale:alternate names a locale; it does not contain that locale’s URL. hreflang connects a language or regional code to a fully qualified alternate URL. See the Open Graph protocol and Google’s localized versions guidance.

2. Add Open Graph tags to each localized page

The protocol requires four properties for a graph object: og:title, og:type, og:image, and og:url. Add the locale properties as well. This example is for a British English page available in French and Spanish:

<!doctype html>
<html lang="en-GB" prefix="og: https://ogp.me/ns#">
<head>
  <meta charset="utf-8">
  <title>Shipping information | Example</title>

  <meta property="og:title" content="Shipping information | Example">
  <meta property="og:type" content="website">
  <meta property="og:url" content="https://example.com/en/shipping">
  <meta property="og:image" content="https://example.com/images/shipping-en.jpg">
  <meta property="og:image:alt" content="A parcel ready for delivery">
  <meta property="og:locale" content="en_GB">
  <meta property="og:locale:alternate" content="fr_FR">
  <meta property="og:locale:alternate" content="es_ES">
</head>
<body>
  <h1>Shipping information</h1>
</body>
</html>

Repeat the page metadata on every localized URL, changing the values to describe that specific version. For example, https://example.com/fr/livraison should have its own French title, URL, image if appropriate, and og:locale set to fr_FR. Its alternate locale tags should name the other available versions. Repeated meta elements express the protocol’s array values; use one element per alternate locale.

Use complete values and escape generated HTML

  • Use an absolute, publicly reachable URL for og:url and og:image.
  • Make og:url identify the localized page being described, not the language selector or another locale’s URL.
  • Use a locale that matches the page’s content and intended region. The Open Graph protocol gives en_US as its default example; localized pages should set their actual locale explicitly.
  • When values are generated from content or configuration, HTML-escape attribute values. A title containing a quote or ampersand must not break the meta element.
  • Use one consistent value for each property. If consumers encounter conflicting repeated values, the protocol says the first value is preferred.

For images, Open Graph supports structured image properties such as secure URL, MIME type, width, height, and alternative text. When a page specifies og:image, provide og:image:alt describing the image, not a caption. Refer to the protocol’s structured image properties.

3. Connect localized URLs with hreflang

On each variant, include the same set of alternate links, including a link to itself. Each page should link to all the other variants, and the variants should link back. For the English and French pages, with a language-selection page as the fallback:

<link rel="alternate" hreflang="en-GB" href="https://example.com/en/shipping">
<link rel="alternate" hreflang="fr-FR" href="https://example.com/fr/livraison">
<link rel="alternate" hreflang="x-default" href="https://example.com/language/shipping">

Use an ISO 639-1 language code and, optionally, an ISO 3166-1 Alpha 2 region code. A region by itself is not a valid language value. Use x-default for a fallback URL when appropriate, such as a language selector or default page. Google documents three delivery options: HTML link elements, XML sitemaps, and HTTP headers. HTML is convenient when you control the page head; sitemaps can be useful for centrally maintained URL inventories; headers can be used for resources such as PDFs.

For HTML implementation details and requirements, follow Google’s localized versions documentation. For broader multilingual-site guidance, see Managing multi-regional and multilingual sites.

4. Generate tags from a locale map

For a site with many localized routes, keep each version’s URL, Open Graph locale, Search locale, title, and image together in one data structure. Generate both metadata systems from that source so their alternate lists stay aligned. The following Node.js function returns HTML for the head metadata; pass it values from trusted page data and use a real HTML-escaping function in production.

function escapeHtml(value) {
  return String(value).replace(/[&<>"']/g, (char) => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#39;"
  })[char]);
}

const pages = [
  {
    language: "en-GB",
    ogLocale: "en_GB",
    url: "https://example.com/en/shipping",
    title: "Shipping information | Example",
    image: "https://example.com/images/shipping-en.jpg"
  },
  {
    language: "fr-FR",
    ogLocale: "fr_FR",
    url: "https://example.com/fr/livraison",
    title: "Informations de livraison | Example",
    image: "https://example.com/images/shipping-fr.jpg"
  },
  {
    language: "es-ES",
    ogLocale: "es_ES",
    url: "https://example.com/es/envio",
    title: "Información de envío | Example",
    image: "https://example.com/images/shipping-es.jpg"
  }
];

function renderHeadMetadata(current, allPages, fallbackUrl) {
  const alternates = allPages.filter((page) => page.url !== current.url);
  const ogAlternates = alternates.map((page) =>
    `<meta property="og:locale:alternate" content="${escapeHtml(page.ogLocale)}">`
  );
  const languageLinks = allPages.map((page) =>
    `<link rel="alternate" hreflang="${escapeHtml(page.language)}" href="${escapeHtml(page.url)}">`
  );
  if (fallbackUrl) {
    languageLinks.push(
      `<link rel="alternate" hreflang="x-default" href="${escapeHtml(fallbackUrl)}">`
    );
  }

  return [
    `<meta property="og:title" content="${escapeHtml(current.title)}">`,
    `<meta property="og:type" content="website">`,
    `<meta property="og:url" content="${escapeHtml(current.url)}">`,
    `<meta property="og:image" content="${escapeHtml(current.image)}">`,
    `<meta property="og:image:alt" content="${escapeHtml(current.title)}">`,
    `<meta property="og:locale" content="${escapeHtml(current.ogLocale)}">`,
    ...ogAlternates,
    ...languageLinks
  ].join("\n");
}

const currentPage = pages[0];
const headMarkup = renderHeadMetadata(
  currentPage,
  pages,
  "https://example.com/language/shipping"
);
console.log(headMarkup);

This example assumes one equivalent page per locale and a single fallback URL. If some pages are not translated into every site language, build the alternate set for the current content item only. Do not emit a locale or URL for a translation that does not exist. Validate locale codes and URLs when content is published, and ensure every variant renders a reciprocal set.

5. Choose the right URLs and fallback behavior

  1. Give language versions distinct URLs. Google recommends separate URLs for different language versions so it can crawl and index each one.
  2. Make the visible content match the URL and metadata. Translate page content, navigation, title, and relevant social image. Google determines page language from visible content, rather than relying on the lang attribute or URL alone.
  3. Provide visible language-switch links. Let people choose another version even when you infer a preferred language.
  4. Avoid automatic locale redirects that hide variants. Google advises against automatically redirecting users based on inferred language because users and crawlers may then have difficulty reaching every version.
  5. Use a deliberate fallback. If there is a language selector or general page for visitors whose language is not covered, identify it as x-default. Do not use a localized page as a fallback unless that is the intended default.

For regional pages written in the same language, canonicalization may also matter. Google describes canonical URLs as hints, not binding directives, and notes that canonicalization together with hreflang can help it understand which regional URL to show. Decide which pages are genuinely distinct before applying canonical links; do not canonicalize every regional version to one URL by default. See Google’s URL canonicalization guidance.

6. Review and troubleshoot the implementation

Pre-release checklist

  • Each localized URL loads directly and returns the intended page.
  • Each page has one correct og:title, og:type, og:url, og:image, and og:locale.
  • Each available alternate locale is represented once by og:locale:alternate; unavailable translations are omitted.
  • Each localized page’s og:url points to itself.
  • The image URL is publicly reachable, and og:image:alt describes its contents.
  • Every variant publishes the same reciprocal hreflang set, including itself.
  • hreflang values use language-region syntax with a hyphen; Open Graph locales use language-territory syntax with an underscore.
  • Any x-default link points to the intended fallback.
  • Page content and navigation are visibly in the language claimed by the page.
  • Language switching remains available without requiring an automatic locale redirect.

Common errors and fixes

Symptom Likely cause Fix
A French URL previews as English The French page reused the English title, image, URL, or og:locale. Render Open Graph values from the current localized page record and set its locale explicitly.
All language versions point to the same page og:url was set to a default URL on every variant, or hreflang links were generated from the wrong route. Give each translation its own stable URL and use it for that page’s og:url and self hreflang entry.
Alternate language is missing in Search The hreflang set is absent, non-reciprocal, invalid, or points to an unreachable URL. Use fully qualified URLs, valid language codes, the same complete set on every variant, and reciprocal links.
The locale code is rejected or ignored Open Graph and hreflang syntax were mixed up, or the code contains a region without a language. Use values such as fr_FR for Open Graph and fr-FR for hreflang.
Social preview uses the wrong or no image The image URL is not public, is incorrect for the locale, or the page emits stale duplicate tags. Check the exact rendered head and ensure a reachable image URL and one consistent value for each property.
Metadata looks correct in a template but is absent on the delivered page The framework only injects tags after client-side JavaScript runs, or a cache serves stale HTML. Inspect the response HTML delivered to crawlers, render metadata server-side where needed, and invalidate the relevant cache after changes.
Users or crawlers cannot reach other locales An automatic language redirect sends visitors away from the requested URL. Keep locale URLs directly accessible and provide explicit language-switch links.

Social platforms can retain previously fetched previews. After correcting page metadata, use the relevant platform’s current preview/debugging tool to request a fresh fetch. The research sources establish the metadata requirements, but do not specify platform cache lifetimes or a universal refresh procedure.

7. Inspect the rendered page and preview image

Check the actual HTML response for every locale, not just the template source. Confirm that the metadata is in the head, values are escaped, links are absolute, and the response does not contain duplicate or stale tags. Then inspect the public page rendering and its social image at the localized URL.

For a manual check, open each locale URL in a browser, inspect the document head and image URL, and compare the result with the checklist above. A page screenshot can help spot visual issues such as an untranslated navigation label or a broken hero image; it does not by itself prove that Open Graph tags or hreflang are correct, so inspect the HTML too.

Or skip the browser setup

To capture a page for visual review, ScreenshotNeo takes a screenshot with one GET request. See the ScreenshotNeo API documentation for the available parameters and response details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/fr/livraison -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/fr/livraison"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/fr/livraison'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);

ScreenshotNeo accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000, and every feature is on every plan.

Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.

8. Performance, reliability, and cost

Open Graph and hreflang are HTML metadata; generating them does not require a screenshot service or paid product. The main reliability concern is keeping route data and rendered output aligned as translations change. Generate metadata from the same translation records used for the page, validate links when publishing, and recheck all variants when a translation is added, removed, or renamed.

For visual review, capturing every URL on every build may be unnecessary. Prioritize changed templates and localized pages, use caching where appropriate, and inspect the HTML directly for metadata correctness. ScreenshotNeo supports caching with a TTL you choose, bulk capture of up to 100 URLs per call, async jobs with signed webhooks, and a usage API. Its cache hits are not billed. Plan choice depends on capture volume: 1,000 monthly shots are free; listed paid options are Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing gives two months free.

9. Frequently asked questions

Should every localized URL use the same Open Graph image?

It can if the image is suitable for every locale, but each page should emit the image URL that best represents that localized version. The protocol supports image metadata and alternative text; it does not require a different image per locale.

Do I need the prefix attribute on the HTML element?

The protocol examples use an Open Graph namespace declaration with the prefix. Follow the protocol’s markup convention if you include it; the essential implementation in this guide is the property meta elements in the head.

Does og:locale:alternate tell social platforms where the translation lives?

No. It names another available locale. Use URL-bearing alternate links such as hreflang for the language-to-URL mapping.

Open Graph tags are for graph metadata and social sharing. For Google’s localized URL associations, implement hreflang and make sure page content is visibly in the intended language.

Can I use a language selector as x-default?

Yes. Google identifies a language selector or other default page as a suitable use for x-default, when it is the intended fallback.