I did the same for C++. I hate many aspects of the language, and it feels really ugly compared to newer languages like C#, or even D, but it's still my language of choice for some projects (mostly because the tools are mature and almost everywhere).
XSLT is a purely functional language so inherits much of the general set of likes/dislikes that go with them.
<xsl:if test="x">template</xsl:test>
is basically this in Lisp:
(if eval(x) (apply-template template) nil)
And <xsl:for-each> is equivalent to map in Lisp. It's a functional projection, not an imperative loop, even if the syntax looks like that of an imperative language.
Functional purity gives you the usual advantages: execution that can be testable and fast and parallelizable and predictable in space and time usage and secure in presenting little attack surface for injections and overflows and such. XSLT is exactly what it claims to be, a declarative way of transforming tree structures.
The problem with XSLT in enterprise land is that real world business requirements don't fit well into that. Real world applications involve things like iteratively computed subtotals or comparing separate records together or picking up user preferences for formatting or other sorts of conditional logic. All of which is a poor fit for XSLT and leads to really complicated XPath hacks.
And your average enterprisey programmer comes from imperative languages and thinks and codes in imperative terms, often not even realizing XSLT is a functional language. So it's no surprise that real-world XSLT ends up as muddy balls of crap.
One thing about XSLT is that when it is bad it is reallyreally bad.
It can be fairly elegant, especially if you know the difference between apply-templates and call-template and are happy with all of the feature of XPath. But I suspect that is fairly rare....