Попробуйте, хотя по общему признанию вызов translate немного уродлив:
<xsl:template match="field">
<xsl:value-of select="string-length(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',''))+1" />
</xsl:template>
Это, конечно, требует, чтобы строка в вызове перевода включала все символы, которые могли появиться в поле, кроме пробелов. Сначала он вызывает normalize-space(.)
, чтобы убрать как двойные пробелы, так и все, кроме текстового содержимого. Затем он удаляет все, кроме пробелов, считает длину полученной строки и добавляет ее. Это означает, что если у вас есть <p>My<b>text</b> test</p>
, это будет считаться как 2, так как Mytext
будет считаться одним словом.
Если вам нужно более надежное решение, оно немного более замысловатое:
<xsl:template match="field">
<xsl:call-template name="countwords">
<xsl:with-param name="text" select="normalize-space(.)" />
</xsl:call-template>
</xsl:template>
<xsl:template name="countwords">
<xsl:param name="count" select="0" />
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="contains($text,' ')">
<xsl:call-template name="countwords">
<xsl:with-param name="count" select="$count + 1" />
<xsl:with-param name="text" select="substring-after($text,' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$count + 1" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
Это передает результат normalize-space(.)
в рекурсивный именованный шаблон, который вызывает себя, когда в $text
есть пробел, увеличивает его параметр count
и отбирает первое слово каждый раз, используя вызов substring-after($text,' ')
. Если пробела нет, он обрабатывает $text
как одно слово и просто возвращает $count + 1
(+1 для текущего слова).
Имейте в виду, что это будет включать ВСЕ текстовое содержимое в поле, включая содержимое внутренних элементов.
РЕДАКТИРОВАТЬ: Примечание для себя: прочитайте вопрос правильно, только заметил, что вам нужно больше, чем просто подсчет слов. Это значительно сложнее сделать, если вы хотите включить какие-либо теги xml, но небольшая модификация вышеперечисленного - это все, что нужно, чтобы выплевывать каждое слово, а не просто считать их:
<xsl:template name="countwords">
<xsl:param name="count" select="0" />
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="$count = 30" />
<xsl:when test="contains($text,' ')">
<xsl:if test="$count != 0"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of select="substring-before($text,' ')" />
<xsl:call-template name="countwords">
<xsl:with-param name="count" select="$count + 1" />
<xsl:with-param name="text" select="substring-after($text,' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$text" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
Есть дополнительное предложение <xsl:when
, чтобы просто прекратить рекурсию, когда число достигает 30, и рекурсивное предложение выводит текст после добавления пробела в начале, если это не было первое слово.
РЕДАКТИРОВАТЬ: Хорошо, вот решение, которое сохраняет экранированный контент XML:
<xsl:template match="field">
<xsl:call-template name="countwords">
<xsl:with-param name="text" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="countwords">
<xsl:param name="count" select="0" />
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="starts-with($text, '<')">
<xsl:value-of select="concat(substring-before($text,'>'),'>')" />
<xsl:call-template name="countwords">
<xsl:with-param name="count">
<xsl:choose>
<xsl:when test="starts-with(substring-after($text,'>'),' ')"><xsl:value-of select="$count + 1" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$count" /></xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="text" select="substring-after($text,'>')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="(contains($text, '<') and contains($text, ' ') and string-length(substring-before($text,' ')) < string-length(substring-before($text,'<'))) or (contains($text,' ') and not(contains($text,'<')))">
<xsl:choose>
<xsl:when test="$count < 29"><xsl:value-of select="concat(substring-before($text, ' '),' ')" /></xsl:when>
<xsl:when test="$count = 29"><xsl:value-of select="substring-before($text, ' ')" /></xsl:when>
</xsl:choose>
<xsl:call-template name="countwords">
<xsl:with-param name="count">
<xsl:choose>
<xsl:when test="normalize-space(substring-before($text, ' ')) = ''"><xsl:value-of select="$count" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$count + 1" /></xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="text" select="substring-after($text,' ')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="(contains($text, '<') and contains($text, ' ') and string-length(substring-before($text,' ')) > string-length(substring-before($text,'<'))) or contains($text,'<')">
<xsl:if test="$count < 30">
<xsl:value-of select="substring-before($text, '<')" />
</xsl:if>
<xsl:call-template name="countwords">
<xsl:with-param name="count" select="$count" />
<xsl:with-param name="text" select="concat('<',substring-after($text,'<'))" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$count < 30">
<xsl:value-of select="$text" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Если вам нужно что-то объяснить лучше, дайте мне знать, я бы не стал вдаваться в подробности, если вам это не нужно!