Author:
nickdunn
Version:
1.0
Release Date:
5 May 2009
Category:
String Operations

Description

Converts an integer to a human-readable string e.g. 123 becomes “one hundred and twenty-three”. Supports minimum/maximum thresholds, rounding, and the ability to auto-round depending on the size of number supplied.

XSLT

View Raw
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:math="http://exslt.org/math" extension-element-prefixes="exsl math">

<!--
Name: Number To Words
Version: 1.0
Author: Nick Dunn <nick@nick-dunn.co.uk>
URL: http://www.getsymphony.com/downloads/xslt/file/20486/

Parameters:
* number (required) an integer
* resolution (optional) round 'number' to the nearest 'resolution', e.g. '10', '50, '1000' etc. Use '-1' to allow the template to choose an appropriate rounding resolution
* minimum (optional) if the number is less than this the returned string will be 'less than $number'
* maximum (optional) if the number is more than this the returned string will be 'more than $number'

Further usage examples:
http://www.getsymphony.com/forum/discussions/763/
-->

<xsl:template name="number-to-words">
	<xsl:param name="number" />
	<xsl:param name="resolution" />
	<xsl:param name="minimum" />
	<xsl:param name="maximum" />
	
	<xsl:variable name="words">
		<words>
			<word integer="0">zero</word>
			<word integer="1">one</word>
			<word integer="2">two</word>
			<word integer="3">three</word>
			<word integer="4">four</word>
			<word integer="5">five</word>
			<word integer="6">six</word>
			<word integer="7">seven</word>
			<word integer="8">eight</word>
			<word integer="9">nine</word>
			<word integer="10">ten</word>
			<word integer="11">eleven</word>
			<word integer="12">twelve</word>
			<word integer="13">thirteen</word>
			<word integer="14">fourteen</word>
			<word integer="15">fifteen</word>
			<word integer="16">sixteen</word>
			<word integer="17">seventeen</word>
			<word integer="18">eighteen</word>
			<word integer="19">nineteen</word>
			<word integer="20">twenty</word>
			<word integer="30">thirty</word>
			<word integer="40">forty</word>
			<word integer="50">fifty</word>
			<word integer="60">sixty</word>
			<word integer="70">seventy</word>
			<word integer="80">eighty</word>
			<word integer="90">ninety</word>
			<word handle="hundred">hundred</word>
			<word handle="thousand">thousand</word>
			<word handle="million">million</word>
			<word handle="less-than">less than</word>
			<word handle="more-than">more than</word>
			<word handle="about">about</word>
			<word handle="and">and</word>
		</words>
	</xsl:variable>
	
	<xsl:variable name="number-negative">
		<xsl:value-of select="$number &lt; 0"/>
	</xsl:variable>
	
	<xsl:variable name="number">
		<xsl:value-of select="math:abs(translate($number,',',''))"/>
	</xsl:variable>
	
	<xsl:if test="string(number($number)) != 'NaN'">

		<xsl:variable name="resolved-number">
		
			<xsl:variable name="auto-resolution">
				<xsl:choose>
					<xsl:when test="$resolution = -1">
						<xsl:choose>
							<xsl:when test="$number &lt; 20">
								<xsl:text>1</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 100">
								<xsl:text>5</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 500">
								<xsl:text>10</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 1000">
								<xsl:text>50</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 5000">
								<xsl:text>100</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 25000">
								<xsl:text>500</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 100000">
								<xsl:text>1000</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 500000">
								<xsl:text>5000</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 1000000">
								<xsl:text>10000</xsl:text>
							</xsl:when>
							<xsl:when test="$number &lt; 5000000">
								<xsl:text>100000</xsl:text>
							</xsl:when>
							<xsl:otherwise>
								<xsl:text>1000000</xsl:text>
							</xsl:otherwise>
						</xsl:choose>
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="$resolution"/>
					</xsl:otherwise>
				</xsl:choose>
			</xsl:variable>
			
			<xsl:choose>
				<xsl:when test="$auto-resolution and $auto-resolution &lt;= $number">
					<xsl:variable name="divided" select="$number div $auto-resolution"/>
					<xsl:choose>
						<xsl:when test="$divided - floor($divided) &gt;= 0.5">
							<xsl:value-of select="ceiling($divided) * $auto-resolution"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:value-of select="floor($divided) * $auto-resolution"/>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:when>
				<xsl:otherwise>
					<xsl:text>0</xsl:text>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:variable>
		
		<xsl:choose>
			
			<xsl:when test="$number &lt; $minimum">
				<xsl:value-of select="exsl:node-set($words)//word[@handle='less-than']"/>
				<xsl:text> </xsl:text>
				<xsl:call-template name="number-to-words">
					<xsl:with-param name="number" select="$minimum" />
				</xsl:call-template>
			</xsl:when>
			
			<xsl:when test="$number &gt; $maximum">
				<xsl:value-of select="exsl:node-set($words)//word[@handle='more-than']"/>
				<xsl:text> </xsl:text>
				<xsl:call-template name="number-to-words">
					<xsl:with-param name="number" select="$maximum" />
				</xsl:call-template>
			</xsl:when>
			
			<xsl:when test="$resolved-number != 0">				
				<xsl:if test="$resolved-number != $number">
					<xsl:value-of select="exsl:node-set($words)//word[@handle='about']"/>
					<xsl:text> </xsl:text>
				</xsl:if>
				
				<xsl:if test="$number-negative = 'true'">
					<xsl:value-of select="exsl:node-set($words)//word[@handle='minus']"/>
					<xsl:text> </xsl:text>
				</xsl:if>
				
				<xsl:call-template name="number-to-words">
					<xsl:with-param name="number" select="$resolved-number" />
				</xsl:call-template>				
			</xsl:when>
			
			<xsl:otherwise>
				
				<xsl:if test="$number-negative = 'true'">
					<xsl:value-of select="exsl:node-set($words)//word[@handle='minus']"/>
					<xsl:text> </xsl:text>
					<xsl:call-template name="number-to-words">
						<xsl:with-param name="number" select="$number"/>
					</xsl:call-template>
				</xsl:if>

				<xsl:choose>

					<xsl:when test="$number &lt; 21">
						<xsl:value-of select="exsl:node-set($words)//word[@integer = $number]"/>
					</xsl:when>

					<xsl:when test="$number &lt; 100">
						<xsl:value-of select="exsl:node-set($words)//word[@integer = (10 * floor($number div 10))]"/>
						<xsl:variable name="remainder" select="floor($number mod 10)"/>
						<xsl:if test="$remainder &gt; 0">
							<xsl:text>-</xsl:text>
							<xsl:value-of select="exsl:node-set($words)//word[position() = ($remainder + 1)]"/>
						</xsl:if>
					</xsl:when>

					<xsl:when test="$number &lt; 1000">
						<xsl:value-of select="exsl:node-set($words)//word[@integer = (floor($number div 100))]"/>
						<xsl:text> </xsl:text>
						<xsl:value-of select="exsl:node-set($words)//word[@handle='hundred']"/>
						<xsl:variable name="remainder" select="floor($number mod 100)"/>
						<xsl:if test="$remainder &gt; 0">
							<xsl:text> </xsl:text>
							<xsl:value-of select="exsl:node-set($words)//word[@handle='and']"/>
							<xsl:text> </xsl:text>
							<xsl:call-template name="number-to-words">
								<xsl:with-param name="number" select="$remainder"/>
							</xsl:call-template>
						</xsl:if>
					</xsl:when>

					<xsl:when test="$number &lt; 1000000">						
						<xsl:call-template name="number-to-words">
							<xsl:with-param name="number" select="floor($number div 1000)"/>
						</xsl:call-template>
						<xsl:text> </xsl:text>
						<xsl:value-of select="exsl:node-set($words)//word[@handle='thousand']"/>
						<xsl:variable name="remainder" select="floor($number mod 1000)"/>
						<xsl:if test="$remainder &gt; 0">
							<xsl:text>, </xsl:text>
							<xsl:if test="$remainder &lt; 100">
								<xsl:value-of select="exsl:node-set($words)//word[@handle='and']"/>
								<xsl:text> </xsl:text>
							</xsl:if>
							<xsl:call-template name="number-to-words">
								<xsl:with-param name="number" select="$remainder"/>
							</xsl:call-template>
						</xsl:if>						
					</xsl:when>

					<xsl:otherwise>						
						<xsl:call-template name="number-to-words">
							<xsl:with-param name="number" select="floor($number div 1000000)"/>
						</xsl:call-template>
						<xsl:text> </xsl:text>
						<xsl:value-of select="exsl:node-set($words)//word[@handle='million']"/>
						<xsl:variable name="remainder" select="floor($number mod 1000000)"/>
						<xsl:if test="$remainder &gt; 0">
							<xsl:text>, </xsl:text>
							<xsl:if test="$remainder &lt; 100">
								<xsl:value-of select="exsl:node-set($words)//word[@handle='and']"/>
								<xsl:text> </xsl:text>
							</xsl:if>
							<xsl:call-template name="number-to-words">
								<xsl:with-param name="number" select="$remainder"/>
							</xsl:call-template>
						</xsl:if>						
					</xsl:otherwise>

				</xsl:choose>
				
			</xsl:otherwise>
			
		</xsl:choose>
		
	</xsl:if>
	
</xsl:template>

</xsl:stylesheet>

Discuss this XSLT Utility

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details