Using .toLocaleString() in Node.js
Just in case someone else stumbles upon this, here's how I formatted a number into a valid US dollar string while in a Node.js environment.
Number.prototype.toMoney = function() {
var integer = this.toString().split('.')[0];
var decimal = this.getDecimal();
integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
if( !decimal || !decimal.length ) {
decimal = "00";
} else if ( decimal.length === 1) {
decimal += '0';
} else if ( decimal.length > 2 ) {
decimal = decimal.substr(0, 2);
}
return '$' + integer + '.' + decimal;
};
Number.prototype.getDecimal = function() {
var n = Math.abs(this);
var dec = n - Math.floor(n);
dec = ( Math.round( dec * 100 ) / 100 ).toString();
if( dec.split('.').length ) {
return dec.split('.')[1];
} else return "";
};
There are a few boo-boo's here, namely extending the native Number
prototype. You will want to avoid this is 90% of the time; this is more specific to my particular implementation.
I blatantly stole the regex for formatting the commas from this question. and hacked together the decimal support of my own volition. Mileage may vary.
The results depend on the ICU data used by Node. 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 13+
Starting from version 13.0.0, Node comes with full ICU support by default. This means that formatting numbers should automatically work the same way in Node (from version 13 onwards) as it does in browsers.
From v13 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: v13 is not an LTS (long-time support) version, but v14 is, so v14 is a better choice. See Node.js Releases.
Node 12 and earlier
You need to install and enable full ICU data manually. Here's how:
Run
npm install full-icu --save
.Run also
npm install cross-env --save
to support Windows users (optional but recommended).Update the
scripts
section ofpackage.json
to set the environment variableNODE_ICU_DATA
. For example:{ "scripts": { // Before "start": "react-scripts start", "test": "react-scripts test", // After (omit "cross-env" if you didn't install the package in step two) "start": "react-scripts start", "test": "cross-env NODE_ICU_DATA=node_modules/full-icu react-scripts test" } }
This way, when you run npm test
, Node will load the full ICU data from node_modules/full-icu
, and you should get consistent results between browser and server environments.
You could also modify the start
script, but it might be unnecessary; depends on what the script does. In the example above, the script opens a browser, so modifying it would be unnecessary.
Disclaimer: I haven't tested whether this affects performance. In my case, I did this to fix Jest tests of an app that runs in the browser, so a small performance hit when running the tests would have been acceptable.
For further details, see "Internationalization support" in Node (v12) docs.
Kudos to Rndmax's answer here: Date toLocaleDateString in node
Based on this issue it appears that it was decided that shipping node.js with internationalization would make it too large. You can npm install intl
and require that, and it will replace toLocaleString
with a version that works.
So to update this for anyone facing the same issue...
We had used intl for our localization solution when server side rendering, but we recently had a requirement to add {timeZoneName: 'short'}
to our .toLocaleString()
options and this field is not supported.
Source Code from intl.js:
case 'timeZoneName':
fv = ''; // ###TODO
break;
Additionally, there was a breaking change in the latest patch release which forced us to lock down our version to 1.2.4.
Ultimately, we dropped usage of intl in favor of full-icu. Simply adding it to our yarn.lock solved all our Node.js server side date localization issues. Haven't verified currency localization, but so far so good. I recommend giving it a try.