Hreflang is an annotation, usually an attribute on a link element, that tells search engines which language and optional region each alternate version of a page targets. Google documents three ways to declare it: HTML link tags, an HTTP Link header, or a sitemap. The language is an ISO 639-1 code and the region an ISO 3166-1 Alpha 2 code, for example en-US. The special value x-default marks the fallback page.
Each page lists every version of itself, including itself, with rel="alternate" and an hreflang value. The format is a language code, then an optional dash and a region code.
The three methods are equivalent to Google, and you can use any one of them. Using more than one gives no extra benefit in Search. Google says the HTML tags must sit in a well-formed head section.
from html.parser import HTMLParser
class Alt(HTMLParser):
def __init__(self):
super().__init__()
self.alts = {}
def handle_starttag(self, tag, attrs):
a = dict(attrs)
if tag == "link" and a.get("rel") == "alternate" and "hreflang" in a:
self.alts[a["hreflang"]] = a["href"]
pages = {
"https://example.com/en/": '''<link rel="alternate" hreflang="en" href="https://example.com/en/">
<link rel="alternate" hreflang="de" href="https://example.com/de/">
<link rel="alternate" hreflang="x-default" href="https://example.com/">''',
"https://example.com/de/": '''<link rel="alternate" hreflang="de" href="https://example.com/de/">''',
}
parsed = {}
for url, html in pages.items():
p = Alt(); p.feed(html); parsed[url] = p.alts
for url, alts in parsed.items():
print(url, "->", alts)
for lang, target in alts.items():
if target != url and target in parsed and url not in parsed[target].values():
print(" no return link from", target, "to", url)
Output from Python 3:
https://example.com/en/ -> {'en': 'https://example.com/en/', 'de': 'https://example.com/de/', 'x-default': 'https://example.com/'}
no return link from https://example.com/de/ to https://example.com/en/
https://example.com/de/ -> {'de': 'https://example.com/de/'}
The English page lists the German page, but the German page does not list the English one. Missing return links are one of the most common hreflang errors, and Google ignores an annotation that is not confirmed from the other page.
The x-default value names the page to show when no listed language or region fits the user. In Google's example it points at a generic page such as a country selector. It is one more link element added to the same set, for instance hreflang="x-default" with the root URL. Since every version lists the full set, include it on each one.