PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception
The Intl extension does not support Accounting Formats…
Error message
The Intl extension does not support Accounting Formats without ICU 53
What it means
Thrown by the Accounting number-format Wizard when a locale-aware accounting mask is requested but the installed ICU library is older than version 53. Accounting formats rely on NumberFormatter::CURRENCY_ACCOUNTING, which ICU only supports from 53 onwards, so getLocaleFormat() refuses to run rather than emit a wrong mask. It fires only when the intl extension reports an ICU version below 53.0, which is rare on current PHP builds.
Solutions
- Upgrade PHP/intl so it links ICU >= 53 (verify with `echo INTL_ICU_VERSION;`), usually via current distro or sury/ondrej-php packages
- Build the accounting format without a locale so the wizard uses its built-in mask (pass '' as the locale argument)
- Catch the exception and fall back to a hand-written format code such as '_-"€"* #,##0.00_-;-"€"* #,##0.00_-;_-"€"* "-"??_-;_-@_-'
Example fix
// before
$mask = (new Accounting(2, '€', 'de-DE'))->format();
// after (guard for old ICU)
if (defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '53.0', '>=')) {
$mask = (new Accounting(2, '€', 'de-DE'))->format();
} else {
$mask = '_-"€"* #,##0.00_-;-"€"* #,##0.00_-;_-"€"* "-"??_-;_-@_-';
} Defensive patterns
Strategy: validation
Validate before calling
$icuOk = extension_loaded('intl') && defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '53.0', '>=');
if ($icuOk) {
$mask = (new Accounting(2, '€', 'de-DE'))->format();
} else {
$mask = '_-"€"* #,##0.00_-;-"€"* #,##0.00_-;_-"€"* "-"??_-;_-@_-';
} Type guard
function icuSupportsAccountingFormats(): bool
{
return extension_loaded('intl')
&& defined('INTL_ICU_VERSION')
&& version_compare(INTL_ICU_VERSION, '53.0', '>=');
} Try / catch
try {
$mask = $accountingWizard->format();
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
// ICU too old: fall back to a hardcoded accounting format code
$mask = '_-"€"* #,##0.00_-;-"€"* #,##0.00_-;_-"€"* "-"??_-;_-@_-';
} Prevention
- Print INTL_ICU_VERSION in your CI to catch old ICU builds before release
- Pin base images/Docker stages that bundle a current ICU
- Avoid locale-driven wizards unless the deployment environment is known
When it happens
Trigger: Calling (new Accounting(2, '€', 'de-DE'))->format(), or any Accounting wizard with a non-empty locale, on a PHP build whose intl extension links ICU < 53 (checked via INTL_ICU_VERSION).
Common situations: Legacy hosting, old Ubuntu/Debian PHP packages, outdated XAMPP/WAMP bundles, or containers built from very old base images where PHP was upgraded but the ICU library was not.
Related errors
- Unable to read locale data for
- Invalid locale code
- A Worksheet has already been assigned. Drawings can only…
- AND Value is only appropriate for range operators
- $bitmap: compression not supported in bitmap image.\n
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/6465c5fb5b28fe7a.
Report an issue: GitHub.
Appendix: source
Thrown at src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php:21
namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
use NumberFormatter;
use PhpOffice\PhpSpreadsheet\Exception;
class Accounting extends CurrencyBase
{
protected ?bool $overrideSpacing = true;
protected ?CurrencyNegative $overrideNegative = CurrencyNegative::parentheses;
/**
* @throws Exception if the Intl extension and ICU version don't support Accounting formats
*/
protected function getLocaleFormat(): string
{
if (self::icuVersion() < 53.0) {
// @codeCoverageIgnoreStart
throw new Exception('The Intl extension does not support Accounting Formats without ICU 53');
// @codeCoverageIgnoreEnd
}
// Scrutinizer does not recognize CURRENCY_ACCOUNTING
$formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY_ACCOUNTING);
$mask = $formatter->format($this->stripLeadingRLM);
if ($this->decimals === 0) {
$mask = (string) preg_replace('/\.0+/miu', '', $mask);
}
return str_replace('¤', $this->formatCurrencyCode(), $mask);
}
public static function icuVersion(): float
{
[$major, $minor] = explode('.', INTL_ICU_VERSION);
return (float) "{$major}.{$minor}";View on GitHub (pinned to 65b080eef4)