Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do I18N with xsl and xml

I am trying to do a page in different languages with xml/xsl. I want to have only one xml and one xsl. On my page Url I have a parameter pLanguage that I think I can use to look if I have selected English or Dutch.

I tried with this code but I don’t know how I put it together:

First I make variables of all the words who has to been translated like this:

<xsl:variable name="lang.pageTitle" select="'This is the title in English'"/>

To get the pageTitle in the template I now can use

<xsl:value-of select="$lang.pageTitle"/>

I thought to replace the first line of code above by using a if-else statement to test if my choosen language is EN or NL like this:

<xsl:choose>
      <xsl:when test="$choosenLanguage &#61; ‘NL’">
        <xsl:variable name="lang.pageTitle" select="Titel in het nederlands'"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="lang.pageTitle" select="'This is the title in English'"/>
      </xsl:otherwise>
    </xsl:choose>

But I get the error: java.lang.IllegalArgumentException: can't parse argument number $lang.opdracht

like image 960
Bigjo Avatar asked Aug 23 '12 06:08

Bigjo


2 Answers

Here is a complete example how this can be done in a generic way:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pLang" select="'nl'"/>

 <my:texts>
  <pageTitle lang="en">This is the title in English</pageTitle>
  <pageTitle lang="nl">Titel in het nederlands</pageTitle>
 </my:texts>

 <xsl:variable name="vTexts" select="document('')/*/my:texts"/>

 <xsl:template match="/">
     <html>
      <title>
        <xsl:value-of select="$vTexts/pageTitle[@lang = $pLang]"/>
      </title>
     </html>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on any XML document (not used), the wanted, correct result (the title is generated in accordance with the global/external parameter $pLang) is produced:

<html>
   <title>Titel in het nederlands</title>
</html>

Do note:

It is recommended that all strings be maintained in an XML document that is separate from the XSLT stylesheet file(s). This allows the strings to be modified/added/deleted without changing the XSLT code.

To access the strings from another XML document the code remains almost the same, the only difference is that the argument to the document() function now is the URI to the strings XML document.

like image 50
Dimitre Novatchev Avatar answered Sep 27 '22 22:09

Dimitre Novatchev


If you perform the transformation in your own application you can use another approach. One that requires some coding but leaves your stylesheets less cluttered.

the stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stylesheet SYSTEM "i18n/humanreadable.ent">
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"...>
    &text1; &text2;

(You can choose whatever name you fancy for the i18n directory, just keep in mind it plays a special role.)

humanreadable.ent:

<!ENTITY text1 "Hello">
<!ENTITY text2 "world!">

So far this is still a good old & valid XSLT. But while it keeps the stylesheet more readable it does not give you the much desired multi-language support. For that you need to do some coding.

Customize the document builder which you parse the stylesheet file with; assign an entity resolver to it:

Source getStylesheetSource(String stylesheetFilename, EntityResolver entityResolver) throws ... {
    DocumentBuilder docBuilder = getDomFactory().newDocumentBuilder();
    docBuilder.setEntityResolver(entityResolver);
    Document stylesheet = docBuilder.parse(new FileInputStream(new File(stylesheetFilename)));
    return new DOMSource(stylesheet);
}

This entity resolver gets called every time a relative URL/path is encountered when your stylesheet is being parsed into a document.
When this happens check whether the path starts with your magic prefix (your special directory), and translate this prefix into a path pointing to the humanreadable.ent of your desired language.

final String targetLanguage = figureOutDesiredLanguage(...);
EntityResolver entityResolver = new EntityResolver() {
        @Override
        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
            if (pointsToMySpecialFolder(systemId)) {
                String lang = targetLanguage;
                String i18n = insertLangIntoThePath(systemId, lang);
                return new InputSource(new FileInputStream(new File(i18n)));
            }
            return null;
        }
};

Source stylesheet = getStylesheetSource("stylesheet.xslt", entityResolver);
Result result = new SAXResult(...);
Transformer transformer = transformerFactory.newTransformer(stylesheet);
transformer.transform(new DOMSource(inputXml), result);

The drawbacks are obvious: you need to do some coding outside of XML/XSLT, and your XSLT stylesheet is multi-lingual only when used within your super-special application.

The benefit is no extra tag soup in my (already rather dense) XSLT stylesheets.

like image 40
Jaroslav Záruba Avatar answered Sep 27 '22 22:09

Jaroslav Záruba