views:

352

answers:

3

I need to figure out the best way to convert locale strings to a human-friendly name. I could write a large <xsl:choose> and just add a condition for each of the locales I want to convert, but I think there is probably a more efficient or clever way.

My input looks like this:

<content name="locale" value="en_US" />
<content name="locale" value="ja_JP" />

And corresponding output might look like this:

<content name="language" value="English" />
<content name="language" value="Japanese" />

In my case I don't care about the country right now, only the language. I also don't have to check all possible locales, only 10 or so currently, but there may be more in the future which is why I'm looking for the least rigid way of processing the conversion.

+1  A: 

Interesting question!

How about an intermediate XML mapping file?:

<content-maps>
<content-map locale="en_US" language="English"/>
<content-map locale="ja_JP" language="Japanese"/>
</content-maps>

Use XSL to create your <xsl:choose> block from that and run the results over your input file.

Chris McCall
A: 

There's probably a better way, but you could have the nicely formatted country lookups in a separate XML document and then refer and zip that information into your output.

That would at least keep the big choose statement at bay, and give you future expansion options.

Brabster
+1  A: 

You can store the mapping in another XML file and access it using the document() function from your stylesheet. Assuming you have a mapping file like the one suggested by Chris McCall in his answer, you could do it like this:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:variable name="mapping" 
                select="document('mapping.xml')"/>

  <xsl:template match="content[@name='locale']">
    <xsl:variable name="locale" select="@value"/>
    <content name="language">
      <xsl:attribute name="value">
        <xsl:value-of
            select="$mapping//content-map[@locale=$locale]/@language"/>
      </xsl:attribute>
    </content>
  </xsl:template>

</xsl:stylesheet>


If you want a really compact solution you can even include the mapping within the stylesheet itself. Since XSLT processors are required to ignore any elements not from the XSLT namespace within the xsl:stylesheet element, you can include the mapping there. You can access the XSLT document itself as document('').

So the self-contained stylesheet with a mapping could look like this:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:map="http://stackoverflow.com/questions/1428947#"
                exclude-result-prefixes="map"
                >

  <map:mapping>
    <map:content-map locale="en_US" language="English"/>
    <map:content-map locale="ja_JP" language="Japanese"/>
  </map:mapping>

  <xsl:variable name="mapping" 
                select="document('')//map:mapping"/>

  <xsl:template match="content[@name='locale']">
    <xsl:variable name="locale" select="@value"/>
    <content name="language">
      <xsl:attribute name="value">
        <xsl:value-of
            select="$mapping//map:content-map[@locale=$locale]/@language"/>
      </xsl:attribute>
    </content>
  </xsl:template>

</xsl:stylesheet>
Jukka Matilainen
Would it be possible to store the mapping in an <xsl:variable> rather than an external document? The application I'm working in doesn't allow me to reference external files -- all of the XML processing is done internally and I can only write `<xsl:template>`s, basically.
Zack Mulgrew
Which XSLT processor is your application using? Does it suppport XSLT version 1 or version 2? It is possible to store the mapping in a variable using XML, but if you want to access the value with XPath, you need to have either a XSLT 2 processor or a XSLT 1 processor which supports the `node-set` extension function (many processors do support it): http://www.xml.com/pub/a/2003/07/16/nodeset.html
Jukka Matilainen
I am trying to understand the constraints. So you can add `xsl:template` elements inside the `xsl:stylesheet` element, but you cannot add other top-level elements, right? Is the `document()` function available? As long as `document('')` works and gets you access to the stylesheet itself, you could use the approach in the second solution, but just tuck the mapping within a `xsl:variable` element within some template. The extra content does not necessarily need to be a top-level element, it could be anywhere within the stylesheet.
Jukka Matilainen
The XSLT processor is a proprietary one, and it supports XSLT version 1 as well as most of, if not all, the EXSLT extensions. I can use 'node-set' so I'll try that route and see what happens. I am working with a search engine and most of its XSLT processing is a black box.
Zack Mulgrew