2011-10-26 6 views
26

Ho un problema con il routing e l'internazionalizzazione del mio sito creato con Symfony2.
Se mi definiscono percorsi nel file routing.yml, come questo:Symfony2 locale predefinito nell'instradamento

example: 
    pattern: /{_locale}/example 
    defaults: { _controller: ExampleBundle:Example:index, _locale: fr } 

Funziona bene con URL del tipo:

mysite.com/en/example 
mysite.com/fr/example 

ma non funziona con

mysite.com/example 

Potrebbe essere che i segnaposto opzionali siano consentiti solo alla fine di un URL?
Se sì, quello che potrebbe essere una possibile soluzione per la visualizzazione di un URL del tipo:

mysite.com/example 

in una lingua predefinita o reindirizzare l'utente a:

mysite.com/defaultlanguage/example 

quando visita:

mysite.com/example. ? 

Sto cercando di capirlo ma senza successo finora.

Grazie.

+0

Potrebbe risolvere questo problema? –

+0

Non ho ancora implementato alcuna soluzione riguardante locale predefinito nel mio sito web. Potrei doverlo fare presto perché stiamo andando all'estero, riferirò la soluzione che implementeremo qui. Ho appena letto le risposte e @mattexx uno sembra il migliore/più pulito. Non sapevo che potessi definire più percorsi per un controller. –

risposta

-3

penso che si potrebbe semplicemente aggiungere un percorso come questo:

example: 
pattern: /example 
defaults: { _controller: ExampleBundle:Example:index } 

In questo modo, il locale sarebbe stato l'ultimo locale selezionato dall'utente, o le impostazioni internazionali predefinite se locali dell'utente non è stato impostato. Si potrebbe anche aggiungere il parametro "_locale" ai "default" nella configurazione di routing se si desidera impostare una localizzazione specifica per/example:

example: 
pattern: /example 
defaults: { _controller: ExampleBundle:Example:index, _locale: fr } 

Non so se c'è un modo migliore per fare questo .

+0

Hai provato questa soluzione? Pattern qui non include alcuna localizzazione e penso che non funzionerà. –

0

Io uso le annotazioni, e lo farò

/** 
* @Route("/{_locale}/example", defaults={"_locale"=""}) 
* @Route("/example", defaults={"_locale"="en"}, , requirements = {"_locale" = "fr|en|uk"}) 
*/ 

Ma per modo yml, provare qualche equivalente ...

+1

Per informazioni, alcune modifiche sulla locale sono in arrivo per la v2.1. Locale sarà solo nella richiesta, non più nella sessione https://github.com/symfony/symfony/commit/74bc699b270122b70b1de6ece47c726f5df8bd41 – webda2l

+1

Non voglio usare lo stesso URL per visualizzare una risorsa in molte lingue diverse in base al locale dell'utente. Voglio utilizzare un URL predefinito per visualizzare una risorsa nella lingua predefinita del sito Web. Ad esempio, questo è ciò che Apple fa nel loro sito web: http://apple.com => US "default" versione; http://apple.com/uk: versione inglese; http://apple.com/fr: versione francese, ecc. –

11

È possibile definire più modelli come questo:

example_default: 
    pattern: /example 
    defaults: { _controller: ExampleBundle:Example:index, _locale: fr } 

example: 
    pattern: /{_locale}/example 
    defaults: { _controller: ExampleBundle:Example:index} 
    requirements: 
     _locale: fr|en 

È dovrebbe essere in grado di ottenere lo stesso tipo di cose con annotazioni:

/** 
* @Route("/example", defaults={"_locale"="fr"}) 
* @Route("/{_locale}/example", requirements={"_locale" = "fr|en"}) 
*/ 

Spero che questo aiuti!

+1

Aiuta. Tuttavia, quello inferiore, con annotazioni, non funziona per me. Perché? – mario

+1

Potrebbe essere dimenticato 'use Sensio \ Bundle \ FrameworkExtraBundle \ Configuration \ Route;' – Jhonne

+0

Assicurati di usare nomi di route diversi per loro – amiroff

6

Questo è quello che uso per la rilevazione automatica locale e il reindirizzamento, funziona bene e non richiede le annotazioni di routing lunghe:

routing.yml

Il percorso locale gestisce principale del sito web e poi ogni altra azione del controller viene anteposta alla locale.

locale: 
    path:/
    defaults: { _controller: AppCoreBundle:Core:locale } 

main: 
    resource: "@AppCoreBundle/Controller" 
    prefix: /{_locale} 
    type: annotation 
    requirements: 
    _locale: en|fr 

CoreController.php

Rileva lingua dell'utente e reindirizza al percorso di vostra scelta. Io uso la casa come predefinito in quanto è il caso più comune.

public function localeAction($route = 'home', $parameters = array()) 
{ 
    $this->getRequest()->setLocale($this->getRequest()->getPreferredLanguage(array('en', 'fr'))); 

    return $this->redirect($this->generateUrl($route, $parameters)); 
} 

Poi, le annotazioni di percorso possono essere semplicemente:

/** 
* @Route("/", name="home") 
*/ 
public function indexAction(Request $request) 
{ 
    // Do stuff 
} 

Ramoscello

Il localeAction può essere utilizzato per consentire all'utente di modificare le impostazioni internazionali, senza di uscire dalla pagina corrente :

<a href="{{ path(app.request.get('_route'), app.request.get('_route_params')|merge({'_locale': targetLocale })) }}">{{ targetLanguage }}</a> 

Pulito & semplice!

+0

Ma in questo modo automaticamente reindirizzati al collegamento con il locale: www.example.com/en www.example.com/fr ma www.example.com non visualizzato. Quello che stiamo cercando di fare è esattamente la stessa cosa che lanciare www.symfony.com sito: se vado a www.example.com viene utilizzata l'impostazione internazionale predefinita se vado a www.example.com/fr è usato fr locale se vado a www.esempio.com/en è usato locale en Guardando il sito web di symfony, ed è anche specificato nella documentazione Capitolo percorsi, se vado a www.example.com e tutti altri percorsi senza specificare il luogo in cui usa le impostazioni internazionali predefinite senza modificare l'URL! – Lughino

+1

Abbastanza giusto. Questo metodo rende così la locale è sempre nell'URL, che personalmente non mi dispiace. Soprattutto che mantiene le annotazioni semplici e totalmente disaccoppiate dalle impostazioni locali. –

+0

Ma non risolve il problema posto .. Soluzione comunque ottimale! – Lughino

2

C'è la mia soluzione, rende questo processo più veloce!

Controller:

/** 
* @Route("/change/locale/{current}/{locale}/", name="locale_change") 
*/ 
public function setLocaleAction($current, $locale) 
{ 
    $this->get('request')->setLocale($locale); 
    $referer = str_replace($current,$locale,$this->getRequest()->headers->get('referer')); 

    return $this->redirect($referer); 
} 

Twig:

<li {% if app.request.locale == language.locale %} class="selected" {% endif %}> 
    <a href="{{ path('locale_change', { 'current' : app.request.locale, 'locale' : language.locale }) }}"> {{ language.locale }}</a> 
</li> 
18

Se qualcuno è interessato a, sono riuscito a mettere un prefisso sul mio routing.yml senza utilizzare altri pacchetti.

Così ora, thoses URL lavoro:

www.example.com/ 
www.example.com//home/ 
www.example.com/fr/home/ 
www.example.com/en/home/ 

Modifica il tuo app/config/routing.yml:

ex_example: 
    resource: "@ExExampleBundle/Resources/config/routing.yml" 
    prefix: /{_locale} 
    requirements: 
     _locale: |fr|en # put a pipe "|" first 

Poi, in te app/config/parameters.yml, è necessario impostare un locale

parameters: 
    locale: en 

Con questo, le persone possono accedere al tuo sito web senza entrare in una specifica localizzazione.

+7

Purtroppo questo non funziona: www.example.com//home/ url dovrebbe essere www.example.com/home/ e non funzionerebbe in questo modo. – amiroff

1

Ho una soluzione completa a questo che ho scoperto dopo alcune ricerche. La mia soluzione presuppone che tu voglia che ogni percorso abbia una localizzazione di fronte ad essa, anche il login. Questo è modificato per supportare Symfony 3, ma credo che funzionerà ancora in 2.

Questa versione presuppone anche che si desideri utilizzare le impostazioni internazionali del browser come impostazioni internazionali predefinite se si spostano su un percorso come/admin, ma se esse vai su/en/admin saprà di usare en locale. Questo è il caso per esempio # 2 sotto.

Così, per esempio:

1. User Navigates To -> "/"   -> (redirects) -> "/en/" 
2. User Navigates To -> "/admin" -> (redirects) -> "/en/admin" 
3. User Navigates To -> "/en/admin" -> (no redirects) -> "/en/admin" 

In tutti gli scenari del locale verrà impostato correttamente come volete che sia per l'uso in tutto il programma.

È possibile visualizzare la soluzione completa di sotto del quale comprende come farlo funzionare con login e la sicurezza, in caso contrario la versione corta probabilmente funzionerà per voi:

versione completa

Symfony 3 Redirect All Routes To Current Locale Version

Versione corta

Per far sì che il caso n. 2 nei miei esempi sia possibile bisogno di farlo utilizzando un httpKernal listner

LocaleRewriteListener.php

<?php 
//src/AppBundle/EventListener/LocaleRewriteListener.php 
namespace AppBundle\EventListener; 

use Symfony\Component\HttpFoundation\RedirectResponse; 

use Symfony\Component\Routing\RouterInterface; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 
use Symfony\Component\HttpKernel\KernelEvents; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\HttpFoundation\Session\Session; 
use Symfony\Component\Routing\RouteCollection; 

class LocaleRewriteListener implements EventSubscriberInterface 
{ 
    /** 
    * @var Symfony\Component\Routing\RouterInterface 
    */ 
    private $router; 

    /** 
    * @var routeCollection \Symfony\Component\Routing\RouteCollection 
    */ 
    private $routeCollection; 

    /** 
    * @var string 
    */ 
    private $defaultLocale; 

    /** 
    * @var array 
    */ 
    private $supportedLocales; 

    /** 
    * @var string 
    */ 
    private $localeRouteParam; 

    public function __construct(RouterInterface $router, $defaultLocale = 'en', array $supportedLocales = array('en'), $localeRouteParam = '_locale') 
    { 
     $this->router = $router; 
     $this->routeCollection = $router->getRouteCollection(); 
     $this->defaultLocale = $defaultLocale; 
     $this->supportedLocales = $supportedLocales; 
     $this->localeRouteParam = $localeRouteParam; 
    } 

    public function isLocaleSupported($locale) 
    { 
     return in_array($locale, $this->supportedLocales); 
    } 

    public function onKernelRequest(GetResponseEvent $event) 
    { 
     //GOAL: 
     // Redirect all incoming requests to their /locale/route equivlent as long as the route will exists when we do so. 
     // Do nothing if it already has /locale/ in the route to prevent redirect loops 

     $request = $event->getRequest(); 
     $path = $request->getPathInfo(); 

     $route_exists = false; //by default assume route does not exist. 

     foreach($this->routeCollection as $routeObject){ 
      $routePath = $routeObject->getPath(); 
      if($routePath == "/{_locale}".$path){ 
       $route_exists = true; 
       break; 
      } 
     } 

     //If the route does indeed exist then lets redirect there. 
     if($route_exists == true){ 
      //Get the locale from the users browser. 
      $locale = $request->getPreferredLanguage(); 

      //If no locale from browser or locale not in list of known locales supported then set to defaultLocale set in config.yml 
      if($locale=="" || $this->isLocaleSupported($locale)==false){ 
       $locale = $request->getDefaultLocale(); 
      } 

      $event->setResponse(new RedirectResponse("/".$locale.$path)); 
     } 

     //Otherwise do nothing and continue on~ 
    } 

    public static function getSubscribedEvents() 
    { 
     return array(
      // must be registered before the default Locale listener 
      KernelEvents::REQUEST => array(array('onKernelRequest', 17)), 
     ); 
    } 
} 

Per capire come che sta lavorando cercare l'interfaccia utente evento sulla documentazione di symfony.

Per attivare The Listener è necessario configurarlo in services.yml

services.yml

# Learn more about services, parameters and containers at 
# http://symfony.com/doc/current/book/service_container.html 
parameters: 
# parameter_name: value 

services: 
# service_name: 
#  class: AppBundle\Directory\ClassName 
#  arguments: ["@another_service_name", "plain_value", "%parameter_name%"] 
    appBundle.eventListeners.localeRewriteListener: 
      class: AppBundle\EventListener\LocaleRewriteListener 
      arguments: ["@router", "%kernel.default_locale%", "%locale_supported%"] 
      tags: 
      - { name: kernel.event_subscriber } 

Infine, questa si riferisce alle variabili che devono essere definito nel config.yml

config.yml

# Put parameters here that don't need to change on each machine where the app is deployed 
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 
parameters: 
    locale: en 
    app.locales: en|es|zh 
    locale_supported: ['en','es','zh'] 

Infine, è necessario assicurarsi che tutti i percorsi inizino con/{locale} per ora. Un esempio di questo è al di sotto della mia controller.php predefinita

<?php 

namespace AppBundle\Controller; 

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Request; 

/** 
* @Route("/{_locale}", requirements={"_locale" = "%app.locales%"}) 
*/ 
class DefaultController extends Controller 
{ 

    /** 
    * @Route("/", name="home") 
    */ 
    public function indexAction(Request $request) 
    { 
     $translated = $this->get('translator')->trans('Symfony is great'); 

     // replace this example code with whatever you need 
     return $this->render('default/index.html.twig', [ 
      'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'), 
      'translated' => $translated 
     ]); 
    } 

    /** 
    * @Route("/admin", name="admin") 
    */ 
    public function adminAction(Request $request) 
    { 
     $translated = $this->get('translator')->trans('Symfony is great'); 

     // replace this example code with whatever you need 
     return $this->render('default/index.html.twig', [ 
      'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'), 
      'translated' => $translated 
     ]); 
    } 
} 
?> 

Nota i requisiti requirements={"_locale" = "%app.locales%"}, questo fa riferimento il file config.yml modo da avere solo definire tali requisiti in un unico luogo per tutte le rotte.

Spero che questo aiuti qualcuno :)

2

La soluzione di The Joseph Astrahan di LocalRewriteListener funziona ad eccezione di percorso con params a causa della $routePath == "/{_locale}".$path)

Es: $routePath = "/{_locale}/my/route/{foo}" è diverso di $path = "/{_locale}/my/route/bar"

ho dovuto usare UrlMatcher (link a Symfony 2.7 api doc) per abbinare il percorso reale con l'url.

Io cambio il isLocaleSupportato per l'utilizzo del codice locale del browser (es: fr -> fr_FR). Uso le impostazioni internazionali del browser come chiave e le impostazioni internazionali del percorso come valore. Ho un array come questo array(['fr_FR'] => ['fr'], ['en_GB'] => 'en'...) (vedi file i parametri di seguito per maggiori informazioni)

le modifiche:

  • Controllare se il locale dato in richiesta è suported. In caso contrario, utilizzare le impostazioni internazionali predefinite.
  • Prova ad abbinare il percorso con la raccolta del percorso dell'app.Se non fai niente (l'app lancia un 404 se la rotta non esiste). Se sì, reindirizzare con le impostazioni internazionali corrette nel parametro route.

Ecco il mio codice. Funziona per qualsiasi percorso con o senza parametri. Questo aggiunge la locale solo quando {_local} è impostato sulla rotta.

file di instradamento (nel mio caso, quello in app/config)

app: 
    resource: "@AppBundle/Resources/config/routing.yml" 
    prefix: /{_locale}/ 
    requirements: 
     _locale: '%app.locales%' 
    defaults: { _locale: %locale%} 

Il parametro in app// file di configurazione parameters.yml

locale: fr 
app.locales: fr|gb|it|es 
locale_supported: 
    fr_FR: fr 
    en_GB: gb 
    it_IT: it 
    es_ES: es 

servizi. yml

app.eventListeners.localeRewriteListener: 
    class: AppBundle\EventListener\LocaleRewriteListener 
    arguments: ["@router", "%kernel.default_locale%", "%locale_supported%"] 
    tags: 
     - { name: kernel.event_subscriber } 

LocaleRewriteListener.php

<?php 
namespace AppBundle\EventListener; 

use Symfony\Component\HttpFoundation\RedirectResponse; 

use Symfony\Component\Routing\RouterInterface; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 
use Symfony\Component\HttpKernel\KernelEvents; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\HttpFoundation\Session\Session; 
use Symfony\Component\Routing\RouteCollection; 
use Symfony\Component\Routing\Matcher\UrlMatcher; 
use Symfony\Component\Routing\RequestContext; 

class LocaleRewriteListener implements EventSubscriberInterface 
{ 
    /** 
    * @var Symfony\Component\Routing\RouterInterface 
    */ 
    private $router; 

    /** 
    * @var routeCollection \Symfony\Component\Routing\RouteCollection 
    */ 
    private $routeCollection; 

    /** 
    * @var urlMatcher \Symfony\Component\Routing\Matcher\UrlMatcher; 
    */ 
    private $urlMatcher; 

    /** 
    * @var string 
    */ 
    private $defaultLocale; 

    /** 
    * @var array 
    */ 
    private $supportedLocales; 

    /** 
    * @var string 
    */ 
    private $localeRouteParam; 

    public function __construct(RouterInterface $router, $defaultLocale = 'fr', array $supportedLocales, $localeRouteParam = '_locale') 
    { 
     $this->router = $router; 
     $this->routeCollection = $router->getRouteCollection(); 
     $this->defaultLocale = $defaultLocale; 
     $this->supportedLocales = $supportedLocales; 
     $this->localeRouteParam = $localeRouteParam; 
     $context = new RequestContext("/"); 
     $this->matcher = new UrlMatcher($this->routeCollection, $context); 
    } 

    public function isLocaleSupported($locale) 
    { 
     return array_key_exists($locale, $this->supportedLocales); 
    } 

    public function onKernelRequest(GetResponseEvent $event) 
    { 
     //GOAL: 
     // Redirect all incoming requests to their /locale/route equivalent when exists. 
     // Do nothing if it already has /locale/ in the route to prevent redirect loops 
     // Do nothing if the route requested has no locale param 

     $request = $event->getRequest(); 
     $baseUrl = $request->getBaseUrl(); 
     $path = $request->getPathInfo(); 

     //Get the locale from the users browser. 
     $locale = $request->getPreferredLanguage(); 

     if ($this->isLocaleSupported($locale)) { 
      $locale = $this->supportedLocales[$locale]; 
     } else if ($locale == ""){ 
      $locale = $request->getDefaultLocale(); 
     } 

     $pathLocale = "/".$locale.$path; 

     //We have to catch the ResourceNotFoundException 
     try { 
      //Try to match the path with the local prefix 
      $this->matcher->match($pathLocale); 
      $event->setResponse(new RedirectResponse($baseUrl.$pathLocale)); 
     } catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { 

     } catch (\Symfony\Component\Routing\Exception\MethodNotAllowedException $e) { 

     } 
    } 

    public static function getSubscribedEvents() 
    { 
     return array(
      // must be registered before the default Locale listener 
      KernelEvents::REQUEST => array(array('onKernelRequest', 17)), 
     ); 
    } 
} 
0

Forse ho risolto questo in un modo abbastanza semplice:

example: 
    path:  '/{_locale}{_S}example' 
    defaults: { _controller: 'AppBundle:Example:index' , _locale="de" , _S: "/" } 
    requirements: 
     _S: "/?" 
     _locale: '|de|en|fr' 

incuriosisce il giudizio della critica ... Auguri, Greg

2

Symfony3

app: 
resource: "@AppBundle/Controller/" 
type:  annotation 
prefix: /{_locale} 
requirements: 
    _locale: en|bg| # put a pipe "|" last 
+0

Ciò consentirà solo 'domain.com // test' ma non' domain.com/test'. –

Problemi correlati