Skip to main content

Set number format in reports

You can define the number format to apply in your reports – set the decimal separator, thousands separator, and the number of decimals. You can define the number format in the following ways:

In both approaches, you need to define the number format using parameters in a helper function. Then, you need to apply the function for each number presented in the template.

If no helper function is used to define the number format, the number format of the JavaScript data type Number will be used. The decimal separator is a period (.), the number of decimals is presented as it is and no thousand separator is applied. For example: 1337.1337.

Dynamic number formatting based on the user-chosen language

In the templates available in the Sample reports section, the Intl.NumberFormat JavaScript object creates a dynamic number format that depends on the language the user chooses when generating a report.

You can use the helper function below to set the number format based on a given locale (for example, en-US, fi-FI or sv-SE). If you don't want the number format to depend dynamically on the selected language, you can hardcode the language each time you apply the helper function in the template code.

The helper function below accepts the following input arguments:

  • numberToFormat (required)

  • locale (optional)

  • style (optional)

  • minFractions (optional)

  • maxfractions (optional)

If left empty, optional parameters are initialized to their default values defined in the function code below.

/**
* Format numbers to the specified locale using Intl.NumberFormat.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
* @param: {number} numberToFormat - The input number to format.
* @param: {string} locale - The locale string defining the format, for example, "en-US".
* @param: {string} style - The type of number to format the input number to, for example, "decimal".
* @param: {number} minFractions - The minimum number of decimals (as an integer).
* @param: {number} maxFractions - The maximum number of decimals (as an integer).
*/

function formatNumber(numberToFormat, locale, style, minFractions, maxFractions){
    if(isNaN(numberToFormat)) return null
    try{
        // Because of how the Handlebars language works, the default values have to be specified here and not where the input parameters are first defined.
        locale = (typeof locale !== 'string' ? undefined : locale)
        style = (typeof style !== 'string' ? 'decimal' : style)
        minFractions = (typeof minFractions !== 'number' ? 0 : minFractions)
        maxFractions = (typeof maxFractions !== 'number' ? 2 : maxFractions)

        const numberFormat = new Intl.NumberFormat(locale, {style: style,minimumFractionDigits: minFractions, maximumFractionDigits: maxFractions})
        return numberFormat.format(Number(numberToFormat))
    }catch(error){
        console.error("Unable to format number.")
        console.error(error)
        return numberToFormat
    }
}

Example 1. Using default values for input parameters. The example below uses the formatNumber() helper function with one required argument (the input number to format). Other input parameters are initialized to their default values.

{{formatNumber 1337.1337}}

Result:

1,337.13

Example 2. Passing arguments for all input parameters. The example below passes arguments to all five input parameters available in the formatNumber() helper function.

{{formatNumber 1337.1337, "sv-SE", "decimal", 2, 2}}

Result:

1 337,13

Static number formatting fully controlled by the user

In some cases, Intl.NumberFormat isn't suitable for defining the number format. For instance, a common interpretation of the Swiss number format is that all numbers except currencies should use the SI system and therefore have a comma (,) as the decimal separator and a space ( ) as the thousands separator. Currencies, however, should use a period (.) as the decimal separator and an apostrophe (') as the thousands separator. Such number format cannot be implemented using Intl.NumberFormat and a single locale.

In such cases, you can use the alternative number formatting function and specify all number formatting parameters directly each time the helper function is applied. This option lets you choose exactly what number format to use for each number.

The helper function below accepts the following input arguments:

  • inputNumber (required)

  • numberOfDecimals (optional)

  • decimal (optional)

  • thousandsSeparator (optional)

  • multiplyer (optional)

  • nan (optional)

  • zeros (optional)

If left empty, optional parameters are initialized to their default values defined in the function code below.

/**
 * Format numbers to a format fully controlled by your input parameters.
 * @param {number} inputNumber - The input number to format.
 * @param {number} numberOfDecimals - The number of decimal places.
 * @param {string} decimal - Decimal separator, for example, "." or ",".
 * @param {string} thousandsSeparator- Thousands separator, for example, " " or "'".
 * @param {number} multiplyer - The number to multiply the input number by (100 is favourably used for percentage).
 * @param {string} nan - The string to return if the input number parameter is not a number.
 * @param {boolean} zeros - Whether zeros should be returned as numbers (true) or emtpy strings (false).
 */
function formatNumberAlternative(inputNumber, numberOfDecimals, decimal, thousandsSeparator, multiplyer, nan, zeros) {
  // Default values are defined below (SI-style with decimal as comma and space as thousand separator).
  numberOfDecimals = typeof numberOfDecimals !== 'number' ? 2 : numberOfDecimals
  decimal = typeof decimal !== 'string' ? ',' : decimal
  thousandsSeparator = typeof thousandsSeparator !== 'string' ? ' ' : thousandsSeparator
  multiplyer = typeof multiplyer !== 'number' ? 1 : multiplyer
  nan = typeof nan !== 'string' ? '' : nan

  if (inputNumber == null || typeof inputNumber !== 'number') {
    return nan
  }

  if (inputNumber == 0.0 && !zeros) {
    return ''
  }

  inputNumber = (+(Math.round(+(inputNumber*multiplyer + 'e' + numberOfDecimals )) + 'e' + -numberOfDecimals )).toFixed(numberOfDecimals)
  const numberArray = inputNumber.toString().split('.')
  const separatedNumber = numberArray[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator)
  inputNumber = inputNumber == 'NaN' ? nan : numberArray[1] ? `${separatedNumber}.${numberArray[1]}` : separatedNumber
  inputNumber = inputNumber.toString().replace('.', decimal)
  return inputNumber
}

Example 1. Using default values for input parameters. The example below uses the helper function formatNumberAlternative() with one required argument (the input number to format). Other input parameters are initialized to their default values.

{{formatNumberAlternative 1337.1337}}

Result:

1 337,13

Example 2. Passing arguments for the first four input parameters. The example below passes arguments to the first four input parameters available in the formatNumberAlternative() helper function. The last three parameters are initialized to their default values specified in the helper function code.

{{formatNumberAlternative 1337.1337 2 "." "'"}}

Result:

1'337.13