12

I would like to integrate a simple HTML form allowing the user to change Symfony2 web application's language (i.e. from page en/faq go to fr/faq). How to do it in a proper way?

I have found a nice way of doing it with Symfony but not with Symfony2: http://symfony.com/blog/play-with-the-user-language

j0k
  • 22,600
  • 28
  • 79
  • 90
cvsoftware
  • 165
  • 1
  • 2
  • 7

5 Answers5

32

The easiest way I have found is to do it directly in the twig template. At least, it works with 2.2:

<ul class="lang-menu">
  <li><a href="{{ path(app.request.get('_route'), app.request.get('_route_params')|merge({'_locale': 'ca'})) }}">Català</a></li>
  <li><a href="{{ path(app.request.get('_route'), app.request.get('_route_params')|merge({'_locale': 'en'})) }}">English</a></li>
</ul>
Waiting for Dev...
  • 12,629
  • 5
  • 47
  • 57
11

You need to call $this->get('session')->setLocale($locale) (replace 'session' by 'request' for Symfony 2.1) inside your controller.

I've created a form, to which I pass an array of languages:

<?php
class LanguageType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $langs = $options['languages'];
        $langs = array_combine($langs, $langs);
        foreach ($langs as &$lang) {
            $lang = \Locale::getDisplayName($lang);
        }

        $builder->add('language', 'choice', array(
            'choices' => $langs,
            'required' => false,
        ));
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'languages' => array('fr_FR', 'en_GB'),
            'csrf_protection' => false,
        );
    }

    public function getName()
    {
        return 'my_language';
    }
}

I submit this form to a separate action in a controller, in which I set the locale and return a redirection to last page:

<?php
class LanguageController extends Controller
{
    public function switchLanguageAction()
    {
        $form = $this->get('form.factory')->create(
            new LanguageType(),
            array('language' => $this->get('session')->getLocale()),
            array('languages' => $this->container->getParameter('roger.admin.languages', null))
        );

        $request = $this->get('request');
        $form->bindRequest($request);
        $referer = $request->headers->get('referer');

        if ($form->isValid()) {
            $locale = $form->get('language')->getData();
            $router = $this->get('router');

            // Create URL path to pass it to matcher
            $urlParts = parse_url($referer);
            $basePath = $request->getBaseUrl();
            $path = str_replace($basePath, '', $urlParts['path']);

            // Match route and get it's arguments
            $route = $router->match($path);
            $routeAttrs = array_replace($route, array('_locale' => $locale));
            $routeName = $routeAttrs['_route'];
            unset($routeAttrs['_route']);

            // Set Locale
            $this->get('session')->setLocale($locale);

            return new RedirectResponse($router->generate($routeName, $routeAttrs));
        }

        return new RedirectResponse($referer);
    }
}

Works with any valid locale (you pass them as 'language' option while creating your form), provided that the PHP intl extension is enabled. If it's not, you'll need to replace \Locale::getDisplayName($lang) by a manually created list of locale names.

j0k
  • 22,600
  • 28
  • 79
  • 90
wdev
  • 2,190
  • 20
  • 26
  • Just take care: the referrer may be overriden by some geeks, then `$router->match()` will throw an exception because there will be no match. A fallback could be nice here :-) – Alain Tiemblo Apr 28 '13 at 17:57
5

I have not done this with a form, but simply with small flag images at the top of the screen. Each flag is a link to the current page, but with the two-letter language code in the URL replaced by the language of the respective flag. My layout template has the following code:

{% for language, description in languages %}
    <a href="{{ replaceLanguageInUrl(app.session.locale, language, app.request.uri) }}">
    <img src="{{ asset('images/flag_' ~ language ~ '.png') }}" alt="" title="{{ description }}"/>
    </a>
{% endfor %}

The replaceLanguageInUrl function is defined in my Twig extension class:

public function getFunctions()
{
    return array(
        'replaceLanguageInUrl' => new \Twig_Function_Method($this, 'replaceLanguageInUrl'),
    );
}    

public function replaceLanguageInUrl($currentLanguage, $newLanguage, $url)
{

    //EDIT BEGIN
    if (strpos($url,$currentLanguage) == false) {
        $url = getBaseUrl($url).'/'.$currentLanguage;
    }
    //EDIT END
    return str_replace('/' . $currentLanguage . '/', '/' . $newLanguage . '/', $url);
}

When a flag is clicked, the same page is loaded, but in the new language. This will also automatically set the new language in the session.

Kristo Aun
  • 526
  • 7
  • 18
2

I did it with the local too but something more simple than wdev's solution, i used some pictures (flags) as buttons. When the flag is clicked, the new locale is set and the page is refreshed (with a redirect) with the new language. You need to use Symfony's translation system. Here is the code:

Controller:

public function englishAction(Request $request)
{
    $this->get('session')->setLocale('en_US');
    return $this->redirect($request->headers->get('referer'));
}

public function chineseAction(Request $request)
{
    $this->get('session')->setLocale('zh_CN');
    return $this->redirect($request->headers->get('referer'));
}

public function frenchAction(Request $request)
{
    $this->get('session')->setLocale('fr_FR');
    return $this->redirect($request->headers->get('referer'));
}

Template:

<ul class="nav pull-right">
     <li>
           <a href="{{ path('english') }}"><img src="{{ asset('bundles/fkmywebsite/images/flag-en.png') }}" alt="English Language" height="30" width="18" /></a>
     </li>
     <li>
          <a href="{{ path('chinese') }}"><img src="{{ asset('bundles/fkmywebsite/images/flag-cn.jpg') }}" alt="Chinese Language" height="30" width="18" /></a>
    </li>
    <li>
        <a href="{{ path('french') }}"><img src="{{ asset('bundles/fkmywebsite/images/flag-fr.png') }}" alt="French Language" height="30" width="18" /></a>
    </li>
</ul>

Edit: This solution works with Symfony2.0, for Symfony2.1 check this question

Community
  • 1
  • 1
fkoessler
  • 6,932
  • 11
  • 60
  • 92
2

Using the _locale parameter in your routing definition automatically sets the user locale.

See http://symfony.com/doc/current/book/translation.html#the-locale-and-the-url

  • Yes, it is what I have done for every page, however I would like to also let the user choose the language he would like by selecting it in a list. – cvsoftware Oct 09 '11 at 19:27