{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Universal language switcher for Craft CMS
Posted on Jul 07, 2019 by Piotr Pogorzelski

Universal language switcher for Craft CMS

Language switcher on github gists
Language switcher is an essential part of every multilingual site. This template component will generate language switcher, along with country flags next to language links.
Table of contents:
  • Overview
  • Language switcher - twig code
  • How does it work?
  • Customizing language switcher
  • Country flags
  • Lang attribute
  • Site switcher plugin
  • Further reading
  • Article update history

Overview #

This article assumes you have a basic understanding of Craft CMS multisite functionality. To learn about it, head to documentation.

Now, let's sum up how language switcher works.

  • Language switcher will display links to alternative versions of the content on the website - links to other sites. Only links to sites belonging to same site group as the current site will be displayed.
  • If a site has "This site has its own base URL" option disabled, it will not appear in language switcher.
  • If you are on the page associated with element - entry, category, commerce product - language switcher will display links to other versions of this element (belonging to other sites). It will work even for elements added by plugins, assuming they have their own URL.
  • While looping through sites, language switcher performs few checks before outputting proper link. If the current element does not have an alternative version (it does not propagate across sites or is not enabled for specific site) or current page is not associated to any element (because it is accessed by custom route or template routing), language switcher link will direct to base URL of another site.
  • If current site is the only site in its site group, language switcher will not be displayed. No need for language switcher if there is nothing to switch to.
  • Each language switcher link consists of language name in its native form (for english "english", for german "deutsche" etc.) and country flag. Thanks to flag icon CSS library, a country flag can be appended to language link automatically.
  • Language switcher has proper ARIA tags that make it accessible - aria-role and aria-label. Aria label text can be provided by static translations.
  • Each language switcher link has hreflang parameter that lets google crawler recognize the multilingual structure of your website.
  • Language switcher link representing currently viewed site has is-active CSS class.

Language switcher - twig code #

This language switcher works out of the box. Just include it in the proper place.

{# v2 #}
{# http://craftsnippets.com/articles/universal-language-switcher-for-craft-cms #}

{# logic #}
{% set currentElement = craft.app.urlManager.matchedElement %}
{% set sites = craft.app.getSites().getGroupById(currentSite.groupId).getSites() %}
{% set switcherLinks = [] %}

{% for site in sites if site.baseUrl is not empty %}
    
    {% set title = craft.app.i18n.getLocaleById(site.language).nativeName %}
    {% set url = site.getBaseUrl() %}
    {% if currentElement %}
        {% set otherLocaleElement = craft.app.getElements().getElementById(currentElement.id, currentElement.className(), site.id) %}
        {% if otherLocaleElement and otherLocaleElement.enabledForSite %}
            {% set url = otherLocaleElement.url %}
        {% endif %}
    {% endif %}

    {% set switcherLinks = switcherLinks|merge([{
        url: url, 
        title: title, 
        countryCode: site.language|split('-')|last,
        current: site.id == currentSite.id ? true : false,
        language: site.language,
    }]) %}

{% endfor %}

{# outputting html #}
{% if switcherLinks|length > 1 %}
<nav aria-label="{{'Switch language'|t}}" aria-role="navigation">
<ul>
{% for switcherLink in switcherLinks %}
    <li class="{{switcherLink.current ? 'is-active'}}">
        <a href="{{switcherLink.url}}" hreflang="{{switcherLink.language}}" lang="{{switcherLink.language}}">
        <span>{{ switcherLink.title }}</span>
        <span class="flag-icon flag-icon-{{switcherLink.countryCode}}"></span>
        </a>
    </li>
{% endfor %}
</ul>
</nav>
{% endif %}

How does it work? #

Let's dive into Twig code and analyze how language switcher works. Understanding its structure will allow you to easily modify it to your needs.

First, we get element associated with currently viewed page using craft.app.urlManager.matchedElement. If not such element exists, matchedElement returns false.

Then, we loop through sites that are in the same group as a currently viewed site. if site.baseUrl is not empty ensures that sites with no public URLs are omitted.

Each site has its locale code - represented by site.language variable. For example, a site set to American locale has en-US. The first part represents language, second part represent a country. We need to extract country code from locale code to use it in CSS class of element containing country flag.

Using craft.app.i18n.getLocaleById we get locale data of specific site. We need it to display locale name in human readable form, in its native spelling.

Now, its time to get URL of link. First, we first set url variable to base URL of site within current for loop. If page is associated to element, we then overwrite url value to URL of alternative version of this element, using craft.app.getElements().getElementById.

Finally, we append all data related to the link to switcherLinks array. After looping through sites, we can loop through links and output them as HTML.

Customizing language switcher #

Here are a few ideas for modifying default language switcher.

If language descriptions like "English (United states)" are too explict to you, you can display only "language" part of locale name by extracting language code from locale code:

{% set languageCode = site.language|split('-')|first %}
{% set title = craft.app.i18n.getLocaleById(languageCode).nativeName %}

You can also display country code next to its name in native language, for example: "English (EN)". It's good alternative to displaying flags:

{% set languageCode = site.language|split('-')|first %}
{% set nativeName = craft.app.i18n.getLocaleById(languageCode).nativeName %}
{% set title = nativeName ~ '(' ~ languageCode ~ ')' %}

If you don't wish to display link to currently browsed site in language switcher, you can exclude it:

{% set sites = craft.app.getSites().getGroupById(currentSite.groupId).getSites()|without(currentSite) %}

Country flags #

In order to display country flags in language switcher links, you need to:

  • Include Flag icons CSS library in your project.
  • Make sure that each site locale has locale code consisting of country code and language code. So - instead of just "en", use "en-US". Country code is needed for setting proper CSS class of element with flag.

But should you really use flags for your language switcher? Well, it depends.

Some countries have multiple official languages. For example, Canada official languages are both english and french. If you display just Canadian flag, it will tell nothing about the actual language used by this version of the site.

There are also multiple countries that use the same language - like USA, UK and Australia using english, or multiple counties that use portugalese. Is your content just translated to a specific language, or tailored to a specific country?

Each multilingual site requires thinking it all through. I recommend reading THIS article on United Language Group website to expand your knowledge on this subject.

Lang attribute #

Besides having proper language switcher, it's important to properly set lang attributes on your website. This attribute specifies the language of content and should be always specified in <html> element. You can do this in Craft like this:

<html lang="{{ currentSite.language }}">

Setting proper lang attribute will tell screen readers how they should pronounce the text. It can also affect text rendering in case of some fonts. It however does not tell Google about the language of your website.

If you have some content in a different language from rest of website, you can set lang attribute to element containing this content. That's why each language switcher link that contains language name in its native spelling has proper lang attribute.

Site switcher plugin #

Site switcher plugin can take care of outputting URLs of alternative versions of the current element. The same thing however can be achieved purely in Twig - like in language switcher described in this article.

Therefore, I don't see the reason to use this plugin.

Further reading #

Here are some useful links that will help you design proper language switcher:

  • Iconography for translations: best practice for communicating availability of translated content
  • Designing a language switch: examples and best practices
  • Language selector is no place for flags
  • Accessible Language Pickers: a11y meets i18n/l10n
  • What’s the best way to implement a language switcher

Article update history #

  • 7 july 2019 - added missing <ul> tags, <nav> is inside {% if %}.

TAGS:
#twig component
If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Quick links for this article:
Language switcher on github gists
Articles on blog:
  • Frontend testing for Craft CMS websites with Codeception and Cypress
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


Frontend testing for Craft CMS websites with Codeception and Cypress

Building reactive Craft Commerce product page with Sprig plugin

Dynamically generated PDF attachments for Freeform submissions

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Copyright ©2023 Piotr Pogorzelski