Building Multilingual CakePHP Applications
Theres already a tutorial for building multilingual applications but i think that’s no good approach. Here is my version of good I18N programming :) I use the PEAR class Translation2 . It is very complex and also provides fallback languages. (Translation2 also does “gettext” and “XML” data stores, but this article only concerns itself with Translation2’s main functions.)
Database
At first we need to create some tables. Heres are the SQL statements:
CREATE TABLE `i18n` ( `id` varchar(100) NOT NULL DEFAULT '', `page_id` varchar(100) NOT NULL DEFAULT '0', `de` varchar(100) NOT NULL DEFAULT '', `en` varchar(100) NOT NULL DEFAULT '', PRIMARY KEY (`id`,`page_id`) ) TYPE=MyISAM; INSERT INTO `i18n` (`id`, `page_id`, `de`, `en`) VALUES ('login', 'global', 'Login', 'Login'), ('password', 'global', 'Passwort', 'Password'), ('user', 'global', 'Username', 'User'), ('preferences', 'global', 'Einstellungen', 'Preferences'), ('welcome', 'global', 'Willkommen', 'Welcome'); CREATE TABLE `langs` ( `id` varchar(100) NOT NULL DEFAULT ' ', `name` varchar(100) NOT NULL DEFAULT ' ', `meta` varchar(100) NOT NULL DEFAULT ' ', `error_text` varchar(100) NOT NULL DEFAULT ' ', `encoding` varchar(100) NOT NULL DEFAULT ' ', PRIMARY KEY (`id`) ) TYPE=MyISAM; INSERT INTO `langs` (`id`, `name`, `meta`, `error_text`, `encoding`) VALUES ('en', 'en', 'en', '', 'englisch error'), ('de', 'de', 'de', '', 'deutscher fehler');
These satetements already include some data for testing. There are two languages defined: English and German. The I18N Table includes the translations for each page. The page_id “global” defines global words which are always the same at ever side (like new, add, and so on...)
Coding
We want to provide Translation for every controller so we extend de “AppController” class. Open “cake/app_controller.php” and add the following code.
require_once 'Translation2.php'; require_once 'Auth/PrefManager.php'; class AppController extends Controller { var $helpers = array('Html','Ajax'); var $prefs; var $translation; function beforeRender() { // Fetch our database configuration and set up a DSN string with it $cm = & new ConnectionManager(); $db = $cm->getDataSource('default'); $db_user = $db->config['login']; $db_pass = $db->config['password']; $db_host = $db->config['host']; $db_name = $db->config['database']; $dsn = "mysql://$db_user:$db_pass@$db_host/$db_name"; // Preferences Manager $this->prefs = new Auth_PrefManager($dsn, array('serialize' => true)); // Check if our Auth_PrefManager object got created correctly if (PEAR::isError($this->prefs->_db)) { die($this->prefs->_db->getMessage()); } $this->prefs->useCache(false); // for debug //$this->prefs->setDefaultPref("language","en"); // steht sowieso schon in der datenbank // Translation Manager $this->translation =& Translation2::factory('DB',$dsn); // Check if our Translation2 object got created correctly if (PEAR::isError($this->translation)) { die($this->translation->getMessage()); } // get daulft language for user or if no user default preferences $defaultLang = $this->prefs->getPref($this->getUserID(), "language"); $this->translation->setLang($defaultLang); // if no translation in default language available fall back to english $this->translation =& $this->translation->getDecorator("Lang"); $this->translation->setOption("fallbackLang", "en"); // give lang to view $lang = $this->translation->getPage($this->name) + $this->translation->getPage('global'); $this->set("lang", $lang); } function getUserID() { return $this->Session->read('uid'); } }
I also use the PrefManager class to retrieve the language Settings for each User.
Now theres the $lang variable available on every view. If you want to show the string “welcome” in the current language use
<? echo $lang['welcome']; ?>
in your view.
Show Translation in the View
On every view there are the global words available and the word for each controller.
More to read on cakephp but in german on my blog: Blog of Christian Trummer
Another way to show translated content in the View
One problem with the method described above is that if your string isn’t present in the database, you’ll get an error because $lang[’word’] won’t be set. To solve this, I created a little Helper that checks to see if the requested string is translated. If so, it returns/prints the translated string, otherwise it will simply return/print the string you passed it.
<?php class TranslationHelper extends Helper { function t($msg, $lang, $return = false) { $val = isset($lang[$msg]) ? $lang[$msg] : $msg; if (AUTO_OUTPUT && $return === false) { print $val; return true; } else { return $val; } } } ?>
You will then need to add the Translation helper to your helpers array, like this:
var $helpers = array('Html','Ajax', 'Translation');
Then, in the view, you can simply call
$translate->t('text to translate', $lang)
and you’ll get your translated or untranslated string without any problem.