0% found this document useful (0 votes)
47 views18 pages

Internacionalization: Building Multi-Language Applications With Cakephp

The document discusses internationalization in CakePHP, including: - Using methods like __() and __n() to translate text and pluralize strings. - Running the i18n extractor to generate translation files that can be edited in POEDIT. - The Translate behavior, which stores translations in a separate i18n table and filters records by language. - Methods for changing the application language and caching translated elements.

Uploaded by

zumbul11
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
47 views18 pages

Internacionalization: Building Multi-Language Applications With Cakephp

The document discusses internationalization in CakePHP, including: - Using methods like __() and __n() to translate text and pluralize strings. - Running the i18n extractor to generate translation files that can be edited in POEDIT. - The Translate behavior, which stores translations in a separate i18n table and filters records by language. - Methods for changing the application language and caching translated elements.

Uploaded by

zumbul11
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

Internacionalization

Building multi-language applications with


CakePHP
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
Oh, BTW

Tomorrow is my birthday
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
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'];
The CakePHP way
● Methods

● __() -> __('My message')


● __n() -> __n('An element', 'Several
elements', $count)
● Configure::write('Config.language', 'en')

● Translate behavior

● i18n extractor
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
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
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
Translate Behavior
● Internationalization for our database
records

● All translations in the same table

● Automatically filters the records to


fetch them in the current language
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`)
); );
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)
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
)
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')
)));
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'));
}
}
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);
}
}
Caching i18n elements

$this->element('news', array('cache' => array(


'time' => '+1 day',
'key' => $currentLanguage
)));

tmp/cache/views
element_eng_news
element_spa_news
And we are done

Questions?

You might also like