我正在开发一个symfony应用程序,我的目标是无论用户在哪个页面上都会导航到页面的语言环境版本.
例如,如果用户导航到"/"主页,它将重定向到"/ en /"
如果它们位于"/ admin"页面,它将重定向到"/ en/admin",以便_locale
从路径设置属性.
此外,如果他们从用户浏览器访问/ admin,则需要确定区域设置,因为没有确定区域设置,因此它知道要重定向到哪个页面.
目前我的默认控制器如下所示,因为我正在测试.我正在使用开发模式和分析器来测试翻译是否正常工作.
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
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 # e.g. 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%"
是引用config参数的部分.
我当前的方法的问题是/ admin例如没有将用户重定向到/ {browsers locale}/admin,这将是保持一切有条理的更优雅的解决方案......但至少路由工作.仍在寻找更好的解决方案.
********更新
我想我可能已经在这里找到答案作为底部答案(为所有路线添加语言环境和要求 - Symfony2),Athlan的答案.只是不确定如何在symfony 3中实现这一点,因为他的指示对我来说不够明确.
我认为这篇文章也可能有所帮助(http://symfony.com/doc/current/components/event_dispatcher/introduction.html)
经过12个小时的研究,我终于找到了一个可以接受的解决方案.如果您可以提高效率,请发布此解决方案的修订版本.
有些事情需要注意,我的解决方案特别适合我的需要.它的作用是强制任何URL转到本地化版本(如果存在).
这需要在创建路径时遵循一些约定.
DefaultController.php
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上创建一个listen来拦截请求,然后再将它们发送到路由器来呈现它们.
LocaleRewriteListener.php
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的值:en.
app.locales很适合使用requirements命令,因为它会为任何不匹配的语言环境导致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错误.我喜欢用这个解决方案(.*?)
与[a-z]{2}
柜面你想使用EN_US类型的语言环境.
SecurityController.php
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}.我喜欢这样,所以我可以为不同的语言环境提供自定义登录.要时刻铭记在心.唯一不需要语言环境的路由是logout工作得很好,因为它实际上只是安全系统的拦截路由.另请注意,它使用了config.yml中设置的要求,因此您只需在一个位置为项目中的所有路径编辑它.
希望这可以帮助有人试图做我正在做的事情!
注意::为了轻松测试,我使用谷歌浏览器的"快速语言切换器"扩展程序,它可以更改所有请求的接受语言标题.
我没有足够的声誉来为正确的解决方案添加评论.所以我正在添加一个新答案
您可以在app/config/routing.yml中添加"prefix:/ {_ locale}",如下所示:
app: resource: "@AppBundle/Controller/" type: annotation prefix: /{_locale}
因此,您无需将其添加到每个操作的每个路径.用于以下步骤.非常感谢你的完美.