A language switcher in Symfony 2.4 - php

I have an object Language, and I can add, delete and update languages from the admin page.
What I want to do, is to add a language switcher, I put this html/twig code:
{% for language in languages %}
<li>{{ language.language | capitalize }} ({{ language.code }})</li>
{% endfor %}
And an action the route for the action is evr_footer_switch_language, the one I used in the switcher above:
public function switchlanguageAction($locale = 'en') {
$this->get('session')->set('_locale', $locale);
$request = $this->getRequest();
$request->setLocale($locale);
return $this->redirect($request->headers->get('referer'));
}
This is the route I defined for the action/controller switchlanguageAction()
evr_footer_switch_language:
pattern: /language/switch/{locale}
defaults: { _controller: EvrHomeBundle:Footer:switchlanguage, locale: en }
It seems to me very simple in principle, you click on the link of the language (got from the database), send the code of the language (exemple : 'fr', 'en', 'zh' etc...) to the action as a $locale variable, then set the Locale of the session/request to this value.
The problem is that none of this works, and the language is still 'EN' (default value).
Note According to the requirements of this project, The language can't be mentioned in the URL (like fr/articles, en/articles), but the same URL (/articles/) can show in different languages, this is why I didn't use the pre-defined slug (_locale).
Thanks

While in search for some more details in order to write an answer I stumbled upon this Symfony cookbook entry: Making the Locale "Sticky" during a User's Session
I think that's exactly what you need ;)

Symfony 2.6:
I used the LocaleListener mentioned in "Making the Locale Sticky", but also had to use this to get things working properly:
/** from Controller
*
* #Route("/changeLanguage/{changeToLocale}", name="changeLanguage")
*
*/
public function changeLanguageAction($changeToLocale){
$this->get('request')->attributes->set('_locale', null);
$this->get('session')->set('_locale', $changeToLocale);
return $this->redirect($this->generateUrl('index'));
}

public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->query->get('swich_language')) {
$request->getSession()->set('_locale', $locale);
$routing = $this->router->match($request->getPathInfo());
$route_params = array();
foreach ($routing as $key => $value) {
if($key[0] !== "_")
{
$route_params[$key] = $value;
}
}
$parameters = \array_merge($route_params, array("_locale" => $locale));
$url = $this->urlGenerator->generate($routing['_route'], $parameters);
$response = new RedirectResponse($url);
$event->setResponse($response);
}
}
You can add as a kernel request and with querystring swich_language you can change it

Related

EasyAdmin3 / Symfony / Twig - url generator with route name and params in Twig template

I'm trying to generate EasyAdmin3 url inside my template with some params, but for some reason they are not present in a controller.
Twig template:
xxx
yyy
Error with missing EA context:
zzz
Controller:
/**
* #Route("/admin/something/{id}", name="rounte_name")
*/
public function xyz($id = null, Request $request, AdminContext $context)
{
dd($_GET['id'], $request->request->all(), $context->getRequest()->request->all());
...
}
The $_GET['id'] works, but request and context are empty [].
Any idea how to generate route by name with params?
Thanks
I don't think you need the ea_url() helper function if you are just generating regular named routes in twig. You should be able to use the path() twig extension provided by Symfony.
{{ path(route_name, route_parameters = [], relative = false) }}
If you are trying to create a link to an EasyAdmin controller action, then you can use the ea_url() helper, but you have to specify the controller and action as well. Try something like:
{% set url = ea_url()
.setController('App\\Controller\\Admin\\FoobarCrudController')
.setAction('customFooAction')
.setEntityId(entity.instance.id)
.set('myParam', 'baz') %}
Custom Foobar Action
Then in your controller, everything should be available as per usual via the $context variable...
public function customFooAction(AdminContext $context)
{
$entity = $context->getEntity()->getInstance();
$myParam = $context->getRequest()->get('myParam');
}
This works for me, I hope it helps. 🙂

OctoberCMS Translate plugin redirect with hash

I am using OctoberCMS Translate plugin (https://octobercms.com/plugin/rainlab-translate) and its working as per my expectations.
However, I have one custom requirement in which I am generating hash in url (i.e. http://localhost/ibis/whats-on/details#2020-sydney-international-whitewater-event - #2020-sydney-international-whitewater-event).
Now the thing is, when I am supposed to redirect using below code,
{% for code, name in locales %}
<a class="dropdown-item" href="#" data-request="onSwitchLocale" data-request-data="locale: '{{ code }}'">
{{ name |upper }}
</a>
I am able to redirect, however, my url looses its hash tag and it becomes something like http://localhost/ibis/fr/whats-on/details (fr is my selected french language).
Here below is my overwrite code in my layout's code tab to onSwitchLocale method which is provided by Translate plugin.
use RainLab\Translate\Models\Locale as LocaleModel;
use RainLab\Translate\Classes\Translator;
use October\Rain\Router\Router as RainRouter;
function onSwitchLocale()
{
$this->translator = Translator::instance();
$locale = post('locale');
if (!$locale = post('locale')) {
return;
}
$this->translator->setLocale($locale);
$pageUrl = $this->translator->withPreservedQueryString($this->translator->makeLocaleUrlFromPage($locale), $locale);
if ($this->property('forceUrl')) {
return Redirect::to($this->translator->getPathInLocale($pageUrl, $locale));
}
return Redirect::to($pageUrl);
}
As you can see I am trying to accomplish url redirect with hash, but I am getting error here saying
Call to undefined method RainLab\Translate\Classes\Translator::withPreservedQueryString()
And unable to proceed this request. I researched further and found withPreservedQueryString has protected method and I tried various ways to execute this method but unable to do so.
So first I need to accomplish this and second I want to append that hash tag in my url.
Can someone guide me from here on how can I achieve this ?
Thanks
I am not sure $this->translator [ Translator::instance(); ] has this method there ! its just method of LocalePicker component.
This is implemented code there, so use this and define your own method
protected function withPreservedQueryString($pageUrl, $locale)
{
$page = $this->getPage();
$query = request()->query();
/**
* #event translate.localePicker.translateQuery
* Enables manipulating the URL query parameters
*
* You will have access to the page object, the old and new locale and the URL query parameters.
*
* Example usage:
*
* Event::listen('translate.localePicker.translateQuery', function($page, $params, $oldLocale, $newLocale) {
* if ($page->baseFileName == 'your-page-filename') {
* return YourModel::translateParams($params, $oldLocale, $newLocale);
* }
* });
*
*/
$translatedQuery = Event::fire('translate.localePicker.translateQuery',
[$page, $query, $this->oldLocale, $locale], true);
$query = http_build_query($translatedQuery ?: $query);
return $query ? $pageUrl . '?' . $query : $pageUrl;
}
Use it like this
$pageUrl = $this->
withPreservedQueryString( // <- this call
$this->translator->makeLocaleUrlFromPage($locale),
$locale
);
if ($this->property('forceUrl')) {
return Redirect::to($this->translator->getPathInLocale($pageUrl, $locale));
}
it should work, if any doubt please comment.

Redirect if route does'n exist

I have a question : so for examples I have an app in symfony3 which have the following routes : /admin/login,admin/news,admin/gallery, but the route /admin/authentification doesn't exist. So the idea is if the route doesn't exist I want to redirect the user to homepage /. Can you help me please ? Thanks in advance and sorry for my english
I'm not confident this is the best solution, but you can use a UrlMatcher to check that the URL you're passing correlates to an available route:
/**
* #Route("/debug")
*/
public function DebugAction(){
$router = $this->get('router');
//Get all the routes that exist.
$routes = $router->getRouteCollection();
$context = $router->getContext();
$urlMatcher = new UrlMatcher($routes, $context);
$url = '/admin/login';
try{
//UrlMatcher::match() will throw a ResourceNotFoundException if the route
//doesn't exist.
$urlMatcher->match($url);
return $this->redirect($url);
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e){
return $this->redirect('/');
}
}
I'm not particularly keen on this solution because it relies on catching an exception, rather than checking a boolean value to determine if the route exists.
You can check if the rout exist.
function routeExists($name)
{
// I assume that you have a link to the container in your twig extension class
$router = $this->container->get('router');
return (null === $router->getRouteCollection()->get($name)) ? false : true;
}
And depending on result, do the redirect to the rout, or to the default webpage, or whatever you need.

How should one redirect locale-less URIs to locale-full ones in Symfony-CMF?

Background
We have a (fairly typical?) arrangement for a multilingual Symfony CMF website, where resource paths are prefixed by the desired locale—for example:
http://www.example.com/en/path/to/english-resource.html; and
http://www.example.com/fr/voie/à/ressource-française.html.
We are using RoutingAutoBundle to store such routes in the content repository, and DynamicRouter to utilise them: simple and easy.
If a GET request arrives without a locale prefix, we would like to:
determine the most appropriate locale for the user; and then
redirect1 the user to the same path but with locale prefix added.
Current Approach
The first part is an obvious candidate for LuneticsLocaleBundle, with router higher in its guessing order than our desired fallback methods: again, simple and easy.
However, how best to implement the second part is a little less obvious. Currently we have configured Symfony's default/static router to have a lower priority in the routing chain than DynamicRouter, and have therein configured a controller as follows:
/**
* #Route("/{path}", requirements={"path" = "^(?!(en|fr)(/.*)?$)"})
* #Method({"GET"})
*/
public function localeNotInUriAction()
{
$request = this->getRequest();
$this->redirect(
'/'
. $request->getLocale() // set by Lunetics
. $request->getRequestUri()
);
}
But this feels rather hacky and I'm on the search for something "cleaner".
A better way?
Initially I thought to modify LuneticsLocaleBundle so that it would fire an event whenever a guesser determines the locale, thinking that if it was not the RouterLocaleGuesser then we could infer that the requested URI did not contain a locale. However this clearly isn't the case, since the RouterLocaleGuesser will only determine the locale if there was a route in the first place—so I'd not have made any progress.
I'm now a bit stuck for any other ideas. Perhaps I'm already doing the right thing after all? If so, then all I need to do is find some way to inject the permitted locales (from the config) into the requirement regex…
External redirection, i.e. via a response with HTTP 302 status.
we use a custom 404 handler and lunetics:
exception_listener:
class: AppBundle\EventListener\ExceptionListener
arguments:
container: "#service_container"
tags:
- { name:"kernel.event_listener", event:kernel.exception, handler:onKernelException }
and the php class
class ExceptionListener
{
/**
* #var ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
if ($this->container->getParameter('kernel.debug')) {
// do not interfere with error handling while debugging
return;
}
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$this->handle404($event);
return;
}
// ...
}
public function handle404(GetResponseForExceptionEvent $event)
{
$request = $event->getRequest();
if (preg_match('#^\/(de|fr|en)\/#', $request->getPathInfo())) {
// a real 404, these are nicely handled by Twig
return;
}
// i *think* that the locale is not set on the request, as lunetics comes after routing, and the routing will raise the 404
$bestLang = $this->container->get('lunetics_locale.guesser_manager')->runLocaleGuessing($request);
if (! $bestLang) {
$bestLang = 'de';
}
$qs = $request->getQueryString();
if (null !== $qs) {
$qs = '?'.$qs;
}
$url = $request->getSchemeAndHttpHost() . $request->getBaseUrl() . '/' . $bestLang . $request->getPathInfo() . $qs;
$this->redirect($event, $url);
}
it would be nicer to also check if the target path actually exists - as is, we will redirect /foobar to /de/foobar and display a 404 for that one, which is not that elegant.

How to configure Symfony2 sticky locale during session

I would like to translate my website thanks to an link on the right top.
I found out that, since Symfony 2.1, the locale is not stored in the session anymore.
So, I followed this Symfony documentation: Making the Locale "Sticky" during a User's Session
...Bundle/Service/LocaleListener.php
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale)
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
$locale = $request->attributes->get('_locale');
var_dump($locale);
if ($locale) {
$request->getSession()->set('_locale', $locale);
} else {
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
static public function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 17)),
);
}
}
...Bundle/Resources/config/services.yml
locale_listener:
class: ..Bundle\Service\LocaleListener
arguments: ["%kernel.default_locale%"]
tags:
- { name: kernel.event_subscriber }
./app/config/config.yml
framework:
translator: { fallback: en }
And, I add two links to translate my website on the parent twig template, shown below (Symfony2 locale languages whole page event listener).
base.html.twig
<li><a href="{{-
path(app.request.get('_route'),
app.request.get('_route_params')|merge({'_locale' : 'fr'}))
-}}">FR</a></li>
<li><a href="{{-
path(app.request.get('_route'),
app.request.get('_route_params')|merge({'_locale' : 'en'}))
-}}">EN</a></li>
Problem and Question
When I click on one of these links, the parameter _locale is added.
For instance:
satisfaction?_locale=fr
So, the value of the _locale parameter is fr. Consequently, my website should be translated in french.
Nevertheless, that
var_dump($locale)
in the listener is displayed three times:
null
en
null
I don't understand why the _locale parameter is not found when it display null and why the en?
With your listener, you will catch all request and subrequest that is not needed. This explain the three times apparition.
Try to add this following code to your onKernelRequest method:
if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) {
return;
}
This will avoid subRequests and possibly resolve your problem.

Categories