Web

hreflang for a bilingual Canadian site

A site in English and French has one job for search: say, unambiguously, which page is the equivalent of which, so that the right language reaches the right reader. hreflang is the mechanism, and it has strict rules. Get them right and it is nearly invisible. Get them wrong and Google ignores the annotations, usually without any message to you.

Pick the codes

An hreflang value is a language code, optionally followed by a region code. Google's documentation says the language is ISO 639-1 (two letters) and the region is ISO 3166-1 Alpha 2, and that only codes in those two standards are supported. Its example of a code outside them is es-419, which is valid in the wider BCP 47 language-tag system but is not supported here. The rules that follow from that:

  • Language alone is valid: en, fr.
  • Language plus region is valid: en-CA, fr-CA.
  • Region alone is not valid. CA on its own does not mean anything, because the first position is always read as a language. Google's own example is be, which is the Belarusian language code, not Belgium.
  • Case does not matter to Google; the convention is lowercase language, uppercase region.
  • Use hyphens, not underscores.

For a Canadian site the question is whether to stop at the language or include the region.

SituationUseWhy
One English version, one French versionen and frSimple and complete
English versions for several countries (for example US and Canada)en-CA, en-US, plus a generic enGoogle advises a catchall page for unspecified regions of a language
French written specifically for Canadian readers, plus a French version for Francefr-CA and fr-FRSeparates vocabulary and conventions
Quebec-only targetingfr-CAQuebec is a province, and the region position takes countries; fr-CA is the closest valid code

Invalid or ineffective codes you will meet in the wild: en-UK, fr-QC, en_CA, eng, french. Google lists UK among the reserved codes that have no effect in hreflang, and says that when a region code is reserved for something else, it ignores that part of the annotation. The country code for the United Kingdom is GB, so the working form is en-GB. QC is not a country, so it has no place in the region position. en_CA uses an underscore where a hyphen belongs, and eng or french are not two-letter ISO 639-1 codes.

The rules that make pairs work

Google lists requirements that apply to every method.

  1. Each version lists every version, including itself. The English page includes a link to itself and one to the French page; the French page includes both too. Google says the set of links is identical for every version of the page.
  2. Pairs must be reciprocal. If page A names page B but B does not name A, Google says the tags are ignored. The rule exists so that someone on another site cannot claim your page as an alternate of theirs.
  3. URLs are fully qualified, including the scheme: https://example.com/foo, not //example.com/foo or /foo.
  4. Each version is its own canonical. The French page's rel="canonical" points to the French page, not to the English one. Google's canonicalization guidance says that if you use hreflang you should specify a canonical page in the same language, or the best possible substitute if none exists. A French page canonicalised to the English one sends two signals that contradict each other, and you are asking Google to choose.
  5. Add x-default for the fallback, the page for visitors whose language you do not target. Google says it was designed for language selector pages and works best with them, so an English-or-French chooser is the natural target. If you have no chooser, a common choice is the English home page.

A pair in the page head:

<link rel="alternate" hreflang="en" href="https://example.com/about/">
<link rel="alternate" hreflang="fr" href="https://example.com/fr/a-propos/">
<link rel="alternate" hreflang="x-default" href="https://example.com/about/">
<link rel="canonical" href="https://example.com/about/">

And the French page carries the same three alternates, with its own canonical:

<link rel="alternate" hreflang="en" href="https://example.com/about/">
<link rel="alternate" hreflang="fr" href="https://example.com/fr/a-propos/">
<link rel="alternate" hreflang="x-default" href="https://example.com/about/">
<link rel="canonical" href="https://example.com/fr/a-propos/">

The <link> tags must sit inside a well-formed <head>, and Google warns against combining hreflang with other attributes such as media in one <link>.

How much markup that is

Each page carries one link per language version plus x-default: 3 elements per page with two languages. Four pages (2 English and French pairs) therefore carry 4 x 3 = 12 elements. A third language makes it 4 per page, so 2 pages in 3 languages (6 URLs) carry 6 x 4 = 24. Hand-edited templates are where one-way pairs come from, so generate the whole set from one list of which pages are counterparts.

HTML, header or sitemap

Google treats the three ways of declaring alternates as equivalent: <link> elements in the head, HTTP Link headers, and annotations in the sitemap. It says you can use all three at once but there is no benefit in Search, and it may be harder to manage three implementations. Choose one and generate it from one source.

  • Head links suit a static site where each page is generated and you know its counterpart.
  • HTTP headers suit non-HTML files such as PDFs.
  • Sitemap annotations suit large sites where editing page templates is harder than editing one file. Each <url> entry lists all its alternates, itself included:
<url>
  <loc>https://example.com/about/</loc>
  <xhtml:link rel="alternate" hreflang="en" href="https://example.com/about/"/>
  <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/a-propos/"/>
</url>

The xmlns:xhtml="http://www.w3.org/1999/xhtml" namespace must be declared on the <urlset> element,. Google adds that these child elements do not count toward the sitemap's URL limit.

Visitors need a way to switch, and so do crawlers, which discover pages by following links. Build the toggle as an ordinary anchor:

<a href="/fr/a-propos/" hreflang="fr" lang="fr">Français</a>

The hreflang attribute on an anchor is only a hint about the linked page's language and has no built-in behaviour, according to MDN, so it does not replace the head annotations. The lang="fr" on the link tells screen readers to pronounce "Français" as French.

Do not implement the toggle as a button that sets a cookie and rewrites the page with JavaScript. A crawler that does not run your script sees only one language, and no URL exists for the other. Each language should have its own address.

On redirects: avoid sending visitors to a language automatically based on IP address or the browser's Accept-Language header. Google documents that the default IP addresses of Googlebot appear to be based in the USA and that its requests carry no Accept-Language header, so an automatic redirect can mean a version is never seen. Google's multilingual guidance says to avoid automatically redirecting users from one language version to another, and to add links so users can choose. If you want to help visitors, show a dismissible banner offering the other language, and still serve the page that was requested.

Googlebot also crawls from IP addresses outside the USA, in addition to the US-based ones, according to the same documentation. That does not make location-based variation dependable, so keep each language on its own URL.

Translating partially

Many real sites are half translated, and the rules cover this honestly.

  • Only declare pairs that exist. A French page with no English counterpart gets no en alternate. Google says that if keeping a full set of bidirectional links becomes difficult you may omit some languages on some pages, and it still processes the pairs that point to each other. It adds that newly added language pages should be linked bidirectionally to the originating or dominant language first.
  • Translating only the template is a weak substitute. Google lists a page with a translated template around untranslated content as a case where indicating alternates is recommended, and says localized versions are only considered duplicates if the main content stays untranslated. The better fix is a full translation or an honest link to the English page.
  • Link to the untranslated page and label it. If the French index lists articles that exist only in English, link them with lang="en" and a visible note such as "en anglais", so that readers and screen readers know what they will get.
  • Keep one language per page. Google says it uses the visible content of the page to determine its language and recommends a single language for content and navigation on each page, and no side-by-side translations.
  • Write the title in the page's language. Google's title-link guidance says that a <title> in a different language or script from the page's main content may be replaced by other text. A French page with an English title invites that.

The lang attribute is a separate job

The lang attribute on the <html> element declares the page language for browsers, translation tools and screen readers, which can choose the right pronunciation from it. WCAG success criterion 3.1.1 requires the default language of each page to be programmatically determinable. It is not a substitute for hreflang, and Google states in two places that it does not use the HTML lang attribute to detect a page's language; it relies on its own analysis of the visible content. Set both: <html lang="fr-CA"> or lang="fr" on the French pages, and lang on any inline passage in another language.

Checking your work

Google's Search Console Help states that the International Targeting report is deprecated, and that Google continues to support and use hreflang tags. Do not count on Search Console to flag hreflang mistakes. Verify them yourself.

  1. Fetch each page and read its alternates:
curl -s https://example.com/about/ | grep -i hreflang
curl -s https://example.com/fr/a-propos/ | grep -i hreflang
  1. Confirm the two outputs list the same set of URLs.
  2. Request each listed URL and confirm it returns 200, has the right lang, and declares itself as canonical.
  3. Use URL Inspection to see which canonical Google selected for each language version.
MistakeWhat happensFix
One-way link (EN names FR, FR does not name EN)Google ignores the tags for that pairMake every set identical on every version
French canonical points to EnglishContradictory signals; Google may treat the French page as a duplicateSelf-canonical on each version
en_CA or fr-QCAnnotation does not do what you meanten-CA or fr-CA, or plain en and fr
Language chosen by script onlyCrawler sees one languageReal anchors to real URLs
Alternates that redirect elsewhereThe listed URL is not the page itselfPoint at final 200 URLs, using the canonical form

A diagnosis for a French page that never appears

Suppose the French About page is not showing in French searches. Work down a fixed order. First, fetch it with curl and confirm the content is French and present without JavaScript. Second, read its canonical: if it names the English page, you have found a likely cause, because Google's guidance wants a same-language canonical. Third, confirm that the English page's alternates name the French URL and that the French page's alternates name the English URL, in the same form (same scheme, same trailing slash). A trailing-slash or scheme difference between the two lists is a plausible reason for a pair to fail to match. This is a diagnostic suspicion, not documented Google behaviour. Fourth, request the URL with URL Inspection and compare Google's selected canonical to yours. Google says it prefers URLs that belong to a reciprocal hreflang cluster when it chooses canonicals, so a page missing from the cluster can lose out to one that is in it.

Four pages and twenty articles (fictional)

The fictional site example.com has an English home at /, a French home at /fr/, an English about page at /about/ and a French one at /fr/a-propos/. It also has twenty articles, English only. The owner chooses plain en and fr codes because there are no other regional variants. Each of the two pairs of pages carries head links to both members and an x-default pointing to the English version, and each is canonical to itself, which makes 4 pages x 3 links = 12 link elements across the site. The French journal index lists the articles with a visible "en anglais" label and lang="en", but declares no hreflang for them. The toggle in the header is a plain link on every page. Running the curl check on all four pages shows identical sets of three alternates.

Checklist

  • Use en and fr unless you really serve several regional versions.
  • Make every version list all versions, itself included, with fully qualified URLs.
  • Give each version its own canonical, and add x-default.
  • Build the toggle from real anchors, and do not redirect by browser language.
  • Declare pairs only for pages that really have a counterpart.

Questions and answers

Should I use en-CA and fr-CA or just en and fr?

If you have one English version and one French version, en and fr are correct and simplest. Use en-CA and fr-CA when you also serve other regional versions of the same language and need to tell them apart, for example an en-US page next to an en-CA page.

Does hreflang improve rankings?

Google describes it as a way to tell Google about localized versions so it can point people to the most appropriate one. It does not describe it as a ranking boost, and Google may still show a different version.

Do I need hreflang if the French pages are only partly translated?

Only for pages that have a real counterpart. Google says localized versions are considered duplicates only when the main content stays untranslated, and it ignores a pair of tags unless both pages point to each other.

Can I redirect visitors automatically by their browser language?

Google recommends against it. Its crawler usually sends no language preference and often appears to come from the USA, so an automatic redirect can hide a version from it. Offer a visible link and let the visitor choose.

Sources

  1. Tell Google about localized versions of your page developers.google.com
  2. How Google crawls locale-adaptive pages developers.google.com
  3. Managing multi-regional and multilingual sites developers.google.com
  4. The International Targeting report is deprecated (Search Console Help) support.google.com
  5. Understanding SC 3.1.1: Language of Page (W3C) w3.org