📄 locale::maketext.3
字号:
.PPBracket Notation is discussed in a later section. Notethat trying to compile a string into Bracket Notation can throwan exception if the string is not syntactically valid (say, by notbalancing brackets right.).PPAlso, calling &$coderef($lh, ...parameters...) can throw any sort ofexception (if, say, code in that sub tries to divide by zero). Buta very common exception occurs when you have BracketNotation text that says to call a method \*(L"foo\*(R", but there is no suchmethod. (E.g., "You have [qua\fBtn\fR,_1,ball]." will throw an exceptionon trying to call \f(CW$lh\fR\->qua\fBtn\fR($_[1],'ball') \*(-- you presumably meant\&\*(L"quant\*(R".) \f(CW\*(C`maketext\*(C'\fR catches these exceptions, but only to make theerror message more readable, at which point it rethrows the exception..PPAn exception \fImay\fR be thrown if \fIkey\fR is not found in anyof \f(CW$lh\fR's \f(CW%Lexicon\fR hashes. What happens if a key is not found,is discussed in a later section, \*(L"Controlling Lookup Failure\*(R"..PPNote that you might find it useful in some cases to overridethe \f(CW\*(C`maketext\*(C'\fR method with an \*(L"after method\*(R", if you want totranslate encodings, or even scripts:.PP.Vb 7\& package YrProj::zh_cn; # Chinese with PRC\-style glyphs\& use base (\*(AqYrProj::zh_tw\*(Aq); # Taiwan\-style\& sub maketext {\& my $self = shift(@_);\& my $value = $self\->maketext(@_);\& return Chineeze::taiwan2mainland($value);\& }.Ve.PPOr you may want to override it with something that trapsany exceptions, if that's critical to your program:.PP.Vb 7\& sub maketext {\& my($lh, @stuff) = @_;\& my $out;\& eval { $out = $lh\->SUPER::maketext(@stuff) };\& return $out unless $@;\& ...otherwise deal with the exception...\& }.Ve.PPOther than those two situations, I don't imagine thatit's useful to override the \f(CW\*(C`maketext\*(C'\fR method. (Ifyou run into a situation where it is useful, I'd beinterested in hearing about it.).ie n .IP "$lh\fR\->fail_with \fIor\fR \f(CW$lh\fR\->fail_with(\fI\s-1PARAM\s0)" 4.el .IP "\f(CW$lh\fR\->fail_with \fIor\fR \f(CW$lh\fR\->fail_with(\fI\s-1PARAM\s0\fR)" 4.IX Item "$lh->fail_with or $lh->fail_with(PARAM)".PD 0.ie n .IP "$lh\->failure_handler_auto" 4.el .IP "\f(CW$lh\fR\->failure_handler_auto" 4.IX Item "$lh->failure_handler_auto".PDThese two methods are discussed in the section \*(L"ControllingLookup Failure\*(R"..Sh "Utility Methods".IX Subsection "Utility Methods"These are methods that you may find it handy to use, generallyfrom \f(CW%Lexicon\fR routines of yours (whether expressed asBracket Notation or not)..ie n .IP "$language\fR\->quant($number, \f(CW$singular)" 4.el .IP "\f(CW$language\fR\->quant($number, \f(CW$singular\fR)" 4.IX Item "$language->quant($number, $singular)".PD 0.ie n .IP "$language\fR\->quant($number, \f(CW$singular\fR, \f(CW$plural)" 4.el .IP "\f(CW$language\fR\->quant($number, \f(CW$singular\fR, \f(CW$plural\fR)" 4.IX Item "$language->quant($number, $singular, $plural)".ie n .IP "$language\fR\->quant($number, \f(CW$singular\fR, \f(CW$plural\fR, \f(CW$negative)" 4.el .IP "\f(CW$language\fR\->quant($number, \f(CW$singular\fR, \f(CW$plural\fR, \f(CW$negative\fR)" 4.IX Item "$language->quant($number, $singular, $plural, $negative)".PDThis is generally meant to be called from inside Bracket Notation(which is discussed later), as in.Sp.Vb 1\& "Your search matched [quant,_1,document]!".Ve.SpIt's for \fIquantifying\fR a noun (i.e., saying how much of it there is,while giving the correct form of it). The behavior of this method ishandy for English and a few other Western European languages, and youshould override it for languages where it's not suitable. You can feelfree to read the source, but the current implementation is basicallyas this pseudocode describes:.Sp.Vb 11\& if $number is 0 and there\*(Aqs a $negative,\& return $negative;\& elsif $number is 1,\& return "1 $singular";\& elsif there\*(Aqs a $plural,\& return "$number $plural";\& else\& return "$number " . $singular . "s";\& #\& # ...except that we actually call numf to\& # stringify $number before returning it..Ve.SpSo for English (with Bracket Notation)\&\f(CW"...[quant,_1,file]..."\fR is fine (for 0 it returns \*(L"0 files\*(R",for 1 it returns \*(L"1 file\*(R", and for more it returns \*(L"2 files\*(R", etc.).SpBut for \*(L"directory\*(R", you'd want \f(CW"[quant,_1,directory,directories]"\fRso that our elementary \f(CW\*(C`quant\*(C'\fR method doesn't think that theplural of \*(L"directory\*(R" is \*(L"directorys\*(R". And you might find that theoutput may sound better if you specify a negative form, as in:.Sp.Vb 1\& "[quant,_1,file,files,No files] matched your query.\en".Ve.SpRemember to keep in mind verb agreement (or adjectives too, inother languages), as in:.Sp.Vb 1\& "[quant,_1,document] were matched.\en".Ve.SpBecause if _1 is one, you get "1 document \fBwere\fR matched".An acceptable hack here is to do something like this:.Sp.Vb 1\& "[quant,_1,document was, documents were] matched.\en".Ve.ie n .IP "$language\->numf($number)" 4.el .IP "\f(CW$language\fR\->numf($number)" 4.IX Item "$language->numf($number)"This returns the given number formatted nicely according tothis language's conventions. Maketext's default method ismostly to just take the normal string form of the number(applying sprintf \*(L"%G\*(R" for only very large numbers), and thento add commas as necessary. (Except thatwe apply \f(CW\*(C`tr/,./.,/\*(C'\fR if \f(CW$language\fR\->{'numf_comma'} is true;that's a bit of a hack that's useful for languages that expresstwo million as \*(L"2.000.000\*(R" and not as \*(L"2,000,000\*(R")..SpIf you want anything fancier, consider overriding this with somethingthat uses Number::Format, or does something elseentirely..SpNote that numf is called by quant for stringifying all quantifyingnumbers..ie n .IP "$language\fR\->sprintf($format, \f(CW@items)" 4.el .IP "\f(CW$language\fR\->sprintf($format, \f(CW@items\fR)" 4.IX Item "$language->sprintf($format, @items)"This is just a wrapper around Perl's normal \f(CW\*(C`sprintf\*(C'\fR function.It's provided so that you can use \*(L"sprintf\*(R" in Bracket Notation:.Sp.Vb 1\& "Couldn\*(Aqt access datanode [sprintf,%10x=~[%s~],_1,_2]!\en".Ve.Spreturning....Sp.Vb 1\& Couldn\*(Aqt access datanode Stuff=[thangamabob]!.Ve.ie n .IP "$language\fR\->\fIlanguage_tag()" 4.el .IP "\f(CW$language\fR\->\fIlanguage_tag()\fR" 4.IX Item "$language->language_tag()"Currently this just takes the last bit of \f(CW\*(C`ref($language)\*(C'\fR, turnsunderscores to dashes, and returns it. So if \f(CW$language\fR isan object of class Hee::HOO::Haw::en_us, \f(CW$language\fR\->\fIlanguage_tag()\fRreturns \*(L"en-us\*(R". (Yes, the usual representation for that languagetag is \*(L"en-US\*(R", but case is \fInever\fR considered meaningful inlanguage-tag comparison.).SpYou may override this as you like; Maketext doesn't use it foranything..ie n .IP "$language\fR\->\fIencoding()" 4.el .IP "\f(CW$language\fR\->\fIencoding()\fR" 4.IX Item "$language->encoding()"Currently this isn't used for anything, but it's provided(with default value of\&\f(CW\*(C`(ref($language) && $language\->{\*(Aqencoding\*(Aq})) or "iso\-8859\-1"\*(C'\fR) as a sort of suggestion that it may be useful/necessary toassociate encodings with your language handles (whether on aper-class or even per-handle basis.).Sh "Language Handle Attributes and Internals".IX Subsection "Language Handle Attributes and Internals"A language handle is a flyweight object \*(-- i.e., it doesn't (necessarily)carry any data of interest, other than just being a member ofwhatever class it belongs to..PPA language handle is implemented as a blessed hash. Subclasses of yourscan store whatever data you want in the hash. Currently the only hashentry used by any crucial Maketext method is \*(L"fail\*(R", so feel free touse anything else as you like..PP\&\fBRemember: Don't be afraid to read the Maketext source if there'sany point on which this documentation is unclear.\fR This documentationis vastly longer than the module source itself..SH "LANGUAGE CLASS HIERARCHIES".IX Header "LANGUAGE CLASS HIERARCHIES"These are Locale::Maketext's assumptions about the classhierarchy formed by all your language classes:.IP "\(bu" 4You must have a project base class, which you load, andwhich you then use as the first argument inthe call to YourProjClass\->get_handle(...). It should derive(whether directly or indirectly) from Locale::Maketext.It \fBdoesn't matter\fR how you name this class, although assuming thisis the localization component of your Super Mega Program,good names for your project class might beSuperMegaProgram::Localization, SuperMegaProgram::L10N,SuperMegaProgram::I18N, SuperMegaProgram::International,or even SuperMegaProgram::Languages or SuperMegaProgram::Messages..IP "\(bu" 4Language classes are what YourProjClass\->get_handle will try to load.It will look for them by taking each language-tag (\fBskipping\fR itif it doesn't look like a language-tag or locale-tag!), turning it toall lowercase, turning dashes to underscores, and appending itto YourProjClass . \*(L"::\*(R". So this:.Sp.Vb 3\& $lh = YourProjClass\->get_handle(\& \*(Aqen\-US\*(Aq, \*(Aqfr\*(Aq, \*(Aqkon\*(Aq, \*(Aqi\-klingon\*(Aq, \*(Aqi\-klingon\-romanized\*(Aq\& );.Ve.Spwill try loading the classes YourProjClass::en_us (note lowercase!), YourProjClass::fr, YourProjClass::kon,YourProjClass::i_klingonand YourProjClass::i_klingon_romanized. (And it'll stop at thefirst one that actually loads.).IP "\(bu" 4I assume that each language class derives (directly or indirectly)from your project class, and also defines its \f(CW@ISA\fR, its \f(CW%Lexicon\fR,or both. But I anticipate no dire consequences if these assumptionsdo not hold..IP "\(bu" 4Language classes may derive from other language classes (although theyshould have "use \fIThatclassname\fR\*(L" or \*(R"use base qw(\fI...classes...\fR)").They may derive from the projectclass. They may derive from some other class altogether. Or viamultiple inheritance, it may derive from any mixture of these..IP "\(bu" 4I foresee no problems with having multiple inheritance inyour hierarchy of language classes. (As usual, however, Perl willcomplain bitterly if you have a cycle in the hierarchy: i.e., ifany class is its own ancestor.).SH "ENTRIES IN EACH LEXICON".IX Header "ENTRIES IN EACH LEXICON"A typical \f(CW%Lexicon\fR entry is meant to signify a phrase,taking some number (0 or more) of parameters. An entryis meant to be accessed by viaa string \fIkey\fR in \f(CW$lh\fR\->maketext(\fIkey\fR, ...parameters...),which should return a string that is generally meant forbe used for \*(L"output\*(R" to the user \*(-- regardless of whetherthis actually means printing to \s-1STDOUT\s0, writing to a file,or putting into a \s-1GUI\s0 widget..PPWhile the key must be a string value (since that's a basicrestriction that Perl places on hash keys), the value inthe lexicon can currently be of several types:a defined scalar, scalarref, or coderef. The use of these isexplained above, in the section 'The \*(L"maketext\*(R" Method', andBracket Notation for strings is discussed in the next section..PPWhile you can use arbitrary unique IDs for lexicon keys(like \*(L"_min_larger_max_error\*(R"), it is oftenuseful for if an entry's key is itself a valid value, likethis example error message:.PP.Vb 1\& "Minimum ([_1]) is larger than maximum ([_2])!\en",.Ve.PPCompare this code that uses an arbitrary \s-1ID\s0....PP.Vb 2\& die $lh\->maketext( "_min_larger_max_error", $min, $max )\& if $min > $max;.Ve.PP\&...to this code that uses a key-as-value:.PP.Vb 4\& die $lh\->maketext(\& "Minimum ([_1]) is larger than maximum ([_2])!\en",\& $min, $max\& ) if $min > $max;.Ve.PPThe second is, in short, more readable. In particular, it's obviousthat the number of parameters you're feeding to that phrase (two) isthe number of parameters that it \fIwants\fR to be fed. (Since you see_1 and a _2 being used in the key there.).PPAlso, once a project is otherwisecomplete and you start to localize it, you can scrape togetherall the various keys you use, and pass it to a translator; and thenthe translator's work will go faster if what he's presented is this:.PP.Vb 2\& "Minimum ([_1]) is larger than maximum ([_2])!\en",\& => "", # fill in something here, Jacques!.Ve.PPrather than this more cryptic mess:.PP.Vb 2\& "_min_larger_max_error"\& => "", # fill in something here, Jacques.Ve.PPI think that keys as lexicon values makes the completed lexiconentries more readable:.PP.Vb 2\& "Minimum ([_1]) is larger than maximum ([_2])!\en",\& => "Le minimum ([_1]) est plus grand que le maximum ([_2])!\en",.Ve.PPAlso, having valid values as keys becomes very useful if you setup an _AUTO lexicon. _AUTO lexicons are discussed in a latersection..PPI almost always use keys that are themselvesvalid lexicon values. One notable exception is when the value isquite long. For example, to get the screenful of data thata command-line program might return when given an unknown switch,I often just use a brief, self-explanatory key such as \*(L"_USAGE_MESSAGE\*(R". At that point I then goand immediately to define that lexicon entry in theProjectClass::L10N::en lexicon (since English is always my \*(L"projectlanguage\*(R"):.PP.Vb 3\& \*(Aq_USAGE_MESSAGE\*(Aq => <<\*(AqEOSTUFF\*(Aq,\& ...long long message...\& EOSTUFF.Ve.PPand then I can use it as:.PP.Vb 1\& getopt(\*(AqoDI\*(Aq, \e%opts) or die $lh\->maketext(\*(Aq_USAGE_MESSAGE\*(Aq);.Ve.PPIncidentally,note that each class's \f(CW%Lexicon\fR inherits-and-extendsthe lexicons in its superclasses. This is not because these arespecial hashes \fIper se\fR, but because you access them via the\&\f(CW\*(C`maketext\*(C'\fR method, which looks for entries across all the\&\f(CW%Lexicon\fR hashes in a language class \fIand\fR all its ancestor classes.(This is because the idea of \*(L"class data\*(R" isn't directly implementedin Perl, but is instead left to individual class-systems to implementas they see fit..).PPNote that you may have things stored in a lexiconbesides just phrases for output: for example, if your programtakes input from the keyboard, asking a \*(L"(Y/N)\*(R" question,you probably need to know what the equivalent of \*(L"Y[es]/N[o]\*(R" isin whatever language. You probably also need to know whatthe equivalents of the answers \*(L"y\*(R" and \*(L"n\*(R" are. You canstore that information in the lexicon (say, under the keys\&\*(L"~answer_y\*(R" and \*(L"~answer_n\*(R", and the long forms as\&\*(L"~answer_yes\*(R" and \*(L"~answer_no\*(R", where \*(L"~\*(R" is just an ad-hoccharacter meant to indicate to programmers/translators that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -