You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ICU behavior discrepancy in Current and LTS Node.js versions and latest browsers #58870
How often does it reproduce? Is there a required condition?
100% cases
What is the expected behavior? Why is that the expected behavior?
Output:
1 234,56 грн
Newer CLDR rules will be used with ICU 77.1+
What do you see instead?
Output:
1 234,56 ₴
Additional information
In newer ICU versions (77.1+), which are used by modern browsers the behavior for Ukrainian currency formatting is:
For locale: uk and currency: UAH with currencyDisplay: symbol, the formatted result should use the textual currency symbol грн, e.g.:
1 000,00 грн
For currencyDisplay narrowSymbol, the formatted result should use the narrow symbol ₴, e.g.:
1 000,00 ₴
So to sumarize:
ICU Version
Locale
currencyDisplay
Output Example
76.1
uk
symbol
1 000,00 ₴
77.1+
uk
symbol
1 000,00 грн
76.1/77.1+
uk
narrowSymbol
1 000,00 ₴
Why?
The change in ICU 77.1+ aligns with the official CLDR (Unicode Common Locale Data Repository) data, which specifies "грн" as the correct symbol for Ukrainian Hryvnia in the Ukrainian locale when using currencyDisplay: symbol. "₴" is reserved for currencyDisplay: narrowSymbol.
The solution will be to update ICU to 77.1+ version.
Yes, you are right about updated ICU versions used in Node.js, so I will close the issue.
And I, on the other hand, had an incorrect assumption that browsers have to be a better "updaters" than they actually are.
Thank you for detailed answer and time ❤
To leave some meta and explain myself:
I got into confusions.
First confusion:
The real data of CLDR v45 and v47 for that area was not touched. So for me it looks like some other parts of CLDR/ICU handling was changed, or I don't have full picture on how this things handled and defined:
CLDR v45
currencyDisplay: "narrowSymbol" - first I thought that it means "short name", so hrn or грн (in uk locale) is really a short name for "Ukrainian hryvnas" and price tags usually use that.
Inside ECMA spec there is a part about currencyDisplay:
[[CurrencyDisplay]] is one of the String values "code", "symbol", "narrowSymbol", or "name", specifying whether to display the currency as an ISO 4217 alphabetic currency code, a localized currency symbol, or a localized currency name if formatting with the "currency" style. https://402.ecma-international.org/#sec-properties-of-intl-numberformat-instances
And looks like there is no description on what should be used for narrowSymbol:
"code" - ISO 4217 alphabetic currency code (UAH)
"symbol" - a localized currency symbol (₴)
"narrowSymbol" - looks like there is no explanation on what should be used for narrowSymbol
"name" - localized currency name (Ukrainian Hryvnia)
P.S. if you know someone from ECMA standardization team, please ping them, so this bug will be addressed (on whatever level (ECMA, CLDR, ICU) that happening).
For me, I'm a bit tired to dig into this problem, so for now I will continue using a hack to correctly show narrowSymbol in my code
You can see the generated data match the data field I mention and there is difference between the CLDR data and the generated data.
In conclude, Node.js is actually close to the expectation on using the CLDR data. Chromium may need to update the transformed data in order to display correctly.
Update a year later: still diverging. Chrome 150 shows грн for symbol; stock Node at CLDR 48 (and Firefox) still show ₴ - upstream CLDR still has symbol=₴ / variant=грн, so the Chromium transformed-data divergence found above remains unfixed.
Activity
climba03003 commentedon Jun 28, 2025
Node.jsis actually using the latest version of ICU 77.1 with CIDR 47.You can check the source in https://github.com/nodejs/node/tree/main/deps/icu-small or using command
node -e "console.log(process.versions)".Chromiumis not using the latest version as you stated.You can find the latest commit in https://chromiumdash.appspot.com/branches
Referencing the version 138.0.7204.140 https://chromium.googlesource.com/chromium/src.git/+/c3479d8b40c9b0340e8f3cbb8fb4838cfb835f6a
Check the third-party CIDR version https://chromium.googlesource.com/chromium/src.git/+/c3479d8b40c9b0340e8f3cbb8fb4838cfb835f6a/third_party/cldr/
Check the third-party ICU version https://chromium.googlesource.com/chromium/src.git/+/c3479d8b40c9b0340e8f3cbb8fb4838cfb835f6a/third_party/icu
The ICU version is linked to https://chromium.googlesource.com/chromium/deps/icu/+/b929596baebf0ab4ac7ec07f38365db4c50a559d
In conclude Chromium 138 is using ICU 74 with CIDR 45.
Most of the time
Node.jsupdates ICU more often than Chromium.If there is difference, Chromium is actually the outdated one.
Note: Firefox shows the same result with Node.js.
sidoruk-sv commentedon Jun 28, 2025
Ooofff... this bug was deeper than I thought.
Yes, you are right about updated ICU versions used in Node.js, so I will close the issue.
And I, on the other hand, had an incorrect assumption that browsers have to be a better "updaters" than they actually are.
Thank you for detailed answer and time ❤
To leave some meta and explain myself:
I got into confusions.
First confusion:
The real data of CLDR v45 and v47 for that area was not touched. So for me it looks like some other parts of CLDR/ICU handling was changed, or I don't have full picture on how this things handled and defined:
CLDR v45
https://github.com/unicode-org/cldr/blob/release-45/common/main/uk.xml#L7834-L7836
CLDR v47
https://github.com/unicode-org/cldr/blob/release-47/common/main/uk.xml#L7889-L7891
Second confusion:
currencyDisplay: "narrowSymbol"- first I thought that it means "short name", sohrnorгрн(in uk locale) is really a short name for "Ukrainian hryvnas" and price tags usually use that.Also as National Bank of Ukraine stated:
скорочена назва – грнthe shortened name is hrnThird confusion:
Inside ECMA spec there is a part about currencyDisplay:
And looks like there is no description on what should be used for narrowSymbol:
"code" - ISO 4217 alphabetic currency code (UAH)
"symbol" - a localized currency symbol (₴)
"narrowSymbol" -
looks like there is no explanation on what should be used for narrowSymbol"name" - localized currency name (Ukrainian Hryvnia)
P.S. if you know someone from ECMA standardization team, please ping them, so this bug will be addressed (on whatever level (ECMA, CLDR, ICU) that happening).
For me, I'm a bit tired to dig into this problem, so for now I will continue using a hack to correctly show
narrowSymbolin my codeclimba03003 commentedon Jun 28, 2025
Here the data being use for the currency.
From my understanding, the currency display should map into the below table.
In other to understand why it's show difference on
symbolandnarrowSymbol.Node.jsis usingicu-smallandChromiumis usingicu.For the data use by
Chromiumis actually generated fromCLDRand transformed in another format.Name: https://chromium.googlesource.com/chromium/deps/icu/+/b929596baebf0ab4ac7ec07f38365db4c50a559d/source/data/curr/uk.txt#932
Symbol: https://chromium.googlesource.com/chromium/deps/icu/+/b929596baebf0ab4ac7ec07f38365db4c50a559d/source/data/curr/uk.txt#931
Narrow Symbol: https://chromium.googlesource.com/chromium/deps/icu/+/b929596baebf0ab4ac7ec07f38365db4c50a559d/source/data/curr/root.txt#189
You can see the generated data match the data field I mention and there is difference between the CLDR data and the generated data.
In conclude,
Node.jsis actually close to the expectation on using the CLDR data.Chromiummay need to update the transformed data in order to display correctly.jay123anta commentedon Aug 6, 2026
Update a year later: still diverging. Chrome 150 shows
грнforsymbol; stock Node at CLDR 48 (and Firefox) still show₴- upstream CLDR still hassymbol=₴/variant=грн, so the Chromium transformed-data divergence found above remains unfixed.Check your own browser against a Node snapshot (~30s, client-side): https://jay123anta.github.io/intl-drift/demo/ - from https://github.com/jay123anta/intl-drift, which catches server-vs-browser drift in CI.