PHP 国际功能不适用于某些国家/地区

PHP Intl functions does not work for some countries

试图从语言代码中获取语言名称,我 运行

function test($local, $fallback)
{
    $bundle = \ResourceBundle::create($local, 'ICUDATA-lang', $fallback);
    if ($bundle === null) {
        return "$local bundle not found";
    }
    $var = $bundle->get('Languages',$fallback);
    return $var->get('fr',$fallback);
}

$locals = ['en', 'en_US', 'foo', 'en_AU', 'en_NZ'];

foreach ($locals as $local) {
    var_dump(test($local, true));
}
echo PHP_EOL;
foreach ($locals as $local) {
    var_dump(test($local, false));
}
string(6) "French"
string(6) "French"
NULL
NULL
NULL

string(6) "French"
string(22) "en_US bundle not found"
string(20) "foo bundle not found"
NULL
NULL

它 returns null 表示澳大利亚和新西兰

的国际错误

Cannot load resource element 'fr': U_MISSING_RESOURCE_ERROR"

\ResourceBundle::create 函数的第三个参数用于回调,这意味着它应该回退到其父语言环境。有趣的是 parent locale of en_AUen_001.

这是一个错误还是我遗漏了什么?

背景

you listed on GitHub 的 ICU-Data-Directory 中(请注意,您在发布分支而不是主分支上),en_USen_UK 的文件都不存在.在 master 分支上,您可以找到一个 en_GB 文件,它似乎是正确的语言环境代码而不是 UK。

虽然我不能说为什么 en_US 不存在(我绝对希望,就像你所做的那样),但似乎在你所有的测试中你得到 "French" 作为结果,您实际上并没有加载正确的 ICU 路径,而是因为找不到那个路径,所以加载了根路径。

您已经通过尝试 en_foobar 证明了这一点。如果您尝试加载该数据目录中不存在的任何其他无意义的语言环境,则同样有效,并且 en_USen_UK 也是如此(因为它们都不存在,如前所述).

附带说明:我认为您可能误解了 fallback 参数。这样一来,如果无法加载该语言,则会加载后备语言 - 而不是您在其上请求的任何数据都会直接在父级上请求。

代码示例

为了更清楚地说明我在说什么,这里有一些例子。

加载无意义的语言环境总是加载整个目录数据所以你可以访问所有根数据:

$bundle = \ResourceBundle::create('Whosebug-is-great', 'ICUDATA-lang', true);
var_dump($bundle->get('Languages')->get('fr')); // string(6) "French"
var_dump($bundle->get('Languages')->get('de')); // string(6) "German"

正在从 en_NZ 加载 不存在的子语言:

$bundle = \ResourceBundle::create('en_NZ', 'ICUDATA-lang', true);
var_dump($bundle->get('Languages')->get('fr')); // NULL
var_dump($bundle->get('Languages')->get('de')); // NULL

正在从 en_NZ 中加载 存在 的子语言:

$bundle = \ResourceBundle::create('en_NZ', 'ICUDATA-lang', true);
var_dump($bundle->get('Languages')->get('mi')); // string(6) "Māori"

简而言之:它实际上按预期工作,并在可用的地方为您提供数据,在 none 可用的地方没有数据。

调试

我是怎么发现的?基于 PHP ResourceBundle 文档页面上的 this user comment。我添加了一个深度输出并且有一个非常方便的调试功能:

function t($rb, $depth = 0) {
    foreach($rb as $k => $v) {
        echo str_repeat('->', $depth);
        if(is_object($v)) {
            print_r($v);
            var_dump($k);
            t($v, ++$depth);
        } else {
            var_dump($k . " " . $v);
        }
    }
}
$rb = new ResourceBundle('en_UK', 'ICUDATA-lang', true);
var_dump($rb->get('Languages')->get('fr'));

t($rb);

这会打印出一个(真的很长,这就是为什么我不在这里添加它的原因)输出,它看起来很像根数据。

最后一点:en_GB 似乎也是 aliased to en_001 here,但我不确定效果如何。

TL;DR: 数据集中并不真正存在前三个语言环境,因此加载了根数据,en_NZen_AU 只是工作就像他们应该的那样。

你从中得到什么:

->get('Languages')->get($lang)

永远不会受到加载存储库有无 fallback.

的影响

$locals = ['en', 'en_US', 'foo', 'en_AU', 'en_NZ'];

  1. "en", "en_AU and "en_NS”直接在的版本库中定义 ICU:所以它们都可以加载 有或没有 fallback.
  2. "en_US" 是 未定义的 BUT 以 "en_" 开头。这意味着它提供了加载 "en_US" 的机会,这将回退到 "en"。如果您使用 "en_FooBar",情况也是如此。如果您尝试使用 'bas_il_ic',它会退回到 'bas'
  3. "foo" 是 not 定义 AND 不能回退,所以这意味着创建 ResourceBundle 不会因 fallback = true 而失败,但是,您可以从中得到的只有 NULLs.

这意味着 fallback 参数只允许 strict/non-strict 在 ResourceBundle::create()locale 与了解语言环境 xx_YYparentxx。与%%Parent{"xxxxxx"}定义引起的继承无关,没有遵循上面定义的parent locale的原则,而是一种跨语言环境共享通用定义的方法。

所以,你得到的结果和文档都是正确的:

后备 true:

  • 'en':"French",因为语言环境 "en" 存在,并且 "French" 是 en.txt
  • 的一部分
  • 'en_US':"French",因为语言环境 "en_US" 没有被定义,它退回到 "en",并且 "French" 是 [=30 的一部分=]
  • 'foo':NULL,因为语言环境"foo"不存在,也无法回退。
  • 'en_AU':NULL,因为语言环境 "en_AU" exists, extends "en_001",但其中 none 定义了 "fr".
  • 'en_NZ':与 "en_AU".
  • 相同

后备 false:

  • 'en':"French",因为语言环境 "en" 存在,并且 "French" 是 en.txt
  • 的一部分
  • 'en_US': "en_US bundle not found": 有点明显,"en_US" 确实没有定义。
  • 'foo': "foo bundle not found": 有点明显,"foo" 确实没有定义。
  • 'en_AU':与后备 true.
  • 相同的解释
  • 'en_NZ':与后备 true.
  • 相同的解释

您可能会问自己: "Why ICU language data of locale xx_YY doesn't inherit the one of xx?" 这对所有语言环境都有效,而不仅仅是基于英语的语言环境。

PHP与ICU库提供的一致,但你可能会质疑ICU的内部数据。

ResourceBundle::createResourceBundle::get 方法都有 fallback 参数。但是只有第一个方法实际使用了这个参数,get 方法简单地忽略了它。这不是 documentation.

中解释的内容

fallback

Whether locale should match exactly or fallback to parent locale is allowed.

使用 Cosmopolitan 包,它按预期工作。

<?php
require_once "vendor/autoload.php";

use Salarmehr\Cosmopolitan\Intl;

function test($local)
{
    return Cosmo::create($local)->get('ICUDATA-lang','Languages','fr');
}

$locals = ['en', 'en_US', 'foo', 'en_AU', 'en_NZ'];

foreach ($locals as $local) {
    var_dump(test($local));
}

输出

string(6) "French"
string(6) "French"
NULL
string(6) "French"
string(6) "French"