DEV Community

Cover image for How to add a language switcher to your Hugo site
tina
tina

Posted on

How to add a language switcher to your Hugo site

Recently, I made a site for client using the static site generator Hugo. It needed to be multilingual: The default was English, but they had German translations too. Hugo has pretty good support for multilingual sites, but what I needed apart from all the config was an easy-to-use language switcher. In this post, I am going to show you how I did it.

This is what it looks like:
Alt Text

If you're following this, make sure you have set up your languages in your config file as described in the Hugo docs.

Criteria

  • Accessible for screenreaders
  • Easy to use
  • Links to the translation of a page, if it exists
  • Not too heavy (ideally CSS only)

The Hugo code itself

For the basic code itself, I created a partial layouts/partials/language-switcher.html

{{ if .Site.IsMultiLingual }}
<nav>
  <ul>
    {{ range .Site.Languages }}
    {{ if eq . $.Site.Language }}
    <li>{{ .LanguageName }}</span>
      {{ end }}
      {{ end }}
      <ul class="dropdown__menu">
        {{ range $.Translations }}
        <li><a title="{{ .Language.LanguageName }}" href="{{ .Permalink }}">{{ .Language.LanguageName }}</a></li>
      </ul>
        {{ end }}
    </li>
  </ul>
</nav>
{{ end }}

So basically what this does is: Hugo checks which languages exist for the site, then checks the language of the current page, and displays that language name in the first li of the list. (Many language switchers have this a link as well, but I find it confusing if a page links to itself, so I did it differently.) Then, it looks for all translations, the output is a link to the translation.

It's as simple as that. Note that I chose this particular HTML (nav, ul, li because I want to style it as a dropdown later.

I got inspiration from this very helpful thread where Hugo developers have shared their own versions of a language switcher. So in case mine does not appeal to you, go and take a look.

CSS

Next up, styling the switcher as a dropdown. I thought long and hard about what design might make it so users

a) understand it is a language switcher, and
b) understand which language they are currently viewing, and what the other options were.

This might sound a bit odd, because what is there to misunderstand, but the design I originally had turned out not to work:
language switcher design example
Here, the current language is grey and obviously not a link, by which I thought it would make the impression to be "activated", and the translation is a link highlighted with the color that all other links on the site have as well. Both are next to each other.

In testing, I got feedback from a user who understood this design as exactly the other way around than I had intended: That the highlighted version signaled the active language.

In the end, I opted for a dropdown. For the design, accessibility is important, and I found a great resource for accessible dropdown menus in this amazing article by Stephanie Eckles. I basically adopted her approach and just made adjustments for my particular HTML. I'm not going to post my CSS here, because her tutorial is super easy to follow and will make it very easy for you to apply yourself.

Aria-labels for language switchers

Just one more thing I want to say about accessibility. Make sure to include aria-labels on your language switcher, because otherwise, it will be difficult for users who rely on screenreaders.

Basic idea is this: If you look at the code above, when a screenreader comes across the language switcher, it would read the names of the language: "English" and "German". What is lacking is the functionality of the menu: it allows users to switch between these two languages. Using aria labels, we can inform users about this.

This is the code:

{{ if .Site.IsMultiLingual }}
<nav aria-label="Language switcher">
  <ul>
    {{ range .Site.Languages }}
    {{ if eq . $.Site.Language }}
    <li><span aria-label="{{ i18n "ariaLanguage" }}{{ .LanguageName }}">{{ .LanguageName }}</span>
      {{ end }}
      {{ end }}
      <ul aria-labelledby="dropdown-title">
        {{ range $.Translations }}
        <li><a title="{{ .Language.LanguageName }}" href="{{ .Permalink }}" aria-label="{{ i18n "ariaTranslation" }}{{ .Language.LanguageName }}">{{ .Language.LanguageName }}</a></li>
      </ul>
        {{ end }}
    </li>
  </ul>
</nav>
{{ end }}

Now the explanation:
Aria label for the whole menu is "Language switcher" - this makes it clear what we are dealing with. For the name of the active language and the link to the translation, I wanted the aria labels to be:

  • Selected language: (Language Name)
  • Available translations: (Language Name)

But, and here we introduce string translation, I wanted these labels to both be in the same language, so a user can understand them. For someone viewing an English page, they should be in English, and for someone viewing the German page, they should be in German. (I know, what if someone lands on a German page but doesn't speak German... in that case, it would be good to detect the browser's language, but I also don't like when websites just assume things about a user, so I ended up solving it like this. Feel free to share your ideas.)

So in i18n/en.toml and i18n/en.toml, respectively, I added string translations for each version. Read more about the way Hugo solves i18n here.

I hope this post gave you some starters. I'm always happy about feedback, so feel free to leave your comments.

Top comments (1)

Collapse
 
future_insight profile image
Future Insight

It's a nice idea to have support for multiple languages.