7

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.

asked Nov 30, 2025 at 18:53
6
  • How do you import 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? Commented Nov 30, 2025 at 20:11
  • Locale is 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). Commented Nov 30, 2025 at 20:26
  • I'm not sure that your output shows that it works. Locale::acceptFromHttp() is claiming that the locales are available, yet setlocale() fails with those locales. Commented Nov 30, 2025 at 20:32
  • What exactly do you mean by "it doesn't work"? What's the expected output? Also, you mention any "script subtag" in the title, can you clarify what that means? Commented Dec 1, 2025 at 9:24
  • By "it doesn't work" I mean that setlocale() returns false. And by a script subtag I mean any script subtag, as defined by RFC 4646. Commented Dec 1, 2025 at 20:47

1 Answer 1

4

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.

answered Nov 30, 2025 at 22:22
Sign up to request clarification or add additional context in comments.

3 Comments

OK, thanks. Apart from trial and error, or using locale -a, which depends on exec() being allowed, is there a way of discovering which locales are supported by setlocale()?
Without doing a system call, no. It doesn't even appear to be possible in the C language (which is likely why PHP has no ability to). You can get a list of available INTL/ICU locales by calling ResourceBundle::getLocales('') but that is not what is supported by PHP's built-in functions.
And the position's even worse on Windows where 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.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.