Локаль Symfony2 по умолчанию в маршрутизации

У меня проблема с маршрутизацией и интернационализацией моего сайта, созданного с помощью Symfony2.
Если я определю маршруты в файле routing.yml, например:

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

Он отлично работает с такими URL-адресами, как:

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

Но не работает с

mysite.com/example 

Может быть, это необязательные заполнители разрешены только в конце URL-адреса?
Если да, каким может быть возможное решение для отображения URL-адреса типа:

mysite.com/example  

на языке по умолчанию или перенаправления пользователя на:

mysite.com/defaultlanguage/example 

, когда он посещает:

mysite.com/example. ? 

Я пытаюсь понять это, но пока безуспешно.

Спасибо.

27
задан Ka. 26 October 2011 в 13:41
поделиться

4 ответа

Решение Джозефа Астрахана о LocalRewriteListener работает за исключением маршрута с параметрами из-за $routePath == "/{_locale}".$path)

Пример: $routePath = "/{_locale}/my/route/{foo}" отличается от $path = "/{_locale}/my/route/bar"

, который мне пришлось использовать UrlMatcher (ссылка на Symfony 2.7 api doc) для сопоставления фактического маршрута с URL.

Я изменяю isLocaleSupported для использования локального кода браузера (например: fr -> fr_FR). Я использую локаль браузера в качестве ключа и локаль маршрута в качестве значения. У меня есть такой массив array(['fr_FR'] => ['fr'], ['en_GB'] => 'en'...) (см. Файл параметров ниже для получения дополнительной информации)

Изменения:

  • Проверьте, поддерживается ли локальное значение, указанное в запросе. Если нет, используйте локаль по умолчанию.
  • Попробуйте сопоставить путь с коллекцией маршрутов приложения. Если не делать ничего (приложение выдает 404, если маршрут не существует). Если да, перенаправьте с правильным языком в параметре маршрута.

Вот мой код. Работает для любого маршрута с или без параметров. Это добавляет языковой стандарт, только если {_local} установлен на маршруте.

Файл маршрутизации (в моем случае это файл в app / config)

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

Параметр в файле app / config / parameters.yml

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

services.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)),
        );
    }
}
4
ответ дан 28 November 2019 в 05:11
поделиться

Если кому-то это интересно, мне удалось поставить префикс на моем routing.yml без использования других пакетов.

Так что теперь, эти URL работают:

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

Отредактируйте app/config/routing.yml:

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

Затем, в вас app/config/parameters.yml, вы должны установить язык

parameters:
    locale: en

Благодаря этому люди могут получить доступ к вашему веб-сайту без ввода конкретной локали.

20
ответ дан 28 November 2019 в 05:11
поделиться

Symfony3

app:
resource: "@AppBundle/Controller/"
type:     annotation
prefix: /{_locale}
requirements:
    _locale: en|bg|  # put a pipe "|" last
3
ответ дан 28 November 2019 в 05:11
поделиться

У меня есть полное решение этого, которое я обнаружил после некоторых исследований. Мое решение предполагает, что вы хотите, чтобы у каждого маршрута была локаль перед ним, даже вход в систему. Это модифицировано для поддержки Symfony 3, но я верю, что оно все еще будет работать в 2.

В этой версии также предполагается, что вы хотите использовать локаль браузера в качестве локали по умолчанию, если они идут по маршруту, например / admin, но если они переходят в / en / admin, он будет знать, что нужно использовать en locale. Это тот случай, например, № 2 ниже.

Так, например:

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

Во всех сценариях языковой стандарт будет настроен правильно, как вы хотите, чтобы он использовался во всей вашей программе.

Ниже вы можете посмотреть полное решение, которое включает в себя, как заставить его работать с логином и безопасностью, иначе короткая версия, вероятно, будет работать для вас:

Полная версия

Symfony 3 перенаправляет все маршруты в текущую версию локали

Короткая версия

Сделать так, чтобы в моем примере был возможен случай № 2 вам нужно сделать это с помощью 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)),
        );
    }
}

Чтобы понять, как это работает, посмотрите интерфейс подписчика событий в документации Symfony.

Чтобы активировать список, вам нужно настроить его в 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 }

Наконец, это относится к переменным, которые необходимо быть определенным в вашем 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']

Наконец, вам нужно убедиться, что все ваши маршруты начинаются с / {locale}, пока , Пример этого приведен ниже в моем файле controller.php по умолчанию

<?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
        ]);
    }
}
?>

Обратите внимание на требования requirements={"_locale" = "%app.locales%"}, это ссылка на файл config.yml, поэтому вам нужно определить эти требования только в одном месте для всех маршрутов. .

Надеюсь, это кому-нибудь поможет:)

2
ответ дан 28 November 2019 в 05:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: