hreflang for sites with three pages and no translation team
The return-link rule, x-default, and the code formats that fail silently. Then the measured state of the 2,183 machine-translated pages I had pointed them at.

On this page
Two versions of one Italian headline. This is the one that got published:
Come Ottimizzare Le Campagne Google Ads
This is the one an Italian would have written:
Come ottimizzare le campagne Google Ads
The difference is Le. It is a definite article, capitalised in the middle of a sentence, and Treccani’s grammar entry on capital usage rules it out without qualification: with the titles of a book, a work of art, a film, a song, “la maiuscola si limita alla prima parola del titolo” (the capital is limited to the first word of the title). The Accademia della Crusca’s standing answer on capitals, from Luca Serianni and Giovanni Nencioni in La Crusca per voi n. 2, April 1991, treats the wider question as genuinely unsettled in places, mostly around the boundary between proper and common nouns. There is no boundary here to be near. Nothing in Italian licenses a capital Le mid-title, and an Italian reader clocks it before finishing the line.
I know how many there were, because I counted them.
That headline came out of a machine-translation pass on this domain,
since deleted. Of the 728 Italian files it produced, 228 carried at least one Italian article, preposition or conjunction capitalised somewhere other than the first word. Thirty-one per cent. Here is how to get that number, against $B, the commit immediately before the deletion:
B=22b7c22d^
git archive $B content/post/insights/ | tar -x -C /tmp/corpus
cd /tmp/corpus/content/post/insights
for f in *.it.md; do
awk '/^title:/{sub(/^title: */,""); gsub(/^"|"$/,""); print; exit}' "$f"
done > /tmp/it-titles.txt
wc -l < /tmp/it-titles.txt # 728
python3 -c '
import re, sys
fw = set("""il lo la i gli le un uno una di a da in con su per tra fra
del dello della dei degli delle al allo alla ai agli alle dal dalla dallo dai dagli dalle
nel nello nella nei negli nelle sul sullo sulla sui sugli sulle col coi
e ed o od che ma se non ne si come quando mentre""".split())
t = [l.strip() for l in sys.stdin if l.strip()]
print(sum(1 for x in t if any(w[0].isupper() and w.lower() in fw
for w in re.findall(r"[A-Za-zÀ-ÿ'"'"']+", x)[1:])), "of", len(t))
' < /tmp/it-titles.txt # 228 of 728
The test is deliberately narrow. It fires only on closed-class words: articles, prepositions, conjunctions, the words whose capitalisation is not a matter of taste in Italian. Nothing a proper noun or a brand name does can trip it, and 228 is therefore a floor rather than an estimate, which is the direction I would rather be wrong in when the number is about my own work.
An earlier draft of this page said 57%. Trying to reproduce it, I ran four different definitions of Title Case over the same 728 titles (every non-initial word capitalised, most of them capitalised, at least two of them capitalised, and the function-word test above), and not one landed near 57. So the 57 is gone. What stands is the number that has a command attached to it.
Onto all 2,183 of those translated pages (728 Italian, 728 Arabic, 727 Chinese) I shipped hreflang, correctly.
What correct looked like
The <link rel="alternate"> sets came out of the template, which is why they were clean. Every version listed every other version and itself. The codes were valid. x-default was present. The return links resolved. Audit the markup and it passes.
The markup was never the problem. The markup worked, and working is what did the damage: hreflang exists to route a reader in a given language to the URL in that language, so it took the headline above and delivered it, efficiently, to the people best equipped to see what was wrong with it.
The mechanics, from Google’s documentation
Google’s page on telling it about localized versions is the primary source and it is short. Five rules carry most of the weight.
Return links are mandatory. “If two pages don’t both point to each other, the tags will be ignored. This is so that someone on another site can’t arbitrarily create a tag naming itself as an alternative version of one of your pages.” One-directional annotations are discarded outright. They do not degrade to a hint.
Every version lists itself. “Each language version must list itself as well as all other language versions.” The set of links is identical on every variant of the page, which is the property that makes the whole thing template-generatable.
x-default is the fallback. “The reserved x-default value is used when no other language/region matches the user’s browser setting.” Google recommends it for language selectors and auto-redirecting home pages.
The code is language first, region optional. First code, the language, ISO 639-1. Optional second code after a hyphen, the region, ISO 3166-1 Alpha 2. Only codes listed in those two standards work; Google’s page names es-419 as one that does not. Then the warning that catches almost everyone: “You can’t specify the country code by itself. The first code stands for the language and Google doesn’t automatically derive the language from a country code.” be is Belarusian. Belgium is de-be, nl-be, fr-be.
Three delivery methods, pick one. Head tags, an HTTP Link: header, or a sitemap.
Head tags
<head>
<title>Widgets, Inc</title>
<link rel="alternate" hreflang="en-gb"
href="https://en-gb.example.com/page.html" />
<link rel="alternate" hreflang="en-us"
href="https://en-us.example.com/page.html" />
<link rel="alternate" hreflang="en"
href="https://en.example.com/page.html" />
<link rel="alternate" hreflang="de"
href="https://de.example.com/page.html" />
<link rel="alternate" hreflang="x-default"
href="https://www.example.com/" />
</head>
Cheapest to implement. Most expensive to serve. With n locales you carry n links in the head of every page, so across the site the head grows as the square of your locale count.
HTTP header
Useful for non-HTML files. The header returned is identical for every version:
Link: <https://example.com/file.pdf>; rel="alternate"; hreflang="en",
<https://de-ch.example.com/file.pdf>; rel="alternate"; hreflang="de-ch",
<https://de.example.com/file.pdf>; rel="alternate"; hreflang="de"
Sitemap
Moves the weight out of the pages entirely. Declare the XHTML namespace, then give every <url> a full set of <xhtml:link> children including itself:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://www.example.com/english/page.html</loc>
<xhtml:link rel="alternate" hreflang="de"
href="https://www.example.de/deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="de-ch"
href="https://www.example.de/schweiz-deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="en"
href="https://www.example.com/english/page.html"/>
</url>
<url>
<loc>https://www.example.de/schweiz-deutsch/page.html</loc>
<xhtml:link rel="alternate" hreflang="de"
href="https://www.example.de/deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="de-ch"
href="https://www.example.de/schweiz-deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="en"
href="https://www.example.com/english/page.html"/>
</url>
</urlset>
Three versions, three <url> entries, three identical children each. It is n² either way. The sitemap simply moves the entries somewhere a static host does not re-send on every request.
One more line from the documentation that people miss once a site grows past a few locales: “If it becomes difficult to maintain a complete set of bidirectional links for every language, you can omit some languages on some pages; Google will still process the ones that point to each other.” What you should not omit is the link from a newly added language back to your dominant one.
Separately, and unrelated to hreflang: set lang on the <html> element. That is the declaration browsers and assistive technology use, W3C’s internationalization guidance covers it, and the tag syntax is RFC 5646. hreflang tells a search engine which URL to show whom. lang tells a screen reader which voice to use. Different jobs, and you need both.
Underneath the markup
Italian. The 228 titles above came from one mechanical cause: the pipeline ran an English-language title-casing function over output that was no longer English. The transform was locale-blind. It fired on every title in every language, and on Italian it produced a visible error in any title containing an article, a preposition or a conjunction past the first word.
Arabic. Here I have to correct myself, and the correction is large. An earlier version of this page claimed 116 Arabic files shipped a verb cut in half with an English stem left inside it. I went to count it and the count did not come back anywhere near 116. Every token in the 728 Arabic files where a lowercase Latin run is fused directly to Arabic letters, with the number of files each appears in:
cd /tmp/corpus/content/post/insights
python3 -c '
import re, glob, collections
AR = r"ء-يٱ-ۓ"
tok = re.compile(f"[A-Za-z{AR}]*(?:[{AR}][a-z]|[a-z][{AR}])[A-Za-z{AR}]*")
n = collections.Counter()
for f in sorted(glob.glob("*.ar.md")):
for t in set(tok.findall(open(f, encoding="utf-8").read())):
n[t] += 1
for t, c in n.most_common():
print(c, t)
'
23 وtur
2 لتautomate
1 تتblur
1 وb
1 السيمantics
1 يautomates
1 treatingها
That is the whole population. لتautomate, يautomates, تتblur and treatingها are the corruption I described: an Arabic verbal prefix or object suffix welded to a bare English stem, because a substitution pass treated a templatic language as if it were space-delimited Latin words. Arabic builds a verb by interleaving a root with affixes. The pass split one open and left English sitting in the gap. السيمantics is the same fault on a noun. Six distinct files, not 116.
The top row is a different bug and it is the common one. Twenty-three files carry a Markdown link whose label was truncated mid-word and published as a fragment: …وtur](https://blog.hubspot.com/topic/data-analytics). It rendered as a fragment, on twenty-three pages, for months. Nobody read it. وb I cannot classify and am not going to guess at.
Chinese. I have no basis to characterise the Chinese quality in either direction. I do not read Chinese and I never had it reviewed, which is itself the finding.
Why the multiplier goes both ways
Take the general shape. hreflang multiplies whatever your per-locale quality is by the number of locales you serve. Positive quality, more reach. Negative quality, more reach.
Which means that everything I did right (the self-referencing sets, the valid codes, the x-default fallback, the bidirectional return links that a Search Console audit would have waved through without a note) worked precisely as designed, and what it was designed to do was take a headline no Italian would write, put it in front of Italian readers, in Italy, at the moment they were looking for exactly that topic, four locales wide, from 31 October 2025 to 27 April 2026.
Google’s spam policies name the pattern under scaled content abuse: “Scraping feeds, search results, or other content to generate many pages (including through automated transformations like synonymizing, translating, or other obfuscation techniques), where little value is provided to users.” Translating is listed as an automated transformation not because translation is suspect, but because translating at volume without review turns one source into many thin pages. As a description of what I built, that is accurate, and the hreflang was the delivery mechanism.
What I would tell a solo operator
Pick one non-English locale, or zero.
Zero is respectable, and it is what this site’s articles use now. If you pick one, pick the language you can read. I live in Messina. I can check Italian output against how people around me actually write, which is precisely the check I skipped at 2,183-file scale and could have done in an afternoon at three-file scale. Then:
- Translate a page only when you have read the translation end to end.
- Never run English text transforms on non-English output: title casing, truncation at a character count, possessive handling. Gate every one of them on locale. The 228 titles and the truncated Arabic link labels are the same bug wearing two hats.
- Add the hreflang set only after the translation passes review. hreflang last, not first.
- Keep the set self-referencing and bidirectional, add
x-default, and let the template generate it so it cannot drift.
Three pages in two languages that a human has read will outperform two thousand in four. I am not offering that as a consolation prize. It is the only configuration in which the mechanics above are worth implementing at all.
Sources
Every source below was opened and checked on the date shown. Links open in this tab.
- Tell Google about localized versions of your page Google Search Central developers.google.com Accessed 5 August 2026
- Managing multi-regional and multilingual sites Google Search Central developers.google.com Accessed 5 August 2026
- Spam policies for Google web search Google Search Central developers.google.com Accessed 5 August 2026
- Uso delle maiuscole (La grammatica italiana) Treccani, Istituto della Enciclopedia Italiana www.treccani.it Accessed 5 August 2026
- Uso delle maiuscole e minuscole, Consulenza linguistica (Luca Serianni and Giovanni Nencioni, La Crusca per voi n. 2, April 1991) Accademia della Crusca accademiadellacrusca.it Accessed 5 August 2026
- Sitemaps XML format sitemaps.org www.sitemaps.org Accessed 5 August 2026
- Language tags in HTML and XML W3C Internationalization www.w3.org Accessed 5 August 2026
- RFC 5646: Tags for Identifying Languages IETF / RFC Editor www.rfc-editor.org Accessed 5 August 2026