DIY international (i18n) urls in cake 1.1.x or lower
CakePHP 1.2 will have decent i18n functionality, but until you switch you can use the simple but decent solution that i have created.
It allows you to use urls in any foreign language, which are translated right before the routing happens, so nothing intervenes with all the other logic (which is usually in english)
So, if you want to program in english (action names in controllers for example), but you want to use urls in an other language, and you are using a cakePHP version prior to 1.2? Here's the solution!
What you can expect: (example from one of my projects)
browse to urls like/activiteiten/overzicht/toekomst
cake will interpret it as/activities/index/future
after the translation and interpretation, the routing happens, so you have all the freedom to define routes for english urls, like you are used to.
put these 2 lines in app/config/routes.php above the routing rules.
then, create the file app/config/translate.php and put this in it:
If you want to use multiple languages, you can put all the rules in the same array. But in this case, you could have collisions (words that are written the same, but mean different things, for example the same word in different languages). This happens very rarily, but if you would encounter this, you could expand the logic by for example adding a 2-letter language identifier like '/nl/' in the url to choose the right array.
What you can expect: (example from one of my projects)
browse to urls like
cake will interpret it as
after the translation and interpretation, the routing happens, so you have all the freedom to define routes for english urls, like you are used to.
put these 2 lines in app/config/routes.php above the routing rules.
PHP Snippet:
<?php
include_once("translate.php");
$from_url = translate($from_url,true);
?>
then, create the file app/config/translate.php and put this in it:
PHP Snippet:
<?php
function translate($str = null,$total = false)
{
$translatetable = array('kalender' => 'events',
'fotos' => 'images',
'afbeeldingen' => 'images',
'activiteiten' => 'events',
'paginas' => 'pages',
'standaard' => 'default',
'voegtoe' => 'add',
'verwijder' => 'delete',
'bewerk' => 'edit',
'bekijk' => 'view',
'overzicht' => 'index',
'toekomst' => 'future',
'verleden' => 'past',
'nu' => 'now',
'vragenenantwoorden' => 'faq'
);
if($str)
{
if($total)
{
$old = explode('/',$str);
$lastone = end($old);
if(empty($lastone)) array_pop($old);
$new = array();
/* translate each part or leave untranslated part */
for($i = 0 ; $i <sizeof($old) ; $i++)
{
$new[$i] = translate($old[$i]);
}
/* construct the translated url. this also adds a trailing "/" even if it wasn't in the original */
$new_url="";
foreach($new as $n)
{
$new_url .= $n."/";
}
return $new_url;
}
else
{
foreach ($translatetable as $orig => $new)
{
if($str == $orig) $str = $new;
}
return $str;
}
}
}
?>
If you want to use multiple languages, you can put all the rules in the same array. But in this case, you could have collisions (words that are written the same, but mean different things, for example the same word in different languages). This happens very rarily, but if you would encounter this, you could expand the logic by for example adding a 2-letter language identifier like '/nl/' in the url to choose the right array.

your article on translatebal URLs is great. But, in the first paragraph, you mentioned that Cake > 1.2 has this feature itself. Could you provide any additional information on that topic as I couldnt find ANY hint or tutorial on how to achieve this.
Thanks in advance,
Philipp Berger
I would like to do something like:
http://domain.com/de/controller/action/id
I am having trouble wrapping my head around this one and would appreciate any help.
I've modified the translate.php to become something you could use, and you could even combine it with other i18n systems that translate the contents of the site itself, by checking the constant LANG_I18N. (or whatever you like to call it)
[code php] function translate($str = null,$total = false)
{
$translatetable['de'] = array(...);
$translatetable['nl'] = array(...);
);
if($str)
{
if($total)
{
$old = explode('/',$str);
$lastone = end($old);
if(empty($lastone)) array_pop($old);
$new = array();
if(isset($old[0])) define('LANG_I18N', $old[0]) ;
/* translate each part or leave untranslated part */
for($i = 0 ; $i
$new[$i] = translate($old[$i+1]);
}
/* construct the translated url. this also adds a trailing "/" even if it wasn't in the original */
$new_url="";
foreach($new as $n)
{
$new_url .= $n."/";
}
return $new_url;
}
else if(isset($translatetable[LANG_I18N]))
{
foreach ($translatetable[LANG_I18N] as $orig => $new)
{
if($str == $orig) $str = $new;
}
return $str;
}
}
}
Do you have a favorite? Or maybe point to a good place to start?
My two cents:
By reworking that last 'else if' block with the following code, adding addition languages is as simple as dropping a new translations file into app/config/i18n/
else if (file_exists(CONFIGS.'i18n'.DS.'translate_table_'.LANG_I18N.'.php')
{
include_once(CONFIGS.'i18n'.DS.'translate_table_'.LANG_I18N.'.php');
foreach ($translatetable as $orig => $new)
{ ...
The german translate_table_de.php for example might contain the $translatetable as it was originally posted, whereas a french table, translate_table_fr.php, would define the same array slightly differently.
A good place to start would be the bakery, cakeforge, cakephp's google group, etc.
With 1.2 it will be built-in btw.
Assuming you have an array of valid languages, I implemented the URLs with the $lang in them by putting the following in routes.php:
foreach ($valid_languages as $language => $mapping) {
// If they just go to /$lang/
$Route->connect("/{$language}", array('controller' => 'addons', 'action' => 'home'));
// connect localized, static pages
$Route->connect("/{$language}/pages/*", array('controller' => 'pages', 'action' => 'display'));
// Magical undocumented routing syntax
$Route->connect("/{$language}/:controller/:action/*", array('controller' => 'pages', 'action' => 'index'));
}
(I hope that code looks better on the page than it does in this textbox...)