This document describes how to make use of Krang's localization API.
Krang's localization API includes the following parts:
alert() , confirm() and prompt() arguments
via the JavaScript method Krang.L10N.loc()
Localization of Krang::Message messages and alerts via
language-specific messages.conf files
Localization of Perl strings via the function localize() exported by Krang::Localization
The remainder of this document will detail these four parts.
The lexicon files for one language reside below lang/LANG/ where
LANG may be a RFC3066-style language tag or an arbitrary tag for a
custom 'business language' like 'us-blog' for a blogger-style Krang
installation.
Each static string in Krang's templates must be wrapped in tmpl_lang tags. Example:
<tmpl_lang Edit Story>
This not only pertains to HTML text fragments, but also to the value property of buttons. To take an example:
<input value="<tmpl_lang Cancel>" onclick="cancel_edit()" type="button">
To figure out what to wrap in tmpl_lang tags, just take the viewpoint of someone looking at the UI: Which strings does he see, and which of them are statically coded in the template. Those strings have to be wrapped.
One more note: Don't split strings using tmpl_if & Co., and don't wrap
the split string's parts separately. Other languages might need to
reverse the order of the split string's parts or express them in
wholly different ways. This is best explained using an example,
coming from templates/User/edit_view.tmpl:
<h2>
<tmpl_if add_mode>
New
<tmpl_else>
Edit
</tmpl_if>
User
</h2>
Depending on the tmpl_if, this results in ``New User'' or ``Edit User''. A naive way of adding tmpl_lang tags, then, would be to wrap all three strings separately:
<h2>
<tmpl_if add_mode>
<tmpl_lang New>
<tmpl_else>
<tmpl_lang Edit>
</tmpl_if>
<tmpl_lang User>
</h2>
Other languages, however, might reverse the order, saying ``User New'' and ``User Edit'' respectively. To make this possible, we have to wrap the whole expression, including ``User'' into each of the tmpl_langs:
<h2>
<tmpl_if add_mode>
<tmpl_lang New User>
<tmpl_else>
<tmpl_lang Edit User>
</tmpl_if>
</h2>
As a positive side-effect, this is also slightly more readable then the piecemeal version above.
The same logic applies when it comes to the interplay of tmpl_lang and tmpl_var tags. Consider the following example:
# in Krang::CGI::ElementEditor we had $template->param(name_of_this_element => $element->display_name);
# and in templates/Story/edit.tmpl <input value="Done Editing <tmpl_var name_of_this_element>" onclick="save_and_go_up()" type="button" class="west">
The button label, then, is built from the static template string ``Done Editing '' and the element name provided by the tmpl_var. Again, other languages might need to reverse the wording. That's why it is necessary to put things in the following way:
# in Krang::CGI::ElementEditor
$template->param(done_with_this_element => localize('Done With '.$element->display_name));
# and in templates/Story/edit.base.tmpl <input value="<tmpl_var done_with_this_element>" onclick="save_and_go_up()" type="button" class="west">
The Krang::HTMLTemplate module automatically honors the DefaultLanguage
directive in krang.conf (which defaults to 'en' if not specified) by
first searching for requested template files in the corresponding language-code
subdirectory of each location in the template search path.
For instance, when Krang::CGI::Story's new_story() method calls
load_tmpl('new.tmpl'), the template will be loaded from the first of the
following locations found, in this order (assuming one addon named MyAddon is
loaded and has no skin):
KrangRoot/addons/MyAddon/templates/Story/en/new.tmpl KrangRoot/addons/MyAddon/templates/en/new.tmpl KrangRoot/addons/MyAddon/templates/Story/new.tmpl KrangRoot/templates/Story/en/new.tmpl KrangRoot/templates/en/new.tmpl KrangRoot/templates/Story/new.tmpl KrangRoot/addons/MyAddon/templates/new.tmpl KrangRoot/templates/new.tmpl
But developers don't actually edit the templates in the language-specific
subdirectories. They are automatically generated each time krang is restarted
(or whenever the krang_localize_templates runs) based on ``base'' templates.
Base templates, which have filenames ending in .base.tmpl, are used
to pre-generate the language-specific templates that krang will use at run-time.
For instance:
KrangRoot/addons/MyAddon/templates/Story/new.base.tmpl
will have its <tmpl_lang> tags expanded, using each of the AvailableLanguages listed in krang.conf, into the final, language-specific templates. So if the AvailableLanguages are English and German, that base template will be expanded into:
KrangRoot/addons/MyAddon/templates/Story/en/new.tmpl KrangRoot/addons/MyAddon/templates/Story/de/new.tmpl
This is so that all the language-specific strings don't need to be changed around at run time, but only when Krang is restarted.
Only the non-language-specific .base.tmpl files need to be maintained by developers, version controlled, and overridden in addons.
Strings passed to the JavaScript functions alert() , confirm() and
prompt() must first be passed to Krang.L10N.loc(). Example:
confirm( Krang.L10N.loc('Are you SURE you want to delete these Categories?') )
Again tmpl_if & Co. should not interfere (see above ``Static template strings'')
You need not care about these messages and alerts, because their localization happens behind the scenes in Krang::Message.
This one is getting a bit more complicated. Here we are dealing with
strings passed into the UI via tmpl_var's. Those strings are
localized using the function localize() exported by
Krang::Localization. Not all Perl strings however have to be
passed through localize() and there's no general rule where to use
localize().
As a general rule, localization should take place in Krang's presentation layer classes Krang::CGI::*. In 'find' runmodes for instance, this pertains to the value property of buttons generated by row handlers. Dynamically generated labels of drop down menus, radiobuttons and the like are candidates for such localizations.
Column labels on find screens, however, are localized in
Krang::HTMLPager, so you don't have to localize() them in
Krang::CGI::*. This might be a questionable approach: Is the pager
part of the presentation layer or of the model?
There may be times you are designing an element library for a customer whose editors like to see Krang elements' display names and labels in their respective languages. Multi-lingual installation require multiple lexicon entries for things like elements' diplay_name property or radiobuttons' label. Here's an example for the 'keywords' elementclass. For German we'd need in lang/de/perl.dict
"Keywords" "Keywords" "Delete Keywords" "Keywords löschen" "Done With Keywords" "Fertig mit Keywords" "Done Bulk Editing Keywords" "Fertig mit Gesamtbearbeitung von Keywords"
The simplest way to create a new lexicon is to just copy the German lexicon lang/de/ and start editing the files in it.