Convert First character of each word to upper case
Asked Answered
C

9

13

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

Cosmic answered 29/10, 2012 at 13:26 Comment(0)
T
18

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>
Thermolysis answered 29/10, 2012 at 13:53 Comment(3)
Thank you very much. This really help me to translate first character to upper. But this one assumes space in between strings. How can I dynamically find any non-alphanumeric letter instead of space and capitalise first letter. For e.g O'connel will be O'Connel and dave-manel will be Dave-ManelKremlin
@Kremlin - it's been a long time since I've done XSLT. I'm afraid I'm unable to answer your supplementary question. You'd be better off asking it as a brand new question. Sorry I can't be of more helpThermolysis
Thats fine no worries and thank you for your response.Kremlin
P
5

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

Proselytism answered 29/10, 2012 at 14:18 Comment(2)
OP has the xslt-1.0 tag... lower-case and upper-case is not available in 1.0Thermolysis
@Thermolysis Ooh, missed that. Wil change answer to reflect this for future reference. thanks!Proselytism
S
4

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.

Streeter answered 29/10, 2012 at 22:52 Comment(0)
H
2

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.

Hardison answered 9/4, 2015 at 22:36 Comment(0)
I
2

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>
Idette answered 6/12, 2017 at 15:19 Comment(0)
N
1

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.

Nickelodeon answered 23/7, 2020 at 10:23 Comment(0)
A
0

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'))
};
Abe answered 17/10, 2013 at 19:5 Comment(0)
C
0

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

Cancel answered 4/2, 2014 at 13:30 Comment(0)
Q
0

I ended up with something a tiny bit different. Using another answer I got:

A sequence of more than one item is not allowed as the result of a call to toddmo:proper-case#1 ("Hey", " ")

so this fixes that.

<xsl:stylesheet xmlns:toddmo="https://todd.mo"

<xsl:function name="toddmo:proper-case" as="xs:string">
    <xsl:param name="text" />
    <xsl:variable name="words">
        <xsl:for-each select="tokenize($text,' ')">
            <xsl:value-of select="concat(upper-case(substring(.,1,1)),lower-case(substring(.,2)))"/>
            <xsl:if test="position()!=last()">
                <xsl:text> </xsl:text>                    
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:value-of select="string-join($words,'')"/>
</xsl:function>

test

<xsl:value-of select="toddmo:proper-case('hI my name is toddmo')"/>

output

Hi My Name Is Toddmo

Quod answered 16/10, 2022 at 16:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.