news

I18N with XSLT and what Perl has got to do with it‎

Please create a contest for the longest talk title so that I can win a book‎!

Michael Kröll

XSLT and I18N

  • No clear best practice how to do it
  • Selection of different approaches exists

TIMTOWTDI

XSLT and I18N – Approaches

  • Message catalog options:
    • One XML file for all languages using xml:lang and lang()
    • One XML file per language
    • Messages stored inline in XML source using xml:lang
  • Templating options:
    • Named I18N template
    • EXSLT I18N function (convenience for I18N content inside attributes)

XSLT and I18N – Example

The XML file (excerpts)
<i18n>
    <i key="This is boring" xml:lang="en">This is boring</i>
    <i key="This is boring" xml:lang="de">Des is faad</i>
The XSL file (excerpts)
<xsl:param name="current_lang" select="'en'"/>
<xsl:variable name="i18n" select="document('i18n.xml')/i18n"/>
<func:function name="i18n:loc">
    <xsl:param name="key" />
    <xsl:param name="lang" select="$current_lang" />
    <func:result select="$i18n/i[@key=$key and lang($current_lang)]"/>
</func:function>
<xsl:value-of select="i18n:loc('This is boring')"/>

XSLT and I18N

  • Good enough for many cases
  • Things like parameter substitution only very hard to do
  • Message catalogs have to be XML based

“…XML based catalogs? But I want to re-use my PO files from the back-end!”

Perl to the rescue!

Two things needed

  1. I18N Perl class for your app
    (e.g. based on Locale::Maketext::Simple or directly Locale::Maketext::Lexicon)
  2. Register localisation methods as extension functions with XML::LibXSLT

Example I18N Perl class – MyApp::I18N

  • Interface and implementation derived from Catalyst::Plugin::I18N
  • Methods
    • new()
    • loc()
    • languages()
    • language()

Example Perl EXSLT function (excerpt)

my $i18n = MyApp::I18N->new( lang => $lang );
my $registeredfuncs = [{
        uri  => 'http://myapp.tld/i18n',
        name => 'loc',
        code => sub {
                      my ( $template, @param ) = @_;
                      return $i18n->loc( $template, @param );
                    },
      },
      {
        uri  => 'http://myapp.tld/i18n',
        name => 'locfrag',
        code => sub {
                      my $fragment = $parser->parse_balanced_chunk( 
                          $i18n->loc( $template, @param ) );
                      return $fragment->find('text()|*');
                    },
      },];

Example Perl EXSLT function (II)

foreach my $function ( @{$registeredfuncs} ) {
    XML::LibXSLT->register_function(
        $function->{uri},
        $function->{name},
        $function->{code},
    );
}

Example Usage (excerpt)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0" xmlns:i18n="http://myapp.tld/i18n"
    extension-element-prefixes="i18n">
<xsl:value-of select="i18n:loc('Eitrige mit Buckel')"/>
<xsl:value-of
  select="i18n:loc("Manage '[_1]'", TITLE)"/>
<xsl:copy-of
  select="i18n:locfrag(
    'For some reason there is a linebreak <br/> here')"/>

Drawbacks and Caveats

  • Quoting of apostrophes and double quotes
  • Quoting of messages which include markup

Advantages

  • Shared I18N catalog for back-end and front-end using standard gettext approach
  • It’s the shared catalog, stupid.

Thank you!
ÄŽakujem!
Danke!





Questions?