Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
damien-roche opened this issue Jul 15, 2011 · 32 comments
Closed

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

damien-roche opened this issue Jul 15, 2011 · 32 comments
Labels

Comments

@damien-roche
Copy link

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
Copy link
Contributor

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:
#1700

@damien-roche
Copy link
Author

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
Copy link
Contributor

craue commented Jul 15, 2011

@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
Copy link
Author

@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
Copy link
Contributor

craue commented Jul 15, 2011

@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
Copy link
Author

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
Copy link
Contributor

craue commented Jul 15, 2011

@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
Copy link
Contributor

craue commented Jul 16, 2011

@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
Copy link
Author

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
Copy link
Author

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
Copy link
Contributor

craue commented Jul 16, 2011

@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
Copy link
Author

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
Copy link
Contributor

maoueh commented Jul 19, 2011

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
Copy link
Contributor

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
Copy link
Contributor

maoueh commented Jul 19, 2011

@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
Copy link

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
Copy link
Member

fabpot commented Feb 13, 2012

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.

@fabpot fabpot closed this as completed Feb 13, 2012
@raziel057
Copy link
Contributor

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
Copy link
Member

stof commented Apr 19, 2013

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

@raziel057
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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

@raziel057
Copy link
Contributor

@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
Copy link
Member

Tobion commented Apr 22, 2013

@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
Copy link
Contributor

@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
Copy link
Member

Tobion commented Apr 22, 2013

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
Copy link
Contributor

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
Copy link
Member

Tobion commented Apr 23, 2013

Use the search please.

@raziel057
Copy link
Contributor

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

@Hyunk3l
Copy link

Hyunk3l commented Jun 5, 2015

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
Copy link

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
Copy link

muhiddingithub commented Oct 6, 2022

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests