Enrutamiento localizado en CakePHP: cómo redirigir al idioma predeterminado
localization routing (2)
Desde 2012, esta publicación parece ser el recurso más definitivo sobre cómo hacer rutas localizadas en CakePHP (código copiado a continuación).
Funciona muy bien, con una excepción: no redirige las solicitudes que faltan al prefijo del idioma. Por ejemplo, http://example.com mostrará el mismo contenido que http://example.com/eng (si el inglés es el idioma predeterminado). Y de manera similar, si no es la página de inicio: http://example.com/foo/bar/ => http://example.com/eng/foo/bar . Hay una cierta mención de este problema en los comentarios, pero no hay una solución concluyente, que es lo que estoy buscando.
Código.
// 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''));
ACTUALIZACIÓN 1
Probé la sugerencia de user221931, pero parece que no funciona. Esto es lo que agregué a mis rutas:
/* 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) );
Parece que no tiene ningún efecto. Las siguientes URL no se redireccionan: http: example.com/, http://example.com/controller/ , http://example.com/controller/action/
ACTUALIZACIÓN 2 Según lo solicitado, aquí está mi archivo completo de rutas:
<?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'';
Eso es bastante simple de hacer desde AppController.php:
const DEFAULT_LANGUAGE = ''eng'';
public function beforeFilter() {
parent::beforeFilter();
if (empty($this->params[''language''])) {
$this->redirect(array(''language'' => self::DEFAULT_LANGUAGE));
}
}
Agregaste el enrutamiento de lenguaje muy específico (si la primera parte de la ruta url es eng
o fra
) pero no eliminaste el enrutamiento predeterminado ( Router::connect(''/:controller/:action/*''
) así que si estas dos palabras son no encontrado, la ruta más genérica de /:controller/:action/*
coincidirá.
Puedes hacer cualquiera de las siguientes tres cosas:
- Elimine las rutas predeterminadas que harán
/:controller/:action/*
y/:controller/*
unroutable y nadie podrá formar urls que accederán al contenido sin el idioma allí. - Agregue
routes
codificadas que redirijan aeng
ie/users
->/eng/users
,/posts/
->/eng/posts
, etc.). Esto puede parecer mucho trabajo, pero aunque la coincidencia de patrones está bien para el desarrollo, la codificación rígida mejora drásticamente la velocidad de enrutamiento, por lo que probablemente deba ir por ese camino en algún momento futuro. La ventaja añadida de esto es que puede agregar una reglapersist
que hará que el navegador recuerde la redirección y no vuelva a tocar el servidor. - Agregue código en su
AppController
que verificará si el idioma está incluido en la ruta y, si no, intente adivinar el idioma de los usuarios desde la configuración del navegador para redireccionar a la url más apropiada o la predeterminada.
El número 1 es el más fácil, el más limpio y el más rápido, pero puede que no sea apropiado si tiene controladores que no necesitan tener un idioma en el frente.
El número 2 no te deja elegir el idioma al que redirigirás, ya que estará codificado. Dependiendo de la aplicación, podría estar perfectamente bien.
El número 3 es la solución más elegante desde la perspectiva de UX, pero está causando una ligera sobrecarga en cada carga de página (al verificar el idioma).