javascriptiossafariwebkitintl

Intl.NumberFormat not showing thousand separator on iOS 18.5 for values < 10000


I've recently updated my iOS device to version 18.5, and I noticed an unexpected behavior with Intl.NumberFormat.

Specifically, the thousands separator (".") is missing for values < 10000, while it still appears correctly for larger values.

I compared the same code on an older version (iOS 18.0), and the output is different:

const formatted = new Intl.NumberFormat("it-IT", {
  style: "currency",
  currency: "EUR"
  }).format(1234.56);

console.log(JSON.stringify(formatted)); 
// iOS 18.0: "1.234,56 €" ✅ (expected)
// iOS 18.5: "1234,56 €" ❌ (missing thousand separator)
Here’s a reproducible test in :

const 
  testCases = 
    [ { value: 1234.89,    expectGrouping: true } 
    , { value: 123456.89,  expectGrouping: true } 
    , { value: 1234567.89, expectGrouping: true } 
    ] 
, formatter        = new Intl.NumberFormat('it-IT', { style: "currency", currency: "EUR" })
, resultsContainer = document.querySelector('#results')
  ;
testCases.forEach(({ value, expectGrouping }) => 
  {
  const
    div                  = document.createElement('div')
  , formatted            = formatter.format(value)
  , hasThousandSeparator = /\d{1,3}([.,\s\u00A0])\d{3}/.test(formatted)
  , passed               = hasThousandSeparator === expectGrouping
    ;
  div.className   = 'result ' + (passed ? 'pass' : 'fail');
  div.textContent = `${value} → ${formatted} → ${passed ? "✅ OK" : "❌ NO separatore"}`;
  resultsContainer.appendChild(div);
  });
body
  {
  font-family : sans-serif;
  padding     : 1em;
  background  : #f9f9f9;
  }
.result 
  {
  padding       : 0.5em;
  margin-bottom : 0.5em;
  border-radius : 5px;
  font-family   : monospace;
  }
.pass 
  {
  background-color : #d4edda;
  color            : #155724;
  }
.fail 
  {
  background-color : #f8d7da;
  color            : #721c24;
  }
<h3>Test formattazione "it-IT" con Intl.NumberFormat</h3>
<div id="results"></div>

I tried looking into the WebKit and Safari changelogs, but I couldn't find anything directly related to this change.

Questions:

Thanks in advance for any insight or help!


Solution

  • I’m not sure if this issue is limited to iOS, this started after Apple bumped its ICU library to 76 (which uses CLDR46). In CLDR 46 the Italian locale’s minimumGroupingDigits was changed from 1 to 2, so by default you only get a "." separator once the integer part is ≥ 5 digits (i.e. ≥ 10,000) Github Issue Unicode JIRA

    For now, the workaround is to use useGrouping:'always' (you can play with other options to check which option works best for you)

    const 
      testCases = 
        [ { value: 1234.89,    expectGrouping: true } 
        , { value: 123456.89,  expectGrouping: true } 
        , { value: 1234567.89, expectGrouping: true } 
        ] 
    , formatter        = new Intl.NumberFormat('it-IT', { style: "currency", currency: "EUR", useGrouping: "always" })
    , resultsContainer = document.querySelector('#results')
      ;
    testCases.forEach(({ value, expectGrouping }) => 
      {
      const
        div                  = document.createElement('div')
      , formatted            = formatter.format(value)
      , hasThousandSeparator = /\d{1,3}([.,\s\u00A0])\d{3}/.test(formatted)
      , passed               = hasThousandSeparator === expectGrouping
        ;
      div.className   = 'result ' + (passed ? 'pass' : 'fail');
      div.textContent = `${value} → ${formatted} → ${passed ? "✅ OK" : "❌ NO separatore"}`;
      resultsContainer.appendChild(div);
      });
    body
      {
      font-family : sans-serif;
      padding     : 1em;
      background  : #f9f9f9;
      }
    .result 
      {
      padding       : 0.5em;
      margin-bottom : 0.5em;
      border-radius : 5px;
      font-family   : monospace;
      }
    .pass 
      {
      background-color : #d4edda;
      color            : #155724;
      }
    .fail 
      {
      background-color : #f8d7da;
      color            : #721c24;
      }
    <h3>Test formattazione "it-IT" con Intl.NumberFormat</h3>
    <div id="results"></div>