Using non-default locales with toLocaleString() in Node.js

Published on in JavaScript and Node.js

Last updated on

With Node.js 13+, it should just work. With earlier Node.js versions, you need to install and use the full-icu package.

Table of contents

The problem

Using Node.js version 12 or earlier, Date.prototype.toLocaleString() and Number.prototype.toLocaleString() don't always produce correct results, at least with non-English (or non-default) locales. For example:

const date = new Date('2021-11-22')

console.log(date.toLocaleString('en'))
//=> Actual/expected: 11/22/2021, 2:00:00 AM (πŸ†—)

console.log(date.toLocaleString('fi'))
//=> Actual:   2021-11-22 2:00:00 (❗)
//=> Expected: 22.11.2021 klo 2.00.00

const number = 1234.56

console.log(number.toLocaleString('en', { style: 'currency', currency: 'USD' }))
//=> Actual/expected: $1,234.56 (πŸ†—)

console.log(number.toLocaleString('fi', { style: 'currency', currency: 'USD' }))
//=> Actual:   US$ 1,234.56 (❗)
//=> Expected: 1 234,56 $

The solution

The results depend on the ICU data (= localization data) used by Node.js. From site.icu-project.org:

ICU is a mature, widely used set of [...] libraries providing Unicode and Globalization support for software applications. ICU is widely portable and gives applications the same results on all platforms [...].

Node.js 13+

Starting from version 13.0.0, Node.js comes with full ICU support by default, so toLocaleString() should produce correct results.

From version 13.0.0 changelog:

Node.js releases are now built with default full-icu support. This means that all locales supported by ICU are now included and Intl-related APIs may return different values than before (Richard Lau) #29887.

Issue #19214 has also relevant discussion about this.

Note: version 13 is not an LTS (long-time support) version. Even-numbered versions – 14, 16, 18 and so on – are LTS versions and thus a better choice. See Node.js Releases.

Node.js 12 and earlier

You need to install and enable full ICU data manually:

  1. Run npm install full-icu cross-env to install the following packages:

    • full-icu: the full ICU data
    • cross-env: needed by Windows users to set the environment variable in the next step.
  2. Update the scripts section of package.json to set the environment variable NODE_ICU_DATA. For example:

    {
      "scripts": {
        // Before
        "start": "react-scripts start",
        "test": "react-scripts test",
    
        // After
        "start": "react-scripts start",
        "test": "cross-env NODE_ICU_DATA=node_modules/full-icu react-scripts test"
      }
    }
    

Now when you run npm test, Node.js will load the full ICU data from node_modules/full-icu, and toLocaleString() should produce correct results.

In the example above, you could also modify the start script, but it might be unnecessary; depends on what the script does. In this case the script opens a browser, so modifying the script would be unnecessary.

Further resources

The same info can be found in my Stack Overflow answer.