开发者

CakePhp: Url based internationalization

I've a small problem with my internationalization:

I want to have some url looking like this: http://mywebsite/eng/controller/action/params...

I found this http://nuts-and-bolts-of-cakephp.com/2008/11/28/cakephp-url-based-language-switching-for-i18n-and-l10n-internationalization-and-localization/

This is working nice most of time. But I've one case where this hasn't the expected result.

When I'm using $this->Html->link with named parameters, I don't get my nice structure, but something like http://mywebsite/controller/action/paramX:aaa/paramxY:bbb开发者_运维技巧/language:eng

I think this is a routing problem, but I can't figure what is going wrong?

Thank you very much


This is because cakephp doens't find a route in routes.php that corresponds to this link. In other words, you'll have to define this route in the routes.php file

Router::connect('/:language/:controller/:action/:paramX/:paramY');

Once this set, $this->Html->link will output a nice url


I finally did this: I created a custom CakeRoute, in this cakeRoute, I override the "match" url and the _writeUrl method.

Now every thing is working like a charm :)

For those which are interessted by the route class:

    <?php
class I18nRoute extends CakeRoute {
/**
 * Constructor for a Route
 * Add a regex condition on the lang param to be sure it matches the available langs
 *
 * @param string $template Template string with parameter placeholders
 * @param array $defaults Array of defaults for the route.
 * @param string $params Array of parameters and additional options for the Route
 * @return void
 * @access public
 */
    public function __construct($template, $defaults = array(), $options = array()) {
        //$defaults['language'] = Configure::read('Config.language');
        $options = array_merge((array)$options, array(
            'language' => join('|', Configure::read('Config.languages'))
        ));
        parent::__construct($template, $defaults, $options);
    }

/**
 * Attempt to match a url array.  If the url matches the route parameters + settings, then
 * return a generated string url.  If the url doesn't match the route parameters false will be returned.
 * This method handles the reverse routing or conversion of url arrays into string urls.
 *
 * @param array $url An array of parameters to check matching with.
 * @return mixed Either a string url for the parameters if they match or false.
 * @access public
 */
    public function match($url) {
        if (empty($url['language'])) {
            $url['language'] = Configure::read('Config.language');
        }

        if (!$this->compiled()) {
            $this->compile();
        }
        $defaults = $this->defaults;

        if (isset($defaults['prefix'])) {
            $url['prefix'] = $defaults['prefix'];
        }

        //check that all the key names are in the url
        $keyNames = array_flip($this->keys);
        if (array_intersect_key($keyNames, $url) != $keyNames) {
            return false;
        }

        $diffUnfiltered = Set::diff($url, $defaults);
        $diff = array();

        foreach ($diffUnfiltered as $key => $var) {
            if ($var === 0 || $var === '0' || !empty($var)) {
                $diff[$key] = $var;
            }
        }

        //if a not a greedy route, no extra params are allowed.
        if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
            return false;
        }

        //remove defaults that are also keys. They can cause match failures
        foreach ($this->keys as $key) {
            unset($defaults[$key]);
        }
        $filteredDefaults = array_filter($defaults);

        //if the difference between the url diff and defaults contains keys from defaults its not a match
        if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
            return false;
        }

        $passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames);
        list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);

        //remove any pass params, they have numeric indexes, skip any params that are in the defaults
        $pass = array();
        $i = 0;
        while (isset($url[$i])) {
            if (!isset($diff[$i])) {
                $i++;
                continue;
            }
            $pass[] = $url[$i];
            unset($url[$i], $params[$i]);
            $i++;
        }
/*
        //still some left over parameters that weren't named or passed args, bail.
        //We don't want this behavior, we use most of args for the matching, and if we have more, we just allow them as parameters
        if (!empty($params)) {
            return false;
        }*/

        //check patterns for routed params
        if (!empty($this->options)) {
            foreach ($this->options as $key => $pattern) {
                if (array_key_exists($key, $url) && !preg_match('#^' . $pattern . '$#', $url[$key])) {
                    return false;
                }
            }
        }
        return $this->_writeUrl(array_merge($url, compact('pass', 'named')));
    }

    function _writeUrl($params) {
        if (isset($params['prefix'], $params['action'])) {
            $params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
            unset($params['prefix']);
        }

        if (is_array($params['pass'])) {
            $params['pass'] = implode('/', $params['pass']);
        }

        $instance =& Router::getInstance();
        $separator = $instance->named['separator'];

        if (!empty($params['named']) && is_array($params['named'])) {
            $named = array();
            foreach ($params['named'] as $key => $value) {
                $named[] = $key . $separator . $value;
            }
            $params['pass'] = $params['pass'] . '/' . implode('/', $named);
        }
        $out = $this->template;

        $search = $replace = array();
        foreach ($this->keys as $key) {
            $string = null;
            if (isset($params[$key])) {
                $string = $params[$key];
            } elseif (strpos($out, $key) != strlen($out) - strlen($key)) {
                $key .= '/';
            }
            $search[] = ':' . $key;
            $replace[] = $string;
        }
        $out = str_replace($search, $replace, $out);

        if (strpos($this->template, '*')) {
            $out = str_replace('*', $params['pass'], $out);
        }
        $out = str_replace('//', '/', $out);
        //Modified part: allows us to print unused parameters
        foreach($params as $key => $value){
            $found = false;
            foreach($replace as $repValue){
                if($value==$repValue){
                    $found=true;
                    break;
                }
            }
            if(!$found && !empty($value)){
                $out.="/$key:$value";
            }
        }
        return $out;
    }
}

And you can set the route like this:

Router::connect('/:language/:controller/*', array(), array('routeClass' => 'I18nRoute'));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜