<p>I have changed the above code a little bit because it causes an error when I have for example a website with three active locales and an entry with only one or two active locales.</p>

<p>The error is: Impossible to access an attribute ("locale") on a NULL variable ("")</p>

<p>The expanded if-statement looks now like this:</p>

<pre>{% if localeEntry.locale is defined and localeEntry.locale == locale %}</pre>

<p>And the complete code:</p>

<pre><code>{% set otherLocales = craft.i18n.getSiteLocaleIds()|without(entry.locale) %}
{% for locale in otherLocales %}
    {% set localeEntry = craft.entries.id(entry.id).locale(locale).first %}
    {% if localeEntry.locale is defined and localeEntry.locale == locale %}
        &lt;a href="{{ localeEntry.getUrl() }}"&gt;{{ locale }}&lt;/a&gt;
    {% else %}
        &lt;a href="{{ craft.config.siteUrl[locale] }}"&gt;{{ locale }}&lt;/a&gt;
    {% endif %}
{% endfor %}
</code></pre>

CC BY-SA 3.0
2014-08-11T18:38:31.943Z
2014-08-11T18:38:31.943Z
Christian Steger
<p>Thought I would share my language switcher, as it shows languages in their native name.</p>

<pre><code>&lt;nav class="nav nav--locale"&gt;
    &lt;ul&gt;
        {% set locales = craft.i18n.getSiteLocales() %}

        {% for locale in locales %}
            {% set entryURL = craft.config.siteUrl[locale.id] %}

            {% if entry is defined %}
                {% set entryLocal = craft.entries.id(entry.id).locale(locale.id).first %}

                {% if entryLocal.getUrl() is defined %}
                    {% set entryURL = entryLocal.getUrl() %}
                {% endif %}

                &lt;li class="nav__item {% if locale.id == craft.locale %}nav__item--active{% endif %}"&gt;
                    &lt;a href="{{ entryURL }}"&gt;{{ locale.nativeName }}&lt;/a&gt;
                &lt;/li&gt;
            {% else %}
                &lt;li class="nav__item {% if locale.id == craft.locale %}nav__item--active{% endif %}"&gt;
                    &lt;a href="{{ entryURL }}"&gt;{{ locale.nativeName }}&lt;/a&gt;
                &lt;/li&gt;
            {% endif %}
        {% endfor %}
    &lt;/ul&gt;
&lt;/nav&gt;
</code></pre>

<p>EDIT: I have fixed an issue when the entry doesn't have the content in all the different locales. This happens when you turn the default locale off in the section settings. In this case the link will take you back to the home page for that language.</p>

CC BY-SA 3.0
2015-01-15T17:24:46.603Z
2015-01-16T09:48:54.420Z
2015-01-16T09:48:54.420Z
Martin
<p>I've created a plugin which makes it incredibly easy to add language-switching links to your website...</p>

<blockquote>
  <p><a href="https://github.com/lindseydiloreto/craft-languagelink" rel="nofollow noreferrer">Language Link plugin for Craft CMS</a></p>
</blockquote>

<h3>Standard Usage</h3>

<p>Add this to your <code>_layout</code>, or create a separate template to <code>include</code> in each page:</p>

<pre class="lang-html prettyprint-override"><code>{% if entry is not defined %}
    {% set entry = null %}
{% endif %}

&lt;ul&gt;
    &lt;li&gt;&lt;a href="{{ craft.languageLink.url('en', entry) }}"&gt;English&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ craft.languageLink.url('es', entry) }}"&gt;Español&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ craft.languageLink.url('fr', entry) }}"&gt;Français&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ craft.languageLink.url('de', entry) }}"&gt;Deutsch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</code></pre>

<hr>

<p>This plugin is based on the code I originally <a href="https://craftcms.stackexchange.com/a/8533/45">shared here</a>, taking into account the feedback received from carlcs on that thread. If an entry exists, the plugin will use the proper slug from each locale of an entry (or category, or any other element type). Otherwise, it will simply share the page URI between locales.</p>

CC BY-SA 3.0
2015-03-09T05:29:53.380Z
2015-03-09T05:29:53.380Z
2017-04-13T12:51:00.303Z
Lindsey D
<p>This simple solution has been working brilliantly for me...</p>

<pre class="lang-html prettyprint-override"><code>{% set pageUri = craft.request.url|replace(siteUrl, '') %}

&lt;ul&gt;
    &lt;li&gt;&lt;a href="{{ craft.config.siteUrl['en'] ~ pageUri }}"&gt;English&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ craft.config.siteUrl['fr'] ~ pageUri }}"&gt;Français&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</code></pre>

<p>The <code>pageUri</code> is basically the current URL, minus the localized <code>siteUrl</code>.</p>

<p>Append the clean <code>pageUri</code> to each localized <code>siteUrl</code>, and voila... whatever page you are currently on will be linked to it's other-language counterpart.</p>

CC BY-SA 3.0
2015-03-06T00:35:50.817Z
Yes, something like this works great as long as you don't translate entry slugs.
carlcs
CC BY-SA 3.0
2015-03-06T00:36:30.977Z
Fair point @carlcs! We're not translating those on our site. :)
Lindsey D
CC BY-SA 3.0
2015-03-06T00:31:49.453Z
2015-03-06T00:31:49.453Z
Lindsey D
<p>I tweaked the version in the answer above by Christian Steger (<a href="https://craftcms.stackexchange.com/a/1794/3835">https://craftcms.stackexchange.com/a/1794/3835</a>). This version, hides the current language from the list of language links, and displays the language links in the target language. </p>

<p>It also allows you to omit any desired languages from the list of language links. This is useful for enabling a locale for use in the control panel without having it appear in the list of language links throughout the site. </p>

<pre><code>{% set currentLocale = craft.i18n.getCurrentLocale() %}
{# Decide which languages should be omitted from language links #}
{% set localeExceptions = ["en", currentLocale] %}
{% set otherLocales = craft.i18n.getSiteLocaleIds()|without(localeExceptions) %}

{% for locale in otherLocales %}
  {% if entry is defined %}
     {% set localeEntry = craft.entries.id(entry.id).locale(locale).first %}
  {% endif %}

  {% if localeEntry.locale is defined and localeEntry.locale == locale %}
      &lt;a href="{{ localeEntry.getUrl() }}"&gt;{{ craft.i18n.getLocaleById(locale).nativeName }}&lt;/a&gt;
  {% else %}
      &lt;a href="{{ craft.config.siteUrl[locale] }}"&gt;{{ craft.i18n.getLocaleById(locale).nativeName }}&lt;/a&gt;
  {% endif %}
{% endfor %}
</code></pre>

CC BY-SA 3.0
2016-03-24T13:42:28.360Z
2016-04-27T19:20:35.150Z
2017-04-13T12:51:00.160Z
Justin K
<p>If you happen to be using the <a href="https://github.com/nystudio107/seomatic/" rel="nofollow noreferrer">SEOmatic</a> plugin for Craft 2.x, it comes with a <a href="https://github.com/nystudio107/seomatic/wiki/15.-Utility-Filters-&amp;-Functions#getlocalizedurls" rel="nofollow noreferrer">getLocalizedUrls()</a> function that:</p>

<blockquote>
  <p>Returns an array of localized URLs for the current page request. This handles elements with localized slugs, etc. This function returns the unique URLs for each language for the current page request, not just the localized site URLs.</p>
</blockquote>

<p>For <a href="https://github.com/nystudio107/craft-seomatic" rel="nofollow noreferrer">SEOmatic for Craft CMS 3</a>,there's a similarly the <a href="https://github.com/nystudio107/craft-seomatic#helper-functions-seomatichelper" rel="nofollow noreferrer">seomatic.helper.getLocalizedUrls(URI, SITE_ID)</a> helper function:</p>

<blockquote>
  <p>Return a list of localized URLs for a given URI that are in the SITE_ID site's group. Both URI and SITE_ID are optional, and will use the current request's URI and the current site's SITE_ID if omitted.</p>
</blockquote>

<p>This makes building a language switcher trivial.</p>

CC BY-SA 4.0
2017-07-28T02:21:39.237Z
2018-07-23T17:46:56.583Z
2018-07-23T17:46:56.583Z
andrew.welch
<p>Here is my language switcher for Craft CMS 3:</p>

<p><a href="http://craftsnippets.com/articles/universal-language-switcher-for-craft-cms?s=s" rel="nofollow noreferrer">http://craftsnippets.com/articles/universal-language-switcher-for-craft-cms?s=s</a></p>

<p>It works for Entries, categories and other elements with their own URLs and has an option for an automatic appending country flag to language links.</p>

CC BY-SA 4.0
2019-07-07T08:08:38.030Z
2019-07-07T09:43:20.087Z
2019-07-07T09:43:20.087Z
Piotr Pogorzelski
<p>Here's my take with the latest CraftCMS 3.2 (using sites groups)</p>

<pre><code>{% set swapLinkDone = false %}

{# ——— Loop through all of the site locales, except the current one #}
{% if entry is defined %}
    {% set otherLocales = craft.app.i18n.siteLocales|filter(locale =&gt; locale.id != craft.app.language) %}
    {% for locale in otherLocales %}
        {% set otherSite = craft.app.sites.allSites|filter(s =&gt; s.language == locale.id and s.groupId == currentSite.groupId)|first %}
        {% if otherSite is defined %}
            {% set localeEntry = craft.entries.id(entry.id).site(otherSite.handle).one %}
            {% if localeEntry is defined and localeEntry.slug != "home" %}
                &lt;a href="{{ siteUrl(localeEntry.uri, [], null, otherSite.id) }}"&gt;{{ locale.id|t }}&lt;/a&gt;
                {% set swapLinkDone = true %}
            {% endif %}
        {% endif %}
    {% endfor %}
{% endif %}

{% if swapLinkDone == false %}
    {% set otherLang = craft.app.language == "en" ? "fr" : "en" %}
    &lt;a href="{{ otherLang == "en" ? "/en" : "/fr" }}"&gt;{{ otherLang|t }}&lt;/a&gt;
{% endif %}

</code></pre>

CC BY-SA 4.0
2019-07-23T19:17:19.297Z
2019-07-23T19:42:50.913Z
2019-07-23T19:42:50.913Z
Alain Duchesneau
<p>I am building my first multi-language site with Craft as well. This is from a thread over at Google+, so I didn't come up with this myself but it works very well:</p>

<pre><code>{% block locale__switch %}

    {# Loop through all of the site locales, except the current one #}
    {% set otherLocales = craft.i18n.getSiteLocaleIds()|without(craft.locale) %}
    {% for locale in otherLocales %}

        {# Find the current entry in the other locale #}
        {% set localeEntry = craft.entries.id(entry.id).locale(locale).first %}

        {# Make sure that it's actually localized to this locale #}
        {% if localeEntry.locale == locale %}

            {# Output a link to it #}
            &lt;a href="{{ localeEntry.getUrl() }}"&gt;{{ locale }}&lt;/a&gt;
        {% else %}

            {# Output a link to the hompage #}
            &lt;a href="{{ craft.config.siteUrl[locale] }}"&gt;{{ locale }}&lt;/a&gt;
        {% endif %}

    {% endfor %}
{% endblock %}
</code></pre>

<p>I set this up in a block to implement the switch in various templates. It loops through all the languages minus the current one and outputs the links. It also matches the entries by ID which is why you can have different slugs. Hope it helps.</p>

<p>Yay for the craftcms.stackexchange site!</p>

CC BY-SA 3.0
2014-06-12T10:39:43.097Z
This is great...how does it work if there's no matching entry ID in one of the other languages?
Patrick Nesbitt
CC BY-SA 3.0
2014-06-12T10:48:09.200Z
Supercool. Thanks. It would be cool to have some sort of explanation of each line becaue I find it quite hard to learn twig. The documentation is scattered across the various craft/twig sites. I added a conditional to check for the homepage because there I don't need the locale.Entry.uri
KSPR
CC BY-SA 3.0
2014-06-12T11:08:29.377Z
@Patrick If there is no matching entry in another locale you could link to the homepage of that locale. Just committed an edit to the original answer.
carlcs
CC BY-SA 3.0
2016-08-30T12:27:13.700Z
I get the following error:
Impossible to access an attribute ("locale") on a null variable

What do I do wrong? I copy/pasted the code you showed.

thanx in advance!
Martijn La Feber
CC BY-SA 3.0
2014-06-12T10:15:19.197Z
2014-06-13T12:50:17.623Z
2014-06-13T12:50:17.623Z
portnull
<p>I'm currently building a site with two languages, and I need a language switcher so wherever you enter the website you can switch the language easily.</p>

<p>I've come this far:</p>

<pre><code>{% for locale in craft.i18n.getSiteLocales() %}
    &lt;li {{ locale == craft.i18n.getCurrentLocale() ? 'class="active"' }}&gt;
        &lt;a href="/{{ locale }}/{{ entry.slug }}"&gt;
           {% if loop.first %}DE{% else %}FR{% endif %}
        &lt;/a&gt;
    &lt;/li&gt;
{% endfor %}
</code></pre>

<p>The problem here is that <code>{{ entry.slug }}</code> only returns the slug for the current language and doesn't loop trough the languages.</p>

<p>When I'm on french it returns <code>/bonjour</code> twice and on german it returns <code>/hallo</code> twice. Why is that?</p>

<localization><navigation>
How can I build a language switcher?
# How can I build a language switcher?

(Asked by: username_0 on 2014-06-12)

I'm currently building a site with two languages, and I need a language switcher so wherever you enter the website you can switch the language easily.
I've come this far:
{% for locale in craft.i18n.getSiteLocales() %}
    
        
           {% if loop.first %}DE{% else %}FR{% endif %}
        
    
{% endfor %}

The problem here is that {{ entry.slug }} only returns the slug for the current language and doesn't loop trough the languages.
When I'm on french it returns /bonjour twice and on german it returns /hallo twice. Why is that?

**Tags:** localization, navigation

---

## 9 answers

### Answer by username_9 on 2014-06-12. Score: 29 | Accepted answer

I am building my first multi-language site with Craft as well. This is from a thread over at Google+, so I didn't come up with this myself but it works very well:
{% block locale__switch %}

    {# Loop through all of the site locales, except the current one #}
    {% set otherLocales = craft.i18n.getSiteLocaleIds()|without(craft.locale) %}
    {% for locale in otherLocales %}

        {# Find the current entry in the other locale #}
        {% set localeEntry = craft.entries.id(entry.id).locale(locale).first %}

        {# Make sure that it's actually localized to this locale #}
        {% if localeEntry.locale == locale %}

            {# Output a link to it #}
            {{ locale }}
        {% else %}

            {# Output a link to the hompage #}
            {{ locale }}
        {% endif %}

    {% endfor %}
{% endblock %}

I set this up in a block to implement the switch in various templates. It loops through all the languages minus the current one and outputs the links. It also matches the entries by ID which is why you can have different slugs. Hope it helps.
Yay for the craftcms.stackexchange site!

> **Comments:**
> **username_10**: This is great...how does it work if there's no matching entry ID in one of the other languages?  
> **username_0**: Supercool. Thanks. It would be cool to have some sort of explanation of each line becaue I find it quite hard to learn twig. The documentation is scattered across the various craft/twig sites. I added a conditional to check for the homepage because there I don't need the locale.Entry.uri  
> **username_4**: @Patrick If there is no matching entry in another locale you could link to the homepage of that locale. Just committed an edit to the original answer.  
> **username_11**: I get the following error: Impossible to access an attribute ("locale") on a null variable  What do I do wrong? I copy/pasted the code you showed.  thanx in advance!  

---

### Answer by username_1 on 2014-08-11. Score: 14

I have changed the above code a little bit because it causes an error when I have for example a website with three active locales and an entry with only one or two active locales.
The error is: Impossible to access an attribute ("locale") on a NULL variable ("")
The expanded if-statement looks now like this:
{% if localeEntry.locale is defined and localeEntry.locale == locale %}
And the complete code:
{% set otherLocales = craft.i18n.getSiteLocaleIds()|without(entry.locale) %}
{% for locale in otherLocales %}
    {% set localeEntry = craft.entries.id(entry.id).locale(locale).first %}
    {% if localeEntry.locale is defined and localeEntry.locale == locale %}
        {{ locale }}
    {% else %}
        {{ locale }}
    {% endif %}
{% endfor %}


---

### Answer by username_2 on 2015-01-15. Score: 10

Thought I would share my language switcher, as it shows languages in their native name.

    
        {% set locales = craft.i18n.getSiteLocales() %}

        {% for locale in locales %}
            {% set entryURL = craft.config.siteUrl[locale.id] %}

            {% if entry is defined %}
                {% set entryLocal = craft.entries.id(entry.id).locale(locale.id).first %}

                {% if entryLocal.getUrl() is defined %}
                    {% set entryURL = entryLocal.getUrl() %}
                {% endif %}

                
                    {{ locale.nativeName }}
                
            {% else %}
                
                    {{ locale.nativeName }}
                
            {% endif %}
        {% endfor %}
    


EDIT: I have fixed an issue when the entry doesn't have the content in all the different locales. This happens when you turn the default locale off in the section settings. In this case the link will take you back to the home page for that language.


---

### Answer by username_3 on 2015-03-09. Score: 9

I've created a plugin which makes it incredibly easy to add language-switching links to your website...

Language Link plugin for Craft CMS

Standard Usage
Add this to your _layout, or create a separate template to include in each page:
{% if entry is not defined %}
    {% set entry = null %}
{% endif %}


    English
    Español
    Français
    Deutsch



This plugin is based on the code I originally shared here, taking into account the feedback received from carlcs on that thread. If an entry exists, the plugin will use the proper slug from each locale of an entry (or category, or any other element type). Otherwise, it will simply share the page URI between locales.


---

### Answer by username_3 on 2015-03-06. Score: 5

This simple solution has been working brilliantly for me...
{% set pageUri = craft.request.url|replace(siteUrl, '') %}


    English
    Français


The pageUri is basically the current URL, minus the localized siteUrl.
Append the clean pageUri to each localized siteUrl, and voila... whatever page you are currently on will be linked to it's other-language counterpart.

> **Comments:**
> **username_4**: Yes, something like this works great as long as you don't translate entry slugs.  
> **username_3**: Fair point @carlcs! We're not translating those on our site. :)  

---

### Answer by username_5 on 2016-03-24. Score: 3

I tweaked the version in the answer above by Christian Steger (https://craftcms.stackexchange.com/a/1794/3835). This version, hides the current language from the list of language links, and displays the language links in the target language. 
It also allows you to omit any desired languages from the list of language links. This is useful for enabling a locale for use in the control panel without having it appear in the list of language links throughout the site. 
{% set currentLocale = craft.i18n.getCurrentLocale() %}
{# Decide which languages should be omitted from language links #}
{% set localeExceptions = ["en", currentLocale] %}
{% set otherLocales = craft.i18n.getSiteLocaleIds()|without(localeExceptions) %}

{% for locale in otherLocales %}
  {% if entry is defined %}
     {% set localeEntry = craft.entries.id(entry.id).locale(locale).first %}
  {% endif %}

  {% if localeEntry.locale is defined and localeEntry.locale == locale %}
      {{ craft.i18n.getLocaleById(locale).nativeName }}
  {% else %}
      {{ craft.i18n.getLocaleById(locale).nativeName }}
  {% endif %}
{% endfor %}


---

### Answer by username_6 on 2017-07-28. Score: 3

If you happen to be using the [SEOmatic](https://github.com/nystudio107/seomatic/) plugin for Craft 2.x, it comes with a [getLocalizedUrls()](https://github.com/nystudio107/seomatic/wiki/15.-Utility-Filters-&-Functions#getlocalizedurls) function that:

> Returns an array of localized URLs for the current page request. This handles elements with localized slugs, etc. This function returns the unique URLs for each language for the current page request, not just the localized site URLs.

For [SEOmatic for Craft CMS 3](https://github.com/nystudio107/craft-seomatic),there's a similarly the [seomatic.helper.getLocalizedUrls(URI, SITE\_ID)](https://github.com/nystudio107/craft-seomatic#helper-functions-seomatichelper) helper function:

> Return a list of localized URLs for a given URI that are in the SITE\_ID site's group. Both URI and SITE\_ID are optional, and will use the current request's URI and the current site's SITE\_ID if omitted.

This makes building a language switcher trivial.


---

### Answer by username_7 on 2019-07-07. Score: 2

Here is my language switcher for Craft CMS 3:

<http://craftsnippets.com/articles/universal-language-switcher-for-craft-cms?s=s>

It works for Entries, categories and other elements with their own URLs and has an option for an automatic appending country flag to language links.


---

### Answer by username_8 on 2019-07-23. Score: 1

Here's my take with the latest CraftCMS 3.2 (using sites groups)
{% set swapLinkDone = false %}

{# ——— Loop through all of the site locales, except the current one #}
{% if entry is defined %}
    {% set otherLocales = craft.app.i18n.siteLocales|filter(locale => locale.id != craft.app.language) %}
    {% for locale in otherLocales %}
        {% set otherSite = craft.app.sites.allSites|filter(s => s.language == locale.id and s.groupId == currentSite.groupId)|first %}
        {% if otherSite is defined %}
            {% set localeEntry = craft.entries.id(entry.id).site(otherSite.handle).one %}
            {% if localeEntry is defined and localeEntry.slug != "home" %}
                {{ locale.id|t }}
                {% set swapLinkDone = true %}
            {% endif %}
        {% endif %}
    {% endfor %}
{% endif %}

{% if swapLinkDone == false %}
    {% set otherLang = craft.app.language == "en" ? "fr" : "en" %}
    {{ otherLang|t }}
{% endif %}


---

