I18N::LangTags - 处理 RFC3066 风格语言标签的函数
use I18N::LangTags();
...或指定要导入的任何函数,如下所示
use I18N::LangTags qw(implicate_supers similarity_language_tag);
所有可导出的函数如下所列——你可以自由地只导入一些,或者一个也不导入。默认情况下,不导入任何函数。如果你说
use I18N::LangTags qw(:ALL)
...那么所有函数都将导出。(这可以让你不必使用像 use I18N::LangTags qw(/./)
这样的不太明显的东西。)
如果你不导入任何这些函数,请假定在以下示例中的所有函数名前面都有一个 &I18N::LangTags::
。
语言标签是一种形式主义,在 RFC 3066(废除 1766)中描述,用于声明给定信息块所使用的语言形式(语言和可能的方言)。
此库提供函数来完成涉及语言标签的常见任务,因为它们在各种协议和应用程序中都是必需的。
请参阅“另请参见”参考,以全面了解如何正确使用语言标签。
函数 is_language_tag($lang1)
如果 $lang1 是一个正式有效的语言标签,则返回 true。
is_language_tag("fr") is TRUE
is_language_tag("x-jicarilla") is FALSE
(Subtags can be 8 chars long at most -- 'jicarilla' is 9)
is_language_tag("sgn-US") is TRUE
(That's American Sign Language)
is_language_tag("i-Klikitat") is TRUE
(True without regard to the fact noone has actually
registered Klikitat -- it's a formally valid tag)
is_language_tag("fr-patois") is TRUE
(Formally valid -- altho descriptively weak!)
is_language_tag("Spanish") is FALSE
is_language_tag("french-patois") is FALSE
(No good -- first subtag has to match
/^([xXiI]|[a-zA-Z]{2,3})$/ -- see RFC3066)
is_language_tag("x-borg-prot2532") is TRUE
(Yes, subtags can contain digits, as of RFC3066)
函数 extract_language_tags($whatever)
返回一个列表,其中包含 $whatever 中看起来像正式有效的语言标签的内容。不太智能,所以不要对你想输入的内容过于有创意。
extract_language_tags("fr, fr-ca, i-mingo")
returns: ('fr', 'fr-ca', 'i-mingo')
extract_language_tags("It's like this: I'm in fr -- French!")
returns: ('It', 'in', 'fr')
(So don't just feed it any old thing.)
输出是未污染的。如果你不知道污染是什么,不用担心。
函数 same_language_tag($lang1, $lang2)
如果 $lang1 和 $lang2 是表示相同语言形式的可接受变体标签,则返回 true。
same_language_tag('x-kadara', 'i-kadara') is TRUE
(The x/i- alternation doesn't matter)
same_language_tag('X-KADARA', 'i-kadara') is TRUE
(...and neither does case)
same_language_tag('en', 'en-US') is FALSE
(all-English is not the SAME as US English)
same_language_tag('x-kadara', 'x-kadar') is FALSE
(these are totally unrelated tags)
same_language_tag('no-bok', 'nb') is TRUE
(no-bok is a legacy tag for nb (Norwegian Bokmal))
same_language_tag
通过查看 encode_language_tag($lang1)
是否与 encode_language_tag($lang2)
相同来工作。
(是的,我知道这个函数的命名有点奇怪。称之为历史原因。)
函数 similarity_language_tag($lang1, $lang2)
返回一个整数,表示标签 $lang1 和 $lang2(顺序无关紧要)之间的相似度,其中相似度是不考虑大小写和 x/i 交替的左侧公共元素的数量。
similarity_language_tag('fr', 'fr-ca') is 1
(one element in common)
similarity_language_tag('fr-ca', 'fr-FR') is 1
(one element in common)
similarity_language_tag('fr-CA-joual',
'fr-CA-PEI') is 2
similarity_language_tag('fr-CA-joual', 'fr-CA') is 2
(two elements in common)
similarity_language_tag('x-kadara', 'i-kadara') is 1
(x/i- doesn't matter)
similarity_language_tag('en', 'x-kadar') is 0
similarity_language_tag('x-kadara', 'x-kadar') is 0
(unrelated tags -- no similarity)
similarity_language_tag('i-cree-syllabic',
'i-cherokee-syllabic') is 0
(no B<leftmost> elements in common!)
函数 is_dialect_of($lang1, $lang2)
如果语言标签 $lang1 表示语言标签 $lang2 的子形式,则返回 true。
顺序正确!反过来不行!
is_dialect_of('en-US', 'en') is TRUE
(American English IS a dialect of all-English)
is_dialect_of('fr-CA-joual', 'fr-CA') is TRUE
is_dialect_of('fr-CA-joual', 'fr') is TRUE
(Joual is a dialect of (a dialect of) French)
is_dialect_of('en', 'en-US') is FALSE
(all-English is a NOT dialect of American English)
is_dialect_of('fr', 'en-CA') is FALSE
is_dialect_of('en', 'en' ) is TRUE
is_dialect_of('en-US', 'en-US') is TRUE
(B<Note:> these are degenerate cases)
is_dialect_of('i-mingo-tom', 'x-Mingo') is TRUE
(the x/i thing doesn't matter, nor does case)
is_dialect_of('nn', 'no') is TRUE
(because 'nn' (New Norse) is aliased to 'no-nyn',
as a special legacy case, and 'no-nyn' is a
subform of 'no' (Norwegian))
函数 super_languages($lang1)
返回一个语言标签列表,这些标签是 $lang1 的上级标签——它通过从 $lang1 的末尾删除子标签来实现,直到什么都没有(或只剩下“i”或“x”)。
super_languages("fr-CA-joual") is ("fr-CA", "fr")
super_languages("en-AU") is ("en")
super_languages("en") is empty-list, ()
super_languages("i-cherokee") is empty-list, ()
...not ("i"), which would be illegal as well as pointless.
如果 $lang1 不是一个有效的语言标签,则在列表上下文中返回空列表,在标量上下文中返回未定义。
此方法有一个显着且不可避免的问题:“x-mingo-tom”有一个“x”,因为整个标签不是 IANA 注册的标签——但 super_languages('x-mingo-tom') 是 ('x-mingo')——这实际上是不正确的,因为 'i-mingo' 已注册。但此模块无法知道这一点。(但请注意,same_language_tag('x-mingo', 'i-mingo') 为 TRUE。)
更重要的是,你自担风险地假设 $lang1 的上级与 $lang1 互通。仔细考虑这一点。
函数 locale2language_tag($locale_identifier)
这采用一个区域设置名称(如“en”、“en_US”或“en_US.ISO8859-1”),并将其映射到一个语言标签。如果无法映射(特别是“C”和“POSIX”),则在列表上下文中返回空列表,或在标量上下文中返回未定义。
locale2language_tag("en") is "en"
locale2language_tag("en_US") is "en-US"
locale2language_tag("en_US.ISO8859-1") is "en-US"
locale2language_tag("C") is undef or ()
locale2language_tag("POSIX") is undef or ()
locale2language_tag("POSIX") is undef or ()
我不太确定区域设置名称是否能令人满意地映射到语言标签。认真考虑如何使用它。你已被警告。
输出是未污染的。如果你不知道污染是什么,不用担心。
函数 encode_language_tag($lang1)
此函数如果给定一个语言标签,则返回一个编码,使得
* 表示不同语言的标签永远不会得到相同的编码。
* 表示相同语言的标签始终会得到相同的编码。
* 正式有效的语言标签的编码始终是一个字符串值,该值已定义、具有长度,并且如果被视为布尔值,则为 true。
请注意,编码本身不是正式有效的语言标签。另请注意,目前您无法从编码返回到它所编码的语言标签。
另请注意,您必须将编码值视为原子值;也就是说,您不应该将其视为不透明的、不可分析的字符串值以外的任何内容。(随着语言标记标准的不断变化,编码方法的内部结构可能会在未来版本中发生变化。)
如果给出的不是正式有效的语言标签,encode_language_tag
将返回未定义。
encode_language_tag
存在的原因是不同的语言标签可能代表相同的语言;这通常可以用 same_language_tag
处理,但考虑这种情况
您有一个用不同语言表达问候语的数据文件。其格式为 “[语言标签]=[如何说‘你好’]”,例如
en-US=Hiho
fr=Bonjour
i-mingo=Hau'
假设您编写了一个程序来读取该文件,然后作为守护程序运行,它会应答指定语言标签的客户端请求,然后期望得到用该语言问候的字符串。因此,交互看起来像
greeting-client asks: fr
greeting-server answers: Bonjour
到目前为止一切都好。但假设您实现此方法的方式是
my %greetings;
die unless open(IN, "<", "in.dat");
while(<IN>) {
chomp;
next unless /^([^=]+)=(.+)/s;
my($lang, $expr) = ($1, $2);
$greetings{$lang} = $expr;
}
close(IN);
此时,%greetings 的内容为
"en-US" => "Hiho"
"fr" => "Bonjour"
"i-mingo" => "Hau'"
然后假设您通过查找 $greetings{$wanted} 来应答语言 $wanted 的客户端请求。
如果客户端请求 “fr”,则将在 %greetings 中成功查找,得到值 “Bonjour”。如果客户端请求 “i-mingo”,则将在 %greetings 中成功查找,得到值 “Hau’”。
但如果客户端请求 “i-Mingo” 或 “x-mingo”,或 “Fr”,则在 %greetings 中的查找将失败。这是错误的。
您也可以使用以下方法对 $wanted 进行查找
use I18N::LangTags qw(same_language_tag);
my $response = '';
foreach my $l2 (keys %greetings) {
if(same_language_tag($wanted, $l2)) {
$response = $greetings{$l2};
last;
}
}
但这效率低下。更好的方法是通过以下方式启动您的程序
use I18N::LangTags qw(encode_language_tag);
my %greetings;
die unless open(IN, "<", "in.dat");
while(<IN>) {
chomp;
next unless /^([^=]+)=(.+)/s;
my($lang, $expr) = ($1, $2);
$greetings{
encode_language_tag($lang)
} = $expr;
}
close(IN);
然后只需通过查找来应答语言 $wanted 的客户端请求
$greetings{encode_language_tag($wanted)}
这样就能得到正确的结果。
函数 alternate_language_tags($lang1)
如果给定语言标签,此函数将返回该语言标签的所有备用形式。(即,指代相同语言的标签。)此函数旨在处理多年来语言标签标准的细微变化导致的旧标签;并且还处理 x-/i- 交替。
请注意,此函数不会尝试将新的(且从未使用和不可用的)ISO639-2 三字母标签等同于旧的(且仍在使用)ISO639-1 二字母等效项,例如 “ara” -> “ar”,因为 “ara”从未作为互联网语言标签使用,并且 RFC 3066 规定它永远不应该被使用,因为存在更短的标签(“ar”)。
示例
alternate_language_tags('no-bok') is ('nb')
alternate_language_tags('nb') is ('no-bok')
alternate_language_tags('he') is ('iw')
alternate_language_tags('iw') is ('he')
alternate_language_tags('i-hakka') is ('zh-hakka', 'x-hakka')
alternate_language_tags('zh-hakka') is ('i-hakka', 'x-hakka')
alternate_language_tags('en') is ()
alternate_language_tags('x-mingo-tom') is ('i-mingo-tom')
alternate_language_tags('x-klikitat') is ('i-klikitat')
alternate_language_tags('i-klikitat') is ('x-klikitat')
如果给定任何内容,除了正式有效的语言标记外,此函数将返回空列表。
函数 @langs = panic_languages(@accept_languages)
此函数采用 0 个或更多语言标记列表,这些标记构成给定用户的 Accept-Language 列表,并返回可能被用户接受的其他(非超级)语言的标记列表,用于如果所有其他方法都失败。
例如,如果用户仅接受“ca”(加泰罗尼亚语)和“es”(西班牙语),并且您提供的文档/界面仅为德语、意大利语和中文,则用户很可能想要意大利语(而不是中文或德语),而不是一无所获。因此,panic_languages('ca', 'es')
返回包含“it”(意大利语)的列表。
英语(“en”)始终在返回列表中,但它是否在最后取决于输入语言。此函数通过查阅内部表来工作,该表规定了哪些常用语言“接近”彼此。
您可以考虑使用一个有用的构造
@fallbacks = super_languages(@accept_languages);
push @fallbacks, panic_languages(
@accept_languages, @fallbacks,
);
函数 implicate_supers( ...languages... )
这将采用一个字符串列表(假定为语言标记;不是字符串的,将被忽略);并且在每个字符串后,此函数将插入尚未出现在列表中的上级形式。将返回原始列表加上这些插入项。
换句话说,它采用这个
pt-br de-DE en-US fr pt-br-janeiro
并返回这个
pt-br pt de-DE de en-US en fr pt-br-janeiro
此函数在惯用语中最有用
implicate_supers( I18N::LangTags::Detect::detect() );
(请参阅 I18N::LangTags::Detect。)
函数 implicate_supers_strictly( ...languages... )
此函数的工作方式类似于 implicate_supers
,但隐含形式被添加到返回列表的末尾。
换句话说,implicate_supers_strictly 采用一个字符串列表(假定为语言标记;不是字符串的,将被忽略),并在整个给定列表后,插入所有给定标记的上级形式,减去任何已出现在输入列表中的标记。
换句话说,它采用这个
pt-br de-DE en-US fr pt-br-janeiro
并返回这个
pt-br de-DE en-US fr pt-br-janeiro pt de en
此函数名称中包含“_strictly”的原因是,当您根据 RFC 处理 Accept-Language 列表时,如果您非常严格地解释 RFC,则可以使用 implicate_supers_strictly,但对于正常使用(即,就我而言,常识使用),您将使用 implicate_supers。
我考虑让所有输出语言标签的上述函数严格返回小写形式的标签。将所有语言标签都设为小写确实会让一些事情变得更容易。但你也可以按需将它们设为小写,或在适当的情况下调用 encode_language_tag($lang1)
。
在 I18N::LangTags 的未来版本中,我计划包含对 RFC2482 风格语言标签的支持——它们基本上只是将 ASCII 字符移到平面 14 的普通语言标签。
* RFC 3066,http://www.ietf.org/rfc/rfc3066.txt
,“语言识别标签”。(废除 RFC 1766)
* RFC 2277,http://www.ietf.org/rfc/rfc2277.txt
,“IETF 关于字符集和语言的政策”。
* RFC 2231,http://www.ietf.org/rfc/rfc2231.txt
,“MIME 参数值和编码单词扩展:字符集、语言和延续”。
* RFC 2482,http://www.ietf.org/rfc/rfc2482.txt
,“Unicode 纯文本中的语言标记”。
* Locale::Codes,位于 https://perldotcom.perl5.cn/CPAN/modules/by-module/Locale/
* ISO 639-2,“语言名称表示代码”,包括两个字母和三个字母的代码,http://www.loc.gov/standards/iso639-2/php/code_list.php
* IANA 注册语言列表(希望是最新的),http://www.iana.org/assignments/language-tags
版权所有 (c) 1998+ Sean M. Burke。保留所有权利。
此库是免费软件;你可以根据与 Perl 相同的条款重新分发或修改它。
此发行版中的程序和文档的发布基于它们是有用的希望,但不提供任何保证;甚至没有对适销性或针对特定目的的适用性的默示保证。
Sean M. Burke [email protected]