📄 i18n::langtags.3
字号:
A notable and rather unavoidable problem with this method:\&\*(L"x\-mingo-tom\*(R" has an \*(L"x\*(R" because the whole tag isn't anIANA-registered tag \*(-- but super_languages('x\-mingo\-tom') is('x\-mingo') \*(-- which isn't really right, since 'i\-mingo' isregistered. But this module has no way of knowing that. (But notethat same_language_tag('x\-mingo', 'i\-mingo') is \s-1TRUE\s0.).SpMore importantly, you assume \fIat your peril\fR that superordinates of\&\f(CW$lang1\fR are mutually intelligible with \f(CW$lang1\fR. Consider thiscarefully..IP "\(bu" 4the function locale2language_tag($locale_identifier).SpThis takes a locale name (like \*(L"en\*(R", \*(L"en_US\*(R", or \*(L"en_US.ISO8859\-1\*(R")and maps it to a language tag. If it's not mappable (as with,notably, \*(L"C\*(R" and \*(L"\s-1POSIX\s0\*(R"), this returns empty-list in a list context,or undef in a scalar context..Sp.Vb 1\& 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 ().Ve.SpI'm not totally sure that locale names map satisfactorily to languagetags. Think \s-1REAL\s0 hard about how you use this. \s-1YOU\s0 \s-1HAVE\s0 \s-1BEEN\s0 \s-1WARNED\s0..SpThe output is untainted. If you don't know what tainting is,don't worry about it..IP "\(bu" 4the function encode_language_tag($lang1).SpThis function, if given a language tag, returns an encoding of it suchthat:.Sp* tags representing different languages never get the same encoding..Sp* tags representing the same language always get the same encoding..Sp* an encoding of a formally valid language tag always is a stringvalue that is defined, has length, and is true if considered as aboolean..SpNote that the encoding itself is \fBnot\fR a formally valid language tag.Note also that you cannot, currently, go from an encoding back to alanguage tag that it's an encoding of..SpNote also that you \fBmust\fR consider the encoded value as atomic; i.e.,you should not consider it as anything but an opaque, unanalysablestring value. (The internals of the encoding method may change infuture versions, as the language tagging standard changes over time.).Sp\&\f(CW\*(C`encode_language_tag\*(C'\fR returns undef if given anything other than aformally valid language tag..SpThe reason \f(CW\*(C`encode_language_tag\*(C'\fR exists is because different languagetags may represent the same language; this is normally treatable with\&\f(CW\*(C`same_language_tag\*(C'\fR, but consider this situation:.SpYou have a data file that expresses greetings in different languages.Its format is \*(L"[language tag]=[how to say 'Hello']\*(R", like:.Sp.Vb 3\& en\-US=Hiho\& fr=Bonjour\& i\-mingo=Hau\*(Aq.Ve.SpAnd suppose you write a program that reads that file and then runs asa daemon, answering client requests that specify a language tag andthen expect the string that says how to greet in that language. So aninteraction looks like:.Sp.Vb 2\& greeting\-client asks: fr\& greeting\-server answers: Bonjour.Ve.SpSo far so good. But suppose the way you're implementing this is:.Sp.Vb 9\& my %greetings;\& die unless open(IN, "<in.dat");\& while(<IN>) {\& chomp;\& next unless /^([^=]+)=(.+)/s;\& my($lang, $expr) = ($1, $2);\& $greetings{$lang} = $expr;\& }\& close(IN);.Ve.Spat which point \f(CW%greetings\fR has the contents:.Sp.Vb 3\& "en\-US" => "Hiho"\& "fr" => "Bonjour"\& "i\-mingo" => "Hau\*(Aq".Ve.SpAnd suppose then that you answer client requests for language \f(CW$wanted\fRby just looking up \f(CW$greetings\fR{$wanted}..SpIf the client asks for \*(L"fr\*(R", that will look up successfully in\&\f(CW%greetings\fR, to the value \*(L"Bonjour\*(R". And if the client asks for\&\*(L"i\-mingo\*(R", that will look up successfully in \f(CW%greetings\fR, to the value\&\*(L"Hau'\*(R"..SpBut if the client asks for \*(L"i\-Mingo\*(R" or \*(L"x\-mingo\*(R", or \*(L"Fr\*(R", then thelookup in \f(CW%greetings\fR fails. That's the Wrong Thing..SpYou could instead do lookups on \f(CW$wanted\fR with:.Sp.Vb 8\& use I18N::LangTags qw(same_language_tag);\& my $response = \*(Aq\*(Aq;\& foreach my $l2 (keys %greetings) {\& if(same_language_tag($wanted, $l2)) {\& $response = $greetings{$l2};\& last;\& }\& }.Ve.SpBut that's rather inefficient. A better way to do it is to start yourprogram with:.Sp.Vb 12\& 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);.Ve.Spand then just answer client requests for language \f(CW$wanted\fR by justlooking up.Sp.Vb 1\& $greetings{encode_language_tag($wanted)}.Ve.SpAnd that does the Right Thing..IP "\(bu" 4the function alternate_language_tags($lang1).SpThis function, if given a language tag, returns all language tags thatare alternate forms of this language tag. (I.e., tags which refer tothe same language.) This is meant to handle legacy tags caused bythe minor changes in language tag standards over the years; andthe x\-/i\- alternation is also dealt with..SpNote that this function does \fInot\fR try to equate new (and never-used,and unusable)\&\s-1ISO639\-2\s0 three-letter tags to old (and still in use) \s-1ISO639\-1\s0two-letter equivalents \*(-- like \*(L"ara\*(R" \-> \*(L"ar\*(R" \*(-- because\&\*(L"ara\*(R" has \fInever\fR been in use as an Internet language tag,and \s-1RFC\s0 3066 stipulates that it never should be, since a shortertag (\*(L"ar\*(R") exists..SpExamples:.Sp.Vb 10\& alternate_language_tags(\*(Aqno\-bok\*(Aq) is (\*(Aqnb\*(Aq)\& alternate_language_tags(\*(Aqnb\*(Aq) is (\*(Aqno\-bok\*(Aq)\& alternate_language_tags(\*(Aqhe\*(Aq) is (\*(Aqiw\*(Aq)\& alternate_language_tags(\*(Aqiw\*(Aq) is (\*(Aqhe\*(Aq)\& alternate_language_tags(\*(Aqi\-hakka\*(Aq) is (\*(Aqzh\-hakka\*(Aq, \*(Aqx\-hakka\*(Aq)\& alternate_language_tags(\*(Aqzh\-hakka\*(Aq) is (\*(Aqi\-hakka\*(Aq, \*(Aqx\-hakka\*(Aq)\& alternate_language_tags(\*(Aqen\*(Aq) is ()\& alternate_language_tags(\*(Aqx\-mingo\-tom\*(Aq) is (\*(Aqi\-mingo\-tom\*(Aq)\& alternate_language_tags(\*(Aqx\-klikitat\*(Aq) is (\*(Aqi\-klikitat\*(Aq)\& alternate_language_tags(\*(Aqi\-klikitat\*(Aq) is (\*(Aqx\-klikitat\*(Aq).Ve.SpThis function returns empty-list if given anything other than a formallyvalid language tag..IP "\(bu" 4the function \f(CW@langs\fR = panic_languages(@accept_languages).SpThis function takes a list of 0 or more languagetags that constitute a given user's Accept-Language list, andreturns a list of tags for \fIother\fR (non-super)languages that are probably acceptable to the user, to beused \fIif all else fails\fR..SpFor example, if a user accepts only 'ca' (Catalan) and\&'es' (Spanish), and the documents/interfaces you haveavailable are just in German, Italian, and Chinese, thenthe user will most likely want the Italian one (and notthe Chinese or German one!), instead of gettingnothing. So \f(CW\*(C`panic_languages(\*(Aqca\*(Aq, \*(Aqes\*(Aq)\*(C'\fR returnsa list containing 'it' (Italian)..SpEnglish ('en') is \fIalways\fR in the return list, butwhether it's at the very end or not dependson the input languages. This function works by consultingan internal table that stipulates what commonlanguages are \*(L"close\*(R" to each other..SpA useful construct you might consider using is:.Sp.Vb 4\& @fallbacks = super_languages(@accept_languages);\& push @fallbacks, panic_languages(\& @accept_languages, @fallbacks,\& );.Ve.IP "\(bu" 4the function implicate_supers( ...languages... ).SpThis takes a list of strings (which are presumed to be language-tags;strings that aren't, are ignored); and after each one, this functioninserts super-ordinate forms that don't already appear in the list.The original list, plus these insertions, is returned..SpIn other words, it takes this:.Sp.Vb 1\& pt\-br de\-DE en\-US fr pt\-br\-janeiro.Ve.Spand returns this:.Sp.Vb 1\& pt\-br pt de\-DE de en\-US en fr pt\-br\-janeiro.Ve.SpThis function is most useful in the idiom.Sp.Vb 1\& implicate_supers( I18N::LangTags::Detect::detect() );.Ve.Sp(See I18N::LangTags::Detect.).IP "\(bu" 4the function implicate_supers_strictly( ...languages... ).SpThis works like \f(CW\*(C`implicate_supers\*(C'\fR except that the implicatedforms are added to the end of the return list..SpIn other words, implicate_supers_strictly takes a list of strings(which are presumed to be language-tags; strings that aren't, areignored) and after the whole given list, it inserts the super-ordinate forms of all given tags, minus any tags that already appear in the input list..SpIn other words, it takes this:.Sp.Vb 1\& pt\-br de\-DE en\-US fr pt\-br\-janeiro.Ve.Spand returns this:.Sp.Vb 1\& pt\-br de\-DE en\-US fr pt\-br\-janeiro pt de en.Ve.SpThe reason this function has \*(L"_strictly\*(R" in its name is that whenyou're processing an Accept-Language list according to the RFCs, ifyou interpret the RFCs quite strictly, then you would useimplicate_supers_strictly, but for normal use (i.e., common-sense use,as far as I'm concerned) you'd use implicate_supers..SH "ABOUT LOWERCASING".IX Header "ABOUT LOWERCASING"I've considered making all the above functions that output languagetags return all those tags strictly in lowercase. Having all yourlanguage tags in lowercase does make some things easier. But youmight as well just lowercase as you like, or call\&\f(CW\*(C`encode_language_tag($lang1)\*(C'\fR where appropriate..SH "ABOUT UNICODE PLAINTEXT LANGUAGE TAGS".IX Header "ABOUT UNICODE PLAINTEXT LANGUAGE TAGS"In some future version of I18N::LangTags, I plan to include supportfor RFC2482\-style language tags \*(-- which are basically just normallanguage tags with their \s-1ASCII\s0 characters shifted into Plane 14..SH "SEE ALSO".IX Header "SEE ALSO"* I18N::LangTags::List.PP* \s-1RFC\s0 3066, \f(CW\*(C`ftp://ftp.isi.edu/in\-notes/rfc3066.txt\*(C'\fR, \*(L"Tags for theIdentification of Languages\*(R". (Obsoletes \s-1RFC\s0 1766).PP* \s-1RFC\s0 2277, \f(CW\*(C`ftp://ftp.isi.edu/in\-notes/rfc2277.txt\*(C'\fR, \*(L"\s-1IETF\s0 Policy onCharacter Sets and Languages\*(R"..PP* \s-1RFC\s0 2231, \f(CW\*(C`ftp://ftp.isi.edu/in\-notes/rfc2231.txt\*(C'\fR, \*(L"\s-1MIME\s0 ParameterValue and Encoded Word Extensions: Character Sets, Languages, andContinuations\*(R"..PP* \s-1RFC\s0 2482, \f(CW\*(C`ftp://ftp.isi.edu/in\-notes/rfc2482.txt\*(C'\fR, \&\*(L"Language Tagging in Unicode Plain Text\*(R"..PP* Locale::Codes, in\&\f(CW\*(C`http://www.perl.com/CPAN/modules/by\-module/Locale/\*(C'\fR.PP* \s-1ISO\s0 639\-2, \*(L"Codes for the representation of names of languages\*(R",including two-letter and three-letter codes,\&\f(CW\*(C`http://www.loc.gov/standards/iso639\-2/langcodes.html\*(C'\fR.PP* The \s-1IANA\s0 list of registered languages (hopefully up-to-date),\&\f(CW\*(C`http://www.iana.org/assignments/language\-tags\*(C'\fR.SH "COPYRIGHT".IX Header "COPYRIGHT"Copyright (c) 1998+ Sean M. Burke. All rights reserved..PPThis library is free software; you can redistribute it and/ormodify it under the same terms as Perl itself..PPThe programs and documentation in this dist are distributed inthe hope that they will be useful, but without any warranty; withouteven the implied warranty of merchantability or fitness for aparticular purpose..SH "AUTHOR".IX Header "AUTHOR"Sean M. Burke \f(CW\*(C`sburke@cpan.org\*(C'\fR
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -