Intereting Posts
htaccess включает флаговые флаги, если определенный IP-адрес Преобразование url в нижний регистр с использованием htaccess, кроме строки запроса Magento Order Order Invoice с обновлением количества товаров с использованием API, который не работает должным образом Apache – обработка TCP-соединений, но не HTTP-запросов FTP Uploader с использованием php Значения в UTF-8, закодированные как NULL в JSON Регулярное выражение для соответствия переносов слов fsock: невозможно найти перенос сокетов «http» Наследование класса в PHP 5.2: переопределение статической переменной в классе расширения? Вызов свойства переменной напрямую vs getter / seters – OOP Design cakephp в одной форме, несколько моделей, не отображая сообщения проверки модели Может ли SimpleXML загружать только часть XML? Как создать сообщение в WordPress Programmatically Как создать файл xlsx без использования какой-либо библиотеки excel PHP Получение атрибута продукта из другого атрибута продукта?

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

Я работаю над приложением symfony, где моя цель – независимо от того, на какой странице находится пользователь, будет перемещаться по языковой версии страницы.

Например, если пользователь переходит к домашней странице «/», он перенаправляет на «/ en /»,

Если они находятся на странице «/ admin», она перенаправляется на «/ en / admin» , таким образом, что свойство _locale задается с маршрута.

Также он должен определить локаль, если они посещают / admin из браузера пользователей, поскольку не было определено локаль, чтобы он знал, к какой странице нужно перенаправить.

В настоящее время мой контроллер по умолчанию выглядит ниже, так как я тестирую. Я использую режим dev и профилировщик, чтобы проверить, что переводы работают корректно.

введите описание изображения здесь

 <?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; class DefaultController extends Controller { /** * @Route("/", name="homepage") * @Route("/{_locale}/", name="homepage_locale") */ 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 ]); } } 

Этот текущий метод будет удерживать пользователя в «/», если они перемещаются туда, но я хочу перенаправить его на «/ en /». Это должно работать и для других страниц, таких как / admin или / somepath / pathagain / article1 (/ en / admin, / en / somepath / pathagain / article1)

Как мне это сделать?

Ссылки, которые я читал, не помогли:

Symfony2 Использовать локаль по умолчанию в маршрутизации (один URL для одного языка)

Стандартная локализация Symfony2 в маршрутизации

::Обновить::

Я не решил свою проблему, но я пришел близко, а узнал несколько трюков, чтобы быть более эффективными.

DefaultController.php

 <?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; class DefaultController extends Controller { /** * @Route("/", name="home", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) * @Route("/{_locale}/", name="home_locale", requirements={"_locale" = "%app.locales%"}) */ 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", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) * @Route("/{_locale}/admin", name="admin_locale", requirements={"_locale" = "%app.locales%"}) */ 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 ]); } } ?> 

config.yml

 imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.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 framework: #esi: ~ translator: { fallbacks: ["%locale%"] } secret: "%secret%" router: resource: "%kernel.root_dir%/config/routing.yml" strict_requirements: ~ form: ~ csrf_protection: ~ validation: { enable_annotations: true } #serializer: { enable_annotations: true } templating: engines: ['twig'] #assets_version: SomeVersionScheme default_locale: "%locale%" trusted_hosts: ~ trusted_proxies: ~ session: # handler_id set to null will use default session handler from php.ini handler_id: ~ save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%" fragments: ~ http_method_override: true assets: ~ # Twig Configuration twig: debug: "%kernel.debug%" strict_variables: "%kernel.debug%" # Doctrine Configuration doctrine: dbal: driver: pdo_mysql host: "%database_host%" port: "%database_port%" dbname: "%database_name%" user: "%database_user%" password: "%database_password%" charset: UTF8 # if using pdo_sqlite as your database driver: # 1. add the path in parameters.yml # eg database_path: "%kernel.root_dir%/data/data.db3" # 2. Uncomment database_path in parameters.yml.dist # 3. Uncomment next line: # path: "%database_path%" orm: auto_generate_proxy_classes: "%kernel.debug%" naming_strategy: doctrine.orm.naming_strategy.underscore auto_mapping: true # Swiftmailer Configuration swiftmailer: transport: "%mailer_transport%" host: "%mailer_host%" username: "%mailer_user%" password: "%mailer_password%" spool: { type: memory } 

Обратите внимание, что в параметрах значение app.locales: en|es|zh . Теперь это значение, которое я могу ссылаться всякий раз, когда я создаю свои маршруты, если я планирую поддерживать больше локалей в будущем, которые я делаю. Эти маршруты – английский, испанский, китайский в этом порядке для тех, кто любопытен. В DefaultController в аннотации "%app.locales%" – это часть, которая ссылается на параметр конфигурации.

Проблема с моим текущим методом заключается в / admin, например, не перенаправляет пользователя в / {браузеры locale} / admin, что было бы более элегантным решением, чтобы все было организовано … но по крайней мере маршруты работают. Все еще ищет лучшее решение.

****Обновить****

Я думаю, что, возможно, я нашел ответ здесь как нижний ответ ( добавьте язык и требования ко всем маршрутам – Symfony2 ), ответ Athlan. Просто не уверен, как реализовать это в symfony 3, поскольку его указания были недостаточно ясны для меня.

Я думаю, что эта статья также может помочь ( http://symfony.com/doc/current/components/event_dispatcher/introduction.html )

    После 12 часов изучения этого я наконец нашел приемлемое решение. Пожалуйста, опубликуйте исправленные версии этого решения, если вы можете сделать его более эффективным.

    Некоторые вещи, которые следует учитывать, мое решение особенно важно для моей потребности. Что он делает, так это заставить любой URL перейти в локализованную версию, если она существует.

    Это требует соблюдения некоторых правил при создании маршрутов.

    DefaultController.php

     <?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; class DefaultController extends Controller { /** * @Route("/{_locale}/", name="home_locale", requirements={"_locale" = "%app.locales%"}) */ 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("/{_locale}/admin", name="admin_locale", requirements={"_locale" = "%app.locales%"}) */ 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 ]); } } ?> 

    Обратите внимание, что оба маршрута всегда начинаются с «/ {_ locale} /». Для этого для работы каждый маршрут в вашем проекте должен иметь это. После этого вы просто указываете название реального маршрута. Для меня я был в порядке с этим сценарием. Вы можете легко изменить свое решение в соответствии с вашими потребностями.

    Первый шаг – создать прослушивание на httpKernal для перехвата запросов до их перехода на маршрутизаторы для их отображения.

    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)), ); } } 

    Наконец, вы устанавливаете 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

     parameters: locale: en app.locales: en|es|zh locale_supported: ['en','es','zh'] 

    Я хотел, чтобы там было только одно место, где вы определяете локали, но мне пришлось сделать 2 … но по крайней мере они находятся в том же месте, которое легко изменить.

    app.locales используется в контроллере по умолчанию (requirements={"_locale" = "%app.locales%"}) а locale_supported используется в LocaleRewriteListener. Если он обнаруживает локаль, которая отсутствует в списке, она будет отступать от локали по умолчанию, которая в этом случае является значением locale: ru.

    app.locales хорош с командой требований, потому что это вызовет 404 для любых локалей, которые не совпадают.

    Если вы используете формы и имеете логин, вам нужно будет сделать следующее для вашего security.yml

    security.yml

     # To get started with security, check out the documentation: # http://symfony.com/doc/current/book/security.html security: encoders: Symfony\Component\Security\Core\User\User: algorithm: bcrypt cost: 12 AppBundle\Entity\User: algorithm: bcrypt cost: 12 role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] providers: # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers database: entity: { class: AppBundle:User } #property: username # if you're using multiple entity managers # manager_name: customer firewalls: # disables authentication for assets and the profiler, adapt it according to your needs dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: pattern: ^/ anonymous: true form_login: check_path: login_check login_path: login_route provider: database csrf_token_generator: security.csrf.token_manager remember_me: secret: '%secret%' lifetime: 604800 # 1 week in seconds path: / httponly: false #httponly false does make this vulnerable in XSS attack, but I will make sure that is not possible. logout: path: /logout target: / access_control: # require ROLE_ADMIN for /admin* #- { path: ^/login, roles: ROLE_ADMIN } - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/(.*?)/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/, roles: ROLE_USER } 

    Важно отметить, что (.*?)/login будет аутентифицироваться анонимно, чтобы ваши пользователи все равно могли войти в систему. Это означает, что такие маршруты, как..dogdoghere / login, могут запускаться, но требования, которые я покажу вам в ближайшее время на маршрутах входа, предотвращают это и будут вызывать 404 ошибки. Мне нравится это решение с (.*?) сравнению с [az]{2} вы хотите использовать локали типа en_US.

    SecurityController.php

     <?php // src/AppBundle/Controller/SecurityController.php namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class SecurityController extends Controller { /** * @Route("{_locale}/login", name="login_route", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) */ public function loginAction(Request $request) { $authenticationUtils = $this->get('security.authentication_utils'); // get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); // last username entered by the user $lastUsername = $authenticationUtils->getLastUsername(); return $this->render( 'security/login.html.twig', array( // last username entered by the user 'last_username' => $lastUsername, 'error' => $error, ) ); } /** * @Route("/{_locale}/login_check", name="login_check", defaults={"_locale"="en"}, requirements={"_locale" = "%app.locales%"}) */ public function loginCheckAction() { // this controller will not be executed, // as the route is handled by the Security system } /** * @Route("/logout", name="logout") */ public function logoutAction() { } } ?> 

    Обратите внимание, что даже эти пути используют {_locale} впереди. Мне это нравится, поэтому я могу предоставить пользовательские логины для разных локалей. Просто имейте это в виду. Единственный маршрут, который не нуждается в локали, – это выход из системы, который отлично работает, поскольку он действительно является только маршрутом перехвата для системы безопасности. Также обратите внимание, что он использует требования, установленные из config.yml, поэтому вам нужно только отредактировать его в одном месте для всех маршрутов в ваших проектах.

    Надеюсь, это поможет кому-то сделать то, что я делаю!

    ПРИМЕЧАНИЕ. Чтобы легко проверить это, я использую расширение «Быстрый языковой коммутатор» для Google Chrome, который изменяет заголовок принимающего языка для всех запросов.

    У меня недостаточно репутации, чтобы добавить комментарий к правильному решению. Поэтому я добавляю новый ответ

    Вы можете добавить «префикс: / {_ locale}» в app / config / routing.yml следующим образом:

     app: resource: "@AppBundle/Controller/" type: annotation prefix: /{_locale} 

    Поэтому вам не нужно добавлять его к каждому маршруту для каждого действия. Для следующих шагов. Большое спасибо, что это прекрасно.