node.jsicu

Different behaviour of Intl.NumberFormat in node and browser


If I run this code in the browser and node I obtain two different results:

const moneyFormatter = new Intl.NumberFormat('it-IT', {
    style: 'currency',
    currency: 'EUR',
    minimumFractionDigits: 2
});

moneyFormatter.format(1);

Browser: 1,00 €

Node: €1.00


Solution

  • ICU and Node

    This problem is caused by the missing ICU data in the default Node build.

    Nodejs docs explains well how the Internationalization feature works:

    Node.js (and its underlying V8 engine) uses ICU to implement these features in native C/C++ code. However, some of them require a very large ICU data file in order to support all locales of the world.

    At the same time, it explains the limitations you have in the default Node build:

    Because it is expected that most Node.js users will make use of only a small portion of ICU functionality, only a subset of the full ICU data set is provided by Node.js by default.

    And so:

    Several options are provided for customizing and expanding the ICU data set either when building or running Node.js.

    Quick solution

    Install the full-icu npm package and you're done: every locale will be installed and will be available in your code. Just start you app with a dedicated env var pointing to the icu dataset installation path:

    NODE_ICU_DATA=node_modules/full-icu node YOURAPP.js
    

    Or, using the specific Node option:

    node --icu-data-dir=node_modules/full-icu YOURAPP.js
    

    The only disadvantage of this solution is the space required for the full icu dataset: ~27Mb.

    Slow, but space optimized solution

    Compile Node from sources bundling it with a specific ICU only.

    Checking available locales

    Intl.NumberFormat.supportedLocalesOf('it')
    

    It returns an empty array [] if the locale is not supported. It returns an array with the locale id ['it'] if the locale is supported.