Localization API - A note for Krang developers

This document describes how to make use of Krang's localization API.

Overview

Krang's localization API includes the following parts:

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.

Static template strings

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">

Language specific template subdirectories and .base.tmpl files

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.

JavaScript strings

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'')

Messages and alerts managed by Krang::Message

You need not care about these messages and alerts, because their localization happens behind the scenes in Krang::Message.

Perl strings in Krang::* modules

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?

Multi-lingual display names and labels in element libraries

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"

A note to lexicon writers

The simplest way to create a new lexicon is to just copy the German lexicon lang/de/ and start editing the files in it.