PHP 找到 setlocale 设置的字符集

PHP find the charset set by setlocale

我所有的网站都使用 UTF-8 作为字符集。但是在设置 setlocale 并从 strftime 获取月份名称和工作日名称的本地化字符串时存在一些问题。

  1. 问题:本地化月份名称在服务器上不起作用。
  2. 问题:如何知道语言环境字符串是 UTF-8 还是 ISO?

具体:我这样设置语言环境:

$locales = ['de_DE.utf-8/utf-8', 'de_DE@euro/utf-8', 'de_DE', 'de-DE', 'german', 'de', 'ge'];
$locale = setlocale(LC_ALL, $locales);

在我的开发系统 (Windows 10, XAMPP) 上它找到并设置 $locale = 'de-DE'.
在我的服务器(Linux、Apache)上,它找到并设置 $locale = 'de_DE'.

因为 DateTime class 中内置的 PHP 不支持本地化名称,我创建了一个 class 来扩展 DateTime class:

class DateTimeExt extends DateTime {

    private function isUTF8() : bool {
        $locale = setlocale(LC_ALL, null);
        if (!$locale) return false;
        $locale = strtoupper($locale);
        return strpos($locale, 'UTF8') !== false || strpos($locale, 'UTF-8') !== false;
    }

    public function weekdayName() : string {
        $weekday = strftime('%A', $this->getTimestamp());
        if (!$weekday) return 'unknown';
        return $this->isUTF8() ? $weekday : utf8_encode($weekday);
    }

    public function monthName() : string {
        $month = strftime('%B', $this->getTimestamp());
        if (!$month) return 'unknown';
        return $this->isUTF8() ? $month : utf8_encode($month);
    }
}

测试:

$date = new DateTimeExt('2021-03-02');
echo $date->weekdayName().'<br />';
echo $date->monthName().'<br />';

开发环境的结果:

// Dienstag
// März

两者都是正确的。如果没有 UTF-8 编码,它将返回:

// Dienstag
// M�rz

但在服务器上结果为:

// Dienstag
// March

卧槽???服务器可以本地化工作日,但不能本地化月份,并且默认为英语?为什么?这是第 1 期。

问题二是检测设置的locale是否支持UTF-8。我的函数 isUTF8 只是获取当前语言环境并在其中搜索模式 'UTF8''UTF-8',如果是,我假定为 UTF-8,否则我假定为 ISO 和所需的 UTF-8编码。我认为这不是最聪明的方法,而且可能很容易出错。有没有更好的方法?

语言环境取决于系统。确保您想要的语言环境显示在 locale -a.

setLocale() 混为一谈是全球性的,可能会产生意想不到的副作用,我建议改为 IntlDateFormatter

$d = new DateTime('2021-03-15');
$mon_formatter = new IntlDateFormatter('de_DE', IntlDateFormatter::NONE, IntlDateFormatter::NONE);
$mon_formatter->setPattern('MMMM');

$day_formatter = new IntlDateFormatter('de_DE', IntlDateFormatter::NONE, IntlDateFormatter::NONE);
$day_formatter->setPattern('EEEE');

var_dump(
    $mon_formatter->format($d),
    $day_formatter->format($d)
);

输出

string(5) "März"
string(6) "Montag"

或者,更可能的是,使用 pattern you define.

格式化整个日期