Click here to Skip to main content
15,868,141 members
Articles / General Programming / Localization

How to localize XSLT

Rate me:
Please Sign up or sign in to vote.
4.88/5 (7 votes)
10 Apr 2012CPOL2 min read 39.7K   363   14   2
Text in an XSLT may be localized by reading translated strings from an XML document. Numbers may also be localized.

Introduction

Text in an XSLT may be localized by reading translated strings from an XML document. Numbers may also be localized.

The XML document may contain either one language with one XML document for each language, or alternatively, a single XML document with all languages. The XML formats in the following example follows Microsoft .NET resource (.resx) files (one file per language) or a single TMX (translation memory exchange) document with all languages. Any format, however, may be used as long as the XPath used to read the text is consistent.

Using the code

Both options use the XPath document function to read the XML with the translated strings. Define parameters for each string used in the XSLT. Using parameters rather than variables allows the values to be overridden when the XSLT is transformed. Use xsl:value-of to display the translated text. When the transform is processed, pass the language code, for example, 'fr', and the URL to the resource XML document for the desired language.

In .NET, the XSLT arguments are passed using the XsltArgumentList class.

C#
System.Xml.Xsl.XsltArgumentList args = new System.Xml.Xsl.XsltArgumentList();
args.AddParam("lang", "", "fr");
args.AddParam("localeUrl", "", "Resources.fr.resx");

But first, consider a sample data XML to process and a sample XSLT before internationalization.

Sample XML data document

XML
<?xml version="1.0"?>
<root>
    <Number>34567.89</Number>
</root>

Sample XSLT without internationalization

XML
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output encoding="utf-8" omit-xml-declaration="yes"/>

<xsl:template match="/root">
    The number is: <xsl:value-of select="Number"/>
</xsl:template>

</xsl:stylesheet>

Sample result

The number is: 34567.89.

Notice the number in the result is not formatted.

Localization of XSLT

Option 1: Create an XML file for each language

In the XSLT, define a parameter to accept the URL to the resource XML document for the selected language.

XSLT localized for RESX format with one RESX file for each language:

XML
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output encoding="utf-8" omit-xml-declaration="yes"/>

<xsl:param name="lang" select="'en'"/>

<xsl:param name="localeUrl" select="'Resources.resx'"/>
<xsl:variable name="localeXml" select="document($localeUrl)/*" />
<xsl:param name="NumberFormat" 
           select="$localeXml/data[@name='NumberFormat']/value/text()"/>
<xsl:param name="NumberCaption" 
           select="$localeXml/data[@name='NumberCaption']/value/text()"/>

<xsl:decimal-format name="en" 
  grouping-separator="," decimal-separator="." />
<xsl:decimal-format name="es" 
  grouping-separator="." decimal-separator="," />
<xsl:decimal-format name="fr" 
  grouping-separator=" " decimal-separator="," />

<xsl:template match="/root">
    <xsl:value-of select="$NumberCaption"/>
    <xsl:value-of select="' '"/>
    <xsl:value-of select="format-number(Number, $NumberFormat, $lang)"/>
</xsl:template>

</xsl:stylesheet>

Resource files in English, Spanish (es) and French (fr)

Resources.resx
XML
<?xml version="1.0" encoding="utf-8" ?>
<root>
    <xsd:schema ...
    </xsd:schema>
    <resheader name="ResMimeType">
        <value>text/microsoft-resx</value>
    </resheader>
    <data name="NumberFormat" xml:space="preserve">
        <value>#,###.00</value>
    </data>
    <data name="NumberCaption" xml:space="preserve">
        <value>The number is:</value>
    </data>
</root>
Result

The number is: 34,567.89.

Notice that the number is formatted with a ',' as the thousands separator.

Resources.es.resx
XML
<?xml version="1.0" encoding="utf-8" ?>
<root>
    <xsd:schema ...
    </xsd:schema>
    <resheader name="ResMimeType">
        <value>text/microsoft-resx</value>
    </resheader>
    <data name="NumberFormat" xml:space="preserve">
        <value>#.###,00</value>
    </data>
    <data name="NumberCaption" xml:space="preserve">
        <value>El número es:</value>
    </data>
</root>
Result

El número es: 34.567,89

Resources.fr.resx
XML
<?xml version="1.0" encoding="utf-8" ?>
<root>
    <xsd:schema ...
    </xsd:schema>
    <resheader name="ResMimeType">
        <value>text/microsoft-resx</value>
    </resheader>
    <data name="NumberFormat" xml:space="preserve">
        <value># ###,00</value>
    </data>
    <data name="NumberCaption" xml:space="preserve">
        <value>Le nombre est:</value>
    </data>
</root>
Result

Le nombre est: 34 567,89

Option 2: Create a single XML file with all languages

XSLT localized for TMX format with one XML file with all languages:

XML
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output encoding="utf-8" omit-xml-declaration="yes"/>

<xsl:param name="lang" select="'en'"/>

<xsl:variable name="tmx" select="document('tmx.xml')/tmx/body"/>
<xsl:param name="NumberFormat" 
           select="$tmx/tu[@tuid='NumberFormat']/tuv[lang($lang)]/seg"/>
<xsl:param name="NumberCaption" 
           select="$tmx/tu[@tuid='NumberCaption']/tuv[lang($lang)]/seg"/>

<xsl:decimal-format name="en" grouping-separator="," decimal-separator="." />
<xsl:decimal-format name="es" grouping-separator="." decimal-separator="," />
<xsl:decimal-format name="fr" grouping-separator=" " decimal-separator="," />

<xsl:template match="/root">
    <xsl:value-of select="$NumberCaption"/>
    <xsl:value-of select="' '"/>
    <xsl:value-of select="format-number(Number, $NumberFormat, $lang)"/>
</xsl:template>

</xsl:stylesheet>

Translated XML document

tmx.xml
XML
<?xml version="1.0"?>
<tmx>
  <body>
    <tu tuid="NumberFormat">
      <tuv xml:lang="en"><seg>#,###.00</seg></tuv>
      <tuv xml:lang="es"><seg>#.###,00</seg></tuv>
      <tuv xml:lang="fr"><seg># ###,00</seg></tuv>
    </tu>
    <tu tuid="NumberCaption">
      <tuv xml:lang="en"><seg>The number is:</seg></tuv>
      <tuv xml:lang="es"><seg>El número es:</seg></tuv>
      <tuv xml:lang="fr"><seg>Le nombre est:</seg></tuv>
    </tu>
  </body>
</tmx>

Go global!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead blackbaud
United States United States
A sr. principal software engineer experienced in software engineering and architecture, UI design, and team leadership. Skills include RESTful Web Services, AWS, node.js, internationalization, C#, Angular, TypeScript, regular expressions, XSLT, ASP.NET, and SQL. Served for many years on OASIS technical committees such as XML Localization Interchange File Format (XLIFF) and Open Architecture for XML Authoring and Localization (OAXAL). Bachelor's degree in computer science and mathematics from Gordon College in Wenham, MA.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey15-Apr-12 22:00
professionalManoj Kumar Choubey15-Apr-12 22:00 
Nice
GeneralMy vote of 5 Pin
Steve Aube 12-Mar-12 9:03
Steve Aube 12-Mar-12 9:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.