Hi Sebastian,
First off, there are a few features in XML and XSLT that can assist you with
localization, e.g...
In XML there is a pre-defined attribute of xml:lang which can contain
language (and optionally, national dialect) values. The useful thing about
utilising the xml:lang rather than defining your own 'lang' attribute is
that the xml:lang is decended down the tree for you until it is overridden.
In XSLT you have the lang() function - which can be used to query/test XML
based upon it's xml:lang attribute. The lang() function also gives you a
certain amount of fallback functionality (see below) and also is one of the
few parts of XML that is not case-sensitive.
So given those features there are several ways in which you can go about
providing a localization system for use in XSLT.
As an example...
Say you had a single vocabulary XML document that defined all of your
'tokenized' words, e.g. something like...
== Vocabulary.xml ===============================
<?xml version="1.0"?>
<vocab xml:lang="EN-GB"> <!-- this lang gives the default/fallback
language -->
<language name="English (GB)">
<token name="bin">Dustbin</token>
<token name="trousers">Trousers</token>
<token name="errtest">Something</token>
</language>
<language name="English (US)" xml:lang="en-us">
<token name="bin">Trash can</token>
<token name="trousers">Pants</token>
</language>
<language name="Deutsch" xml:lang="de">
<token name="bin">Mülleimer</token>
<token name="trousers">Hose</token>
</language>
</vocab>
== end of Vocabulary.xml ========================
using the lang() function in XSLT if you did used the XPath selection...
/vocab/language/token[@name = 'bin'][lang('en')]
it would give you the nodes...
<token name="bin">Dustbin</token>
<token name="bin">Trash can</token>
because both are defined under an element whose xml:lang is "en" (i.e.
"en-gb" and "en-us").
So if you do a more specific selection of...
/vocab/language/token[@name = 'bin'][lang('en-gb')]
this would give just the node...
<token name="bin">Dustbin</token>
So utilising that in XSLT could look something like...
== TestVocab.xsl ===============================
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
<xsl
aram name="lang-select" select="'en'"/>
<xsl
utput method="text"/>
<!-- get the vocabulary document -->
<xsl:variable name="vocab-doc" select="document('Vocabulary.xml')/*"/>
<!-- pick the token words from the vocab document according to the selected
language -->
<!-- also providing fallback when the selected language does not exist -->
<xsl:variable name="vocab"
select="$vocab-doc/language[lang($lang-select)]/token |
$vocab-doc/language[lang(/vocab/@default-lang)][not(/vocab/language[lang($la
ng-select)])][1]/token"/>
<xsl:template match="/">
<xsl:value-of select="$vocab[@name = 'bin']"/>
</xsl:template>
</xsl:stylesheet>
== end of TestVocab.xsl ========================
NB. Selecting the approriate tokens into the $vocab variable once saves you
keep specifying the language in later XPath expressions.
Also, some people like to keep each language in a seperate XML document - in
which case you could use system entities to accomplish this and still only
refer to one document() in your XSLT, e.g.
== Vocabulary.xml ===============================
<?xml version="1.0"?>
<!DOCTYPE includes [
<!ENTITY en-gb SYSTEM "VocabEN-GB.xml">
<!ENTITY en-us SYSTEM "VocabEN-US.xml">
<!ENTITY de SYSTEM "VocabDE.xml">
]>
<vocab xml:lang="EN-GB"> <!-- this lang gives the default/fallback
language -->
&en-gb;
&en-us;
&de;
</vocab>
== end of Vocabulary.xml ========================
and the the individual language XML documents...
== VocabEN-GB.xml ===============================
<language name="English (GB)" xml:lang="en-gb">
<token name="bin">Dustbin</token>
<token name="trousers">Trousers</token>
<token name="errtest">Something</token>
</language>
== end of VocabEN-GB.xml ========================
== VocabEN-US.xml ===============================
<language name="English (US)" xml:lang="en-us">
<token name="bin">Trash can</token>
<token name="trousers">Pants</token>
</language>
== end of VocabEN-US.xml ========================
== VocabDE.xml ===============================
<language name="Deutsch" xml:lang="de">
<token name="bin">Mülleimer</token>
<token name="trousers">Hose</token>
</language>
== end of VocabDE.xml ========================
Using such a system it would then be fairly 'easy' to write a stylesheet
that
checked the existence of each token in the languages, e.g.
== CheckVocab.xsl ===============================
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
<xsl
utput method="html" indent="yes"/>
<xsl:variable name="vocab" select="/vocab/language[lang('invalid')]/token |
/vocab/language[lang(/vocab/@xml:lang)][not(/vocab/language[lang('invalid')]
)][1]/token"/>
<xsl:key name="kXMLLangAtts" match="@xml:lang" use="'x'"/>
<xsl:key name="kDistinctLanguages" match="@xml:lang"
use="translate(substring(.,1,2),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmn
opqrstuvwxyz')"/>
<xsl:key name="kDistinctLangDialect" match="@xml:lang"
use="translate(.,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"
/>
<xsl:key name="kDistinctTokens" match="token" use="@name"/>
<xsl:variable name="ucase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="lcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vDistinctLanguages"
select="key('kXMLLangAtts','x')[generate-id() =
generate-id(key('kDistinctLanguages',translate(substring(.,1,2),$ucase,$lcas
e)))]"/>
<xsl:variable name="vDistinctTokens"
select="/vocab/language/token[generate-id() =
generate-id(key('kDistinctTokens',@name))]"/>
<xsl:template match="/">
<html>
<head>
<title>
<xsl:text>My Vocabulary Checks</xsl:text>
</title>
</head>
<body>
<h2>
<xsl:text>Vocabulary Checks</xsl:text>
</h2>
<hr/>
<table border="1">
<tr>
<th rowspan="2" align="center" valign="bottom">
<xsl:text>Token</xsl:text>
</th>
<th rowspan="2" align="center" valign="bottom">
<xsl:text>Default</xsl:text>
<br/>
<xsl:text>Value</xsl:text>
</th>
<xsl:for-each select="$vDistinctLanguages">
<xsl:variable name="this-lang"
select="translate(substring(.,1,2),$ucase,$lcase)"/>
<xsl:variable name="no-dialects"
select="count(key('kDistinctLanguages',$this-lang)[contains(.,'-')][generate
-id() =
generate-id(key('kDistinctLangDialect',translate(.,$ucase,$lcase)))])+1"/>
<th align="center" colspan="{$no-dialects}">
<xsl:text>Language: </xsl:text>
<xsl:value-of select="$this-lang"/>
</th>
</xsl:for-each>
</tr>
<tr>
<xsl:for-each select="$vDistinctLanguages">
<xsl:variable name="this-lang"
select="translate(substring(.,1,2),$ucase,$lcase)"/>
<th>
<xsl:value-of select="$this-lang"/>
</th>
<xsl:variable name="dialects"
select="key('kDistinctLanguages',$this-lang)[contains(.,'-')][generate-id()
= generate-id(key('kDistinctLangDialect',translate(.,$ucase,$lcase)))]"/>
<xsl:for-each select="$dialects">
<th>
<xsl:value-of select="translate(.,$ucase,$lcase)"/>
</th>
</xsl:for-each>
</xsl:for-each>
</tr>
<xsl:for-each select="$vDistinctTokens">
<xsl:sort/>
<tr>
<th align="left">
<xsl:value-of select="@name"/>
</th>
<td>
<xsl:value-of select="$vocab[@name = current()/@name]"/>
</td>
<xsl:variable name="this-name" select="@name"/>
<xsl:for-each select="$vDistinctLanguages">
<xsl:variable name="this-lang"
select="translate(substring(.,1,2),$ucase,$lcase)"/>
<xsl:variable name="dialects"
select="key('kDistinctLanguages',$this-lang)[contains(.,'-')][generate-id()
= generate-id(key('kDistinctLangDialect',translate(.,$ucase,$lcase)))]"/>
<td>
<xsl:variable name="this-token"
select="/vocab/language[lang($this-lang)]/token[@name = $this-name]"/>
<xsl:choose>
<xsl:when test="$this-token">
<xsl:value-of select="$this-token"/>
</xsl:when>
<xsl
therwise>
<xsl:attribute name="bgcolor">
<xsl:text>Red</xsl:text>
</xsl:attribute>
<xsl:text>(undefined)</xsl:text>
</xsl
therwise>
</xsl:choose>
</td>
<xsl:for-each select="$dialects">
<td>
<xsl:variable name="this-token"
select="/vocab/language[lang(current())]/token[@name = $this-name]"/>
<xsl:choose>
<xsl:when test="$this-token">
<xsl:value-of select="$this-token"/>
</xsl:when>
<xsl
therwise>
<xsl:attribute name="bgcolor">
<xsl:text>Red</xsl:text>
</xsl:attribute>
<xsl:text>(undefined)</xsl:text>
</xsl
therwise>
</xsl:choose>
</td>
</xsl:for-each>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
== end of CheckVocab.xsl ========================
HTH
Marrow
http://www.marrowsoft.com - home of Xselerator (XSLT IDE and debugger)
http://www.topxml.com/Xselerator