formatNumber function

String formatNumber(
  1. num value, {
  2. String? locale,
  3. int? decimalDigits,
  4. bool useGrouping = true,
  5. String style = 'decimal',
})

Formats a num value with locale-aware grouping and decimal separators in pure Dart.

Formats integers and floating point numbers according to language conventions, using appropriate thousand separators (",", ".", " ", or "٬") and decimal points (".", ",", or "٫"). When locale is omitted, uses the active BloomI18n.instance.locale signal value.

Limitations vs Full CLDR

This is a lightweight, pure-Dart implementation covering major language families (English, French, German, Spanish, Portuguese, Italian, Russian, Japanese, Chinese, Arabic, etc.). It does not include full Unicode CLDR tables or localized numbering systems (e.g. eastern Arabic-Indic numerals).

formatNumber(1234567.89, locale: 'en-US'); // "1,234,567.89"
formatNumber(1234567.89, locale: 'de-DE'); // "1.234.567,89"
formatNumber(1234567.89, locale: 'fr-FR'); // "1 234 567,89"

See also:

Implementation

String formatNumber(
  num value, {
  String? locale,
  int? decimalDigits,
  bool useGrouping = true,
  String style = 'decimal',
}) {
  final loc = (locale ?? BloomI18n.instance.locale.value).replaceAll('_', '-').toLowerCase();
  final lang = loc.split('-').first;

  // Determine separators
  String decimalSep = '.';
  String groupSep = ',';

  if (['de', 'it', 'es', 'pt', 'nl', 'tr', 'id'].contains(lang)) {
    decimalSep = ',';
    groupSep = '.';
  } else if (['fr', 'ru', 'sv', 'pl', 'cs', 'fi', 'no', 'uk', 'bg'].contains(lang)) {
    decimalSep = ',';
    groupSep = ' ';
  } else if (['ar'].contains(lang)) {
    decimalSep = '٫';
    groupSep = '٬';
  }

  // Handle decimals
  final isInt = value is int || value == value.roundToDouble();
  String formatted;

  if (decimalDigits != null) {
    formatted = value.toStringAsFixed(decimalDigits);
  } else if (isInt) {
    formatted = value.toInt().toString();
  } else {
    formatted = value.toString();
  }

  final parts = formatted.split('.');
  String integerPart = parts[0];
  final isNegative = integerPart.startsWith('-');
  if (isNegative) integerPart = integerPart.substring(1);

  if (useGrouping && integerPart.length > 3) {
    final buf = StringBuffer();
    final len = integerPart.length;
    for (int i = 0; i < len; i++) {
      if (i > 0 && (len - i) % 3 == 0) {
        buf.write(groupSep);
      }
      buf.write(integerPart[i]);
    }
    integerPart = buf.toString();
  }

  if (isNegative) integerPart = '-$integerPart';

  if (parts.length > 1 && parts[1].isNotEmpty) {
    return '$integerPart$decimalSep${parts[1]}';
  }
  return integerPart;
}