Friday 10 April 2009

Redirección interna con RewriteRule, RewriteCond y paso de variables PHP

Llevo 5 horas leyendo, buscando, probando... y por fin hallé lo que necesitaba!!!!!!!! Lo más frustante ha sido encontrar apenas casos como el mío... lo cuál no acabo de entender pues creo que es de lo más normal.

Mi caso es el siguiente: tengo un site funcionando en inglés

http://midominio.com

y como es un CMS hecho en PHP tengo URLS del tipo:

http://midominio.com/index.php?pag=foro

Lo que necesito es convertir mi CMS en multi-idiomas con los menos cambios posibles y de una forma amigable para usuarios y buscadores. ¿Creo que es algo muy generalizado, verdad? Por eso no entiendo el porque me ha costado encontrar referencias que me ayudaran. Tal vez he estado buscando mal ;)

La solución que buscaba era la de poder tener URLS como

http://midominio.com/es/index.php?pag=foro

que llevara "internamente" (es decir, sin que el visitante se dé cuenta... es decir, sin que camb ie la URL en su navegador) a esta otra URL

http://midominio.com/index.php?pag=foro&idioma=es

De esta forma el visitante podría estar navegando siempre por el "subdominio" /es/ pero en realidad la aplicación de PHP que es llamada es siempre la misma solo que se le pasa la variable idioma y de esta forma puede mostrar los contenidos adecuados! ;)

La última condición que necesitaba cumplir es que el archivo PHP llamado fuera cualquiera y no solo index.php, y que las variables de la URL fueran cualesquiera y cuantas sean necesarias.

Y en fin, después de 5 horas de lecturas y pruebas hallé el contenido del archivo .htaccess que tengo que poner en la raiz de ese dominio:

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^es/$ $1?idioma=es
RewriteRule ^es$ $1?idioma=es
RewriteCond %{QUERY_STRING} ^([^/]+)
RewriteRule ^es/(.+)\.php$ $1.php?%1&idioma=es
RewriteRule ^es/(.+)$ $1

No quiero hacer un tutorial aquí del tema, porque sería larguísimo y además no tengo los conocimientos adecuados. Posiblemente incluso la sintaxis que he utilizado podría mejorarse. Pero voy a detallar lo poco que he entendido de estas líneas de código y su función:
  • las dos primeras líneas son necesarias sí o sí si se quieren utilizar en APACHE estas reglas de redirección
  • la 3a y 4a líneas son para añadir "?idioma=es" a la URL cuando no menciona ningún archivo PHP: http://midominio.com/es
  • las líneas 5a y 6a van "ligadas". La primera "lee" el QUERY_STRING que son las variables $_GET que vienen después del "archivo.php?", y las insertamos en el output de la línea 6a como %1.
  • la línea 7 es para que se redireccionen todas las demás URLS, esta vez sin añadir nada del idioma, como las imágenes o las hojas de estilo CSS.
Para el que quiera saber más, decir que las sentencias RewriteRule manejan siempre la sintaxis:

RewriteRule input output

En donde input es una "expresión regular" (expresión que usa un lenguaje de comodines) que coincidirá solamente con algunas URLs concretas, y output es simplemente las URLS que finalmente debe construir y llamar Apache, utilizando normalmente "partes" del input. Esas partes son $1,$2,$3... (tal como se utilizan en la sintaxis del output), y se corresponden con el contenido de los diferentes paréntesis () que hay en la expresión regular del input.

Ejemplo:

RewriteRule ^es/(.+)\.php$ $1.php?%1&idioma=es

coincidirá con URLS del tipo:

es/index.php?pag=foro (pag=foro sería el QUERY_STRING,%1)
y será transformada en index.php?pag=foro&idioma=es
Como véis, el nombre del archivo php (index) es una variable! ;)
y el QUERY_STRING también aunque se lee en una línea RewriteCond previa.

La verdad es que el tema es un auténtico lío, jejeje... así que no dudo de que alguno de vosotros no haya entendido la mitad! Lo que si puedo aseguraros es que si queréis entender algo deberíais empezar por leer algún tutorial sobre el tema de .htaccess o de las expresiones regulares, que es de lo que va esto ;)




Actualización 6 Enero 2010

Podemos simplificar aún más el trabajo con idiomas

Trabajando esta semana en el tema de los idiomas (de nuevo) he descubierto que se puede conseguir lo mismo que pretendíamos al inicio de este artículo pero de manera un poco más "limpia" y cómoda. Me explico.

Arriba hemos utilizado el archivo .htaccess para realizar una redirección interna de:

http://midominio.com/es/index.php?pag=foro

a

http://midominio.com/index.php?pag=foro&idioma=es


Y sin embargo he descubierto que podemos optar a una redirección más sencilla (con menos líneas y menos complicación en el archivo .htaccess) si usamos bien el PHP ;) En concreto, la redirección la llevaremos simplemente hacia:

http://midominio.com/index.php?pag=foro

Es decir, que literalmente nos "comemos" el "es/" (más abajo pongo como ha de ser el .htaccess), y usamos el PHP para "leer" el "idioma" (en nuestro ejemplo 'es') en la URL (a la que accedemos con $_SERVER['REQUEST_URI']). El código PHP sería algo como esto:

$a_idiomas=array('es','en','fr');
$REQUEST_URI=$_SERVER['REQUEST_URI'];
foreach($a_idiomas as $v){
    if ( !strpos($REQUEST_URI,'/'.$v.'/')===false
       or substr($REQUEST_URI,-1*(strlen($v)+1))=='/'.$v )
          $idioma=$v;
}


En el array $a_idiomas cargamos las siglas de los idiomas que usamos en nuestro site. En $REQUEST_URI cargamos la URL solicitada (sin incluir el dominio o host), es decir, en nuestro ejemplo sería /es/index.php?pag=foro. Después, solamente ejecutamos un bucle que recorre cada uno de los idiomas buscando si sus siglas aparecen como /es/ o como /es (en una URL del tipo http://midominio.com/es). Si ocurre alguno de estos casos, entonces ya hemos encontrado cuál es el idioma que el visitante está requiriendo ;)

Fijaros que no hemos necesitado que la redirección interna nos lleve a una URL que contenga la variable GET &idioma=es. Y eso se va a traducir en lo siguiente: un archivo .htaccess mucho más limpio:

Options +FollowSymLinks
RewriteEngine On

RewriteRule ^es$ $1
RewriteRule ^es/$ $1
RewriteRule ^es/(.+)$ $1


Es decir, de 5 reglas nos hemos quedado con solo 3, y además las más sencillas! No entiendo muy bien la razón pero no he podido deshacerme de una de ellas. Intuyo que en realidad las 2 últimas RewriteRule son redundantes, pero después de hacer pruebas he comprobado que ambas son necesarias :S

Pero no se vayan... aún hay más! jejeje

Cuando no trabajamos en la raíz del dominio

Uno de los comentaristas (Carlos) preguntó acerca de trabajar en "local", en dónde lo anterior sufre algunos problemillas... :S En mi caso, también tuve problemas cuando quise trabajar en un directorio por debajo de la raíz del dominio, como por ejemplo en:

http://midominio.com/sports/es/index.php

Para no extenderme mucho, la solución que me ha funcionado ha sido usar la cláusula RewriteBase /directorio que indica al servidor Apache que si usamos redirecciones absolutas queremos que la "raíz" no sea la del dominio (http://midominio.com/), sino http://midominio.com/directorio. Esta cláusula nos será útil y necesaria solo si usamos el parámetro [R] al final de algún RewriteRule. Este parámetro obliga al servidor a CAMBIAR la URL que vino del navegador por la resultante de aplicar la regla Rewrite !!!

Para aclararnos, veamos como yo he dejado mi archivo .htaccess que he colocado en /sports:

Options +FollowSymLinks
RewriteEngine On

RewriteBase /sports

RewriteRule ^es$ es/ [R]
RewriteRule ^es/$ $1
RewriteRule ^es/(.+)$ $1


Añadir a lo dicho, que ahora la primera regla ya no es una redirección INTERNA, sino EXTERNA, es decir, al llegar a ella, si la URL pedida por el visitante cumple con la expresión ^es$ entonces se va a modificar la URL del navegador del visitante por una URL exactamente igual pero acabada en 'slash' /.

¿Para qué complicarnos tanto la vida? Pues porque de esta forma si alguien escribe:

http://midominio.com/sports/es

será redirigido a:

http://midominio.com/sports/es/

y con esto los enlaces relativos que hayan en esa página serán considerados por el navegador como relativos al directorio /es/ !!!! de otra forma serían considerados como relativos al directorio /sports/.

Bueno, como pueden ver... este tema es más complejo de lo que aparenta. Y me da la sensación de que hay muchos casos diferentes, y que han de buscar en su caso qué es lo que funciona. Les recomiendo que prueben y prueben... el "ensayo y error" también sirve ;)

Aquí tienen dos enalces por si necesitan leer más, del sitio oficial de Apache:



Una última aclaración: mod_rewrite es el módulo de Apache que usa el .htaccess, y está activado por defecto diría que en casi cualquier servidor de hosting Linux+Apache.

Suerte!
SERGI