[oak perl] fwd: Internationalizing a Perl application

David Fetter david at fetter.org
Wed Jan 17 12:38:08 PST 2007


Folks,

I'd like to get this one in the archives.  Thanks to Nik Clayton of
London.pm for this, and to David Alban for asking London.pm about it :)

Cheers,
D

On Tue, Jan 16, 2007 at 03:02:31PM +0000, Nik Clayton wrote:
> >With DBI-Link's growing popularity world-wide, I'm getting more
> >requests for translation of the docs and strings.  This presents a
> >huge bunch of opportunities for me to make mistakes, so I'm asking for
> >the collective wisdom out there.
> >
> >* Where do I start?
> 
> Ignoring the documentation aspect, and just dealing with your application's 
> output for the time being.
> 
> You need to:
> 
> a) Have a mechanism that, given a string-code, can return the correct 
> version of that string given the users preferred language choice.
> 
> b) Have a mechanism to allow users to specify their preferred language.
> 
> c) Have translations of your strings available.
> 
> For (a) and (b) I recommend using Locale::Maketext::Simple, which does most 
> of the hard work for you.  This supports keeping your translations in 
> separate files (en.po for English, fr.po for French, and so on), and, given 
> a string, finds the translation of that string.
> 
> It handles most of the hard work concerning what to do with different 
> languages and how they behave with things like plurals.
> 
> I do this in SVN::Web.  Internally, any string that might be displayed to 
> the user is represented as "(short string)" -- I use brackets because it 
> makes it obvious in the user interface if an untranslated string is being 
> displayed.
> 
> Then the proper representations of those strings (even the English ones) 
> are kept in separate files.  Locale::Maketext::Simple provides loc_lang() 
> (to set the current language) and loc() (to look up a strings 
> representation in the language dictionary).  So the code looks something 
> like:
> 
>   my $lang = get_users_preferred_language(); # 'en', 'fr', etc
> 
>   loc_lang($lang);  # Set the preferred language
> 
>   print loc('(choose repos)');
> 
> If $lang is 'en' then it will look in the English dictionary, which has 
> this entry;
> 
>   msgid "(choose repos)"
>   msgstr "Please select a repository to browse:"
> 
> fr.po has this in it.
> 
>   msgid "(choose repos)"
>   msgstr "Veuillez selectionner le depot parcourir :"
> 
> Same message id, different message string.
> 
> You can either use Locale::Maketext::Simple as is, or you can subclass it 
> if you need new functionality.  I had to do that in SVN::Web, where I need 
> to be able to add new paths to language dictionaries at runtime.  You can 
> see the resulting code in SVN::Web::I18N.
> 
> So, broadly:
> 
> 1.  Get Locale::Maketext::Simple working
> 
> 2.  Use loc_lang() to set the current language
> 
> 3.  Replace code like
> 
>       print "This is a string";
> 
>     with code like
> 
>       print loc("(this-is-a-string)");
> 
>     and in en.po have
> 
>       msgid "(this-is-a-string)"
>       msgstr "This is a string"
> 
>     You don't have to use the same convention with braces that I have.
> 
> You can do variable interpolation in to translated strings using %1, %2, %3 
> placeholders.  For example, given this entry in the .po file:
> 
>   msgid "(file not found: %1 %2)"
>   msgstr "File '%1' could not be found in directory '%2'"
> 
> you'd write code like so:
> 
>   print loc('(file not found: %1 %2)', $file, $directory);
> 
> Suppose you then want to change the output.  The code stays the same, but 
> the msgstr becomes:
> 
>   msgstr "Directory %2 does not contain file %1"
> 
> Your dictionary can also specify many alternatives depending on the value 
> of a parameter.  For example, suppose you want to write:
> 
>   my $duration = a_long_process();
>   print "Process took $duration seconds";
> 
> But you might be on really fast hardware, and it only took one second. 
> Typically you might solve that with:
> 
>   print "Process took $duration second" . $duration != 1 ? 's' : '';
> 
> With message dictionaries you can put that logic in the dictionary instead.
> 
>   print loc('(%1 second)'); # in the code
> 
>   msgid "(%1 second)"
>   msgstr "%quant(%1, second, seconds)"
> 
> See SVN::Web::I18N for code, Locale::Maketext::TPJ13 for an article from 
> "The Perl Journal" which should help make some of this clear.
> 
> Hope that helps,
> 
> N

-- 
David Fetter <david at fetter.org> http://fetter.org/
phone: +1 415 235 3778        AIM: dfetter666
                              Skype: davidfetter

Remember to vote!


More information about the Oakland mailing list