Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-12-08T22:44:04.377Z
Answers.Text: "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.
Answers.UserDisplayName: tremby
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-12-07T10:39:25.357Z
Answers.LastActivityDate: 2021-12-07T10:39:25.357Z
Answers.UserDisplayName: MoritzLost
Body: <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>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2021-12-06T23:32:26.617Z
Comments.Text: 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)?
Comments.UserDisplayName: Mats Mikkel Rummelhoff
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2021-12-06T23:41:21.737Z
Comments.Text: 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.
Comments.UserDisplayName: tremby
Tags: <localization><upgrades>
Title: Given a global, how to get a particular language's version of it?
ThreadText: # 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](https://craftcms.stackexchange.com/a/24009/8782) 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:
  
  ```twig
  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](https://docs.craftcms.com/api/v3/craft-services-globals.html) (`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](https://craftcms.com/docs/3.x/extend/extending-twig.html). You can use the `GlobalsExtension` that provides the globals for the current site [as a starting point](https://github.com/craftcms/cms/blob/develop/src/web/twig/GlobalsExtension.php). 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:
  
  ```php
  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:
  
  ```twig
  {# 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.  
  
  ---
  
  