If I run the following code on a Linux server
<?php
declare(strict_types=1);
error_reporting(-1);
ini_set('display_errors', '1');
var_dump(Locale::acceptFromHttp('zh_Hant_TW'));
echo "<br>\n";
var_dump(setlocale(LC_ALL, 'zh_Hant_TW'));
echo "<br>\n";
var_dump(Locale::acceptFromHttp('zh_TW'));
echo "<br>\n";
var_dump(setlocale(LC_ALL, 'zh_TW'));
echo "<br>\n";
I get the output
string(10) "zh_Hant_TW"
bool(false)
string(10) "zh_Hant_TW"
string(5) "zh_TW"
It seems that although the system is claiming the locale 'zh_Hant_TW' is available, when I try and use it with setlocale() it doesn't work, with setlocale() returning false. Yet when I try 'zh_TW' it does work. Why is this?
I get similar results when I try other locales containing script subtags.
1 Answer 1
The setlocale() function and Locale class use two entirely different libraries. Just because one supports a locale, does not mean the other does.
PHP's setlocale() is just a wrapper for the C language's setlocale() function. Here is a list of supported locales on Windows and Linux.
In general, setlocale() only uses some (a subset) of the languages, countries, and scripts that the INTL extension supports. The longer locales can be supported by the INTL extension as it is more robust, but the built-in library C supports fewer locales and formats.
This is why it is returning false when you are trying to set zh_Hant_TW as a locale - the Hant script is not supported by the setlocale() function, while just using the language and country code zh_TW is supported. The full zh_Hant_TW locale is supported by INTL (that is what is being reported by the Locale class' acceptFromHttp() method).
The setlocale() function only affects the built-in PHP functions for formatting numbers and time/dates. Examples include number_format() (with proper variable use), strftime() (though deprecated as of PHP 8), and strcoll() - among a few others. The PHP developers are slowly deprecating these functions in favor of the INTL extension.
In contrast, the INTL extension classes and functions make use of the ICU library that is installed separately on the vast majority of systems. These classes and functions are not affected by the locale set via a setlocale() function call. They require passing in their own locale string in order to function (though you can rely on getlocale() if you wish but it may not be as accurate). The INTL extension also offers significantly more robust translation and formatting for strings, sorting, dates/times, numbers & currency, and lists (as of PHP 8.5) than the built-in formatting functions do.
3 Comments
locale -a, which depends on exec() being allowed, is there a way of discovering which locales are supported by setlocale()?ResourceBundle::getLocales('') but that is not what is supported by PHP's built-in functions.setlocale() only checks that the locale looks roughly right, not that the locale has been set. So for example setlocale(LC_ALL, 'xyz.utf-8') doesn't return false even though it didn't set the locale. Therefore trial and error isn't an option on Windows.
Locale? I can’t run this bug because it is missing: 3v4l.org/4DkNT Update: actually it works on PHP < 8.5. What PHP version do you use?Localeis part of the 'intl' extension. I've run it on PHP 8.3.27 and 7.2.34 (yes, I know it's not supported).Locale::acceptFromHttp()is claiming that the locales are available, yetsetlocale()fails with those locales.setlocale()returnsfalse. And by a script subtag I mean any script subtag, as defined by RFC 4646.