<p>I'd rethink that approach – if your users want to see the site in French, why are they seeing the site in English? And if they're viewing the English site, does it make sense to show an isolated piece of content in French instead? Of course, this depends on what you're doing with your site, but I would go back to gathering requirements for this feature before implementing it.</p>
<hr />
<p>If you really do need to replicate this behaviour, your last code snippet is the correct approach:</p>
<pre class="lang-twig prettyprint-override"><code>craft.app.getGlobals().getSetByHandle('myGlobalHandle', craft.app.sites.getSiteByHandle(componentLocale).id)
</code></pre>
<p>Craft provides the global sets for the current site to your templates as a convenience. But there's no way to go from the global set for one site to the global set in another site. So using the <a href="https://docs.craftcms.com/api/v3/craft-services-globals.html" rel="nofollow noreferrer">globals service</a> (<code>craft.app.getGlobals()</code>) to query for the global in the appropriate site (<code>getSetByHandle()</code>) is the way to go.</p>
<hr />
<p>If you need this behaviour in multiple places, you could generalize it using a <a href="https://craftcms.com/docs/3.x/extend/extending-twig.html" rel="nofollow noreferrer">twig extension</a>. You can use the <code>GlobalsExtension</code> that provides the globals for the current site <a href="https://github.com/craftcms/cms/blob/develop/src/web/twig/GlobalsExtension.php" rel="nofollow noreferrer">as a starting point</a>. This is the extension that provides all globals as variables to your template using each global's handle.</p>
<p>For example, you could write a twig extension that automatically provides the global sets in the user's preferred language to your templates:</p>
<pre class="lang-php prettyprint-override"><code>public function getGlobals(): array
{
    // replace with current user preference
    $userPreferredSite = Craft::$app-&gt;getSites()-&gt;getSiteByHandle('en_ca');
    $globals = [];
    foreach (Craft::$app-&gt;getGlobals()-&gt;getAllSetIds() as $id) {
        $globalSet = Craft::$app-&gt;getGlobals()-&gt;getSetByHandle($id, $userPreferredSite-&gt;id);
        $globals[$globalSet-&gt;handle] = $globalSet;
    }
    return [
        'localGlobals' =&gt; $globals,
    ];
}
</code></pre>
<p>Now you can access the global sets for both the current site and the user's preferred language easily in your templates:</p>
<pre class="lang-twig prettyprint-override"><code>{# Global set for current site. #}
{{ myGlobalHandle }}

{# Global set in the user's preferred language. #}
{{ localGlobals.myGlobalHandle }}
</code></pre>

CC BY-SA 4.0
2021-12-08T22:44:04.377Z
"if your users want to see the site in French, why are they seeing the site in English? And if they're viewing the English site, does it make sense to show an isolated piece of content in French instead?" I tend to agree but I'm not in charge of these decisions. The site already exists and is high-traffic. My job is to update Craft and leave all functionality exactly the same, as far as is possible. Your answer is very helpful, and if I do need this elsewhere I will likely use your extension idea. Thank you very much.
tremby
CC BY-SA 4.0
2021-12-07T10:39:25.357Z
2021-12-07T10:39:25.357Z
MoritzLost
<p>I'm upgrading a legacy site from Craft 2 to Craft 3.</p>
<p>It's very much one-error-at-a-time and I'm stuck currently at a particular component's template file.</p>
<p>I have this legacy code:</p>
<pre><code>{% set componentLocale = componentLocale is defined ? componentLocale : '' %}
{% set myGlobal = componentLocale ? myGlobal.locale(componentLocale) : myGlobal %}

{% if myGlobal is defined and myGlobal.myGlobalTitle is not empty %}
    ... HTML for component
{% endif %}
</code></pre>
<p>If I understand it correctly, it's to show the component in the user's preferred language if that exists (we might be on a page available only in a different language), or fall back to the current language, or if that all fails show nothing.</p>
<p>I get <code>Calling unknown method: craft\elements\GlobalSet::locale()</code>.</p>
<p>I have come to understand that &quot;locales&quot; no longer exist in Craft 3; they're just different &quot;sites&quot; instead. I read in the upgrade guide that in certain cases I can replace <code>.locale(x)</code> <code>.siteId(x)</code> or <code>.site(x)</code>, but <code>siteId</code> and <code>site</code> methods do not exist on this object either.</p>
<p>At the moment, where I get this error, <code>componentLocale</code> is <code>en_ca</code>. I have two languages and so two sites, which the upgrade process has migrated to:</p>
<ul>
<li>handle: <code>en_ca</code>, language: <code>en-CA</code></li>
<li>handle: <code>fr_ca</code>, language: <code>fr-CA</code></li>
</ul>
<p><code>myGlobal</code> is a <code>craft\elements\GlobalSet</code> instance.</p>
<p>Is there an equivalent function to the old <code>.locale(x)</code> to which I can pass <code>en_ca</code> and get an equivalent (maybe the same) object back?</p>
<p>Failing that, how can I get the same effect?</p>
<p><a href="https://craftcms.stackexchange.com/a/24009/8782">This answer</a> looked promising, and I tried <code>craft.entries.id(myGlobal.id).site(componentLocale).one()</code> but that gives nothing, presumably because that's looking for entries where I want a global.</p>
<p>I eventually found that I can do <code>craft.app.getGlobals().getSetByHandle('myGlobalHandle', craft.app.sites.getSiteByHandle(componentLocale).id)</code> and I think it is doing what I wanted. But yikes. Is there a nicer way?</p>

CC BY-SA 4.0
2021-12-06T23:32:26.617Z
In most circumstances, [Craft 3 will automatically know which site you're viewing based on the site's base URL](https://craftcms.com/docs/3.x/sites.html), and you shouldn't need to jump through hoops like this to pull content (globals, entries etc) in the correct language. If you simply do `{{ myGlobal.myGlobalTitle }}`, does the `myGlobalTitle` not display in the correct language (i.e. site)?
Mats Mikkel Rummelhoff
CC BY-SA 4.0
2021-12-06T23:41:21.737Z
Based on what I understand of the code, the idea was that this component should show in the user's preferred language, which may not be the same as the current language, such as if they prefer French but are looking at a page only available in English.
tremby
<localization><upgrades>
Given a global, how to get a particular language's version of it?
# Given a global, how to get a particular language's version of it?

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

I'm upgrading a legacy site from Craft 2 to Craft 3.
It's very much one-error-at-a-time and I'm stuck currently at a particular component's template file.
I have this legacy code:
{% set componentLocale = componentLocale is defined ? componentLocale : '' %}
{% set myGlobal = componentLocale ? myGlobal.locale(componentLocale) : myGlobal %}

{% if myGlobal is defined and myGlobal.myGlobalTitle is not empty %}
    ... HTML for component
{% endif %}

If I understand it correctly, it's to show the component in the user's preferred language if that exists (we might be on a page available only in a different language), or fall back to the current language, or if that all fails show nothing.
I get Calling unknown method: craft\elements\GlobalSet::locale().
I have come to understand that "locales" no longer exist in Craft 3; they're just different "sites" instead. I read in the upgrade guide that in certain cases I can replace .locale(x) .siteId(x) or .site(x), but siteId and site methods do not exist on this object either.
At the moment, where I get this error, componentLocale is en_ca. I have two languages and so two sites, which the upgrade process has migrated to:

handle: en_ca, language: en-CA
handle: fr_ca, language: fr-CA

myGlobal is a craft\elements\GlobalSet instance.
Is there an equivalent function to the old .locale(x) to which I can pass en_ca and get an equivalent (maybe the same) object back?
Failing that, how can I get the same effect?
This answer looked promising, and I tried craft.entries.id(myGlobal.id).site(componentLocale).one() but that gives nothing, presumably because that's looking for entries where I want a global.
I eventually found that I can do craft.app.getGlobals().getSetByHandle('myGlobalHandle', craft.app.sites.getSiteByHandle(componentLocale).id) and I think it is doing what I wanted. But yikes. Is there a nicer way?

**Tags:** localization, upgrades

> **Comments:**
> **username_1**: In most circumstances, [Craft 3 will automatically know which site you're viewing based on the site's base URL](https://craftcms.com/docs/3.x/sites.html), and you shouldn't need to jump through hoops like this to pull content (globals, entries etc) in the correct language. If you simply do `{{ myGlobal.myGlobalTitle }}`, does the `myGlobalTitle` not display in the correct language (i.e. site)?  
> **username_0**: Based on what I understand of the code, the idea was that this component should show in the user's preferred language, which may not be the same as the current language, such as if they prefer French but are looking at a page only available in English.  

---

## 1 answers

### Answer by username_2 on 2021-12-07. Score: 1 | Accepted answer

I'd rethink that approach – if your users want to see the site in French, why are they seeing the site in English? And if they're viewing the English site, does it make sense to show an isolated piece of content in French instead? Of course, this depends on what you're doing with your site, but I would go back to gathering requirements for this feature before implementing it.

If you really do need to replicate this behaviour, your last code snippet is the correct approach:
craft.app.getGlobals().getSetByHandle('myGlobalHandle', craft.app.sites.getSiteByHandle(componentLocale).id)

Craft provides the global sets for the current site to your templates as a convenience. But there's no way to go from the global set for one site to the global set in another site. So using the globals service (craft.app.getGlobals()) to query for the global in the appropriate site (getSetByHandle()) is the way to go.

If you need this behaviour in multiple places, you could generalize it using a twig extension. You can use the GlobalsExtension that provides the globals for the current site as a starting point. This is the extension that provides all globals as variables to your template using each global's handle.
For example, you could write a twig extension that automatically provides the global sets in the user's preferred language to your templates:
public function getGlobals(): array
{
    // replace with current user preference
    $userPreferredSite = Craft::$app->getSites()->getSiteByHandle('en_ca');
    $globals = [];
    foreach (Craft::$app->getGlobals()->getAllSetIds() as $id) {
        $globalSet = Craft::$app->getGlobals()->getSetByHandle($id, $userPreferredSite->id);
        $globals[$globalSet->handle] = $globalSet;
    }
    return [
        'localGlobals' => $globals,
    ];
}

Now you can access the global sets for both the current site and the user's preferred language easily in your templates:
{# Global set for current site. #}
{{ myGlobalHandle }}

{# Global set in the user's preferred language. #}
{{ localGlobals.myGlobalHandle }}

> **Comments:**
> **username_0**: "if your users want to see the site in French, why are they seeing the site in English? And if they're viewing the English site, does it make sense to show an isolated piece of content in French instead?" I tend to agree but I'm not in charge of these decisions. The site already exists and is high-traffic. My job is to update Craft and leave all functionality exactly the same, as far as is possible. Your answer is very helpful, and if I do need this elsewhere I will likely use your extension idea. Thank you very much.  

---

