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.

Uses package:intl locale data, including locale-specific grouping and numbering patterns. An unrecognized locale falls back to Bloom's small built-in formatter.

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 intlLocale =
      (locale ?? BloomI18n.instance.locale.value).replaceAll('-', '_');
  try {
    final formatter = intl.NumberFormat.decimalPatternDigits(
        locale: intlLocale, decimalDigits: decimalDigits);
    if (!useGrouping) formatter.turnOffGrouping();
    return formatter.format(value);
  } catch (_) {
    // Preserve the lightweight fallback for a locale absent from intl data.
  }
  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;
}