---
title: "Formatting & parsing — Money for Laravel | Roundly"
description: "Locale-aware output with FormatOptions, a deterministic fallback formatter, a strict parser for localized input and the @money directive."
url: https://roundly-consulting.com/open-source/docs/money-for-laravel/formatting-parsing
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Money for Laravel](https://roundly-consulting.com/open-source/docs/money-for-laravel.md)

# Formatting & parsing

format() renders through the bound MoneyFormatter: ICU when ext-intl is loaded, otherwise a deterministic formatter. The locale comes from the argument, then money.formatting.locale, then the app locale:

```php
use RoundlyConsulting\Money\Formatting\FormatOptions;

$usd = Money::ofMajor('1234.50', 'USD');

$usd->format();                                // app locale (or money.formatting.locale)
$usd->format('en_US');                         // "$1,234.50"
$usd->format('en_US', FormatOptions::code());  // "USD 1,234.50"
$usd->format('en_US', FormatOptions::none());  // "1,234.50"
$usd->format('en_US', new FormatOptions(grouping: false));   // "$1234.50"
$usd->format('en_US', new FormatOptions(fractionDigits: 0, rounding: RoundingMode::HalfEven)); // "$1,234"
Money::ofMajor('10', 'USD')->format('en_US', FormatOptions::whole());  // "$10"

Money::ofMajor('1234.50', 'EUR')->format('de_CH', FormatOptions::code()); // "EUR 1’234.50"
Money::ofMajor('1234.50', 'EUR')->format('sk', FormatOptions::code());    // "1 234,50 EUR"
Money::ofMinor(1234, 'JPY')->format('en_US');   // "¥1,234" — exponent 0
Money::ofMinor(1234, 'BHD')->format('en_US');   // "BHD 1.234" — exponent 3
Money::ofMinor('1234567890123456789', 'USD')->format('en_US'); // "$12,345,678,901,234,567.89"
```

## FormatOptions

- display — CurrencyDisplay::Symbol, Code or None; shortcuts FormatOptions::code() and FormatOptions::none().
- fractionDigits — null means the currency exponent; fewer digits round with rounding (null = money.rounding).
- trimTrailingZeros — FormatOptions::whole() drops the zero fraction of whole amounts.
- grouping — false turns off thousands separators.

ICU formats doubles, exact to 15 significant digits. Beyond that the package lays the exact decimal digits into ICU’s template, so separators, grouping (Indian 3/2 included) and the locale’s numbering system still match. Custom currencies unknown to ICU render with their own symbol, such as “pts 1,200”.

## Deterministic fallback

Set MONEY\_FORMATTER=decimal (or run auto without ext-intl) for identical output on every host, driven by the fallback pattern and separators. Setting intl explicitly without the extension fails loud with FormatterUnavailable.

```php
// config/money.php — used when MONEY_FORMATTER=decimal, or with auto and no ext-intl
'formatting' => [
    'driver' => env('MONEY_FORMATTER', 'auto'),
    'fallback' => [
        'pattern' => '{sign}{amount} {code}',   // placeholders: {sign} {amount} {code} {symbol}
        'decimal_separator' => '.',
        'thousands_separator' => ',',
    ],
],

Money::ofMajor('1234.50', 'USD')->format();   // "1,234.50 USD" — identical on every host
```

## Parsing input

```php
Money::parse('1 234,50 EUR', locale: 'sk');     // 1234.50 EUR
Money::parse('(1,234.50)', 'USD', 'en');         // -1234.50 USD — accounting negative
Money::parse("CHF 1’234.50", locale: 'de_CH');   // 1234.50 CHF
Money::parse('12,34,567.50', 'INR', 'hi_IN');    // 1234567.50 INR — Indian grouping
Money::parse('$5', 'CAD', 'en');                 // 5.00 CAD — a shared symbol reads as the argument
Money::parse('$5');                              // 5.00 USD in a USD shop — InvalidAmount in a EUR one

Money::parse('1.23.4', 'EUR', 'en');             // throws InvalidAmount — refused, never guessed
Money::parse('10.005', 'USD', 'en');             // throws RoundingNecessary
```

- An optional currency token at the start or the end: a registered code or a symbol. A symbol several countries write ($, £, ¥, kr) is read as the currency argument, or as money.default\_currency when that currency writes it; otherwise it is refused (InvalidAmount), never guessed — Money::parse('$5') is 5.00 USD in a USD shop and an error in a EUR one.
- A detected currency that differs from the argument throws CurrencyMismatch; no token and no argument fall back to money.default\_currency.
- Signs: a leading or trailing minus or plus, or accounting parentheses. Two signs or unbalanced parentheses are refused.
- At most one decimal separator; grouping only in the integer part and only in well-formed groups — ambiguous input is refused, never guessed.
- Excess precision throws RoundingNecessary; input is capped at 128 characters.

## Blade

```blade
<p>{{ __('Total') }}: @money($order->total)</p>
<p>@money($order->total, 'sk')</p>   {{-- null renders an empty string --}}
```

@money($price) or @money($price, 'sk') prints the escaped output of the bound formatter; null renders an empty string. Toggle it with macros.blade.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/money-for-laravel.md)
