As Tim Robinson points out, translate
will do the trick. (I wouldn't call it a "hack," but then I've long been at the identifying-with-my-torturers stage of my relationship with XSLT.) Your code will be a lot more readable if you use something like this:
<xsl:variable name="uc" value="ABCDEFGHIJKLMNOPQRSTUVWXYZ"/>
<xsl:variable name="lc" value="abcdefghijklknopqrstuvwxyz"/>
<xsl:variable name="ws" value=" 	"/>
...which is part of the globals.xslt
file that I include at the top of most transforms I write. Then this:
<xsl:value-of select="translate(x, concat($uc, $ws), $lc)"/>
translates each upper-case letter into its lower-case equivalent, and each whitespace character into nothing.
Usually the reason you use xsl:variable
is to make code more readable (as in the above example), or to store intermediate results that can't otherwise be effectively calculated. A fourth way of getting data into the output is one you didn't mention, and that's pretty darned useful: the attribute value template. All of these do the same thing:
<link>
<xsl:attribute name="name">
<xsl:value-of select="translate(name, concat($uc, $ws), $lc)"/>
</xsl:attribute>
</link>
<link>
<xsl:attribute name="name" value="translate(name, concat($uc, $ws), $lc)"/>
</link>
<xsl:variable name="linkName" value="translate(name, concat($uc, $ws), $lc)"/>
<link name="{$linkName}"/>
In this particular case, it's arguable which of the last two is simpler and clearer. Most of the time, though, it's not: separating the calculation of values from how they get inserted into the output makes both easier to understand, as does using an AVT instead of more verbose XSLT constructs that do the same thing.