<?xml version="1.0" encoding="utf-8"?> <!-- Format numbers according to ISO 80000-1 specifications. In particular, sequences of digits longer than four are separated into groups of up to three digits, separated by a thin space. --> <stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- General template for formatting numbers. --> <template name="number" match="number"> <!-- LaTeX just uses the \num macro from the siunitx package. --> <common formats="/latex/xelatex/"> <xsl:text>\num{</xsl:text> <xsl:apply-templates /> <xsl:text>}</xsl:text> </common> <common formats="/html/xhtml/"> <xsl:value-of select="infosci:format-number( node() )" /> </common> </template> <!-- Format a number in according to ISO 80000-1, as follows: * Sign is preserved. * A decimal number with no digits before the decimal point has a zero (0) inserted before the decimal point. * Sequences of digits longer than four are grouped into thousands separated by a thin space. Note: HTML only, as LaTeX can simply use the \num macro in the siunitx package. $unformatted-value: The value to be formatted. Returns: The formatted number as a string. --> <function name="infosci:format-number" as="xs:string"> <common formats="/html/xhtml/"> <xsl:param name="unformatted-value" /> <xsl:variable name="sign" select=" if ( matches( $unformatted-value, '^[-+]' ) ) then replace( $unformatted-value, '^([-+]).*', '$1' ) else ''" /> <xsl:variable name="left" select=" if ( matches( $unformatted-value, '^[-+]?\d+' ) ) then replace( $unformatted-value, '^[-+]?(\d+).*', '$1' ) else '0'" /> <xsl:variable name="right" select=" if ( matches ( $unformatted-value, '^[-+]?\d*\.\d+' ) ) then replace ( $unformatted-value, '^[-+]?\d*\.(\d+)', '$1' ) else ''" /> <xsl:sequence select=" concat( $sign, if ( string-length ( $left ) > 4 ) then infosci:separate-thousands( $left, 'l' ) else $left, if ( $right != '' ) then '.' else '', if ( string-length ( $right ) > 4 ) then infosci:separate-thousands( $right, 'r' ) else $right )" /> </common> </function> <!-- Splits a sequence of digits into thousand groups, separated by thin spaces (U+2009 THIN SPACE; this appears to work in both UTF-8 and ISO-8859-1). $unseparated-value: The value to be formatted. $mode: The "direction" in which to separate the digit sequence. 'l' or 'L': the input sequence is left of the decimal point. [default] 'r' or 'R': if the input sequence is right of the decimal point. Returns: The formatted number as a string. --> <function name="infosci:separate-thousands" as="xs:string"> <common formats="/html/xhtml/"> <xsl:param name="unseparated-value" /> <xsl:param name="mode" /> <!-- Sanity check. --> <xsl:choose> <xsl:when test="lower-case( $mode ) = 'l'" /> <xsl:when test="lower-case( $mode ) = 'r'" /> <xsl:otherwise> <xsl:message terminate="yes">ERROR: The mode parameter of function infosci:separate-thousands must be one of the values 'L', 'l', 'R' or 'r'.</xsl:message> </xsl:otherwise> </xsl:choose> <!-- Recursively subdivide the sequence. Terminate when sequence length <= 3. Note that we have to use the numeric entity for thin space rather than the character entity   because of the weird way XSLT handles character entities. Using the character entity, the HTML visibly displays as " ". --> <xsl:sequence select=" if ( string-length( $unseparated-value ) > 3 ) then concat( if ( lower-case( $mode ) = 'r' ) then substring( $unseparated-value, 1, 3 ) else infosci:separate-thousands( substring( $unseparated-value, 1, string-length( $unseparated-value ) - 3 ), $mode ), ' ', if ( lower-case( $mode ) = 'r' ) then infosci:separate-thousands( substring( $unseparated-value, 4 ), $mode ) else substring( $unseparated-value, string-length( $unseparated-value ) - 2 ) ) else $unseparated-value" /> </common> </function> </stylesheet>