CakePHP 1.2 i18n/l10n
A few month ago I started working with the CakePHP framework. I needed the i18n functionality that was in version 1.2 (late alpha). After a lot of research I was able to get what I needed. I wrote up my findings and submitted this to the CakePHP developers. They have accepted my submission on using i18n in 1.2 and it will appear in the CakePHP 1.2 Manual. This is my first contribution back to the open source community and while it is just a small bit, I am quite proud to have made even a small contribution back to what has given me so much. Since the 1.2 manual is not yet available I thought I’d post my contribution here:
Localization and Internationalization
What is the difference? Localization refers to the ADAPTATION of a product, application or document content to meet the language, cultural and other requirements of a specific target market (a “locale”). Internationalization is the DESIGN AND DEVELOPMENT of a product, application or document content that ENABLES easy localization for target audiences that vary in culture, region, or language.
- http://www.w3.org/International/questions/qa-i18n
- “internationalization” and “localization” are often abbreviated as i18n and l10n respectively; 18 and 10 are the number of characters between the first and last character.
How to Use Localization
- Include l10n Class
uses('L10n'); class OrderController extends AppController { ... } - Create Language File Specific language translations are placed here:
locale/eng/LC_MESSAGES/default.po (English) locale/fre/LC_MESSAGES/default.po (French)
Language directories must be named with the official three-digit language code; you can find these here: http://www.loc.gov/standards/iso639-2/php/code_list.php
- Populate Language file Create entries in the appropriate default.po file in this form:
msgid "close_window" msgstr "Close" msgid "where_pin" msgstr "Where is my PIN?"
There is a limit of 1014 characters in a single msgstr.
- Using Use the underscore-underscore method, providing the msgid as the first parameter.
__("closeWindow"); __("closeWindow", true);If the msgid provided is not found, then the provided value id used, hence some people like to use the default text as the msgid:
__("Close Window");With the second parameter as true the value is returns, without the second parameter is echo’d. If you see your msgid on your page instead of cooresponding msgstr, and the msgid/msgstr pair exists in the relevant default.po file, then you probably are missing dbouel quotes around the msgid or msgstr entry.
- Selecting Language To select the language you want:
$this->L10n = new L10n();$this->L10n->get("en"); $_SESSION['Config']['language'] = "en";Use the first two-digits of the language code (yes, I know it is weird to use two-digits here and three-digits as the language directory name). Configure::write(’Config.language’, “en”) will work in place of $_SESSION['Config']['language'] = “en” in the future, but it does not work as of 1.2.0.4798alpha.
Real-Life Implementation Example
I created a language helper method:
public function setLang($language = 'en') {
$this->L10n = new L10n();
$this->L10n->get($language);
//Configure::write('Config.language', $language);
$_SESSION['Config']['language'] = $language;
}
Then I use it in my controller methods as needed.
function balance() {
Utility::setLang(LOCALE);
...
}
And LOCALE is a constant = “en”.
Special Characters
Save the default.po files with an encoding of ISO-8859-1 htmlentities will not render correctly if when used in $form->error() unless you turn off the espcae option:
echo $form->error('Card.cardNumber',
__("errorCardNumber", true),
array('escape' => false));











January 31st, 2008 at 8:46 am
nicee, congratz

what about how to use the i18n… ???
January 31st, 2008 at 10:40 am
This is the full functionality, unless something changed in the recent 1.2 beta. See the above section “How to Use Localization”. This is really translation. l10n includes things such as different currency and date/time formats, using appropriate colors/symbols that have meaning to the local audience, and hide/showing content with particular meaning to the target audience. Translation is only a piece of 18n/l10n, but the most visible piece. l10n and i18n are used interchangeably in common practice, even though they are technically different. i18n being the design of a l10n system, while l10n is the implementation.
February 16th, 2008 at 3:29 pm
Damn, it doesn’t work for me, as i did everything like in this manual
version Beta: 1.2.0.6311
February 17th, 2008 at 12:03 pm
Can you email detailed samples of what you did that I can look at and offer some help?
February 17th, 2008 at 11:41 pm
Seems like msgstr needs to begin on a new line.
February 18th, 2008 at 12:48 am
You are correct. My mistake in formatting. The msgid and the companion msgstr each need to appear on a separate line. Thanks for noticing this.
February 20th, 2008 at 9:15 am
Thank you very much for the tutorial
February 20th, 2008 at 10:22 am
Is it possible to pass variables inside __() somehow? I’ve tried something like without success
February 20th, 2008 at 10:56 am
I never tried before, but I was able to do this:
$test = ‘Line’;
__(’tag’ . $test, true);
This constructed ‘tagLine’, which is a valid msgid in my default.po file.
February 23rd, 2008 at 9:38 am
Thanks
March 15th, 2008 at 4:08 am
When writing the default.po file, remember to use double quotes around the text, when defining the msgid.
Single does not work!
It would be nice if both kind of quotes did work!
April 8th, 2008 at 4:45 pm
Some useful comments from Daniel.S:
… thought I should mention that with __(), it helps to use the full string text, not just a message ID. That way, if people need to create new translations, or you look at it a long time down the track - you won’t be wondering what ‘where_pin’ is actually meant to say.
For example:
__(’Where is my PIN?’)
and in the PO file, it will have
msgid “Where is my PIN?”
msgstr “”
That way, when you create a translation, all you need to do is look at
the POT file and translate away:
msgid “Where is my PIN?”
msgstr “Hvor finner jeg PIN-koden min?”
This is the way i’ve seen it done in Drupal, and it makes sense.
April 10th, 2008 at 10:59 am
I would like to switch language within a component, like the page is in german but emails which are being sent via email component are in english. How can i switch the language (and switch back afterwards) .
April 10th, 2008 at 12:05 pm
I have not tested but presumably German is the default language, and then you could use setLang(”en”), as defined above, before composing the email, and then setLang(”de”) after.
April 11th, 2008 at 1:15 am
I tried it your way but it is not working. There is a language found property ($found ) in l10n you can not change once it is set. (or I haven’t found a way yet
April 11th, 2008 at 1:26 am
Ok, i’ve got it, but it is a bad hack:
Configure::write(’Config.language’, ‘de’);
$curInst = I18n::getInstance();
$curInst->__l10n->found = false;
June 30th, 2008 at 2:22 pm
Nice post!
Do you know how to do if I want to make a whole page “translateable”, msgstr’s has a limit at some charachters.
June 30th, 2008 at 2:27 pm
I do not know what the character limit is, but I would break the page content into several smaller strings. The largest single string I have ever used is a single paragraph.
June 30th, 2008 at 2:31 pm
Ok, that’s how I’ve done so far, thought perhaps it existed an easier solution
August 26th, 2008 at 11:50 pm
thx for this article
great thing to help you with your translation files are the gettext tools. for example it can create the .po files for you automatically by extracting all the strings defined in the __() function.
http://www.gnu.org/software/gettext/#TOCdownloading