Change global locale programmatically

Permalink 1 user found helpful
For users to open a single page in a multilingual site in their own language, I send them a link to that single page on email where the users' language is included as a URL parameter.

In the single page's view.php I then set the language with the following code as an example:

Localization::changeLocale("nb_NO");

This code however does not change user's language cookie (or session) and the page's global areas are not translated consequently.

How to change user's language cookie (or session) instead, so that the page contents in total is changed?

Janks
 
Janks replied on at Permalink Reply
Janks
I found the clue from the standard switch language controller. This as an example will set the Norwegian locale for the Norwegian guest user:

Session::set('multilingual_default_locale', 'nb_NO');

When that Session::set... however is now set in the single page's controller, the single page display language is not changed and remains the default English. The single page then becomes complete useless for the Norwegian guest not understanding the English language. How to let that Session::set... make the resulting single page display in Norwegian?
mlocati replied on at Permalink Best Answer Reply
mlocati
The problem is that you are setting the session key too late: concrete5 already initialized the page language.

In order to solve this, you can use a middleware.

To do that, edit the /application/bootstrap/app.php file and add this PHP code:

$app->make(Concrete\Core\Http\ServerInterface::class)->addMiddleware($app->make(Application\Concrete\SetLocaleMiddleware::class));


Then create the file /application/src/Concrete/SetLocaleMiddleware.php with this contents:

<?php
namespace Application\Concrete;
use Concrete\Core\Application\Application;
use Concrete\Core\Http\Middleware\DelegateInterface;
use Concrete\Core\Http\Middleware\MiddlewareInterface;
use Symfony\Component\HttpFoundation\Request;
class SetLocaleMiddleware implements MiddlewareInterface
{
    /**
     * @var \Concrete\Core\Application\Application
     */
    protected $app;
    /**
     * @param \Concrete\Core\Application\Application $app
     */


This middleware will set the multilingual_default_locale session key to the value of the set-locale query string value (that is, for examplehttps://www.example.com/page?set-locale=nb_NO... )
Janks replied on at Permalink Reply
Janks
Thanks mlocati!

I did realize the session key is set too late as you confirms.

Thanks for the input on how to solve this. I will now implement your given solution.

In general, this week I have now realized the total Concrete5 multilingual concept, how it all works and what it's based on, and have implemented therefore the 2 initial Home pages required ie. English and Norwegian, with several more languages to come the next months and years as the site evolves. The challenge however was found to be the single pages translation to be included in various page links sent the guests in various emails, after these guests first visited the site and selected the Norwegian flag, then visited the resulting Norwegian translated single page and asked for a page URL to be sent on email. What I have done therefore, is to include in the page URL sent them their selected language as a URL parameter. That language parameter (nb) is then retreived by the single page's controller... and the reason for this post...

I am back with an updated status when your given solution is tested and implemented.
Janks replied on at Permalink Reply
Janks
The Middleware implementation is now tested, with no sucess.

The /application/bootstrap/app.php file is edited, adding the above presented code. The file /application/src/Concrete/SetLocaleMiddleware.php is then created next.

In single page's controller the following is given for the Norwegian locale to be set:
Session::set('multilingual_default_locale', 'nb_NO');

The single page however comes up in the default English language.

When page is then refreshed with F5 the page comes up in the Norwegian language.

This means the session setting works OK, but the Middleware usage fails.

Does that Middleware implementation need to be activated in some way some place?
mlocati replied on at Permalink Reply
mlocati
It's activated by the line in the /application/bootstrap/app.php file.

You can check if it works by placing an xdebug breakpoint in it, or by adding dd($locale) in that middleware:

// After this line
$locale = (string) $request->query->get('set-locale', '');
// add this line
dd($locale);


if the middleware is running, you should see a var_dump of the value of $locale.
Janks replied on at Permalink Reply
Janks
It works!

The reason for the Middleware usage failure was found to be the resulting URL from the URL parameters construction done.

In single page controller, the URL parameters are set by this code:

...$mapId,
'?set-locale=nb_NO');

The resulting URL with the $mapId and language setting then becomes the following wrong one:

...101/%3Fset-locale%3Dnb_NO

When manual changed to ...101/?set-locale=nb_NO it all works OK.

How to avoid that resulting wrong URL ...101/%3Fset-locale%3Dnb_NO ?
mlocati replied on at Permalink Reply
mlocati
> In single page controller, the URL parameters are set by this code:
> ...$mapId,
> '?set-locale=nb_NO');

Exactly how do you build the full URL?
Janks replied on at Permalink Reply
Janks
The Middleware usage is now tested also on the standard change password mechanism.

The URL is built then with the following code, including my new $Language variable for various test purposes, and the final ?set-locale=nb_NO language setting for the Middleware usage:

$changePassURL = View::url(
'/login',
'callback',
$this->getAuthenticationType()->getAuthenticationTypeHandle(),
'change_password',
$Language,
$uHash,
'?set-locale=nb_NO');
mlocati replied on at Permalink Reply
mlocati
You should '?set-locale=nb_NO' to the result of View::url():
$changePassURL = View::url(
    '/login',
    'callback',
    $this->getAuthenticationType()->getAuthenticationTypeHandle(),
    'change_password',
    $Language,
    $uHash
) . '?set-locale=nb_NO';
Janks replied on at Permalink Reply
Janks
That works perfect! Thanks!

Utilizing this Middleware usage, I can now build my multilingual site with the many single pages planned to be developed.

In start, I was some wary about Concrete5's multilingual concept, and whether my planned single pages would be operational with the various languages implemented.

Now, I see it's awesome!
anwaar replied on at Permalink Reply
Wonderful...
Can we use this Middle ware code in controller?
thanks
Janks replied on at Permalink Reply
Janks
The language being set is found not to be reflected in single pages other than the core single pages like /login, /register etc.

For a single page UserDetails now created with path application/single_pages/user/details as an example, the previously selected Norwegian language is gone when navigating to that single page, with the resulting default English language displayed instead.

The middleware usage does not work either for that single page when ?lg=nb is added to the URL. This middleware usage however works perfect for the core single pages like /login, /register etc.

What needs to be included in the UserDetails single page for the selected language to be reflected? And the middleware usage also to work?
mlocati replied on at Permalink Reply
mlocati
Try adding this method to your single page controller:
/**
     * {@inheritdoc}
     *
     * @see \Concrete\Core\Page\Controller\PageController::useUserLocale()
     */
    public function useUserLocale()
    {
        return true;
    }
Janks replied on at Permalink Reply
Janks
Thanks for the tip on how to implement the boolean true setting for the single page to be presented in user's selected language.

To avoid setting that for the many single pages to come, I have instead now edited the core file concrete/src/Page/Controller/PageController.php and set the boolean true instead of the original boolean false:
public function useUserLocale() {
   $User = new User();
   if($User->IsLoggedIn()) {
      return true;
   }
   else {
      return false;
   }
}

That makes sense to me, as all pages available to the user are requested to be in the language being selected and set by user in user's profile. If a user has set the Norwegian language, the user does not want to enter a page and then get the default English language presented.

This setting however does not help me with the guests visiting the various single pages available to the guests. What to implement in these single pages for the language to be according to the language flag previously set, and the middleware usage also to work when ?lg=nb is included in a URL being sent the guest?