Language toggle

The language toggle is the EN / FR pair at the right end of the site header. Each option is a plain link to the current page in that language, and the active language is highlighted. It sits in the same visible location on every page — top right, next to search — so readers always know where to switch.

When to use#

  • One instance only, rendered by the site header on every page; pages and layouts never add their own
  • Two languages, two links — a pair of links is smaller and clearer than a dropdown or radio group, and each option names its language directly
  • When the current page has no translation, the option links to that language’s home page instead of a 404 or a disabled control — both options stay operable everywhere
  • Not for switching anything other than language; other header actions (search, navigation) have their own components

Implementation#

<nav class="language-switcher" aria-label="Language">
  {{ $page := . }}
  {{ range hugo.Sites }}
    {{ $site := . }}
    {{ $translation := where $page.AllTranslations "Lang" $site.Language.Lang }}
    {{ $href := $site.Home.RelPermalink }}
    {{ with index $translation 0 }}{{ $href = .RelPermalink }}{{ end }}
    <a class="language-link {{ if eq $site.Language.Lang $page.Lang }}language-link-active{{ end }}" href="{{ $href }}"{{ if eq $site.Language.Lang $page.Lang }} aria-current="true"{{ end }}>{{ upper $site.Language.Lang }}</a>
  {{ end }}
</nav>
<nav class="language-switcher" aria-label="Language">
  <a class="language-link language-link-active" href="/en/docs/site-implementation/components/language-toggle/" aria-current="true">EN</a>
  <a class="language-link" href="/fr/docs/site-implementation/components/selecteur-de-langue/">FR</a>
</nav>

The switcher lives inline in layouts/partials/header.html, inside the header-actions group, and takes no options. It loops over hugo.Sites, resolves the current page’s translation for each language through AllTranslations, and falls back to that language’s home page when no translation exists. There is no JavaScript and no separate CSS file; styling is the .language-switcher, .language-link, and .language-link-active classes in assets/css/site.css.

Interface manifest

Kind
Component
Category
Navigation
Status
Implemented
Implementation
Partial:layouts/partials/header.htmlCSS:assets/css/site.cssi18n:languageSwitcher
Used by
layouts/partials/header.html
Related interfaces
Navigation

Accessibility#

  • The switcher is a <nav> landmark, localized through the languageSwitcher i18n key, so screen readers can tell it apart from the main navigation
  • Each option is a plain link whose text names the language (EN, FR) — screen readers identify every option by its link text; this is a nav of links, not a radio group, so no role or aria-checked plumbing is involved
  • The active language link carries aria-current="true" alongside the language-link-active class, so the current language is announced rather than only styled
  • Keyboard: Tab reaches each link and Enter follows it — no JavaScript, no custom key handling
  • Avoid pointing an option at a 404 or disabling it when no translation exists; the fallback to the language’s home page keeps both options always operable