-->

Silex : allow user to change langage by clicking o

2019-02-25 11:49发布

问题:

I am using Silex and Twig for a website and I want to allow the user to change the langage of the site.

My problem

Right now, it works if I change the locale in the URL :

/my-account: my page content is in English (default _locale)

/fr/my-account: my page content is in French

/en/my-account: my page content is in English

How can I do the do the same by clicking on an html element?

I am looking for some idea to solve my problem and some good practice to do this "the right way" if possible.

My code

Here is the Silex component I use to manage the multilangue :

// TRANSLATION
$app->register(new Silex\Provider\LocaleServiceProvider());
$app->register(new Silex\Provider\TranslationServiceProvider());
$app->register(new \Pmaxs\Silex\Locale\Provider\LocaleServiceProvider(), [
    'locale.locales' => ['en', 'fr'],
    'locale.default_locale' => 'en',
    'locale.resolve_by_host' => false,
    'locale.exclude_routes' => ['^_']
]);
$app->extend('translator', function($translator, $app) {
    $translator->addLoader('yaml', new YamlFileLoader());

    $translator->addResource('yaml', __DIR__.'/../src/locales/en.yml', 'en');
    $translator->addResource('yaml', __DIR__.'/../src/locales/fr.yml', 'fr');

    return $translator;
});

Here is my html for the user to change the langage:

<li id="drop-langue" data-lg="en">
    <span id="current-lg">EN</span> // My current langue
    <div class="drop-langue">
        // The list of langage the user can choose : here ONE -> FR
        <div id="list_langue">
          <a class="change_langue" href="#" data-lg="fr"> <span>FR</span></a> // Could be nice to change the langue staying on the same page
        </div>
    </div>
</li>

Now my jQuery to get the value :

$(document).ready(function() {
    $(".change_langue").on("click", function() {
        var new_langue = $(this).data("lg");

        $.ajax({
            url: "{{ path('new-langue') }}",
            type: 'POST',
            data: {'langue': new_langue},
            success: function (resp) {
                console.log(resp);
            },
            error: function (resp) {
                console.log(resp);
            }
        });
    });
});

My Ajax controller :

$app->match('/new-langue', function (Request $request) use ($app) {
    $new_langue = $request->get('langue');

    // some code to change the langage

    return New Response($new_langue);
})->bind('new-langue');

If I do this, my Ajax success console.log(resp); give me en as I want.

Some ideas / questions about how to do it

  1. Is it a good idea to do it with an Ajax call?
  2. If I change fr by en in my url it works, is it a good idea to try to do it in javascript using window.location.href for example? (I think not but still asking)
  3. I saw this solution for an other problem and try it in my controller but I get this error : 500 (Internal Server Error) (I did the same, with $new_langue instead of $app['defaultLanguage'] and with the right name for my homepage).

It's the first time I create a full website with Silex and I'm beginner with php framework so if someone can help to achieve what I want...thanks by advance !

EDIT :

According to the anwser I get and what I want to achieve, is it possible to change the locale and staying in the same page with Silex / Twig?

For example, this give me the current route : global.request.get('_route') and this global.request.get('_locale') give me the locale.

If I take the example of my Home Page, this is how my controller look right now (I just show the template) :

$app->match('/', function (Request $request) use ($app) {
    return $app['twig']->render('home.html.twig');
})->bind('home');

As you can see, I have no {_locale} param in my URL. So can I keep this "clean URL" without the {_locale} and make a click that stay in the same page + change the current langage?

I'd like to make somehting like this : <a href="{{ path(global.request.get('_route')), global.request.set('_locale', 'FR') }}">FR</a>

But it won't work for sure...can I do this?

回答1:

So, from your message, i can extract your goal is to allow the language to be changed when you click on an HTML element.

The usage of the a tag defines a hyperlink, which is used to link from one page to another and can be used to achieve what you want.

I've done multilang sites previously and you can achieve this in a variety of ways.

///////////////////////////////Method 1: define the location in the HTML attribute href

In your HTML code you've used hash - # within the hyperlink .

Hyperlinks require the href property, because it specifies a location.

Looking at your code, you have the tag but it's missing the link to the specific location you want to redirect the user to.

Let's say you want it to go to my-account page in french.

So, your HTML code should be:

<li id="drop-langue" data-lg="en">
    <span id="current-lg">EN</span> // My current langue
    <div class="drop-langue">
        // The list of langage the user can choose : here ONE -> FR
        <div id="list_langue">
          <a class="change_langue" href="http://yourwebsite.com/fr/my-account" data-lg="fr"><span>FR</span></a> 
        </div>
    </div>
</li>

To note: substitute yourwebsite.com with your website's domain.

///////////////////////////////end of Method 1

In your HTML code you mention // Could be nice to change the langue staying on the same page.

That can be done using AJAX, yes.

But I don't suggest you to do that because from looking at bigger companies that also have websites in multiple languages such as SAP or Apple they all redirect to a different page.

///////////////////////////////UPDATE: the answer until now was created because I thought your need was simply to allow the language to be changed when you click on an HTML element.

Apparently, your goal is slightly more complex than that.

Accordingly to Silex documentation, the usage of _locale is the way to go.

In this question you can see working code:

<?php

require_once __DIR__.'/../vendor/autoload.php';

$app = new Silex\Application();
$app['debug'] = true;

$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider(), array(
    'twig.path' => __DIR__.'/../views',
));

$app->register(new Silex\Provider\TranslationServiceProvider(array(
    'locale_fallbacks' => array('hr'),
)));


$app['translator'] = $app->share($app->extend('translator', function($translator) {

    $translator->addLoader('xlf', new \Symfony\Component\Translation\Loader\XliffFileLoader());

    $translator->addResource('xlf', __DIR__.'/../locales/hr.xlf', 'hr');
    $translator->addResource('xlf', __DIR__.'/../locales/en.xlf', 'en');
    $translator->addResource('xlf', __DIR__.'/../locales/sl.xlf', 'sl');

    return $translator;
}));


$app->get('/', function () use ($app) {

    $app['translator']->setLocale('hr');

    return $app['twig']->render('home.twig', array('d' => $app['translator']->getLocale()));

});


$app->get('/{_locale}/', function() use ($app) {
    $app['translator']->setLocale($app['request']->get('locale'));

    return $app['twig']->render('home.twig', array('d' => $app['translator']->getLocale()));
});



$app->run();


回答2:

In you case its just the matter of adding proper links to allow users so they can switch language. Rest of the work is already done by you. The language switcher dropdown must contain all supported languages including English. See below am example image.

Instead of redirecting the users to http://yourwebsite.com/[fr]/my-account you must redirect to a special method like http://www.yourwebsite.com/language_change?lang=fr. You will get HTTP_REFERRER in $_SERVER variable so you can redirect users back to the original page they were at. This way you can preserve users current visiting page/url.

Secondly, Ajax vs Redirect: Its better to use a redirect instead of AJAX. If you use AJAX you will increase the complicity to yourself.

When working with multilingual website, we must follow these best practices.

  • Specific routes per language

  • Menus, labels and forms

  • The content of the website

  • Extra: translate FOSUserBundle



回答3:

The simple idea here is: how your application determines the language to be used to render the pages.

solution #1 (your current solution) based on a URL parameter (that's _locale ), and fallback to some default value in case no URL-parameter given.
So in this solution the translation component always looks for the selected language in the parameters bag prepared by the router component.

Solution #2 put your language identifier inside a session variable. Your /new-langue page will have to set this session variable with the value it receives. and your router will populate the _locale parameter from that session variable.

I may be able to provide some code snippets a bit later.



回答4:

I finally find a solution to do what I want, using pmaxs/silex-locale (https://github.com/pmaxs/silex-locale).

As I said in my question, I already used it for my translation but I didn't used the "Url generation" as I should...So here is a recap of how to use it (read the documentation if you use Silex v1.x):

1/ Loading Provider

$app->register(new \Pmaxs\Silex\Locale\Provider\LocaleServiceProvider(), [
    'locale.locales' => ['en', 'fr'],  //I want to translate my site in English and French
    'locale.default_locale' => 'en',   // By default, the locale is "en"
    'locale.resolve_by_host' => false,
    'locale.exclude_routes' => ['^_']
]);
$app->register(new Silex\Provider\LocaleServiceProvider());

2/ Usage

Here is my home route controller :

// this page is accessible by urls '/', '/fr/' or '/en/'
$app->match('/', function (Request $request) use ($app) {

    // my code

     return $app['twig']->render('home.html.twig');
})->bind('home');

As you can see, I have no {_locale} variable in my routing, but still can use it to change the langage of my page.

3/ Url generator

Now, here is how I change my langage :

{% set locale_list = ["en", "fr"] %} // the list of all langage in my website
{% set current_locale = global.request.getLocale() %} // the current langage of my page

<li id="drop-langue">
    // I display the current langage
    <span id="current-lg">{{ current_locale|upper }}</span> 
    <div class="drop-langue">
        <div id="list_langue">
            {% for locale in locale_list %}
                {% if locale != current_locale %}
                    // I create one link to change the langage according to all the langage I want, minus the current langage
                    <a class="change_langue" href="{{ locale_generate(locale , global.request.get('_route')) }}" >
                        <span>{{ locale|upper }}</span>
                    </a>
                 {% endif %}
             {% endfor %}
         </div>
     </div>
 </li>

I'm using the "Url Generator", it works like this : locale_generate('es', $name, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) (more details in the documentation).

This way, when I come in my website I'm in english (my default locale) with my "clean Url" /. Then by clicking on FR my url become /fr/ and my content is translate in French with a new selection menu (current-langue = FR and I can select "EN").