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)
此函數如果給定語言標籤,則會傳回該標籤的編碼,使得
* 代表不同語言的標籤絕不會取得相同的編碼。
* 代表相同語言的標籤總是會取得相同的編碼。
* 正式有效語言標籤的編碼總是會是一個已定義、有長度且視為布林值時為真值的字串值。
請注意,編碼本身不是正式有效的語言標籤。另請注意,您目前無法從編碼返回到其編碼的語言標籤。
另請注意,您必須將編碼值視為原子;亦即,您不應將其視為不透明、無法分析的字串值以外的任何東西。(編碼方法的內部結構可能會在未來版本中變更,因為語言標籤標準會隨著時間而變更。)
如果給定任何正式有效的語言標籤以外的東西,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 個或多個語言標籤,這些標籤構成給定使用者的「接受語言」清單,並傳回標籤清單,表示其他(非超級)語言,這些語言可能為使用者所接受,用於在所有其他方法都失敗時。
例如,如果使用者只接受「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,在 http://www.perl.com/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 sburke@cpan.org