Number::currency

Formats a value as currency, choosing the symbol and its placement from the locale.

Signature
Number::currency(int|float $number, string $in = '', ?string $locale = null, ?int $precision = null): string|false
  • Added in Laravel 10.33.0

Parameters

ParameterTypeDescription
$$numberint|floatThe monetary amount, in major units (dollars, not cents).
$$inoptionalstring= ''ISO 4217 currency code such as USD or EUR. An empty string falls back to the class default currency.
$$localeoptional?string= nullICU locale that decides the symbol, grouping and symbol placement.
$$precisionoptional?int= nullExact fraction digits, overriding the currency's own default.

Return value

string|falseThe formatted currency string, or false if the underlying NumberFormatter fails.

Errors

Throws RuntimeException when the intl PHP extension is not loaded.

Changelog

  • PHP 10.33.0Introduced together with the Illuminate\Support\Number class.
  • PHP 11.35.0The default currency became configurable through Number::useCurrency() and Number::withCurrency(), and readable through Number::defaultCurrency().
  • PHP 11.42.0Added the $precision parameter.

Number::currency() formats an amount using ICU's currency rules. The currency code decides the symbol and the default number of fraction digits; the locale decides everything else — grouping character, decimal character, and whether the symbol sits before or after the number. This is why Number::currency(1000, in: 'EUR') gives €1,000.00 under en but 1.000,00 € under de. The pair matters, not just the code.

Two things that bite

First, the amount is in major units. If you store cents as integers — and you should — divide by 100 before calling this, and never let the formatted string flow back into arithmetic.

Second, locales that place the symbol after the number separate it with a non-breaking space (U+00A0), not a plain space. String comparisons in tests and str_contains($output, ' €') checks fail on this constantly.

Defaults

An empty $in falls back to the class default, initially USD. Change it once in a service provider with Number::useCurrency('GBP'), or scope it with Number::withCurrency(). Zero-decimal currencies such as JPY get no fraction digits unless you force precision.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Rendering a cart total in the customer's own locale from a single amount column
  • Producing an invoice line in EUR for a German customer and USD for an American one
  • Showing a zero-decimal currency such as JPY without inventing cents

Frequently asked questions

How do I format a price stored in cents?

Divide by 100 immediately before formatting: Number::currency($model->price_cents / 100). Keep every sum, discount and tax calculation in integer cents, because the formatted string is display output and rounding it back into arithmetic loses money.

Why does my assertion on a euro amount fail even though the string looks right?

Locales that place the symbol after the number use a non-breaking space (U+00A0) as the separator, not an ordinary space. Compare against "1.000,00\u{00A0}€" or normalise the whitespace before asserting.

How do I change the default currency from USD?

Call Number::useCurrency('GBP') in a service provider's boot method, available since Laravel 11.35. For a single block, wrap it in Number::withCurrency('EUR', fn () => ...), which restores the previous default afterwards.

More examples

Cents in the database, dollars on the page

Every sum happens in integer cents and the division to major units happens only at the point of display, so no rounding error accumulates. Printed: $2,499.00, $15.00, $200.83, $2,714.83.

Example · php
<?php

use Illuminate\Support\Number;

$lines = [
    ['label' => 'Subtotal', 'cents' => 249900],
    ['label' => 'Shipping', 'cents' => 1500],
    ['label' => 'Tax',      'cents' => 20083],
];

$total = array_sum(array_column($lines, 'cents'));

foreach ($lines as $line) {
    printf("%-9s %s\n", $line['label'], Number::currency($line['cents'] / 100));
}

printf("%-9s %s\n", 'Total', Number::currency($total / 100));

One amount, several markets

The same 1234.5 renders as $1,234.50, then with the euro sign trailing under de and fr but with different grouping characters, then £1,234.50, then ¥1,235 — JPY has zero fraction digits by definition, so ICU rounds rather than showing decimals.

Example · php
<?php

use Illuminate\Support\Number;

$markets = [
    ['USD', 'en'],
    ['EUR', 'de'],
    ['EUR', 'fr'],
    ['GBP', 'en_GB'],
    ['JPY', 'ja'],
];

foreach ($markets as [$code, $locale]) {
    printf("%-3s %-5s %s\n", $code, $locale, Number::currency(1234.5, in: $code, locale: $locale));
}

Setting the application default

After boot(), a bare Number::currency(1000) yields £1,000.00. withCurrency() swaps the default for the duration of the closure and restores it afterwards even if the callback throws, so the report cannot leak its currency into the rest of the request.

Example · php
<?php

namespace App\Providers;

use Illuminate\Support\Number;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Number::useCurrency('GBP');
        Number::useLocale('en_GB');
    }
}

// Elsewhere, a single report in another currency:
$eur = Number::withCurrency('EUR', fn () => Number::currency(1000));

Discussion

  • Be the first to comment on this lesson.