symfony / symfony

The Symfony PHP framework
https://symfony.com
MIT License
29.86k stars 9.49k forks source link

any way to automatically set locale within path() twig function? #1700

Closed damien-roche closed 12 years ago

damien-roche commented 13 years ago

Personally I think it should be supported by default, only having to specify locale to override.

For example if I have routes:

/contact /about

/fr/contact /fr/about

on /fr/about I have a path:

{{ path('contact') }}

Why would that create a path for /contact by default instead of /fr/contact ?

From what I understand to create what should be default behavior on a per link basis, I must:

{{ path('address', {'_locale': app.request.attributes.get('_locale')}) }}

Any plans to amend this?

beberlei commented 13 years ago

The locale is automatically set, however only under certain conditions when you set it before the Routercontext object is created by the Routing Listener.

@Fabien: That reminds me we should make the Locale an object with __toString() so that you can change it without having to hax0r the RouterContext aswell.

On Thu, 14 Jul 2011 23:57:58 -0700 ivrock reply@reply.github.com wrote:

Personally I think it should be supported by default, only having to specify locale to override.

For example if I have routes:

/contact /about

/fr/contact /fr/about

on /fr/about I have a path:

{{ path('contact') }}

Why would that create a path for /contact by default instead of /fr/contact ?

From what I understand to create what should be default behavior on a per link basis, I must:

{{ path('address', {'_locale': app.request.attributes.get('_locale')}) }}

Any plans to amend this?

Reply to this email directly or view it on GitHub: https://github.com/symfony/symfony/issues/1700

damien-roche commented 13 years ago

Hmm..

What about this situation? We have a default application in english and so no /en/ prefix. All routes automatically have /en/ prefix inserted which is causing inconsistencies with url structure. Is there any way to bypass that so if default locale is active then no prefix is used on the routes?

craue commented 13 years ago

@ivrock: This is exactly the behavior I'm also looking for - having the URL for routes with default locale not contain the locale parameter at all. That would even allow an application to be initially extended with support for locales without breaking all old URLs around in email notifications, bookmarks, etc. Although when asking about this in IRC I was told that this would not be possible (and I'm still wondering why).

@beberlei: You mean @fabpot, right? ;)

damien-roche commented 13 years ago

@craue: The only viable solutions I can see in the mean time is to:

  1. use locale routes from the start
  2. use the locale session instead

Did they give any reason as to why that isn't possible?

craue commented 13 years ago

@ivrock: Yes, someone had given an example. It's not explicitly about locales but hiding parameters with default values from the URL. Remainder of this comment is quoting http://pastebin.com/gavYkF0C :

foo:
  pattern: /foo/{bar}/{car}
  defaults:
    bar: barValue
    car: carValue

Generate a route in a controller:

$this->get('router')->generate('foo', array('bar' => 'barValue', 'car' => 'somethingElse');

In your scenario where a default value is hidden from the url 'barValue' would NOT be added to the url because it matches the default value so the generated url would be: /foo/somethingElse

Now if someone clicked that link and requested /foo/somethingElse and it happened to match the foo route then it would be the equivalent of /foo/somethingElse/carValue

The bar and car param has been mixed up.

damien-roche commented 13 years ago

Thanks, that should help!

Although, that is only one half. How about doing the same in the twig templates? (see original message). It would seem I have to extend the path() function to automatically include the default locale if that is the current locale.

craue commented 13 years ago

@ivrock: Sure that's possible (see https://github.com/craue/TwigExtensionsBundle/blob/92ef57f757a24493314c0029d3e21c6ee5563d05/Twig/Extension/ChangeLanguageExtension.php#L223) ;), but doesn't look like the solution to me.

craue commented 13 years ago

@ivrock: Based on your initial description you are defining all routes twice? Those without locale prefix (for the default locale) on the one hand and those locale-prefixed on the other hand? I tried that by importing the routing config file twice which results in duplicate defined route names, of course. What would be a good solution?

damien-roche commented 13 years ago

Hi. No, that is what I'm trying to avoid. If I have 10 languages suddenly I have hundreds of routes, where 10 would suffice.

Although that seems like a decent idea if I only have a few languages.

I currently have:

app.com/fr/contact app.com/en/contact

where /en/ is default locale for the application. So I want the route

app.com/contact

To work as /en/ automatically. At the moment I have to create another route which doesn't contain locale, and when you are on this default locale all path() routes generated for twig template include that locale.

A solution to that would be great.

damien-roche commented 13 years ago

I should clarify exactly what output I'm looking for.

I have:

pattern: /{locale}/contact

I want the routes:

app.com/contact (default locale set in config file) app.com/fr/contact

Now, when I'm viewing app.com/contact, I want the path() in twig to not include /{locale}/pagename because all my routes should default to /pagename.

The 2 roadblocks to this are:

  1. when using pattern: /{locale}/contact, /contact does not exist
  2. path() always includes {locale}

If I use more than 1 path to create /contact as well then I have an even bigger problem: path() requires the path param, yet I have now have 2 depending on the locale.

path('contact') path('contactIN')

It all seems very messy. Even if I extend twig to remove locale if default locale selected on path(), I still have problem with routes not working. Appreciate any ideas, though I suspect this isn't possible as is right now.

craue commented 13 years ago

@ivrock: You should use the (kind of) "magic" parameter {_locale} instead of {locale} which means you would have to define all routes exactly twice as I mentioned, not one branch for each locale. But the problem you described persists and I also doubt there's a clean solution for that currently.

damien-roche commented 13 years ago

Sorry, I didn't know there was a difference. I use {_locale}, just used {locale} for the example, and yes, sadly, problem still persists.

maoueh commented 13 years ago

Hi,

I'm using the {_locale} parameter with a defaults for this parameter to achieve what you want. I'm using annotations but I don't see why it could not work using a YAML routing file. Here some info.

Say I have the following paths I want to support:

app.com/ => (should be in english) app.com/en => (should be in english) app.com/fr => (should be in french)

I achieve this with the following annotations in my controller:

/**
 * @Route("/{_locale}", name = "index", defaults = {"_locale" = "en"})
 */

I did not set any requirements because for now, it is not possible to use parameters in routing definition so adding a new locale would mean changing a lots of requirements clauses. This is also true for the defaults clause but changing the default locale is less likely to happen then adding a new locale. Anyway for now, trying to load app.com/es will output the page in english which is my default locale.

In Twig, I then do {{ path('index') }} and the url app.com/ will be produced if the current {_locale} is en and app.com/fr will be outputted if the current {_locale} is fr. This work event for routes without a default set. For example, with this route:

/**
 * @Route("/{_locale}/event/create", name = "event_create")
 */

If a do this in Twig, {{ path('event_create') }}, the output change in respect to the current value of the parameter {_locale}.

In en => app.com/en/event/create In fr => app.com/fr/event/create

Without me having to specify the locale anywhere.

Hope this helps.

Regards, Matt

schmittjoh commented 13 years ago

I have also faced some problems with this, and you can find my solution here: http://github.com/schmittjoh/JMSI18nRoutingBundle

Basically, you can start without any localized routes, and then add translations for your routes or just for some of them as needed.

Let me know what you think and maybe we can include some of its functionality in Symfony 2.1.

maoueh commented 13 years ago

@schmittjoh: Thx, this seems indeed interesting. I'm gonna look at it in the next few days and will give you my feedback about it.

Thanks for your work on this bundle and on Symfony.

Regards, Matt

tawfekov commented 13 years ago

Hi , maybe this link would help : http://stackoverflow.com/questions/5800675/symfony2-locale-in-route/5803144#5803144

{{ path('address', {'_locale': app.request.attributes.get('_locale')}) }}
fabpot commented 12 years ago

In master, the issue described by @beberlei has been fixed (but it's not possible to backport this fix to 2.0). So, the _locale argument is now always available and you don't need to pass it explicitly when generating a route for the current locale.

raziel057 commented 11 years ago

For the moment it's possible (and efficient to do this):

PTCIdentialsBundle:
    resource: "@PTCIdentialsBundle/Controller/"
    type: annotation
    prefix: /{_locale}

But it's not possible to add a defaults: { _locale: en } parameter here. It's only possible when defining the routes for one controller, like this:

contact:
    pattern:   /{_locale}/contact
    defaults:  { _controller: AcmeDemoBundle:Contact:index, _locale: en }
    requirements:
        _locale: en|fr|de

So in order to not have problems when accessing to the root of the site, I need to create a redirect like this:

root:
    pattern: /
    defaults:
        _controller: FrameworkBundle:Redirect:redirect
        route: homepage
        permanent: true # this is for 301

And for all my routes I always have the locale, even for the defaut locale:

In en => http://my-app.com/en (default locale) In fr => http://my-app.com/fr

It would be more interesting to have the same behavior as if I had added the {_locale} directly on a specific controller:

/**
 * @Route("/{_locale}", name = "homepage", defaults = {"_locale" = "en"})
 */
stof commented 11 years ago

@raziel057 It is possible to defaults when importing when you are using Sf 2.2

raziel057 commented 11 years ago

I use the last version of Symfony (v 2.2.1) but defaults has no effect when I write:

PTCIdentialsBundle:
    resource: "@PTCIdentialsBundle/Controller/"
    type: annotation
    prefix: /{_locale}
    defaults: { _locale: en }

I can't access to http://my-app.com/

raziel057 commented 11 years ago

In fact to be accurate, defaults is taken into account as we can see with router:debug command

> php app/console router:debug welcome_index
[router] Route "welcome_index"
Name         welcome_index
Path         /{_locale}/
Host         ANY
Scheme       ANY
Method       ANY
Class        Symfony\Component\Routing\Route
Defaults     _controller: PTC\IdentialsBundle\Controller\WelcomeController::indexAction
             _locale: en
Requirements _locale: en|fr
Options      compiler_class: Symfony\Component\Routing\RouteCompiler
Path-Regex   #^/(?P<_locale>en|fr)/$#s

But if I try to access http://my-app.com/ , I get a "NotFoundHttpException".

henrikbjorn commented 11 years ago

@raziel057 i would try and remove the traling "/" and see if it works.

raziel057 commented 11 years ago

@henrikbjorn I already tried but it doesn't works.

Moreover, if I have an other route like this:

> php app/console router:debug login
[router] Route "login"
Name         login
Path         /{_locale}/secured/login
Host         ANY
Scheme       ANY
Method       ANY
Class        Symfony\Component\Routing\Route
Defaults     _controller: PTC\IdentialsBundle\Controller\AuthenticationController::indexAction
Requirements _locale: en|fr
Options      compiler_class: Symfony\Component\Routing\RouteCompiler
Path-Regex   #^/(?P<_locale>en|fr)/secured/login$#s

{_locale}/ shoud be replaced by an empty string when accessing in default language in order to have: http://my-app.com/secured/login in en http://my-app.com/fr/secured/login in fr

Tobion commented 11 years ago

@raziel057 you are hitting #4322 when importing a route. The locale is not optional for /{_locale}/ even with a default. So / does not match. You can see it with the help of the regex.

{_locale}/ shoud be replaced by an empty string when accessing in default language in order to have: http://my-app.com/secured/login in en http://my-app.com/fr/secured/login in fr

The same reason: It's not possible to have optional placeholders in the middle. #7051 would solve it.

raziel057 commented 11 years ago

@Tobion Ok. Thanks for your explanations. I thought it was possible when reading the comment of @maoueh.

Do you know what is used to display the official documentation of Symfony? http://symfony.com/doc/current/book/forms.html <- content in "en" http://symfony.com/fr/doc/current/book/forms.html <- content in "fr"

Is it this bundle? http://github.com/schmittjoh/JMSI18nRoutingBundle

Tobion commented 11 years ago

Don't know. But as a workaround you can define two routes: one with the locale and one without. You just need to chose the approriate one when generating a route.

raziel057 commented 11 years ago

When I use http://github.com/schmittjoh/JMSI18nRoutingBundle with this configuration:

jms_i18n_routing:
    default_locale: en
    locales: [en, fr]
    strategy: prefix_except_default

I have the following routes when I execute php app/console router:debug:

en__RG__welcome_index                  ANY    ANY    ANY  /
fr__RG__welcome_index                  ANY    ANY    ANY  /fr/
en__RG__view_about                     ANY    ANY    ANY  /about
fr__RG__view_about                     ANY    ANY    ANY  /fr/about
en__RG__view_contact                   ANY    ANY    ANY  /contact
fr__RG__view_contact                   ANY    ANY    ANY  /fr/contact
...

So we can see that routes are well duplicated. But when I try to access to http://my-app.com/ I got the following error:

FatalErrorException: Error: Maximum function nesting level of '100' reached, aborting! in /home/lallement/workspace/Identials/vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php line 54

in /home/lallement/workspace/Identials/vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php line 54
at ErrorHandler??handleFatal() in /home/lallement/workspace/Identials/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php line 0
at DumperCollection??add() in /home/lallement/workspace/Identials/vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php line 70
at DumperPrefixCollection??addPrefixRoute() in /home/lallement/workspace/Identials/vendor/symfony/symfony/src/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php line 72
Tobion commented 11 years ago

Use the search please.

raziel057 commented 11 years ago

@Tobion Yes sorry ;). The max_nesting_level of xdebug was to low.

Hyunk3l commented 9 years ago

anyone found a solution to the very first @damien-roche problem? I've exactly the same problem and can't find a clean solution

jsalamin commented 4 years ago

I'm not sure that this is what you're looking for and after so many years it may not be so useful... but here is what I've done to solve my problem that seems to be pretty similar to the situation described by @damien-roche .

First of all I'm working with version 5.1.2 of Symfony.

Here is my translation.yaml file:

framework:
    default_locale: fr
    translator:
        default_path: '%kernel.project_dir%/translations'
        fallbacks:
            - en
        enabled_locales: ['fr', 'en']

I defined the supported locales and defined FR as the default one with a fallback to EN. This configuration is not part of your routing question but as translations management is also part of my solution to manage a multi-language website, I share it also.

I changed the configuration of my routes so that all defined annotations don't have to be changed for multi-language support, I simply defined a prefix for each supported language in routes/annotations.yaml:

controllers:
    resource: ../../src/Controller/
    type: annotation
    defaults:
        _locale: '%kernel.default_locale%'
    requirements:
        _locale: fr|en
    prefix:
        fr: ''
        en: '/en'

Then here is what I've configured in security.yaml to have all my routes working as expected:

    access_control:
        - { path: ^/(_|en/)login, roles: IS_AUTHENTICATED_ANONYMOUSLY}
        - { path: ^/(_|en/)/admin, roles: ROLE_ADMIN}
        - { path: ^/(_|en/)/, roles: IS_AUTHENTICATED_ANONYMOUSLY}

This configuration allows me to use all existing routes for my default language (/, /login...) and the routes for English version with defined en prefix (/en, /en/login...).

Now when I run php bin/console debug:router I have all the routes that I can use from my twig templates:

image

It means that when I simply need to define navigation in my website without worrying about the current locale, I can use my routes as normal:

<a href="{{ path('app_login') }}">{% trans %}action.menu.member.access{% endtrans %}</a>

And when I specifically need to use one of my "localized" routes (for example for the links to switch website from FR to EN), I simply use the appropriate route including the desired locale:

            {% if app.request.locale != 'en' %}
                <a href="{{ path('app_home_index.en') }}">EN</a>
            {% endif %}
            {% if app.request.locale != 'fr' %}
                <a href="{{ path('app_home_index.fr') }}">FR</a>
            {% endif %}

I hope that this will help.

Regards,

muhiddingithub commented 2 years ago

If you want to change language in current route You may try this:

{% set routeParams = app.request.attributes.get('_route_params') %}

<a href="{{ path(app.request.attributes.get('_route'), routeParams|merge({'_locale': 'fr'})) }}">FR</a>