Re: [xsl] recursive string replace with tags

Subject: Re: [xsl] recursive string replace with tags
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Wed, 2 Jul 2003 22:34:03 +0100
Hi François,

Thanks for spelling out your problem so clearly.

> *The goal:*
> I'm inserting a <a> tag in my source document around each occurence
> of words mentioned in a dictionary (mygloss.xml).

OK, here goes. You need a recursive template that takes a string and a
list of replacements:

<xsl:template name="gloss">
  <xsl:param name="string" />
  <xsl:param name="gloss" select="document('mygloss.xml')/dic/item" />
  ...
</xsl:template>

There are two stopping conditions that should cause the template to
stop recursing: one is when $string is empty and one is when $gloss is
empty. In either case, giving the value of $string gives you what you
want:

<xsl:template name="gloss">
  <xsl:param name="string" />
  <xsl:param name="gloss" select="document('mygloss.xml')/dic/item" />
  <xsl:choose>
    <xsl:when test="$string and $gloss">
      ...
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$string" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The next test is to see if the first word in the glossary appears in
the string:

<xsl:template name="gloss">
  <xsl:param name="string" />
  <xsl:param name="gloss" select="document('mygloss.xml')/dic/item" />
  <xsl:choose>
    <xsl:when test="$string and $gloss">
      <xsl:choose>
        <xsl:when test="contains($string, $gloss[1]/mot)">
          ...
        </xsl:when>
        <xsl:otherwise>
          ...
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$string" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

If the string doesn't contain the first word in the glossary, then the
result needs to be the result of calling the template on the string
with the rest of the words in the glossary:

<xsl:template name="gloss">
  <xsl:param name="string" />
  <xsl:param name="gloss" select="document('mygloss.xml')/dic/item" />
  <xsl:choose>
    <xsl:when test="$string and $gloss">
      <xsl:choose>
        <xsl:when test="contains($string, $gloss[1]/mot)">
          ...
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="gloss">
            <xsl:with-param name="string" select="$string" />
            <xsl:with-param name="gloss"
                            select="$gloss[position() > 1]" />
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$string" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

If the string does contain the first word, then you need to do three
things:

  1. Process the string before the first word to see if it contains
     any of the remaining words in the glossary.

  2. Create an <a> element around the word to link to the translation.

  3. Process the string after the first word to see if it contains the
     word again.

Thus the completed template is:

<xsl:template name="gloss">
  <xsl:param name="string" />
  <xsl:param name="gloss" select="document('mygloss.xml')/dic/item" />
  <xsl:choose>
    <xsl:when test="$string and $gloss">
      <xsl:choose>
        <xsl:when test="contains($string, $gloss[1]/mot)">
          <xsl:variable name="mot" select="$gloss[1]/mot" />
          <xsl:call-template name="gloss">
            <xsl:with-param name="string"
                            select="substring-before($string, $mot)" />
            <xsl:with-param name="gloss"
                            select="$gloss[position() > 1]" />
          </xsl:call-template>
          <a href="{$gloss[1]/trans}">
            <xsl:value-of select="$mot" />
          </a>
          <xsl:call-template name="gloss">
            <xsl:with-param name="string"
                            select="substring-after($string, $mot)" />
            <xsl:with-param name="gloss" select="$gloss" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="gloss">
            <xsl:with-param name="string" select="$string" />
            <xsl:with-param name="gloss"
                            select="$gloss[position() > 1]" />
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$string" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

To invoke the stylesheet use something like:

<xsl:template match="doc">
  <xsl:call-template name="gloss">
    <xsl:with-param name="string" select="string(.)" />
  </xsl:call-template>
</xsl:template>

I hope that gets you on the right track. Let us know if anything is
unclear.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread