PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception
Unable to read locale data for '{$locale}'
Error message
Unable to read locale data for '{$locale}' What it means
The Locale helper used by all NumberFormat Wizards creates a NumberFormatter for the requested locale, then compares the locale ICU actually resolved. ICU silently falls back to a root/default locale when it has no data for the requested one, so a mismatch between requested and returned locale means the locale is unknown or its data is not installed on this system. The offending locale is embedded in the message.
Source
Thrown at src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php:25
final class Locale
{
/**
* Language code: ISO-639 2 character, alpha.
* Optional script code: ISO-15924 4 alpha.
* Optional country code: ISO-3166-1, 2 character alpha.
* Separated by underscores or dashes.
*/
public const STRUCTURE = '/^(?P<language>[a-z]{2})([-_](?P<script>[a-z]{4}))?([-_](?P<country>[a-z]{2}))?$/i';
private NumberFormatter $formatter;
public function __construct(?string $locale, int $style)
{
$formatterLocale = str_replace('-', '_', $locale ?? '');
$this->formatter = new NumberFormatter($formatterLocale, $style);
if ($this->formatter->getLocale() !== $formatterLocale) {
throw new Exception("Unable to read locale data for '{$locale}'");
}
}
public function format(bool $stripRlm = true): string
{
$str = $this->formatter->getPattern();
return ($stripRlm && str_starts_with($str, "\xe2\x80\x8f")) ? substr($str, 3) : $str;
}
}
View on GitHub (pinned to 65b080eef4)
Solutions
- Correct the locale code to a standard ISO-639 language + ISO-3166 country pair ICU knows ('en-GB', 'de-DE', 'fr-FR')
- Pre-verify the locale resolves: create a probe NumberFormatter and confirm ->getLocale() matches the requested locale (mirroring the library's own check)
- Catch the exception and fall back to a non-localized wizard or a hardcoded format mask
Example fix
// before
$wizard->setLocale('en-UK');
// after
$wizard->setLocale('en-GB'); Defensive patterns
Strategy: validation
Validate before calling
function icuKnowsLocale(string $locale): bool
{
$fmt = new \NumberFormatter(str_replace('-', '_', $locale), \NumberFormatter::DECIMAL);
return $fmt->getLocale() === str_replace('-', '_', $locale);
}
$locale = icuKnowsLocale('en-GB') ? 'en-GB' : 'en';
$wizard->setLocale($locale); Try / catch
try {
$wizard->setLocale($requestedLocale);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
// unknown locale: degrade to the non-localized wizard
$wizard = new Number(2);
} Prevention
- Keep a whitelist of locales your app actually supports
- Test with a probe NumberFormatter in CI on the production image
- Normalize codes early (en_UK -> en-GB) instead of trusting input
When it happens
Trigger: Calling setLocale('en-UK'), setLocale('xx-YY') or any code that passes the STRUCTURE regex but has no ICU locale data; e.g. $wizard->setLocale('en-UK') where the valid form is 'en-GB'.
Common situations: Typo'd locale strings; codes valid in other ecosystems (Java, CLDR aliases) but not installed in the system ICU; slim Docker images where ICU locale data was pruned.
Related errors
- The Intl extension does not support Accounting Formats witho
- Invalid locale code '{$locale}'
- Unsupported binary comparison operator
- Cloning the calculation engine is not allowed!
- Unsupported numeric binary operation
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/2dcfbc44bc2d08ec.
Report an issue: GitHub.