Internationalizing CakePHP Applications
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Internationalizing CakePHP Applications

  • 13,199 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
13,199
On Slideshare
13,140
From Embeds
59
Number of Embeds
5

Actions

Shares
Downloads
134
Comments
2
Likes
5

Embeds 59

http://www.slideshare.net 45
http://www.pierre-martin.fr 8
http://www.apurva.com 3
http://www.php.rk.edu.pl 2
http://coderwall.com 1

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?