Internationalizing CakePHP Applications

  • 9,966 views
Uploaded on

Slides from the talk given by Mariano Iglesias during the CakeFest #3 - July 2009 …

Slides from the talk given by Mariano Iglesias during the CakeFest #3 - July 2009

Note: the original pdf and the code related to this talk can be found on cakephp.org (http://cakephp.org/downloads/CakeFest/CakeFest%203%20-%20Berlin%202009/Mariano%20Iglesias%20-%20Internationalizing%20CakePHP%20Applications)

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
9,966
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
134
Comments
2
Likes
5

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Internacionalization Building multi-language applications with CakePHP
  • 2. Who am I? ● The argentinean in the Cake team ● A C++ developer that turned to Java, then PHP ● Working exclusively with CakePHP for 3 years
  • 3. Oh, BTW Tomorrow is my birthday
  • 4. Why do we need i18n? ● Expand your audience ● CakePHP makes it simple ● Translators don't have to know our application ● Works with files (views, models, controllers), and database records ● Think about your future needs
  • 5. This could get you killed ● The switch lover switch ($language) { case 'en': echo 'My message'; break; case 'es': echo 'Mi mensaje'; break; } ● The table lover $terms = $this->Term->find('all'); $terms = Set::combine($terms, '/Term/code', '/Term/text'); $this->set(compact('terms')); // ... echo $term['my_message'];
  • 6. The CakePHP way ● Methods ● __() -> __('My message') ● __n() -> __n('An element', 'Several elements', $count) ● Configure::write('Config.language', 'en') ● Translate behavior ● i18n extractor
  • 7. The CakePHP way ● Multibyte ● 1 letter != 1 byte ● 8 bits -> 256 ● wchar_t (L'w') ● mb_strlen(), mb_strpos(), mb_substr(), ... ● Multibyte::checkMultibyte() -> ord($char) > 128 ● Multibyte::utf8($string) -> array (values higher than 128 allowed) ● Multibyte::ascii($array) -> string
  • 8. The CakePHP way <p><?php __('Welcome to my page'); ?></p> <?php echo $html->link(__('Home', true), '/'); ?> <p><?php echo sprintf(__('Your name is %s', true), $name); ?></p> $ cake i18n extract -output app/locale locale/eng/LC_MESSAGES locale/ default.po default.pot POEDIT default.mo
  • 9. Let's go to work ● Our own example ● Modify the view ● Run the extractor ● Look at the generated template file ● Run POEDIT → nplurals=2; plural=(n != 1); ● Look at the translated files ● Some tips when using POEDIT
  • 10. Translate Behavior ● Internationalization for our database records ● All translations in the same table ● Automatically filters the records to fetch them in the current language
  • 11. Translate Behavior class Post extends AppModel { public $actsAs = array('Translate' => array( 'title', 'body' )); public $belongsTo = array('User'); } CREATE TABLE `posts`( CREATE TABLE `posts`( `id` INT NOT NULL AUTO_INCREMENT, `id` INT NOT NULL AUTO_INCREMENT, `user_id` INT NOT NULL, `user_id` INT NOT NULL, `title` VARCHAR(255) NOT NULL, `title` VARCHAR(255) NOT NULL, `body` TEXT, `body` TEXT, `created` DATETIME, `created` DATETIME, `modified` DATETIME, `modified` DATETIME, PRIMARY KEY(`id`) PRIMARY KEY(`id`) ); );
  • 12. Translate Behavior mysql> desc i18n; +-------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+----------------+ | id | int(10) | NO | PRI | NULL | auto_increment | | locale | varchar(6) | NO | MUL | NULL | | | model | varchar(255) | NO | MUL | NULL | | | foreign_key | int(10) | NO | MUL | NULL | | | field | varchar(255) | NO | MUL | NULL | | | content | text | YES | | NULL | | +-------------+--------------+------+-----+---------+----------------+ mysql> select * from i18n; +----+--------+-------+-------------+-------+------------------------------------------+ | id | locale | model | foreign_key | field | content | +----+--------+-------+-------------+-------+------------------------------------------+ | 1 | eng | Post | 1 | title | Pre-registration opened | | 2 | spa | Post | 1 | title | Pre-inscripciones abiertas | | 3 | eng | Post | 1 | body | Body for Pre-registration opened | | 4 | spa | Post | 1 | body | Cuerpo para Pre-inscripciones abiertas | +----+--------+-------+-------------+-------+------------------------------------------+ 4 rows in set (0.00 sec)
  • 13. Translate Behavior $posts = $this->Post->find('all', array( 'recursive' => -1, 'fields' => array('title', 'body') )); $posts = Set::combine($posts, '/Post/title', '/Post/body'); SELECT `I18n__title`.`content`, `I18n__body`.`content` FROM `posts` AS `Post` LEFT JOIN `i18n` AS `I18n__title` ON (`Post`.`id` = `I18n__title`.`foreign_key` AND `I18n__title`.`model` = 'Post' AND `I18n__title`.`field` = 'title') LEFT JOIN `i18n` AS `I18n__body` ON (`Post`.`id` = `I18n__body`.`foreign_key` AND `I18n__body`.`model` = 'Post' AND `I18n__body`.`field` = 'body') WHERE `I18n__title`.`locale` = 'eng' AND `I18n__body`.`locale` = 'eng' array( [Pre-registration opened] => Body for Pre-registration opened [Site Updates] => Body for Site Updates )
  • 14. Translate Behavior $this->Post->create(); $this->Post->save(array('Post' => array( 'user_id' => 1, 'title' => array('eng' => 'ENG 1', 'spa' => 'spa1'), 'body' => array('eng' => 'Body for ENG 1', 'spa' => 'Cuerpo para spa1') ))); $this->Post->create(); $this->Post->save(array('Post' => array( 'user_id' => 1, 'title' => array('eng' => 'ENG 1'), 'body' => array('eng' => 'Body for ENG 1') ))); $this->Post->save(array('Post' => array( 'id' => $this->Post->id, 'title' => array('spa' => 'spa1'), 'body' => array('spa' => 'Cuerpo para spa1') )));
  • 15. Changing the language class AppController extends Controller { public $components = array('Cookie'); public function beforeFilter() { $lang = null; if (!empty($this->params['url']['lang'])) { $lang = $this->params['url']['lang']; $this->Cookie->write('CakeFestLanguage', $lang, false, '+365 days'); } else { $lang = $this->Cookie->read('CakeFestLanguage'); } if (empty($lang)) { $lang = Configure::read('CakeFest.defaultLanguage'); } Configure::write('Config.language', $lang); } function beforeRender() { $this->set('currentLanguage', Configure::read('Config.language')); } }
  • 16. Changing the language class AppHelper extends Helper { Public function url($url = null, $full = false) { if (!empty($url) && !is_array($url) && $url[0] == '/') { $urlRoute = Router::parse($url); if (!empty($urlRoute['controller'])) { $url = array_merge(array_intersect_key($urlRoute, array_flip(array('admin', 'controller', 'action', 'plugin'))), $urlRoute['pass'], $urlRoute['named']); } } if (is_array($url)) { if (!isset($url['lang']) && Configure::read('Config.language') != Configure::read('CakeFest.defaultLanguage')) { $url['lang'] = Configure::read('Config.language'); } } return parent::url($url, $full); } }
  • 17. Caching i18n elements $this->element('news', array('cache' => array( 'time' => '+1 day', 'key' => $currentLanguage ))); tmp/cache/views element_eng_news element_spa_news
  • 18. And we are done Questions?