CakePHP 中的本地化路由:如何重定向到默认语言

Posted

技术标签:

【中文标题】CakePHP 中的本地化路由:如何重定向到默认语言【英文标题】:Localized routing in CakePHP: How to redirect to default language 【发布时间】:2015-01-31 01:53:12 【问题描述】:

自 2012 年以来,this post 似乎是关于如何在 Cakephp 中进行本地化路由的最权威资源(代码复制如下)。

它很好用,但有一个例外:它不会重定向缺少语言前缀的请求。例如,http://example.com 将显示与http://example.com/eng 相同的内容(如果英语是默认语言)。同样,如果不是主页:http://example.com/foo/bar/ => http://example.com/eng/foo/bar。 cmets中提到了这个问题,但没有确定的解决方案,这正是我正在寻找的。​​p>

代码。

// Step 1: app/Config/routes.php
Router::connect('/:language/:controller/:action/*',
                  array(),
                  array('language' => 'eng|fra'));

Router::connect('/:language/:controller',
                  array('action' => 'index'),
                  array('language' => 'eng|fra'));  

Router::connect('/:language',
                  array('controller' => 'welcome', 'action' => 'index'),
                  array('language' => 'eng|fra'));

//Step 2: app/Config/core.php
Configure::write('Config.language', 'eng');

//Step 3: create app/View/Helper/MyhtmlHelper.php
App::uses('HtmlHelper', 'View/Helper');
class MyHtmlHelper extends HtmlHelper 
    public function url($url = null, $full = false) 
        if(!isset($url['language']) && isset($this->params['language'])) 
          $url['language'] = $this->params['language'];
        
        return parent::url($url, $full);
   


//Step 4: app/Controller/AppController.php
class AppController extends Controller 
    public $components = array('Cookie','Session');
    //set an alias for the newly created helper: Html<->MyHtml
    public $helpers = array('Html' => array('className' => 'MyHtml'));

    public function beforeFilter() 
          $this->_setLanguage();
        

    private function _setLanguage() 
    //if the cookie was previously set, and Config.language has not been set
    //write the Config.language with the value from the Cookie
        if ($this->Cookie->read('lang') && !$this->Session->check('Config.language')) 
            $this->Session->write('Config.language', $this->Cookie->read('lang'));
         
        //if the user clicked the language URL 
        else if (   isset($this->params['language']) && 
        ($this->params['language'] !=  $this->Session->read('Config.language'))
                ) 
            //then update the value in Session and the one in Cookie
            $this->Session->write('Config.language', $this->params['language']);
            $this->Cookie->write('lang', $this->params['language'], false, '20 days');
        
    

    //override redirect
    public function redirect( $url, $status = NULL, $exit = true ) 
        if (!isset($url['language']) && $this->Session->check('Config.language')) 
            $url['language'] = $this->Session->read('Config.language');
        
        parent::redirect($url,$status,$exit);
    


//add the links to the languages:
//Step 5: app/View/...
echo $this->Html->link('English', array('language'=>'eng')); 
echo $this->Html->link('Français', array('language'=>'fra')); 

更新 1

我尝试了 user221931 的建议,但似乎不起作用。这是我添加到路线中的内容:

/* Add default language param */
Router::redirect('/:controller/:action/*', 
                    array('language' => 'fra'), 
                    array('persist' => false) );
Router::redirect('/:controller/', 
                    array('language' => 'fra'), 
                    array('persist' => false) );
Router::redirect('/', 
                    array('controller'=>'pages', 'action'=>'display', 'language' => 'fra', 'home'), 
                    array('persist' => false) );

好像没什么效果。 重定向以下 URL:http:example.com/、http://example.com/controller/、http://example.com/controller/action/

更新 2 根据要求,这是我的完整路线文件:

<?php
/**
 * Routes configuration
 *
 * In this file, you set up routes to your controllers and their actions.
 * Routes are very important mechanism that allows you to freely connect
 * different URLs to chosen controllers and their actions (functions).
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       app.Config
 * @since         CakePHP(tm) v 0.2.9
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */

#http://book.cakephp.org/2.0/en/views/json-and-xml-views.html
Router::parseExtensions('json');

/**
 * Here, we are connecting '/' (base path) to controller called 'Pages',
 * its action called 'display', and we pass a param to select the view file
 * to use (in this case, /app/View/Pages/home.ctp)...
 */
    Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
/**
 * ...and connect the rest of 'Pages' controller's URLs.
 */
    Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));


/**
 * LOCALIZED URLs
 * See: http://colorblindprogramming.com/multiple-languages-in-a-cakephp-2-application-in-5-steps
 */
Router::connect('/:language/:controller/:action/*',
                       array(),
                       array('language' => 'eng|fra'));

Router::connect('/:language/:controller',
                   array('action' => 'index'),
                   array('language' => 'eng|fra')); 

Router::connect('/:language',
                   array('controller' => 'pages', 'action' => 'display', 'home'),
                   array('language' => 'eng|fra'));

# prevent routing conflicts with plugins...
# http://www.omaroid.com/cakephp-locale-language-routing/
// make an array of loaded plugins
$loaded = CakePlugin::loaded();
array_walk($loaded, function(&$item,$key)
    $item = Inflector::underscore($item);
);
$loaded = implode('|', $loaded);

Router::connect('/:language/:plugin/:controller/:action/*', 
                    array(), 
                    array('language' => 'eng|fra','plugin' => "($loaded)"));

/* HIDE /index */
//Router::connect('/:controller/', array('action'=>'index') );
Router::connect('/:language/:controller/', array('action'=>'index') );

/**
 * Load all plugin routes. See the CakePlugin documentation on
 * how to customize the loading of plugin routes.
 */
    CakePlugin::routes();

/**
 * Load the CakePHP default routes. Only remove this if you do not want to use
 * the built-in default routes.
 */
    require CAKE . 'Config' . DS . 'routes.php';

【问题讨论】:

【参考方案1】:

这在 AppController.php 中非常简单:

const DEFAULT_LANGUAGE = 'eng';
public function beforeFilter() 
    parent::beforeFilter();
    if (empty($this->params['language'])) 
        $this->redirect(array('language' => self::DEFAULT_LANGUAGE));
    

【讨论】:

感谢您的帮助。它似乎适用于普通的控制器/操作 URL (http://example.com/con/act/),但是当我尝试访问主页 (http://example.com) 时它会中断。它重定向到http://example.com/pages/language:fra。我认为这与主页和/或页面的默认 Router::connects 有关。 我删除了Router::connect('/', array('controller' =&gt; 'pages', 'action' =&gt; 'display', 'home'));,现在主页似乎可以正常工作了。剩下的就是我的语言切换按钮仍然指向不正确的 URL(上图),但这只是页面控制器控制的视图上的问题。知道为什么吗? 似乎一定是 MyHtmlHelper 的问题? 其实你不需要MyHtmlHelper。您可以在 AppHelper 中覆盖 url()。如果 HtmlHelper 生成像 example.com/pages/language:fra 这样的 URL,这意味着问题与 routes.php 中缺少或未应用的路由有关。 没有覆盖 url() MyHtmlHelper 在做什么?在 AppHelper 中这样做有优势吗?另外,你对哪条路线会导致这种情况有什么建议吗?【参考方案2】:

您添加了非常具体的语言路由(如果 url 路径的第一部分是 engfra),但没有删除默认路由(Router::connect('/:controller/:action/*'),因此如果找不到这两个词,则更通用/:controller/:action/* 的路由将匹配。

你可以做以下三件事中的任何一件:

    删除默认路由,这将使/:controller/:action/*/:controller/* 无法路由,并且没有人将能够形成可以访问其中没有语言的内容的url。 添加硬编码的routes 重定向到eng/users -> /eng/users/posts/ -> /eng/posts 等)。这似乎需要做很多工作,但是虽然模式匹配对于开发来说是可以的,但硬编码可以显着提高路由的速度,因此您可能需要在未来的某个时候采用这种方式。它的另一个优点是您可以添加一个persist 规则,这将使浏览器记住重定向并且不再访问服务器。 在您的AppController 中添加代码,以检查路径中是否包含语言,如果没有,则尝试从浏览器设置中猜测用户语言,以便重定向到最合适的网址或默认网址。

1 号是最简单、最干净和最快的,但如果您的控制器不需要前面有语言,则可能不合适。

数字 2 不会让您选择重定向到的语言,因为它将被硬编码。根据应用程序,它可能完全没问题。

从用户体验的角度来看,数字 3 是最优雅的解决方案,但会在每个页面加载中造成一些轻微的开销(通过检查语言)。

【讨论】:

选项 #2 可以。我不介意在 url 中缺少一种默认语言的情况下硬编码。你能告诉我怎么做吗?我阅读了 Cakes Router::redirect 的文档,但不知道如何在这种情况下使用它。 我现在无法对此进行测试,但我认为Router::redirect('/:controller/:action/*', array('language' =&gt; 'eng'), array('persist' =&gt; false)); 会起作用。如果是这样,将persist 更改为true 将使浏览器将重定向保留在其缓存中,并且根本不会访问服务器,因此如果您不想清除所有缓存,请先尝试它。时间。 我试过你的 sn-p 但它似乎不起作用。我已经更新了上面的问题。 我明白了,我仍然无法尝试,因为我在没有 php 的计算机上。您可以肯定地重定向到一个固定的网址Router::redirect('/', '/fra/', array('persist' =&gt; false) );,如果您甚至不能这样做,那么肯定是其他问题。 好的。我发现了一个问题。我仍然有两个默认的::connect 规则(一个用于页面,一个用于“/”)。如果我删除这些,重定向开始工作,但不正确。它似乎混淆了语言/控制器/动作参数。

以上是关于CakePHP 中的本地化路由:如何重定向到默认语言的主要内容,如果未能解决你的问题,请参考以下文章

Cakephp Auth 与多个“用户”表

如何将没有参数的 Laravel 路由重定向到具有默认参数的控制器方法?

我可以从 cakephp 中的视图重定向吗

如何使 Laravel 5 重定向到没有哈希(#)的 Angular 路由?

如何从 ReactJS 中的外部函数重定向到路由

CakePHP 登录后重定向到当前页面