skip to Main Content

Is there a way to format currency conditionally depending on number of decimals?
For example :

CHF 1’200.00 becomes CHF 1’200.-

CHF 1’200.2 becomes CHF 1’200.20

CHF 1’200.20 becomes CHF 1’200.20

MIN_FRACTION_DIGITS attribute will set it all to .00, which solves 2nd and 3rd case, but will not solve the first one.

  $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, 2);

Is there a way to do that? Or at least format decimals as subscript, for example?

2

Answers


  1. The only way I see is to set the settings for the NumberFormatter based on a condition and then adding .- if the result is a whole number.

    // Check if we have a decimal that isn't .00 and adjust settings accordingly.
    if (round($value, 2) == round($value, 0)) {
        $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 0);
    } else {
        $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2);
    }
    
    // Format the number
    $formattedValue = $formatter->formatCurrency($value, 'CHF');
    
    // Add '.-' if we have a whole number.
    if (strpos($formattedValue, '.') === false) {
        $formattedValue .= '.-';
    }
    

    This should cover the cases in your question:

    • 1200.00 -> CHF 1'200.-
    • 1200 -> CHF 1'200.-
    • 1200.20 -> CHF 1'200.20
    • 1200.2 -> CHF 1'200.20
    Login or Signup to reply.
  2. You can use a regular expression replace with a strictly-formatted value where decimals are always included to replace the zero padded values with a custom ending and will work with various locales that have different monetary separators.

    function customCurrencyFormat(
        NumberFormatter $formatter,
        string $currency,
        string|int|float $value
    ): string {
        // Ensure there are at least and no more than 2 fractional digits
        $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, 2);
        $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2);
        // Get the locale's monetary separator (usually , or .)
        $monetarySep = $formatter->getSymbol(
            NumberFormatter::MONETARY_SEPARATOR_SYMBOL
        );
        // Replace zero padded endings (like .00) with the separator symbol followed
        // by a dash.
        $formattedValue = preg_replace(
            '/' . preg_quote($monetarySep) . '0+(?=([^0-9]+$|$))/',
            $monetarySep . '-',
            $formatter->formatCurrency($value, $currency)
        );
        return $formattedValue;
    }
    
    customCurrencyFormat($formatter, 'CHF', '634598'); // CHF 634’598.-
    customCurrencyFormat($formatter, 'CHF', '3254636.4'); // CHF 3’254’636.40
    

    This function will work even if you switch to other locales with different separators and currencies. Note that this could become confusing for some since it appears like a negative value.

    Also note that if you use PHP earlier than 8.0 you may need to replace the type hint on the function’s last $value parameter to mixed since earlier versions don’t support union types.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search