<p>Little addition with a fallback to the homepage and check if entry exist.
currentSite is a global variable already defined in craft3 <a href="https://docs.craftcms.com/v3/templating/global-variables.html#currentsite" rel="noreferrer">https://docs.craftcms.com/v3/templating/global-variables.html#currentsite</a></p>

<pre><code>&lt;ul&gt;
  {% for site in currentSite.group.sites %}
    &lt;li class="{% if currentSite.id == site.id %} is-active{% endif %}"&gt;
      {% set url = alias(site.baseUrl) %}
      {% if entry is defined %}
        {% set entryForOtherSite = craft.entries.id(entry.id).site(site).one() %}
        {% if entryForOtherSite %}
          {% set url = entryForOtherSite.getUrl() %}
        {% endif %}
      {% endif %}
      &lt;a href="{{ url }}"&gt;{{ site.name }}&lt;/a&gt;
    &lt;/li&gt;
  {% endfor %}
&lt;/ul&gt;
</code></pre>

CC BY-SA 4.0
2018-05-31T08:04:03.033Z
2018-05-31T08:04:03.033Z
Jeroen
<p>As of today, <strong>Site Switcher</strong> is now available in the plugin store! *</p>

<p>It was called "Language Link" in Craft 2, but the premise is the same... It allows you to switch between sites while staying on the same page.</p>

<pre class="lang-html prettyprint-override"><code>{% set element = (category ?? entry ?? null) %}

&lt;ul&gt;
    &lt;li&gt;&lt;a href="{{ siteSwitcher('english', element) }}"&gt;English&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ siteSwitcher('spanish', element) }}"&gt;Español&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ siteSwitcher('french', element) }}"&gt;Français&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="{{ siteSwitcher('german', element) }}"&gt;Deutsch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</code></pre>

<blockquote>
  <p><a href="https://github.com/doublesecretagency/craft-siteswitcher" rel="nofollow noreferrer">https://github.com/doublesecretagency/craft-siteswitcher</a></p>
</blockquote>

<p><em>* Disclaimer: I am the author of Site Switcher.</em></p>

CC BY-SA 4.0
2018-08-30T20:59:54.907Z
2018-08-30T20:59:54.907Z
Lindsey D
<p>If you're using <a href="https://github.com/nystudio107/craft-seomatic" rel="nofollow noreferrer">SEOmatic</a> for Craft CMS 3, there's a built-in function that will make it super easy to do: </p>

<p><strong><code>seomatic.helper.getLocalizedUrls(URI, SITE_ID)</code></strong> - Return a list of localized URLs for a given <code>URI</code> that are in the <code>SITE_ID</code> site's group. Both <code>URI</code> and <code>SITE_ID</code> are optional, and will use the current request's <code>URI</code> and the current site's <code>SITE_ID</code> if omitted.</p>

<p>c.f.: <a href="https://github.com/nystudio107/craft-seomatic#helper-functions-seomatichelper" rel="nofollow noreferrer">https://github.com/nystudio107/craft-seomatic#helper-functions-seomatichelper</a></p>

CC BY-SA 4.0
2018-05-09T21:13:22.690Z
Any chance any of you knows how to output the language's name instead of code?  English instead of EN, like we could do with locale.nativeName.
Eric Chantigny
CC BY-SA 3.0
2018-03-31T21:33:49.513Z
2018-03-31T21:33:49.513Z
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-08-24T20:37:36.637Z
2019-08-24T20:37:36.637Z
Piotr Pogorzelski
<p>The concept of localization has changed in Craft 3, there are not only <code>locales</code> anymore - now you have different sites and site groups.</p>

<p><strong>Example without multisite - only different locals</strong></p>

<p>You can get all available sites for an entry with </p>

<pre><code>{% set supportedSites = entry.getSupportedSites %}
</code></pre>

<p>This will return you an array of all supported sites for the entry.</p>

<pre><code>{% for siteForEntry in entry.getSupportedSites %}
    {# get the site #}
    {% set site = craft.app.getSites.getSiteById(siteForEntry.siteId) %}
    {# check if it's the current site #}
    {% if site.id == entry.siteId %}
        active site = {{ site.id }} {{ site.language }} 
    {% else %}
        {# if it's not the current site, get the entry for this site #}
        {% set entryForOtherSite = craft.entries.id(entry.id).site(site).one() %}
        link to site with id {{ entryForOtherSite.siteId }} &lt;a href="{{ entryForOtherSite.getUrl() }}"&gt;{{ site.language }} - {{ entryForOtherSite.title }}&lt;/a&gt;
    {% endif %}
{% endfor %}
</code></pre>

<p>When you take a look at your <code>/admin/utilities/deprecation-errors</code> you'll see many records because all your <code>locale</code> functions will do this </p>

<pre><code>public function locale(string $value)
{
    Craft::$app-&gt;getDeprecator()-&gt;log('ElementQuery::locale()', 'The “locale” element query param has been deprecated. Use “site” or “siteId” instead.');
    $this-&gt;site($value);

    return $this;
}
</code></pre>

<p>They tell you: "This function is deprecated, use "site" instead" </p>

<p><strong>Example with multisite - different sites and locales</strong></p>

<p>If you are going to have multiple different sites with multiple locales you can manage each site in one group and insert all the locales for these sites in each group. </p>

<pre><code>- Site group A
    - language A (siteId 1)
    - Language B (siteId 2)
- Site group B
    - Language A (siteId 3)
    - Language C (siteId 4)
</code></pre>

<p>For Example if the user is in site group A you want to show only language A and language B but not Language C even if the entry exists in all sites.
So you can grab the group of the current site and loop through all sites of the group.</p>

<pre><code>{% set currentSite = craft.app.sites.currentSite %}
{% for siteInGroup in currentSite.getGroup.getSites() %}
    {% if siteInGroup.id == entry.siteId %}
        active site = {{ siteInGroup.id }}{{ siteInGroup.language }}
    {% else %}
        {% set entryForOtherSite = craft.entries.id(entry.id).site(siteInGroup).one() %}
        link to site {{ entryForOtherSite.siteId }} &lt;a href="{{ entryForOtherSite.getUrl() }}"&gt;{{ siteInGroup.language }} - {{ entryForOtherSite.title }}&lt;/a&gt;
    {% endif %}
{% endfor %}
</code></pre>

CC BY-SA 4.0
2018-05-14T13:05:11.610Z
I really like this answer, however often you don't have an entry available in the templates. So you should check for entry, category and maybe fallback to the homepage?
Jan_dh
CC BY-SA 4.0
2019-02-01T16:29:26.453Z
It's great that you have to come to StackExchange to figure out how to use a core feature.
joep
CC BY-SA 3.0
2018-02-05T09:29:43.837Z
2018-02-05T16:12:08.620Z
2018-02-05T16:12:08.620Z
Robin Schambach
<p>Does anybody successfully implemented a language switch run Craft 3? Trying the Craft 2 variant slightly adjusted but still running into issues: Invalid site handle: nl</p>

<pre><code>&lt;ul&gt;
{% set otherLocales = craft.app.i18n.siteLocaleIds|without(craft.locale) %}
{% for locale in otherLocales %}
   {% set localeEntry = craft.entries.id(entry.id).locale(locale).one() %}
    {#  set localeCategory = craft.categories.group('worksCat').locale(locale).first #}
    {% if localeEntry.locale is defined and localeEntry.locale == locale %}
    &lt;li&gt;
        &lt;a href="{{ localeEntry.getUrl() }}"&gt;
            &lt;img src="/{{ craft.app.i18n.getLocaleById(locale) }}.svg"  {{ craft.app.i18n.getLocaleById(locale).nativeName }}
        &lt;/a&gt;
    &lt;/li&gt;
    {% else %}
    &lt;li&gt;
        &lt;a href="{{ localeCategory.getUrl() }}"&gt;
            &lt;img src="/{{ craft.app.i18n.getLocaleById(locale) }}.svg" &gt; {{ craft.app.i18n.getLocaleById(locale).nativeName }}
        &lt;/a&gt;
    &lt;/li&gt;
    {% endif %}
{% endfor %}
</code></pre>

<p></p>

<localization><craft3><multi-site>
Craft 3 language switcher
# Craft 3 language switcher

(Asked by: username_0 on 2018-02-01)

Does anybody successfully implemented a language switch run Craft 3? Trying the Craft 2 variant slightly adjusted but still running into issues: Invalid site handle: nl

{% set otherLocales = craft.app.i18n.siteLocaleIds|without(craft.locale) %}
{% for locale in otherLocales %}
   {% set localeEntry = craft.entries.id(entry.id).locale(locale).one() %}
    {#  set localeCategory = craft.categories.group('worksCat').locale(locale).first #}
    {% if localeEntry.locale is defined and localeEntry.locale == locale %}
    
        
            
    
    {% else %}
    
        
             {{ craft.app.i18n.getLocaleById(locale).nativeName }}
        
    
    {% endif %}
{% endfor %}

**Tags:** localization, craft3, multi-site


---

## 5 answers

### Answer by username_6 on 2018-02-05. Score: 15 | Accepted answer

The concept of localization has changed in Craft 3, there are not only locales anymore - now you have different sites and site groups.
Example without multisite - only different locals
You can get all available sites for an entry with 
{% set supportedSites = entry.getSupportedSites %}

This will return you an array of all supported sites for the entry.
{% for siteForEntry in entry.getSupportedSites %}
    {# get the site #}
    {% set site = craft.app.getSites.getSiteById(siteForEntry.siteId) %}
    {# check if it's the current site #}
    {% if site.id == entry.siteId %}
        active site = {{ site.id }} {{ site.language }} 
    {% else %}
        {# if it's not the current site, get the entry for this site #}
        {% set entryForOtherSite = craft.entries.id(entry.id).site(site).one() %}
        link to site with id {{ entryForOtherSite.siteId }} {{ site.language }} - {{ entryForOtherSite.title }}
    {% endif %}
{% endfor %}

When you take a look at your /admin/utilities/deprecation-errors you'll see many records because all your locale functions will do this 
public function locale(string $value)
{
    Craft::$app->getDeprecator()->log('ElementQuery::locale()', 'The “locale” element query param has been deprecated. Use “site” or “siteId” instead.');
    $this->site($value);

    return $this;
}

They tell you: "This function is deprecated, use "site" instead" 
Example with multisite - different sites and locales
If you are going to have multiple different sites with multiple locales you can manage each site in one group and insert all the locales for these sites in each group. 
- Site group A
    - language A (siteId 1)
    - Language B (siteId 2)
- Site group B
    - Language A (siteId 3)
    - Language C (siteId 4)

For Example if the user is in site group A you want to show only language A and language B but not Language C even if the entry exists in all sites.
So you can grab the group of the current site and loop through all sites of the group.
{% set currentSite = craft.app.sites.currentSite %}
{% for siteInGroup in currentSite.getGroup.getSites() %}
    {% if siteInGroup.id == entry.siteId %}
        active site = {{ siteInGroup.id }}{{ siteInGroup.language }}
    {% else %}
        {% set entryForOtherSite = craft.entries.id(entry.id).site(siteInGroup).one() %}
        link to site {{ entryForOtherSite.siteId }} {{ siteInGroup.language }} - {{ entryForOtherSite.title }}
    {% endif %}
{% endfor %}

> **Comments:**
> **username_7**: I really like this answer, however often you don't have an entry available in the templates. So you should check for entry, category and maybe fallback to the homepage?  
> **username_8**: It's great that you have to come to StackExchange to figure out how to use a core feature.  

---

### Answer by username_1 on 2018-05-31. Score: 5

Little addition with a fallback to the homepage and check if entry exist.
currentSite is a global variable already defined in craft3 https://docs.craftcms.com/v3/templating/global-variables.html#currentsite

  {% for site in currentSite.group.sites %}
    
      {% set url = alias(site.baseUrl) %}
      {% if entry is defined %}
        {% set entryForOtherSite = craft.entries.id(entry.id).site(site).one() %}
        {% if entryForOtherSite %}
          {% set url = entryForOtherSite.getUrl() %}
        {% endif %}
      {% endif %}
      {{ site.name }}
    
  {% endfor %}


---

### Answer by username_2 on 2018-08-30. Score: 4

As of today, Site Switcher is now available in the plugin store! *
It was called "Language Link" in Craft 2, but the premise is the same... It allows you to switch between sites while staying on the same page.
{% set element = (category ?? entry ?? null) %}


    English
    Español
    Français
    Deutsch



https://github.com/doublesecretagency/craft-siteswitcher

* Disclaimer: I am the author of Site Switcher.


---

### Answer by username_3 on 2018-03-31. Score: 3

If you're using [SEOmatic](https://github.com/nystudio107/craft-seomatic) for Craft CMS 3, there's a built-in function that will make it super easy to do:

**`seomatic.helper.getLocalizedUrls(URI, SITE_ID)`** - 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.

c.f.: <https://github.com/nystudio107/craft-seomatic#helper-functions-seomatichelper>

> **Comments:**
> **username_4**: Any chance any of you knows how to output the language's name instead of code?  English instead of EN, like we could do with locale.nativeName.  

---

### Answer by username_5 on 2019-08-24. Score: 1

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.


---

