I18N with XSLT and what Perl has got to do with it
XSLT and I18N
- No standard or 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="Frankfurter" xml:lang="de-at">Frankfurter</i>
<i key="Frankfurter" xml:lang="de-de">Wiener</i>
<i key="Frankfurter" xml:lang="de-ch">Wienerli</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('Frankfurter')"/> mit Senf.
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!
God: "We hacked most of it together with Perl".
Two things needed
- I18N Perl class for your app
(e.g. based on Locale::Maketext::Simple or directly Locale::Maketext::Lexicon)
- 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('Frankfurter')"/>
<xsl:value-of
select="i18n:loc("Do you want to have
more '[_1]'?", SAUSAGENAME)"/>
<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.
Danke!
Fragen?