Numbering and formatting output
Auto-numbering headings, formatting money and percentages, and the date formatting that only exists from XSLT 2.0 onwards.
xsl:number
<xsl:template match="h2">
<h2>
<xsl:number level="multiple" count="h1|h2" format="1.1." />
<xsl:value-of select="."/>
</h2>
</xsl:template>
<!-- Let the source carry the number, formatting it as roman numerals -->
<xsl:number value="@part" format="I" />
<!-- Numbering list items per section, restarting inside each section -->
<xsl:number level="any" count="li" from="section" format="1" />| Attribute | Meaning | Common values |
|---|---|---|
level | How siblings and ancestors are counted | single, multiple, any |
count | Which nodes contribute a number | A pattern, e.g. h1|h2 |
from | Where counting restarts | A pattern such as section |
format | Number style | 1, 01, a, I, with separators |
value | Number an explicit value instead of a node | Any numeric expression |
grouping-separator | Thousands separator | , or . |
format-number and dates
<xsl:decimal-format name="eu" decimal-separator="," grouping-separator="."/>
<xsl:value-of select="format-number(1234.5, '#,##0.00')"/> <!-- 1,234.50 -->
<xsl:value-of select="format-number(0.075, '0.0%')"/> <!-- 7.5% -->
<xsl:value-of select="format-number(42, '000')"/> <!-- 042 -->
<xsl:value-of select="format-number(1234.5, '#.##0,00', 'eu')"/> <!-- 1.234,50 -->
<xsl:value-of select="format-number(-12.3, '0.00;(0.00)')"/> <!-- (12.30) -->
<!-- XSLT 2.0 date formatting; in 1.0 use substring() or a host extension -->
<xsl:value-of select="format-date(current-date(), '[D01] [MNn] [Y0001]')"/>
<xsl:value-of select="format-dateTime(@updated, '[Y0001]-[M01]-[D01] [H01]:[m01]')"/>- The pattern is a picture, not a mask:
#is optional,0is mandatory, and the grouping separator is taken from the decimal format. - A negative subpattern after a semicolon lets you print accounting-style negatives.
- In XSLT 1.0,
format-datedoes not exist — a common workaround is a named template with a lookup table of month names. - Round-tripping a decimal through a double can lose precision; for money, format at the presentation layer and keep the exact value in the source.
💡
Locale is a parameter, not a stylesheet constant. Pass the locale in from the calling application and select a decimal format with it, otherwise every number in the site changes shape the moment a second language is added.
FAQ
Why does my heading numbering restart in every section?
Because the default level is single, which counts siblings only. Use level="multiple" with a count pattern covering the whole hierarchy to get 1.2.3 style numbering.
Can I format a date in XSLT 1.0?
Not with format-date, which is 2.0. Use substring() plus a lookup table, or pass an already-formatted string in as a parameter from the application.
Related
Output methods and real use cases Whitespace, text and special characters
Last refreshed 2026-09-18.