Skip to main content

Setting hreflang and x-default on a multilingual site (with Hugo) - part 2

In this post, I will show you how to set hreflang and x-default on a fully translated multilingual site. Hreflang and x-default are HTTP headers that can be used to tell search engines about the language and region of your website. By setting these headers, you can help search engines to show your website to users in the correct language.

Back in 2023, I have looked at how to add relevant hreflang tags to my personal website, which considered adding self-reference to the current language, reference to a translated page (if available), and falling back with x-default to a page that is designed as a language switcher page.

Since then, I thought that this approach was correct, but found out that this solution, on my website, where not all content is 100% translated, is not good for sites fully translated by default.

Here is how I learned from it and how I optimised hreflang and x-default on websites built with Hugo.


I recently made “yet another Hugo” website. My friend, who is a highly skilled auditor in multiple standards, needs to have a website to advertise what she can offer to potential new customers.

She is from Poland, lives in Sweden, working for a British body of the company, and offering her services to the whole world.

The website needs to be in English by default, to have a global reach; however, it needs to be available in her home language (Polish) as well as in the place of her main residence (Swedish).

With such an approach, the website emiliawardach.com comes to life.

As always, after finishing the website, the work is not done. There are always some small fixes and TLCs. When I started running it through various validators and tools like ahrefs.com, to my surprise, I was faced with excessive issues reported as Missing reciprocal hreflang (no return-tag).

I also looked at my other friend’s website, yummyrecipes.uk, that was fully translated some time ago and found similar issues.

I thought that I implemented things correctly as per my previous post Setting hreflang and x-default on a multilingual site (with Hugo).

Once I started to re-read what I wrote, I found some mistakes that resulted in this approach.


Firstly, the start of the website in Hugo contained the following:

<!DOCTYPE html>
<html lang="{{ .Language.LanguageCode }}" xml:lang="{{ .Language.LanguageCode }}">

This generates the following code for the English part.

<!DOCTYPE html>
<html lang="en-GB" xml:lang="en-GB">

As you will notice, I am not only targeting English visitors, but I am going more specifically and reflecting that the site is in British English, which is more than correct.

The problem was the initial implementation.

<link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}">
{{ range .Translations }}
<link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}">
{{ end }}

I will exclude x-default from this for now, as I will come back to that later.

Also, I moved from using the relative link {{ .RelPermalink }} and stuck with the full link to the site using {{ .Permalink }}.

The above code will generate something like this:

<link rel="alternate" hreflang="en" href="https://emiliawardach.com/">
<link rel="alternate" hreflang="pl" href="https://emiliawardach.com/pl/">
<link rel="alternate" hreflang="sv" href="https://emiliawardach.com/sv/">

The first mismatch was here.

As I am specifying the language using {{ .Language.LanguageCode }} in the <html> tag, the English site will return en-GB, Polish will have pl-PL, and Swedish will have sv-SE.

As I am specifying document languages in the <html> tag more specifically, indicating that the English is not just English but British English, I should follow this in any hreflang references that I am using on the website.

The {{ .Lang }} will only return two characters of the language; for English, it will be just en, and this is a mismatch to what I am advising in the first place.


To clarify, the languageCode I have specified in hugo.conf is as follows for the English part:

  [languages.en]
    languageCode = 'en-GB'

Of course, I can change languageCode to be more universal (just en), but I prefer to stick to the correct way to accurately reflect British English rather than generic English.

In that case, I need to amend my code with .Language.LanguageCode instead of just .Lang as follows:

<link rel="alternate" hreflang="{{ .Language.LanguageCode }}" href="{{ .Permalink }}">
{{ range .Translations }}
<link rel="alternate" hreflang="{{ .Language.LanguageCode }}" href="{{ .Permalink }}">
{{ end }}

Notice that I am reporting the first link rel="alternate" as a self-reference to the site that I am currently on in the selected language.

The second, within {{ range .Translations }}, will return all other languages available for the selected page (/about/), as shown below:

<link rel="alternate" hreflang="en-GB" href="https://emiliawardach.com/about/">
<link rel="alternate" hreflang="pl-PL" href="https://emiliawardach.com/pl/o-mnie/">
<link rel="alternate" hreflang="sv-SE" href="https://emiliawardach.com/sv/om/">

The hreflang will match the language specified at the document level in the <html> tag. It will self-reference the currently visited page /about in British English and list other available links to translated pages.

First issue fixed.

Now, we need to rethink the use of x-default.

In my previous post, I referenced a specially designed page where users can decide on selecting the language:

<link rel="alternate" href="{{ .Site.Params.langSelector | absURL }}" hreflang="x-default">

This will look as follows on my site:

<link rel="alternate" href="https://dariusz.wieckiewicz.org/en/language-selector/" hreflang="x-default">

This is not a perfect solution, but on my site, where not everything is (nor must be) translated, it’s a sort of compromise.

The x-default should always work as a fallback.

Remember that I have specified languages as specific. English is not just English; it’s British English.

If a person whose language set on their device is English but they come from Liberia, it will be reported as en-LR.

This will, of course, not match with pl-PL and sv-SE, but it will also not match with en-GB.

In that case, we need to provide a default fallback.

As English is much more universal than Polish or Swedish, if the person lands, through a search engine or direct link, on /pl/o-mnie/, which doesn’t match their main language, the x-default should suggest that they view another language as their default page.

In that case, the default language will be English in the form of British English.

While on /pl/o-mnie, the x-default should report as follows:

<link rel="alternate" href="https://emiliawardach.com/about/" hreflang="x-default">

In Hugo, we can achieve this in various ways, as reported by Joe Mooring on the Hugo Support forum, in response to the question How to get a specific translation?.

I liked the option provided with the index function, and for my purpose, I added the following into my code:

{{ $p := index (where .AllTranslations "Language.Lang" "en") 0 }}
<link rel="alternate" href="{{ $p.Permalink }}" hreflang="x-default">

This code will list all translations (.AllTranslations) but limit the output to a single selected language, which in this case is English en.

The full code will look like this:

<link rel="alternate" hreflang="{{ .Language.LanguageCode }}" href="{{ .Permalink }}">
{{ range .Translations -}}
<link rel="alternate" hreflang="{{ .Language.LanguageCode }}" href="{{ .Permalink }}">
{{ end -}}
{{ $p := index (where .AllTranslations "Language.Lang" "en") 0 }}
<link rel="alternate" href="{{ $p.Permalink }}" hreflang="x-default">

This approach will use x-default for its intended purpose. It will ensure consistency across content (pages) and follow what is known as common practice.

“It’s common practice for the primary or most generic language version (often English) to serve as the x-default.”

For my Polish page /pl/o-mnie/, the output will look as follows:

<link rel="alternate" hreflang="en-GB" href="https://emiliawardach.com/about/">
<link rel="alternate" hreflang="pl-PL" href="https://emiliawardach.com/pl/o-mnie/">
<link rel="alternate" hreflang="sv-SE" href="https://emiliawardach.com/sv/om/">

<link rel="alternate" href="https://emiliawardach.com/about/" hreflang="x-default">

This resolves the issue with Missing reciprocal hreflang (no return-tag) once and for all.

Regards.

Share on Threads
Share on Bluesky
Share on Linkedin
Share via WhatsApp
Share via Email

Comments & Reactions

Categories