{"record":{"id":"ec8830b64bed4caa","repo":"PHPOffice/PhpSpreadsheet","slug":"invalid-locale-code-locale","errorCode":null,"errorMessage":"Invalid locale code '{$locale}'","messagePattern":"Invalid locale code '(.+?)'","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php","lineNumber":58,"sourceCode":"        $this->locale = $this->validateLocale($locale);\n\n        if (class_exists(NumberFormatter::class)) {\n            $this->localeFormat = $this->getLocaleFormat();\n        }\n    }\n\n    /**\n     * Stub: should be implemented as a concrete method in concrete wizards.\n     */\n    abstract protected function getLocaleFormat(): string;\n\n    /**\n     * @throws Exception If the locale code is not a valid format\n     */\n    private function validateLocale(string $locale): string\n    {\n        if (preg_match(Locale::STRUCTURE, $locale, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {\n            throw new Exception(\"Invalid locale code '{$locale}'\");\n        }\n\n        ['language' => $language, 'script' => $script, 'country' => $country] = $matches;\n        // Set case and separator to match standardised locale case\n        $language = strtolower($language);\n        $script = ($script === null) ? null : ucfirst(strtolower($script));\n        $country = ($country === null) ? null : strtoupper($country);\n\n        $this->fullLocale = implode('-', array_filter([$language, $script, $country]));\n\n        return $country === null ? $language : \"{$language}-{$country}\";\n    }\n\n    public function format(): string\n    {\n        return NumberFormat::FORMAT_GENERAL;\n    }\n","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php#L40-L76","documentation":"NumberBase::validateLocale() enforces the Locale::STRUCTURE regex before ICU is consulted: exactly a 2-letter language, optionally a 4-letter script and/or 2-letter country, separated by '-' or '_'. Anything else — 3-letter language codes, 3-letter countries, free text, extra variants — is rejected as an invalid locale code.","triggerScenarios":"Wizard setLocale('english'), setLocale('en-USA'), setLocale('zh_Hans_CN_POSIX') — any string not matching ^(ll)([-_]ssss)?([-_]cc)?$ shape.","commonSituations":"Pasting locale identifiers from other ecosystems (ISO-639-2 'eng', POSIX variants, BCP-47 extension subtags) into the wizard's setLocale(); passing user input unvalidated.","solutions":["Pass codes of the form 'll', 'll-CC' or 'll-Ssss-CC' ('fr', 'pt-BR', 'zh-Hans-CN')","Normalize input at your boundary against Locale::STRUCTURE or a whitelist before calling setLocale","Catch the exception and surface the bad value early to the caller/UI"],"exampleFix":"// before\n$wizard->setLocale($userLocale); // 'english' -> throws\n\n// after\nif (preg_match(\\PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Locale::STRUCTURE, $userLocale) === 1) {\n    $wizard->setLocale($userLocale);\n}","handlingStrategy":"validation","validationCode":"$structure = \\PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Locale::STRUCTURE;\nif (preg_match($structure, $userLocale) !== 1) {\n    throw new InvalidArgumentException(\"Bad locale '$userLocale'\");\n}\n$wizard->setLocale($userLocale);","typeGuard":"function isValidWizardLocale(string $locale): bool\n{\n    return preg_match(\n        \\PhpOffice\\PhpSpreadsheet\\Style\\NumberFormat\\Wizard\\Locale::STRUCTURE,\n        $locale\n    ) === 1;\n}","tryCatchPattern":"try {\n    $wizard->setLocale($userLocale);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Exception $e) {\n    // log and continue with default (non-localized) format\n}","preventionTips":["Reject free-text locale input at the API boundary","Convert 3-letter ISO-639-2 codes to 2-letter ISO-639-1 before passing them in"],"tags":["locale","validation","number-format","wizard","phpspreadsheet"],"backgroundTag":"invalid-locale-code","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}