String localization with dynamic content
Cake has a wonderful shell script function built into it called extract that will run through your code and create a .po file full of all of the text contained within your __('My text here') calls. You can then pass these files onto to translators to modify them for your languages. When you want to add variables though, you have to break it up into pieces which may change the context of the phrase. Here's a way around that.
Normally you'd do something like this:
Download code
Which will provide you a snippet of 'My friend ' and ' enjoys writing code.' in your .po file. In a case where the context of the entire phrase might matter, a translator will only get the small pieces rather than the entire phrase. They aren't going to see how it's spliced together in your application, only the individual parts.
We can solve that with the Cake's extremely handy String::insert() method which allows for easy descriptive templating like this:
Download code
And then finally make it play nice with extract like this:
Download code
You only need to make translators aware that they should not modify words with a colon prefix and then you'll get something that gives them context to what is being said in your .po file. Now they'll only see this:
"My friend :friend_name enjoys writing code."
Download code
<?php
$message = __('My friend ',true) . $friend . __(' enjoys writing code.',true);
?>
Which will provide you a snippet of 'My friend ' and ' enjoys writing code.' in your .po file. In a case where the context of the entire phrase might matter, a translator will only get the small pieces rather than the entire phrase. They aren't going to see how it's spliced together in your application, only the individual parts.
We can solve that with the Cake's extremely handy String::insert() method which allows for easy descriptive templating like this:
Download code
<?php
$message = String::insert('My friend :friend_name enjoys writing code.',array('friend_name' => $friend_name));
?>
And then finally make it play nice with extract like this:
Download code
<?php
$message = String::insert(__('My friend :friend_name enjoys writing code.',true),array('friend_name' => $friend_name));
?>
You only need to make translators aware that they should not modify words with a colon prefix and then you'll get something that gives them context to what is being said in your .po file. Now they'll only see this:
"My friend :friend_name enjoys writing code."
Comments
Comment
1 what happend to the good old printf?
printf(__('My friend %1$s enjoys writing code for %2$s.', true), $friend_name, $product_name);
Comment
2 named variables instead of just format string are more verbose
Because if you just look at the locale file and don't know the variable names you get no hint about value %1$s and %2$s take.
My friend :friend_name enjoys writing code for :product_name.on the other hand is much more verbose and can help in translation.Comment
3 Thanks
In many languages you need to rephrase the text around the variable so just translating two snippets as in
message = __('My friend ',true) . $friend . __(' enjoys writing code.',true);will most probably be useless for the translator as they might need something different.Your tip does exactly that!
Comment
4 eh
Yes it may be better to read, but personally I would prefer speed over accessibility.
Comment
5 eh.. eh..
I think you are forgetting that the main benefit of a using a framework is speed and ease of development, not speed of execution. It will obviously add some overhead to most operations but if it provides a feature which eases the developer and/or translator's job i would definitely use it.
Comment
6 I agree completely regarding performance
It really depends on the site though. With Cake's caching abilities I tend to be a little over-the-top with caching everything that can be cached (full pages, elements, query caching, mem-caching the core files, and even using ajax to load certain dynamic pieces of a page just so I can cache the rest of it) and in the case of most static text accessed through __() files I nearly always have it cached in elements.
I'm kind've a performance / caching / optimization nut-job though. :-)
Question
7 Full view caching and localization
Barry - do you have any tips on implementing a full view cache on a localized site? More specifically, cake does view cache based on url - so if 1st person goes to mysite.com/controller/view and is English locale, cake will cache English version of view. 2nd guy comes along and is Spanish but sees English cache.
I've seen some posts ( http://bit.ly/7EOedv ) where they say hack the cache code to insert the locale prefix of the current user before writing/reading cache. Have you found a better way to accomplish this?
This method also loses out on search engines indexing your site in diff langs. I'd really like to prefix my urls with mysite.com/$locale/controller/view, but then wont that mess up the route to my controller?
thx in advance.
Comment
8 Nice Post.