Question

I have a String and I need to convert the first letter of each word to upper case and rest to lower case using xsl, For example,

Input String= dInEsh sAchdeV kApil Muk

Desired Output String= Dinesh Sachdev Kapil Muk

Although, I know I have to use translate function for the purpose but how can I translate the first charter of each word to Upper-case and rest all in lower- case using XSLT 1.0

Thanks

Was it helpful?

Solution

The following is not "nice", and I'm sure somebody (mainly Dimitri) could come up with something far simpler (especially in XSLT 2.0)... but I've tested this and it works

<xsl:template name="CamelCase">
  <xsl:param name="text"/>
  <xsl:choose>
    <xsl:when test="contains($text,' ')">
      <xsl:call-template name="CamelCaseWord">
        <xsl:with-param name="text" select="substring-before($text,' ')"/>
      </xsl:call-template>
      <xsl:text> </xsl:text>
      <xsl:call-template name="CamelCase">
        <xsl:with-param name="text" select="substring-after($text,' ')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="CamelCaseWord">
        <xsl:with-param name="text" select="$text"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="CamelCaseWord">
  <xsl:param name="text"/>
  <xsl:value-of select="translate(substring($text,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" /><xsl:value-of select="translate(substring($text,2,string-length($text)-1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')" />
</xsl:template>

The basic idea is that you call CamelCase, if it find a space, then it runs CamelCaseWord on everything before the space (i.e. the first word) and then calls CamelCase again with the everything after the space (i.e. the rest of the sentence). Otherwise if no space is found (because it's got to the last word in the sentence), then it just calls CamelCaseWord.

The CamelCaseWord template simply translates the first character from lower to upper (if necessary) and all remaining characters from upper to lower (if necessary).

So to call it you'd have...

<xsl:call-template name="CamelCase">
   <xsl:with-param name="text">dInEsh sAchdeV kApil Muk</xsl:with-param>
</xsl:call-template>

OTHER TIPS

Additional:

I missed the 1.0 requirement in the question. This will only work from version 2.0.

Original answer below here.

I believe this one worked for me a while ago. Declare a function:

<xsl:function name="my:titleCase" as="xs:string">
    <xsl:param name="s" as="xs:string"/>
    <xsl:choose>
        <xsl:when test="lower-case($s)=('and','or')">
            <xsl:value-of select="lower-case($s)"/>
        </xsl:when>
        <xsl:when test="$s=upper-case($s)">
            <xsl:value-of select="$s"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="concat(upper-case(substring($s, 1, 1)), lower-case(substring($s, 2)))"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:function>

And use it:

<xsl:sequence select="string-join(for $x in tokenize($text,'\s') return my:titleCase($x),' ')"/>

credit goes to samjudson => http://p2p.wrox.com/xslt/80938-title-case-string.html

Here is a 8 years old FXSL 1.x (an XSLT 1.0 libray written completely in XSLT 1.0) solution:

test-strSplit-to-Words10.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
>
   <xsl:import href="strSplitWordDel.xsl"/>

   <!-- To be applied on: test-strSplit-to-Words10.xml -->

   <xsl:output indent="yes" omit-xml-declaration="yes"/>
   <xsl:variable name="vLower" 
        select="'abcdefgijklmnopqrstuvwxyz'"/>
   <xsl:variable name="vUpper" 
        select="'ABCDEFGIJKLMNOPQRSTUVWXYZ'"/>

    <xsl:template match="/">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-word-del">
          <xsl:with-param name="pStr" select="/"/>
          <xsl:with-param name="pDelimiters" 
                          select="', .(&#9;&#10;&#13;'"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>

    <xsl:template match="word">
       <xsl:choose>
         <xsl:when test="not(position() = last())">
           <xsl:value-of 
           select="translate(substring(.,1,1),$vLower,$vUpper)"/>
           <xsl:value-of select="substring(.,2)"/>
         </xsl:when>
         <xsl:otherwise>
           <xsl:value-of select="."/>
         </xsl:otherwise>
       </xsl:choose>
    </xsl:template>

    <xsl:template match="delim">
      <xsl:value-of select="."/>
    </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document (test-strSplit-to-Words10.xml):

<t>004.lightning crashes (live).mp3</t>

the result is:

004.Lightning Crashes (Live).mp3

When applied to this XML document (your provided sample):

dInEsh sAchdeV kApil Muk

the result is:

DInEsh SAchdeV KApil Muk

With just a little tweek, we get this code:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
>
   <xsl:import href="strSplitWordDel.xsl"/>

   <!-- To be applied on: test-strSplit-to-Words10.xml -->

   <xsl:output indent="yes" omit-xml-declaration="yes"/>
   <xsl:variable name="vLower" 
        select="'abcdefgijklmnopqrstuvwxyz'"/>
   <xsl:variable name="vUpper" 
        select="'ABCDEFGIJKLMNOPQRSTUVWXYZ'"/>

    <xsl:template match="/">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-word-del">
          <xsl:with-param name="pStr" select="/"/>
          <xsl:with-param name="pDelimiters" 
                          select="', .(&#9;&#10;&#13;'"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>

    <xsl:template match="word">
           <xsl:value-of 
           select="translate(substring(.,1,1),$vLower,$vUpper)"/>
           <xsl:value-of select="translate(substring(.,2), $vUpper, $vLower)"/>
    </xsl:template>

    <xsl:template match="delim">
      <xsl:value-of select="."/>
    </xsl:template>
</xsl:stylesheet>

which now produces the wanted result:

Dinesh Sachdev Kapil Muk

Explanation:

The str-split-word-del template of FXSL can be used for tokenization with (possibly more than one) delimiters specified as a string parameter.

You can also try this:

http://www.xsltfunctions.com/xsl/functx_camel-case-to-words.html

 <xsl:function name="functx:camel-case-to-words" as="xs:string"
                  xmlns:functx="http://www.functx.com">
      <xsl:param name="arg" as="xs:string?"/>
      <xsl:param name="delim" as="xs:string"/>

      <xsl:sequence select="
       concat(substring($arg,1,1),
                 replace(substring($arg,2),'(\p{Lu})',
                            concat($delim, '$1')))
     "/>

</xsl:function>

and backwards: http://www.xsltfunctions.com/xsl/functx_words-to-camel-case.html

<xsl:function name="functx:words-to-camel-case" as="xs:string"
              xmlns:functx="http://www.functx.com">
  <xsl:param name="arg" as="xs:string?"/>

  <xsl:sequence select="
     string-join((tokenize($arg,'\s+')[1],
       for $word in tokenize($arg,'\s+')[position() > 1]
       return functx:capitalize-first($word))
      ,'')
 "/>

</xsl:function>

Here's another short solution. It uses pure XSL-T 2.0. I know OP had a requirement for XSL-T 1.0, but since this page is ranked #1 on Google for 'xsl-t title case function' in 2015, this seems more relevant:

<xsl:function name="xx:fixCase">
    <xsl:param name="text" />
    <xsl:for-each select="tokenize($text,' ')">
        <xsl:value-of select="upper-case(substring(.,1,1))" />
        <xsl:value-of select="lower-case(substring(.,2))" />
        <xsl:if test="position() ne last()">
            <xsl:text> </xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:function>

Where 'xx' is your own namespace.

This same function on XQUERY:

function Xquery to Camel Case.

declare function xf:toCamelCase($text as xs:string?) as xs:string{
    if(contains($text,' ')) then
        fn:concat(xf:CamelCaseWord(substring-before($text,' ')),' ', xf:toCamelCase(substring-after($text,' ')))
    else
        xf:CamelCaseWord($text)
};

declare function xf:CamelCaseWord($text as xs:string?) as xs:string{
    fn:concat( translate(substring($text,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 
                translate(substring($text,2,string-length($text)-1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'))
};

A very short solution, utilising EXSLT's split() function:

<xsl:variable name='text' select='"dInEsh sAchdeV kApil Muk"' />
<xsl:variable name='lowers' select='"abcdefghijklmnopqrstuvwxyz"' />
<xsl:variable name='uppers' select='"ABCDEFGHIJKLMNOPQRSTUVWXYZ"' />

<xsl:template match="/">

    <xsl:for-each select='str:split($text, " ")'>
        <xsl:value-of select='concat(
            translate(substring(., 1, 1), $lowers, $uppers),
            translate(substring(., 2), $uppers, $lowers),
            " "
        )' />
    </xsl:for-each>
</xsl:template>

Working demo: http://www.xmlplayground.com/CNmKdF

It can be achieved using single line expression. Try out this

string-join(for $x in tokenize($text,'\s') return concat(upper-case(substring($x, 1, 1)), lower-case(substring($x, 2))),' ')

Note - This has been verified with Orbeon Forms formula.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top