I'm trying to internationalize my website using Symfony.
This is my routing.yml:
index:
pattern: /{_locale}
defaults: { _controller: AppBundle:Index:index, _locale: en }
requirements:
_locale: en|fr
When the URL is just "/", "en" locale is set automatically, this is great but I want the browser locale.
For exemple, if I'm in France and I type "/", I want redirect to "/fr/", etc.
Can you help me?
you can get client locale and set redirection in controller
$clientLocale = strtolower(str_split($_SERVER['HTTP_ACCEPT_LANGUAGE'], 2)[0]);
switch ($clientLocale) {
case 'fr':
return $this->redirect($this->generateUrl('fr_route'));
break;
default:
return $this->redirect($this->generateUrl('en_route'));
break;
}
Based on b3da answer, I'll apply it only for the index route (as I think that is the main case when someone type your domain without params) and relying on my YML translations:
#routing.yml
index:
path: /
defaults:
_controller: AppBundle:Pub:index
index2:
path: /{_locale}
defaults:
_controller: AppBundle:Pub:index
#PubController.php
public function indexAction($_locale=null, Request $request) {
if (!$_locale) {
$browserLocale = strtolower(str_split($_SERVER['HTTP_ACCEPT_LANGUAGE'], 2)[0]);
return $this->redirect($this->generateUrl('index2', ['_locale' => $browserLocale]));
}
return $this->render('AppBundle::index.html.twig', []);
}
If the local browser is not translated on your app/Resources/translation YMLs then it will render the texts using your fallback locale (app/config/parameters.yml).
Related
I want to open my websites like these:
http://127.0.0.1:8000/ (Works)
http://127.0.0.1:8000/en/ (Works)
http://127.0.0.1:8000/en/mrg (Works)
http://127.0.0.1:8000/mrg (Doesn't Work)
So I put this code in routing.yml:
teach:
resource: "#TeachBundle/Controller/"
type: annotation
prefix: /{_locale}
requirements:
_locale: "|en|fa"
and my controller is like this:
/**
* #Route("/mrg")
*/
public function mrgAction(Request $request)
{
$lang=$request->getLocale();
return new Response("<html><body>Your language: <b> $lang </b></body></html>");
}
All urls worked but http://127.0.0.1:8000/mrg doesn't work and returns:
No route found for "GET /mrg"
I need use default language for example if I try to open http://127.0.0.1:8000/mrg then open http://127.0.0.1:8000/en/mrg.
Is there any solution to fix this problem?
Add defaults option to your routing configuration to set the default locale if it's not set in your controllers #Routes:
teach:
resource: "#TeachBundle/Controller/"
type: annotation
prefix: /{_locale}
requirements:
_locale: "|en|fa"
defaults:
_locale: 'en' # or '%locale%'
Try this:
{_locale}
/**
* #Route("/{_locale}/mrg")
*/
public function mrgAction(Request $request)
{
$lang=$request->getLocale();
return new Response("<html><body>Your language: <b> $lang </b></body></html>");
}
Setting a Default Locale
# app/config/config.yml
framework:
default_locale: en
If doesn't work, Can You show your app/config/routing.yml.
I advice you to use : BeSimpleI18nRoutingBundle
https://github.com/BeSimple/BeSimpleI18nRoutingBundle
use BeSimple\I18nRoutingBundle\Routing\Annotation\I18nRoute;
class NoPrefixController
{
/**
* #I18nRoute({ "en": "/welcome", "fr": "/bienvenue", "de": "/willkommen" }, name="homepage")
*/
public function indexAction() { }
}
Here is the scheme of the required URLs:
/service/getBalance should map to CustomerController::getBalance
/service/addBalance should map to CustomerController::addBalance
/customer/getBalance should map to CustomerController::getBalance
/customer/addBalance should map to CustomerController::addBalance
Here is a simple controller
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class CustomerController extends Controller {
/**
* #Route("/getBalance")
*/
public function getBalanceAction(Request $request) {
}
/**
* #Route("/addBalance")
*/
public function addBalanceAction(Request $request) {
}
} // class CustomerController
Have tried the following ways. None of them worked.
# rounting.yml
v1:
resource: "#AppBundle/Controller/CustomerController.php"
prefix: /service
type: annotation
v2:
resource: "#AppBundle/Controller/CustomerController.php"
prefix: /customer
type: annotation
loading the same resource with different prefix always overrides the previous occurrence (the last one works). The following also doesn't work for the same reason and have the same behavior.
# rounting.yml
v1:
resource: "#AppBundle/Resources/config/routing_customer.yml"
prefix: /service
v2:
resource: "#AppBundle/Resources/config/routing_customer.yml"
prefix: /customer
# routing_customer.yml
getBalance:
path: /getBalance
defaults : { _controller: "AppBundle:Customer:getBalance" }
addBalance:
path: /addBalance
defaults : { _controller: "AppBundle:Customer:addBalance" }
Third not working option:
# rounting.yml
v1:
resource: "#AppBundle/Resources/config/routing_v1.yml"
prefix: / # evenr putting /service here instead of inside
v2:
resource: "#AppBundle/Resources/config/routing_v2.yml"
prefix: / # evenr putting /customer here instead of inside
# routing_v1.yml
getBalance:
path: /service/getBalance
defaults : { _controller: "AppBundle:Customer:getBalance" }
addBalance:
path: /service/addBalance
defaults : { _controller: "AppBundle:Customer:addBalance" }
# routing_v2.yml
getBalance:
path: /customer/getBalance
defaults : { _controller: "AppBundle:Customer:getBalance" }
addBalance:
path: /customer/addBalance
defaults : { _controller: "AppBundle:Customer:addBalance" }
I actually need to route / and /customer to the same controller:
/getBalance and /customer/getBalance.
I want to give two prefixes for a group of methods. How to do that in Symfony?
Conclusion from my trials, #goto's answer and #Cerad's comments:
The last Example above could have worked if I used different route names. Route names are unique though out the whole project (Not just unique in file). v1_getBalance and v2_getBalance.
The other solution is to use a custom loader as #goto described.
You could do routes this way:
#Route(
"/{subject}/getBalance",
requirements={
"subject": "customer|service"
}
)
in yml:
subject_getbalance:
path: /{subject}/getBalance
requirements:
subject: customer|service
The requirement is safer than nothing: it allows you to route another route like foo/getBalance on another controller (as it does not match the requirement)
EDIT: for your special case, as you need /getBalance to map to your route too you could do:
subject_getbalance:
path: /{subject}/getBalance
default: { _controller: YourBundle:Controller }
requirements:
subject: customer|service
default_getbalance:
path: /getBalance
default: { _controller: YourBundle:Controller, subject: customer }
Edit: the last idea is to a custom route loader (but I've never tried):
class ExtraLoader extends Loader
{
public function load($resource, $type = null)
{
/* ... */
$prefixes = [ 'default_' =>'','customer_' =>'/customer','service_' =>'/service']
// prepare a new route
$path = '/getbalance/{parameter}';
$defaults = array(
'_controller' => 'YourBundle:Actiona',
);
$requirements = array(
'parameter' => '\d+',
);
foreach($prefixes as $prefixName => $prefixRoute) {
$route = new Route($prefixRoute . $path, $defaults, $requirements);
$routes->add($prefixName . 'getBalance', $route);
}
This will allows you to have 3 routes generated:
default_getBalance: /getBalance
customer_getBalance: /customer/getBalance
service_getBalance: /service/getBalance
I have similar routes, symfony2 call second route(subcategory), if i type somethinkg like "example.com/cars/insert/.
web_portal_category:
path: /{category}/
defaults: { _controller: WebPortalBundle:Default:category }
web_portal_subcategory:
path: /{category}/{subcategory}/
defaults: { _controller: WebPortalBundle:Default:subcategory }
web_portal_insert:
path: /{category}/insert/
defaults: { _controller: WebPortalBundle:Default:upload }
How can I force them to call right one?
You put them in the right order, because as soon as it finds a match, the routing component will stop and execute that action:
web_portal_insert:
path: /{category}/insert/
defaults: { _controller: WebPortalBundle:Default:upload }
web_portal_subcategory:
path: /{category}/{subcategory}/
defaults: { _controller: WebPortalBundle:Default:subcategory }
web_portal_category:
path: /{category}/
defaults: { _controller: WebPortalBundle:Default:category }
That is correct. Symfony reads te routes from top to bottom and takes the first route that matches. In your example the url cars/insert/ matches with the second and the third route. Because the second route has only variables {category} & {subcategory} all the urls x/y/ will match and the third route will never be reached.
if you swap the second and the third route like this than things will change.
web_portal_category:
path: /{category}/
defaults: { _controller: WebPortalBundle:Default:category }
web_portal_insert:
path: /{category}/insert/
defaults: { _controller: WebPortalBundle:Default:upload }
web_portal_subcategory:
path: /{category}/{subcategory}/
defaults: { _controller: WebPortalBundle:Default:subcategory }
In this case only the /x/insert url will match the second route and all others will continue with the third route in the row.
my_module.content:
path: '/admin/my_module/{method}'
defaults:
_controller: '\Drupal\my_module\Controller\MyModuleController::%{method}' // Not working
How can I send a a parameter to the _controller? to switch between the methods of the controller
Thank you!
In the routing you have something like this
example.content:
path: '/example'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::content'
requirements:
_permission: 'access content'
To give params from the URL:
Add a variable you want to receive in the controller later.
NOTICE: path: '/example/{id_param}'
example.content:
path: '/example/{id_param}'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::content'
requirements:
_permission: 'access content'
Now, in the controller:
You'll have something like this
function content($id_param) {
echo($id_param);
}
That prints whatever you pass to the URL. For example yourdrupalsite.com/example/1920
Prints
1920
Passing parameter to form :
MODULE_NAME.contactform_results:
path: '/contactform/results/{PARAMETER}'
defaults:
_title: 'Contact Form Submissions'
_form: '\Drupal\MODULE_NAME\Form\TicketSendingForm'
requirements:
_permission: 'access control'
Then in form builder function
public function buildForm(array $form, FormStateInterface $form_state, $PARAMETER = null) {
echo ($PARAMETER);
}
In case you want to cast the parameter to an entity (fx. an ID), be aware that the parameter can't have underscores - alternatively use camelcase.
example.entity:
path: '/entity/{entityName}'
defaults:
_controller: '\Drupal\example\Controller\ExampleController::content'
requirements:
_permission: 'access content'
options:
parameters:
entityName:
type: entity:entity_name
I want to declare both methods GET and POST for a same route in my routing.yml.
According the documentation it's possible with annotations like that :
/**
* #Route("/edit/{id}")
* #Method({"GET", "POST"})
*/
But how to YAML ? I tried different things :
contact_envoi:
pattern: /contact-envoi
defaults: { _controller: AcmeDemoBundle:Default:contactEnvoi }
requirements:
sf_method: ['get','post']
and
...
requirements:
_method: { 'GET', 'POST' }
but it still doesn't work ... Please help, I found nothing in the documentation about that.
Thanks to Touki for his comment, it works!
I had to declare twice the same URL on two separate shares and each with their own method as explained here for Symfony 2.1 and here for Symfony 2.2.
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
methods: [GET]
contact_process:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
methods: [POST]
You can get the same route with the GET and POST methods.
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
methods: ['GET','POST']
Then manage in your controller the method used.
public function contactAction(Request $request)
{
if ('POST' === $request->getMethod()) {
..
}
}
just remove the methods
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }