IMHO your code is fine. The "problem" with XSLT variables is that they are NOT variables but more resemble constants: once set they cannot be changed anymore. Also, their scope is restricted to anything that follows their definition until the closing tag. In particular they are NOT visible in any ancestor tag. The combination of both results in the type of constructs that you have used above.
What is most disturbing is the fact that you usually have to repeat the testing of the criterion again and again if several variables depend on the same criterion. Again there's nothing generic that you can do about this, but of course, you can always at least create another variable holding the result of the test and then use that several times.
Depending on the context of your XSLT I would suggest two alternatives which may or may not be more elegant:
a) works in XSLT 1.0 and 2.0: If you just define the variables to be used as parameters to a template-call consider writing the template-call twice once in the when
as once in the otherwise
portion. This way you will end up with just one <choose>
.
b) Works only well in XSLT 2.0 (or a little awkward in XSLT with node-set extensions): move the check of the criterion and the selection of the definition values into a template and return the "set" of values to the assigned in a node-set. In a way you can regard this node-set as a record whose attributes have been set according to the criterion. See example below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template name="get_values">
<xsl:param name="catalog"/>
<xsl:choose>
<xsl:when test="count($catalog/lookup) > 0">
<PRODUCT VALUE="{$catalog/lookup/Product}"/>
<AREA VALUE="{$catalog/lookup/Area}"/>
</xsl:when>
<xsl:otherwise>
<PRODUCT VALUE="UNK"/>
<AREA VALUE="UNK"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="Catalog" select="."/>
<xsl:variable name="my_config">
<xsl:call-template name="get_values">
<xsl:with-param name="catalog" select="$Catalog"/>
</xsl:call-template>
</xsl:variable>
<xsl:message>
The value of PRODUCT is <xsl:value-of select="$my_config/PRODUCT/@VALUE"/>
and the value of AREA is <xsl:value-of select="$my_config/AREA/@VALUE"/>
</xsl:message>
</xsl:template>
</xsl:stylesheet>
This solution is definitely not shorter than yours but it resembles more other standard programming languages. The record type approach may come in especially handy if you have a lot of templates requiring the attributes in the record.