Blame lib/Locale/Maketext.pod

Packit f92f8e
Packit f92f8e
# Time-stamp: "2004-01-11 18:35:34 AST"
Packit f92f8e
Packit f92f8e
=head1 NAME
Packit f92f8e
Packit f92f8e
Locale::Maketext - framework for localization
Packit f92f8e
Packit f92f8e
=head1 SYNOPSIS
Packit f92f8e
Packit f92f8e
  package MyProgram;
Packit f92f8e
  use strict;
Packit f92f8e
  use MyProgram::L10N;
Packit f92f8e
   # ...which inherits from Locale::Maketext
Packit f92f8e
  my $lh = MyProgram::L10N->get_handle() || die "What language?";
Packit f92f8e
  ...
Packit f92f8e
  # And then any messages your program emits, like:
Packit f92f8e
  warn $lh->maketext( "Can't open file [_1]: [_2]\n", $f, $! );
Packit f92f8e
  ...
Packit f92f8e
Packit f92f8e
=head1 DESCRIPTION
Packit f92f8e
Packit f92f8e
It is a common feature of applications (whether run directly,
Packit f92f8e
or via the Web) for them to be "localized" -- i.e., for them
Packit f92f8e
to a present an English interface to an English-speaker, a German
Packit f92f8e
interface to a German-speaker, and so on for all languages it's
Packit f92f8e
programmed with.  Locale::Maketext
Packit f92f8e
is a framework for software localization; it provides you with the
Packit f92f8e
tools for organizing and accessing the bits of text and text-processing
Packit f92f8e
code that you need for producing localized applications.
Packit f92f8e
Packit f92f8e
In order to make sense of Maketext and how all its
Packit f92f8e
components fit together, you should probably
Packit f92f8e
go read L<Locale::Maketext::TPJ13|Locale::Maketext::TPJ13>, and
Packit f92f8e
I<then> read the following documentation.
Packit f92f8e
Packit f92f8e
You may also want to read over the source for C<File::Findgrep>
Packit f92f8e
and its constituent modules -- they are a complete (if small)
Packit f92f8e
example application that uses Maketext.
Packit f92f8e
Packit f92f8e
=head1 QUICK OVERVIEW
Packit f92f8e
Packit f92f8e
The basic design of Locale::Maketext is object-oriented, and
Packit f92f8e
Locale::Maketext is an abstract base class, from which you
Packit f92f8e
derive a "project class".
Packit f92f8e
The project class (with a name like "TkBocciBall::Localize",
Packit f92f8e
which you then use in your module) is in turn the base class
Packit f92f8e
for all the "language classes" for your project
Packit f92f8e
(with names "TkBocciBall::Localize::it", 
Packit f92f8e
"TkBocciBall::Localize::en",
Packit f92f8e
"TkBocciBall::Localize::fr", etc.).
Packit f92f8e
Packit f92f8e
A language class is
Packit f92f8e
a class containing a lexicon of phrases as class data,
Packit f92f8e
and possibly also some methods that are of use in interpreting
Packit f92f8e
phrases in the lexicon, or otherwise dealing with text in that
Packit f92f8e
language.
Packit f92f8e
Packit f92f8e
An object belonging to a language class is called a "language
Packit f92f8e
handle"; it's typically a flyweight object.
Packit f92f8e
Packit f92f8e
The normal course of action is to call:
Packit f92f8e
Packit f92f8e
  use TkBocciBall::Localize;  # the localization project class
Packit f92f8e
  $lh = TkBocciBall::Localize->get_handle();
Packit f92f8e
   # Depending on the user's locale, etc., this will
Packit f92f8e
   # make a language handle from among the classes available,
Packit f92f8e
   # and any defaults that you declare.
Packit f92f8e
  die "Couldn't make a language handle??" unless $lh;
Packit f92f8e
Packit f92f8e
From then on, you use the C<maketext> function to access
Packit f92f8e
entries in whatever lexicon(s) belong to the language handle
Packit f92f8e
you got.  So, this:
Packit f92f8e
Packit f92f8e
  print $lh->maketext("You won!"), "\n";
Packit f92f8e
Packit f92f8e
...emits the right text for this language.  If the object
Packit f92f8e
in C<$lh> belongs to class "TkBocciBall::Localize::fr" and
Packit f92f8e
%TkBocciBall::Localize::fr::Lexicon contains C<("You won!"
Packit f92f8e
=E<gt> "Tu as gagnE<eacute>!")>, then the above
Packit f92f8e
code happily tells the user "Tu as gagnE<eacute>!".
Packit f92f8e
Packit f92f8e
=head1 METHODS
Packit f92f8e
Packit f92f8e
Locale::Maketext offers a variety of methods, which fall
Packit f92f8e
into three categories:
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Methods to do with constructing language handles.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
C<maketext> and other methods to do with accessing %Lexicon data
Packit f92f8e
for a given language handle.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Methods that you may find it handy to use, from routines of
Packit f92f8e
yours that you put in %Lexicon entries.
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
These are covered in the following section.
Packit f92f8e
Packit f92f8e
=head2 Construction Methods
Packit f92f8e
Packit f92f8e
These are to do with constructing a language handle:
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
$lh = YourProjClass->get_handle( ...langtags... ) || die "lg-handle?";
Packit f92f8e
Packit f92f8e
This tries loading classes based on the language-tags you give (like
Packit f92f8e
C<("en-US", "sk", "kon", "es-MX", "ja", "i-klingon")>, and for the first class
Packit f92f8e
that succeeds, returns YourProjClass::I<language>->new().
Packit f92f8e
Packit f92f8e
If it runs thru the entire given list of language-tags, and finds no classes
Packit f92f8e
for those exact terms, it then tries "superordinate" language classes.
Packit f92f8e
So if no "en-US" class (i.e., YourProjClass::en_us)
Packit f92f8e
was found, nor classes for anything else in that list, we then try
Packit f92f8e
its superordinate, "en" (i.e., YourProjClass::en), and so on thru 
Packit f92f8e
the other language-tags in the given list: "es".
Packit f92f8e
(The other language-tags in our example list: 
Packit f92f8e
happen to have no superordinates.)
Packit f92f8e
Packit f92f8e
If none of those language-tags leads to loadable classes, we then
Packit f92f8e
try classes derived from YourProjClass->fallback_languages() and
Packit f92f8e
then if nothing comes of that, we use classes named by
Packit f92f8e
YourProjClass->fallback_language_classes().  Then in the (probably
Packit f92f8e
quite unlikely) event that that fails, we just return undef.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
$lh = YourProjClass->get_handleB<()> || die "lg-handle?";
Packit f92f8e
Packit f92f8e
When C<get_handle> is called with an empty parameter list, magic happens:
Packit f92f8e
Packit f92f8e
If C<get_handle> senses that it's running in program that was
Packit f92f8e
invoked as a CGI, then it tries to get language-tags out of the
Packit f92f8e
environment variable "HTTP_ACCEPT_LANGUAGE", and it pretends that
Packit f92f8e
those were the languages passed as parameters to C<get_handle>.
Packit f92f8e
Packit f92f8e
Otherwise (i.e., if not a CGI), this tries various OS-specific ways
Packit f92f8e
to get the language-tags for the current locale/language, and then
Packit f92f8e
pretends that those were the value(s) passed to C<get_handle>.
Packit f92f8e
Packit f92f8e
Currently this OS-specific stuff consists of looking in the environment
Packit f92f8e
variables "LANG" and "LANGUAGE"; and on MSWin machines (where those
Packit f92f8e
variables are typically unused), this also tries using
Packit f92f8e
the module Win32::Locale to get a language-tag for whatever language/locale
Packit f92f8e
is currently selected in the "Regional Settings" (or "International"?)
Packit f92f8e
Control Panel.  I welcome further
Packit f92f8e
suggestions for making this do the Right Thing under other operating
Packit f92f8e
systems that support localization.
Packit f92f8e
Packit f92f8e
If you're using localization in an application that keeps a configuration
Packit f92f8e
file, you might consider something like this in your project class:
Packit f92f8e
Packit f92f8e
  sub get_handle_via_config {
Packit f92f8e
    my $class = $_[0];
Packit f92f8e
    my $chosen_language = $Config_settings{'language'};
Packit f92f8e
    my $lh;
Packit f92f8e
    if($chosen_language) {
Packit f92f8e
      $lh = $class->get_handle($chosen_language)
Packit f92f8e
       || die "No language handle for \"$chosen_language\""
Packit f92f8e
            . " or the like";
Packit f92f8e
    } else {
Packit f92f8e
      # Config file missing, maybe?
Packit f92f8e
      $lh = $class->get_handle()
Packit f92f8e
       || die "Can't get a language handle";
Packit f92f8e
    }
Packit f92f8e
    return $lh;
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
$lh = YourProjClass::langname->new();
Packit f92f8e
Packit f92f8e
This constructs a language handle.  You usually B<don't> call this
Packit f92f8e
directly, but instead let C<get_handle> find a language class to C<use>
Packit f92f8e
and to then call ->new on.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
$lh->init();
Packit f92f8e
Packit f92f8e
This is called by ->new to initialize newly-constructed language handles.
Packit f92f8e
If you define an init method in your class, remember that it's usually
Packit f92f8e
considered a good idea to call $lh->SUPER::init in it (presumably at the
Packit f92f8e
beginning), so that all classes get a chance to initialize a new object
Packit f92f8e
however they see fit.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
YourProjClass->fallback_languages()
Packit f92f8e
Packit f92f8e
C<get_handle> appends the return value of this to the end of
Packit f92f8e
whatever list of languages you pass C<get_handle>.  Unless
Packit f92f8e
you override this method, your project class
Packit f92f8e
will inherit Locale::Maketext's C<fallback_languages>, which
Packit f92f8e
currently returns C<('i-default', 'en', 'en-US')>.
Packit f92f8e
("i-default" is defined in RFC 2277).
Packit f92f8e
Packit f92f8e
This method (by having it return the name
Packit f92f8e
of a language-tag that has an existing language class)
Packit f92f8e
can be used for making sure that
Packit f92f8e
C<get_handle> will always manage to construct a language
Packit f92f8e
handle (assuming your language classes are in an appropriate
Packit f92f8e
@INC directory).  Or you can use the next method:
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
YourProjClass->fallback_language_classes()
Packit f92f8e
Packit f92f8e
C<get_handle> appends the return value of this to the end
Packit f92f8e
of the list of classes it will try using.  Unless
Packit f92f8e
you override this method, your project class
Packit f92f8e
will inherit Locale::Maketext's C<fallback_language_classes>,
Packit f92f8e
which currently returns an empty list, C<()>.
Packit f92f8e
By setting this to some value (namely, the name of a loadable
Packit f92f8e
language class), you can be sure that
Packit f92f8e
C<get_handle> will always manage to construct a language
Packit f92f8e
handle.
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
=head2 The "maketext" Method
Packit f92f8e
Packit f92f8e
This is the most important method in Locale::Maketext:
Packit f92f8e
Packit f92f8e
    $text = $lh->maketext(I<key>, ...parameters for this phrase...);
Packit f92f8e
Packit f92f8e
This looks in the %Lexicon of the language handle
Packit f92f8e
$lh and all its superclasses, looking
Packit f92f8e
for an entry whose key is the string I<key>.  Assuming such
Packit f92f8e
an entry is found, various things then happen, depending on the
Packit f92f8e
value found:
Packit f92f8e
Packit f92f8e
If the value is a scalarref, the scalar is dereferenced and returned
Packit f92f8e
(and any parameters are ignored).
Packit f92f8e
Packit f92f8e
If the value is a coderef, we return &$value($lh, ...parameters...).
Packit f92f8e
Packit f92f8e
If the value is a string that I<doesn't> look like it's in Bracket Notation,
Packit f92f8e
we return it (after replacing it with a scalarref, in its %Lexicon).
Packit f92f8e
Packit f92f8e
If the value I<does> look like it's in Bracket Notation, then we compile
Packit f92f8e
it into a sub, replace the string in the %Lexicon with the new coderef,
Packit f92f8e
and then we return &$new_sub($lh, ...parameters...).
Packit f92f8e
Packit f92f8e
Bracket Notation is discussed in a later section.  Note
Packit f92f8e
that trying to compile a string into Bracket Notation can throw
Packit f92f8e
an exception if the string is not syntactically valid (say, by not
Packit f92f8e
balancing brackets right.)
Packit f92f8e
Packit f92f8e
Also, calling &$coderef($lh, ...parameters...) can throw any sort of
Packit f92f8e
exception (if, say, code in that sub tries to divide by zero).  But
Packit f92f8e
a very common exception occurs when you have Bracket
Packit f92f8e
Notation text that says to call a method "foo", but there is no such
Packit f92f8e
method.  (E.g., "You have [quaB<tn>,_1,ball]." will throw an exception
Packit f92f8e
on trying to call $lh->quaB<tn>($_[1],'ball') -- you presumably meant
Packit f92f8e
"quant".)  C<maketext> catches these exceptions, but only to make the
Packit f92f8e
error message more readable, at which point it rethrows the exception.
Packit f92f8e
Packit f92f8e
An exception I<may> be thrown if I<key> is not found in any
Packit f92f8e
of $lh's %Lexicon hashes.  What happens if a key is not found,
Packit f92f8e
is discussed in a later section, "Controlling Lookup Failure".
Packit f92f8e
Packit f92f8e
Note that you might find it useful in some cases to override
Packit f92f8e
the C<maketext> method with an "after method", if you want to
Packit f92f8e
translate encodings, or even scripts:
Packit f92f8e
Packit f92f8e
    package YrProj::zh_cn; # Chinese with PRC-style glyphs
Packit f92f8e
    use base ('YrProj::zh_tw');  # Taiwan-style
Packit f92f8e
    sub maketext {
Packit f92f8e
      my $self = shift(@_);
Packit f92f8e
      my $value = $self->maketext(@_);
Packit f92f8e
      return Chineeze::taiwan2mainland($value);
Packit f92f8e
    }
Packit f92f8e
Packit f92f8e
Or you may want to override it with something that traps
Packit f92f8e
any exceptions, if that's critical to your program:
Packit f92f8e
Packit f92f8e
  sub maketext {
Packit f92f8e
    my($lh, @stuff) = @_;
Packit f92f8e
    my $out;
Packit f92f8e
    eval { $out = $lh->SUPER::maketext(@stuff) };
Packit f92f8e
    return $out unless $@;
Packit f92f8e
    ...otherwise deal with the exception...
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
Other than those two situations, I don't imagine that
Packit f92f8e
it's useful to override the C<maketext> method.  (If
Packit f92f8e
you run into a situation where it is useful, I'd be
Packit f92f8e
interested in hearing about it.)
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item $lh->fail_with I<or> $lh->fail_with(I<PARAM>)
Packit f92f8e
Packit f92f8e
=item $lh->failure_handler_auto
Packit f92f8e
Packit f92f8e
These two methods are discussed in the section "Controlling
Packit f92f8e
Lookup Failure".
Packit f92f8e
Packit f92f8e
=item $lh->blacklist(@list)
Packit f92f8e
Packit f92f8e
=item $lh->whitelist(@list)
Packit f92f8e
Packit f92f8e
These methods are discussed in the section "Bracket Notation
Packit f92f8e
Security".
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
=head2 Utility Methods
Packit f92f8e
Packit f92f8e
These are methods that you may find it handy to use, generally
Packit f92f8e
from %Lexicon routines of yours (whether expressed as
Packit f92f8e
Bracket Notation or not).
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item $language->quant($number, $singular)
Packit f92f8e
Packit f92f8e
=item $language->quant($number, $singular, $plural)
Packit f92f8e
Packit f92f8e
=item $language->quant($number, $singular, $plural, $negative)
Packit f92f8e
Packit f92f8e
This is generally meant to be called from inside Bracket Notation
Packit f92f8e
(which is discussed later), as in 
Packit f92f8e
Packit f92f8e
     "Your search matched [quant,_1,document]!"
Packit f92f8e
Packit f92f8e
It's for I<quantifying> a noun (i.e., saying how much of it there is,
Packit f92f8e
while giving the correct form of it).  The behavior of this method is
Packit f92f8e
handy for English and a few other Western European languages, and you
Packit f92f8e
should override it for languages where it's not suitable.  You can feel
Packit f92f8e
free to read the source, but the current implementation is basically
Packit f92f8e
as this pseudocode describes:
Packit f92f8e
Packit f92f8e
     if $number is 0 and there's a $negative,
Packit f92f8e
        return $negative;
Packit f92f8e
     elsif $number is 1,
Packit f92f8e
        return "1 $singular";
Packit f92f8e
     elsif there's a $plural,
Packit f92f8e
        return "$number $plural";
Packit f92f8e
     else
Packit f92f8e
        return "$number " . $singular . "s";
Packit f92f8e
     #
Packit f92f8e
     # ...except that we actually call numf to
Packit f92f8e
     #  stringify $number before returning it.
Packit f92f8e
Packit f92f8e
So for English (with Bracket Notation)
Packit f92f8e
C<"...[quant,_1,file]..."> is fine (for 0 it returns "0 files",
Packit f92f8e
for 1 it returns "1 file", and for more it returns "2 files", etc.)
Packit f92f8e
Packit f92f8e
But for "directory", you'd want C<"[quant,_1,directory,directories]">
Packit f92f8e
so that our elementary C<quant> method doesn't think that the
Packit f92f8e
plural of "directory" is "directorys".  And you might find that the
Packit f92f8e
output may sound better if you specify a negative form, as in:
Packit f92f8e
Packit f92f8e
     "[quant,_1,file,files,No files] matched your query.\n"
Packit f92f8e
Packit f92f8e
Remember to keep in mind verb agreement (or adjectives too, in
Packit f92f8e
other languages), as in:
Packit f92f8e
Packit f92f8e
     "[quant,_1,document] were matched.\n"
Packit f92f8e
Packit f92f8e
Because if _1 is one, you get "1 document B<were> matched".
Packit f92f8e
An acceptable hack here is to do something like this:
Packit f92f8e
Packit f92f8e
     "[quant,_1,document was, documents were] matched.\n"
Packit f92f8e
Packit f92f8e
=item $language->numf($number)
Packit f92f8e
Packit f92f8e
This returns the given number formatted nicely according to
Packit f92f8e
this language's conventions.  Maketext's default method is
Packit f92f8e
mostly to just take the normal string form of the number
Packit f92f8e
(applying sprintf "%G" for only very large numbers), and then
Packit f92f8e
to add commas as necessary.  (Except that
Packit f92f8e
we apply C if $language->{'numf_comma'} is true;
Packit f92f8e
that's a bit of a hack that's useful for languages that express
Packit f92f8e
two million as "2.000.000" and not as "2,000,000").
Packit f92f8e
Packit f92f8e
If you want anything fancier, consider overriding this with something
Packit f92f8e
that uses L<Number::Format|Number::Format>, or does something else
Packit f92f8e
entirely.
Packit f92f8e
Packit f92f8e
Note that numf is called by quant for stringifying all quantifying
Packit f92f8e
numbers.
Packit f92f8e
Packit f92f8e
=item $language->numerate($number, $singular, $plural, $negative)
Packit f92f8e
Packit f92f8e
This returns the given noun form which is appropriate for the quantity
Packit f92f8e
C<$number> according to this language's conventions.  C<numerate> is
Packit f92f8e
used internally by C<quant> to quantify nouns.  Use it directly --
Packit f92f8e
usually from bracket notation -- to avoid C<quant>'s implicit call to
Packit f92f8e
C<numf> and output of a numeric quantity.
Packit f92f8e
Packit f92f8e
=item $language->sprintf($format, @items)
Packit f92f8e
Packit f92f8e
This is just a wrapper around Perl's normal C<sprintf> function.
Packit f92f8e
It's provided so that you can use "sprintf" in Bracket Notation:
Packit f92f8e
Packit f92f8e
     "Couldn't access datanode [sprintf,%10x=~[%s~],_1,_2]!\n"
Packit f92f8e
Packit f92f8e
returning...
Packit f92f8e
Packit f92f8e
     Couldn't access datanode      Stuff=[thangamabob]!
Packit f92f8e
Packit f92f8e
=item $language->language_tag()
Packit f92f8e
Packit f92f8e
Currently this just takes the last bit of C<ref($language)>, turns
Packit f92f8e
underscores to dashes, and returns it.  So if $language is
Packit f92f8e
an object of class Hee::HOO::Haw::en_us, $language->language_tag()
Packit f92f8e
returns "en-us".  (Yes, the usual representation for that language
Packit f92f8e
tag is "en-US", but case is I<never> considered meaningful in
Packit f92f8e
language-tag comparison.)
Packit f92f8e
Packit f92f8e
You may override this as you like; Maketext doesn't use it for
Packit f92f8e
anything.
Packit f92f8e
Packit f92f8e
=item $language->encoding()
Packit f92f8e
Packit f92f8e
Currently this isn't used for anything, but it's provided
Packit f92f8e
(with default value of
Packit f92f8e
C<(ref($language) && $language-E<gt>{'encoding'})) or "iso-8859-1">
Packit f92f8e
) as a sort of suggestion that it may be useful/necessary to
Packit f92f8e
associate encodings with your language handles (whether on a
Packit f92f8e
per-class or even per-handle basis.)
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
=head2 Language Handle Attributes and Internals
Packit f92f8e
Packit f92f8e
A language handle is a flyweight object -- i.e., it doesn't (necessarily)
Packit f92f8e
carry any data of interest, other than just being a member of
Packit f92f8e
whatever class it belongs to.
Packit f92f8e
Packit f92f8e
A language handle is implemented as a blessed hash.  Subclasses of yours
Packit f92f8e
can store whatever data you want in the hash.  Currently the only hash
Packit f92f8e
entry used by any crucial Maketext method is "fail", so feel free to
Packit f92f8e
use anything else as you like.
Packit f92f8e
Packit f92f8e
B
Packit f92f8e
any point on which this documentation is unclear.>  This documentation
Packit f92f8e
is vastly longer than the module source itself.
Packit f92f8e
Packit f92f8e
=head1 LANGUAGE CLASS HIERARCHIES
Packit f92f8e
Packit f92f8e
These are Locale::Maketext's assumptions about the class
Packit f92f8e
hierarchy formed by all your language classes:
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
You must have a project base class, which you load, and
Packit f92f8e
which you then use as the first argument in
Packit f92f8e
the call to YourProjClass->get_handle(...).  It should derive
Packit f92f8e
(whether directly or indirectly) from Locale::Maketext.
Packit f92f8e
It B<doesn't matter> how you name this class, although assuming this
Packit f92f8e
is the localization component of your Super Mega Program,
Packit f92f8e
good names for your project class might be
Packit f92f8e
SuperMegaProgram::Localization, SuperMegaProgram::L10N,
Packit f92f8e
SuperMegaProgram::I18N, SuperMegaProgram::International,
Packit f92f8e
or even SuperMegaProgram::Languages or SuperMegaProgram::Messages.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Language classes are what YourProjClass->get_handle will try to load.
Packit f92f8e
It will look for them by taking each language-tag (B<skipping> it
Packit f92f8e
if it doesn't look like a language-tag or locale-tag!), turning it to
Packit f92f8e
all lowercase, turning dashes to underscores, and appending it
Packit f92f8e
to YourProjClass . "::".  So this:
Packit f92f8e
Packit f92f8e
  $lh = YourProjClass->get_handle(
Packit f92f8e
    'en-US', 'fr', 'kon', 'i-klingon', 'i-klingon-romanized'
Packit f92f8e
  );
Packit f92f8e
Packit f92f8e
will try loading the classes 
Packit f92f8e
YourProjClass::en_us (note lowercase!), YourProjClass::fr, 
Packit f92f8e
YourProjClass::kon,
Packit f92f8e
YourProjClass::i_klingon
Packit f92f8e
and YourProjClass::i_klingon_romanized.  (And it'll stop at the
Packit f92f8e
first one that actually loads.)
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
I assume that each language class derives (directly or indirectly)
Packit f92f8e
from your project class, and also defines its @ISA, its %Lexicon,
Packit f92f8e
or both.  But I anticipate no dire consequences if these assumptions
Packit f92f8e
do not hold.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Language classes may derive from other language classes (although they
Packit f92f8e
should have "use I<Thatclassname>" or "use base qw(I<...classes...>)").
Packit f92f8e
They may derive from the project
Packit f92f8e
class.  They may derive from some other class altogether.  Or via
Packit f92f8e
multiple inheritance, it may derive from any mixture of these.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
I foresee no problems with having multiple inheritance in
Packit f92f8e
your hierarchy of language classes.  (As usual, however, Perl will
Packit f92f8e
complain bitterly if you have a cycle in the hierarchy: i.e., if
Packit f92f8e
any class is its own ancestor.)
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
=head1 ENTRIES IN EACH LEXICON
Packit f92f8e
Packit f92f8e
A typical %Lexicon entry is meant to signify a phrase,
Packit f92f8e
taking some number (0 or more) of parameters.  An entry
Packit f92f8e
is meant to be accessed by via
Packit f92f8e
a string I<key> in $lh->maketext(I<key>, ...parameters...),
Packit f92f8e
which should return a string that is generally meant for
Packit f92f8e
be used for "output" to the user -- regardless of whether
Packit f92f8e
this actually means printing to STDOUT, writing to a file,
Packit f92f8e
or putting into a GUI widget.
Packit f92f8e
Packit f92f8e
While the key must be a string value (since that's a basic
Packit f92f8e
restriction that Perl places on hash keys), the value in
Packit f92f8e
the lexicon can currently be of several types:
Packit f92f8e
a defined scalar, scalarref, or coderef.  The use of these is
Packit f92f8e
explained above, in the section 'The "maketext" Method', and
Packit f92f8e
Bracket Notation for strings is discussed in the next section.
Packit f92f8e
Packit f92f8e
While you can use arbitrary unique IDs for lexicon keys
Packit f92f8e
(like "_min_larger_max_error"), it is often
Packit f92f8e
useful for if an entry's key is itself a valid value, like
Packit f92f8e
this example error message:
Packit f92f8e
Packit f92f8e
  "Minimum ([_1]) is larger than maximum ([_2])!\n",
Packit f92f8e
Packit f92f8e
Compare this code that uses an arbitrary ID...
Packit f92f8e
Packit f92f8e
  die $lh->maketext( "_min_larger_max_error", $min, $max )
Packit f92f8e
   if $min > $max;
Packit f92f8e
Packit f92f8e
...to this code that uses a key-as-value:
Packit f92f8e
Packit f92f8e
  die $lh->maketext(
Packit f92f8e
   "Minimum ([_1]) is larger than maximum ([_2])!\n",
Packit f92f8e
   $min, $max
Packit f92f8e
  ) if $min > $max;
Packit f92f8e
Packit f92f8e
The second is, in short, more readable.  In particular, it's obvious
Packit f92f8e
that the number of parameters you're feeding to that phrase (two) is
Packit f92f8e
the number of parameters that it I<wants> to be fed.  (Since you see
Packit f92f8e
_1 and a _2 being used in the key there.)
Packit f92f8e
Packit f92f8e
Also, once a project is otherwise
Packit f92f8e
complete and you start to localize it, you can scrape together
Packit f92f8e
all the various keys you use, and pass it to a translator; and then
Packit f92f8e
the translator's work will go faster if what he's presented is this:
Packit f92f8e
Packit f92f8e
 "Minimum ([_1]) is larger than maximum ([_2])!\n",
Packit f92f8e
  => "",   # fill in something here, Jacques!
Packit f92f8e
Packit f92f8e
rather than this more cryptic mess:
Packit f92f8e
Packit f92f8e
 "_min_larger_max_error"
Packit f92f8e
  => "",   # fill in something here, Jacques
Packit f92f8e
Packit f92f8e
I think that keys as lexicon values makes the completed lexicon
Packit f92f8e
entries more readable:
Packit f92f8e
Packit f92f8e
 "Minimum ([_1]) is larger than maximum ([_2])!\n",
Packit f92f8e
  => "Le minimum ([_1]) est plus grand que le maximum ([_2])!\n",
Packit f92f8e
Packit f92f8e
Also, having valid values as keys becomes very useful if you set
Packit f92f8e
up an _AUTO lexicon.  _AUTO lexicons are discussed in a later
Packit f92f8e
section.
Packit f92f8e
Packit f92f8e
I almost always use keys that are themselves
Packit f92f8e
valid lexicon values.  One notable exception is when the value is
Packit f92f8e
quite long.  For example, to get the screenful of data that
Packit f92f8e
a command-line program might return when given an unknown switch,
Packit f92f8e
I often just use a brief, self-explanatory key such as "_USAGE_MESSAGE".  At that point I then go
Packit f92f8e
and immediately to define that lexicon entry in the
Packit f92f8e
ProjectClass::L10N::en lexicon (since English is always my "project
Packit f92f8e
language"):
Packit f92f8e
Packit f92f8e
  '_USAGE_MESSAGE' => <<'EOSTUFF',
Packit f92f8e
  ...long long message...
Packit f92f8e
  EOSTUFF
Packit f92f8e
Packit f92f8e
and then I can use it as:
Packit f92f8e
Packit f92f8e
  getopt('oDI', \%opts) or die $lh->maketext('_USAGE_MESSAGE');
Packit f92f8e
Packit f92f8e
Incidentally,
Packit f92f8e
note that each class's C<%Lexicon> inherits-and-extends
Packit f92f8e
the lexicons in its superclasses.  This is not because these are
Packit f92f8e
special hashes I<per se>, but because you access them via the
Packit f92f8e
C<maketext> method, which looks for entries across all the
Packit f92f8e
C<%Lexicon> hashes in a language class I<and> all its ancestor classes.
Packit f92f8e
(This is because the idea of "class data" isn't directly implemented
Packit f92f8e
in Perl, but is instead left to individual class-systems to implement
Packit f92f8e
as they see fit..)
Packit f92f8e
Packit f92f8e
Note that you may have things stored in a lexicon
Packit f92f8e
besides just phrases for output:  for example, if your program
Packit f92f8e
takes input from the keyboard, asking a "(Y/N)" question,
Packit f92f8e
you probably need to know what the equivalent of "Y[es]/N[o]" is
Packit f92f8e
in whatever language.  You probably also need to know what
Packit f92f8e
the equivalents of the answers "y" and "n" are.  You can
Packit f92f8e
store that information in the lexicon (say, under the keys
Packit f92f8e
"~answer_y" and "~answer_n", and the long forms as
Packit f92f8e
"~answer_yes" and "~answer_no", where "~" is just an ad-hoc
Packit f92f8e
character meant to indicate to programmers/translators that
Packit f92f8e
these are not phrases for output).
Packit f92f8e
Packit f92f8e
Or instead of storing this in the language class's lexicon,
Packit f92f8e
you can (and, in some cases, really should) represent the same bit
Packit f92f8e
of knowledge as code in a method in the language class.  (That
Packit f92f8e
leaves a tidy distinction between the lexicon as the things we
Packit f92f8e
know how to I<say>, and the rest of the things in the lexicon class
Packit f92f8e
as things that we know how to I<do>.)  Consider
Packit f92f8e
this example of a processor for responses to French "oui/non"
Packit f92f8e
questions:
Packit f92f8e
Packit f92f8e
  sub y_or_n {
Packit f92f8e
    return undef unless defined $_[1] and length $_[1];
Packit f92f8e
    my $answer = lc $_[1];  # smash case
Packit f92f8e
    return 1 if $answer eq 'o' or $answer eq 'oui';
Packit f92f8e
    return 0 if $answer eq 'n' or $answer eq 'non';
Packit f92f8e
    return undef;
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
...which you'd then call in a construct like this:
Packit f92f8e
Packit f92f8e
  my $response;
Packit f92f8e
  until(defined $response) {
Packit f92f8e
    print $lh->maketext("Open the pod bay door (y/n)? ");
Packit f92f8e
    $response = $lh->y_or_n( get_input_from_keyboard_somehow() );
Packit f92f8e
  }
Packit f92f8e
  if($response) { $pod_bay_door->open()         }
Packit f92f8e
  else          { $pod_bay_door->leave_closed() }
Packit f92f8e
Packit f92f8e
Other data worth storing in a lexicon might be things like
Packit f92f8e
filenames for language-targetted resources:
Packit f92f8e
Packit f92f8e
  ...
Packit f92f8e
  "_main_splash_png"
Packit f92f8e
    => "/styles/en_us/main_splash.png",
Packit f92f8e
  "_main_splash_imagemap"
Packit f92f8e
    => "/styles/en_us/main_splash.incl",
Packit f92f8e
  "_general_graphics_path"
Packit f92f8e
    => "/styles/en_us/",
Packit f92f8e
  "_alert_sound"
Packit f92f8e
    => "/styles/en_us/hey_there.wav",
Packit f92f8e
  "_forward_icon"
Packit f92f8e
   => "left_arrow.png",
Packit f92f8e
  "_backward_icon"
Packit f92f8e
   => "right_arrow.png",
Packit f92f8e
  # In some other languages, left equals
Packit f92f8e
  #  BACKwards, and right is FOREwards.
Packit f92f8e
  ...
Packit f92f8e
Packit f92f8e
You might want to do the same thing for expressing key bindings
Packit f92f8e
or the like (since hardwiring "q" as the binding for the function
Packit f92f8e
that quits a screen/menu/program is useful only if your language
Packit f92f8e
happens to associate "q" with "quit"!)
Packit f92f8e
Packit f92f8e
=head1 BRACKET NOTATION
Packit f92f8e
Packit f92f8e
Bracket Notation is a crucial feature of Locale::Maketext.  I mean
Packit f92f8e
Bracket Notation to provide a replacement for the use of sprintf formatting.
Packit f92f8e
Everything you do with Bracket Notation could be done with a sub block,
Packit f92f8e
but bracket notation is meant to be much more concise.
Packit f92f8e
Packit f92f8e
Bracket Notation is a like a miniature "template" system (in the sense
Packit f92f8e
of L<Text::Template|Text::Template>, not in the sense of C++ templates),
Packit f92f8e
where normal text is passed thru basically as is, but text in special
Packit f92f8e
regions is specially interpreted.  In Bracket Notation, you use square brackets ("[...]"),
Packit f92f8e
not curly braces ("{...}") to note sections that are specially interpreted.
Packit f92f8e
Packit f92f8e
For example, here all the areas that are taken literally are underlined with
Packit f92f8e
a "^", and all the in-bracket special regions are underlined with an X:
Packit f92f8e
Packit f92f8e
  "Minimum ([_1]) is larger than maximum ([_2])!\n",
Packit f92f8e
   ^^^^^^^^^ XX ^^^^^^^^^^^^^^^^^^^^^^^^^^ XX ^^^^
Packit f92f8e
Packit f92f8e
When that string is compiled from bracket notation into a real Perl sub,
Packit f92f8e
it's basically turned into:
Packit f92f8e
Packit f92f8e
  sub {
Packit f92f8e
    my $lh = $_[0];
Packit f92f8e
    my @params = @_;
Packit f92f8e
    return join '',
Packit f92f8e
      "Minimum (",
Packit f92f8e
      ...some code here...
Packit f92f8e
      ") is larger than maximum (",
Packit f92f8e
      ...some code here...
Packit f92f8e
      ")!\n",
Packit f92f8e
  }
Packit f92f8e
  # to be called by $lh->maketext(KEY, params...)
Packit f92f8e
Packit f92f8e
In other words, text outside bracket groups is turned into string
Packit f92f8e
literals.  Text in brackets is rather more complex, and currently follows
Packit f92f8e
these rules:
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Bracket groups that are empty, or which consist only of whitespace,
Packit f92f8e
are ignored.  (Examples: "[]", "[    ]", or a [ and a ] with returns
Packit f92f8e
and/or tabs and/or spaces between them.
Packit f92f8e
Packit f92f8e
Otherwise, each group is taken to be a comma-separated group of items,
Packit f92f8e
and each item is interpreted as follows:
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
An item that is "_I<digits>" or "_-I<digits>" is interpreted as
Packit f92f8e
$_[I<value>].  I.e., "_1" becomes with $_[1], and "_-3" is interpreted
Packit f92f8e
as $_[-3] (in which case @_ should have at least three elements in it).
Packit f92f8e
Note that $_[0] is the language handle, and is typically not named
Packit f92f8e
directly.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
An item "_*" is interpreted to mean "all of @_ except $_[0]".
Packit f92f8e
I.e., C<@_[1..$#_]>.  Note that this is an empty list in the case
Packit f92f8e
of calls like $lh->maketext(I<key>) where there are no
Packit f92f8e
parameters (except $_[0], the language handle).
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Otherwise, each item is interpreted as a string literal.
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
The group as a whole is interpreted as follows:
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
If the first item in a bracket group looks like a method name,
Packit f92f8e
then that group is interpreted like this:
Packit f92f8e
Packit f92f8e
  $lh->that_method_name(
Packit f92f8e
    ...rest of items in this group...
Packit f92f8e
  ),
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
If the first item in a bracket group is "*", it's taken as shorthand
Packit f92f8e
for the so commonly called "quant" method.  Similarly, if the first
Packit f92f8e
item in a bracket group is "#", it's taken to be shorthand for
Packit f92f8e
"numf".
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
If the first item in a bracket group is the empty-string, or "_*"
Packit f92f8e
or "_I<digits>" or "_-I<digits>", then that group is interpreted
Packit f92f8e
as just the interpolation of all its items:
Packit f92f8e
Packit f92f8e
  join('',
Packit f92f8e
    ...rest of items in this group...
Packit f92f8e
  ),
Packit f92f8e
Packit f92f8e
Examples:  "[_1]" and "[,_1]", which are synonymous; and
Packit f92f8e
"C<[,ID-(,_4,-,_2,)]>", which compiles as
Packit f92f8e
C<join "", "ID-(", $_[4], "-", $_[2], ")">.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Otherwise this bracket group is invalid.  For example, in the group
Packit f92f8e
"[!@#,whatever]", the first item C<"!@#"> is neither the empty-string,
Packit f92f8e
"_I<number>", "_-I<number>", "_*", nor a valid method name; and so
Packit f92f8e
Locale::Maketext will throw an exception of you try compiling an
Packit f92f8e
expression containing this bracket group.
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
Note, incidentally, that items in each group are comma-separated,
Packit f92f8e
not C</\s*,\s*/>-separated.  That is, you might expect that this
Packit f92f8e
bracket group:
Packit f92f8e
Packit f92f8e
  "Hoohah [foo, _1 , bar ,baz]!"
Packit f92f8e
Packit f92f8e
would compile to this:
Packit f92f8e
Packit f92f8e
  sub {
Packit f92f8e
    my $lh = $_[0];
Packit f92f8e
    return join '',
Packit f92f8e
      "Hoohah ",
Packit f92f8e
      $lh->foo( $_[1], "bar", "baz"),
Packit f92f8e
      "!",
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
But it actually compiles as this:
Packit f92f8e
Packit f92f8e
  sub {
Packit f92f8e
    my $lh = $_[0];
Packit f92f8e
    return join '',
Packit f92f8e
      "Hoohah ",
Packit f92f8e
      $lh->foo(" _1 ", " bar ", "baz"),  # note the <space> in " bar "
Packit f92f8e
      "!",
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
In the notation discussed so far, the characters "[" and "]" are given
Packit f92f8e
special meaning, for opening and closing bracket groups, and "," has
Packit f92f8e
a special meaning inside bracket groups, where it separates items in the
Packit f92f8e
group.  This begs the question of how you'd express a literal "[" or
Packit f92f8e
"]" in a Bracket Notation string, and how you'd express a literal
Packit f92f8e
comma inside a bracket group.  For this purpose I've adopted "~" (tilde)
Packit f92f8e
as an escape character:  "~[" means a literal '[' character anywhere
Packit f92f8e
in Bracket Notation (i.e., regardless of whether you're in a bracket
Packit f92f8e
group or not), and ditto for "~]" meaning a literal ']', and "~," meaning
Packit f92f8e
a literal comma.  (Altho "," means a literal comma outside of
Packit f92f8e
bracket groups -- it's only inside bracket groups that commas are special.)
Packit f92f8e
Packit f92f8e
And on the off chance you need a literal tilde in a bracket expression,
Packit f92f8e
you get it with "~~".
Packit f92f8e
Packit f92f8e
Currently, an unescaped "~" before a character
Packit f92f8e
other than a bracket or a comma is taken to mean just a "~" and that
Packit f92f8e
character.  I.e., "~X" means the same as "~~X" -- i.e., one literal tilde,
Packit f92f8e
and then one literal "X".  However, by using "~X", you are assuming that
Packit f92f8e
no future version of Maketext will use "~X" as a magic escape sequence.
Packit f92f8e
In practice this is not a great problem, since first off you can just
Packit f92f8e
write "~~X" and not worry about it; second off, I doubt I'll add lots
Packit f92f8e
of new magic characters to bracket notation; and third off, you
Packit f92f8e
aren't likely to want literal "~" characters in your messages anyway,
Packit f92f8e
since it's not a character with wide use in natural language text.
Packit f92f8e
Packit f92f8e
Brackets must be balanced -- every openbracket must have
Packit f92f8e
one matching closebracket, and vice versa.  So these are all B<invalid>:
Packit f92f8e
Packit f92f8e
  "I ate [quant,_1,rhubarb pie."
Packit f92f8e
  "I ate [quant,_1,rhubarb pie[."
Packit f92f8e
  "I ate quant,_1,rhubarb pie]."
Packit f92f8e
  "I ate quant,_1,rhubarb pie[."
Packit f92f8e
Packit f92f8e
Currently, bracket groups do not nest.  That is, you B<cannot> say:
Packit f92f8e
Packit f92f8e
  "Foo [bar,baz,[quux,quuux]]\n";
Packit f92f8e
Packit f92f8e
If you need a notation that's that powerful, use normal Perl:
Packit f92f8e
Packit f92f8e
  %Lexicon = (
Packit f92f8e
    ...
Packit f92f8e
    "some_key" => sub {
Packit f92f8e
      my $lh = $_[0];
Packit f92f8e
      join '',
Packit f92f8e
        "Foo ",
Packit f92f8e
        $lh->bar('baz', $lh->quux('quuux')),
Packit f92f8e
        "\n",
Packit f92f8e
    },
Packit f92f8e
    ...
Packit f92f8e
  );
Packit f92f8e
Packit f92f8e
Or write the "bar" method so you don't need to pass it the
Packit f92f8e
output from calling quux.
Packit f92f8e
Packit f92f8e
I do not anticipate that you will need (or particularly want)
Packit f92f8e
to nest bracket groups, but you are welcome to email me with
Packit f92f8e
convincing (real-life) arguments to the contrary.
Packit f92f8e
Packit f92f8e
=head1 BRACKET NOTATION SECURITY
Packit f92f8e
Packit f92f8e
Locale::Maketext does not use any special syntax to differentiate
Packit f92f8e
bracket notation methods from normal class or object methods. This
Packit f92f8e
design makes it vulnerable to format string attacks whenever it is
Packit f92f8e
used to process strings provided by untrusted users.
Packit f92f8e
Packit f92f8e
Locale::Maketext does support blacklist and whitelist functionality
Packit f92f8e
to limit which methods may be called as bracket notation methods.
Packit f92f8e
Packit f92f8e
By default, Locale::Maketext blacklists all methods in the
Packit f92f8e
Locale::Maketext namespace that begin with the '_' character,
Packit f92f8e
and all methods which include Perl's namespace separator characters.
Packit f92f8e
Packit f92f8e
The default blacklist for Locale::Maketext also prevents use of the
Packit f92f8e
following methods in bracket notation:
Packit f92f8e
Packit f92f8e
  blacklist
Packit f92f8e
  encoding
Packit f92f8e
  fail_with
Packit f92f8e
  failure_handler_auto
Packit f92f8e
  fallback_language_classes
Packit f92f8e
  fallback_languages
Packit f92f8e
  get_handle
Packit f92f8e
  init
Packit f92f8e
  language_tag
Packit f92f8e
  maketext
Packit f92f8e
  new
Packit f92f8e
  whitelist
Packit f92f8e
Packit f92f8e
This list can be extended by either blacklisting additional "known bad"
Packit f92f8e
methods, or whitelisting only "known good" methods.
Packit f92f8e
Packit f92f8e
To prevent specific methods from being called in bracket notation, use
Packit f92f8e
the blacklist() method:
Packit f92f8e
Packit f92f8e
  my $lh = MyProgram::L10N->get_handle();
Packit f92f8e
  $lh->blacklist(qw{my_internal_method my_other_method});
Packit f92f8e
  $lh->maketext('[my_internal_method]'); # dies
Packit f92f8e
Packit f92f8e
To limit the allowed bracked notation methods to a specific list, use the
Packit f92f8e
whitelist() method:
Packit f92f8e
Packit f92f8e
  my $lh = MyProgram::L10N->get_handle();
Packit f92f8e
  $lh->whitelist('numerate', 'numf');
Packit f92f8e
  $lh->maketext('[_1] [numerate, _1,shoe,shoes]', 12); # works
Packit f92f8e
  $lh->maketext('[my_internal_method]'); # dies
Packit f92f8e
Packit f92f8e
The blacklist() and whitelist() methods extend their internal lists
Packit f92f8e
whenever they are called. To reset the blacklist or whitelist, create
Packit f92f8e
a new maketext object.
Packit f92f8e
Packit f92f8e
  my $lh = MyProgram::L10N->get_handle();
Packit f92f8e
  $lh->blacklist('numerate');
Packit f92f8e
  $lh->blacklist('numf');
Packit f92f8e
  $lh->maketext('[_1] [numerate,_1,shoe,shoes]', 12); # dies
Packit f92f8e
Packit f92f8e
For lexicons that use an internal cache, translations which have already
Packit f92f8e
been cached in their compiled form are not affected by subsequent changes
Packit f92f8e
to the whitelist or blacklist settings. Lexicons that use an external
Packit f92f8e
cache will have their cache cleared whenever the whitelist of blacklist
Packit f92f8e
setings change.  The difference between the two types of caching is explained
Packit f92f8e
in the "Readonly Lexicons" section.
Packit f92f8e
Packit f92f8e
Methods disallowed by the blacklist cannot be permitted by the
Packit f92f8e
whitelist.
Packit f92f8e
Packit f92f8e
=head1 AUTO LEXICONS
Packit f92f8e
Packit f92f8e
If maketext goes to look in an individual %Lexicon for an entry
Packit f92f8e
for I<key> (where I<key> does not start with an underscore), and
Packit f92f8e
sees none, B<but does see> an entry of "_AUTO" => I<some_true_value>,
Packit f92f8e
then we actually define $Lexicon{I<key>} = I<key> right then and there,
Packit f92f8e
and then use that value as if it had been there all
Packit f92f8e
along.  This happens before we even look in any superclass %Lexicons!
Packit f92f8e
Packit f92f8e
(This is meant to be somewhat like the AUTOLOAD mechanism in
Packit f92f8e
Perl's function call system -- or, looked at another way,
Packit f92f8e
like the L<AutoLoader|AutoLoader> module.)
Packit f92f8e
Packit f92f8e
I can picture all sorts of circumstances where you just
Packit f92f8e
do not want lookup to be able to fail (since failing
Packit f92f8e
normally means that maketext throws a C<die>, although
Packit f92f8e
see the next section for greater control over that).  But
Packit f92f8e
here's one circumstance where _AUTO lexicons are meant to
Packit f92f8e
be I<especially> useful:
Packit f92f8e
Packit f92f8e
As you're writing an application, you decide as you go what messages
Packit f92f8e
you need to emit.  Normally you'd go to write this:
Packit f92f8e
Packit f92f8e
  if(-e $filename) {
Packit f92f8e
    go_process_file($filename)
Packit f92f8e
  } else {
Packit f92f8e
    print qq{Couldn't find file "$filename"!\n};
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
but since you anticipate localizing this, you write:
Packit f92f8e
Packit f92f8e
  use ThisProject::I18N;
Packit f92f8e
  my $lh = ThisProject::I18N->get_handle();
Packit f92f8e
   # For the moment, assume that things are set up so
Packit f92f8e
   # that we load class ThisProject::I18N::en
Packit f92f8e
   # and that that's the class that $lh belongs to.
Packit f92f8e
  ...
Packit f92f8e
  if(-e $filename) {
Packit f92f8e
    go_process_file($filename)
Packit f92f8e
  } else {
Packit f92f8e
    print $lh->maketext(
Packit f92f8e
      qq{Couldn't find file "[_1]"!\n}, $filename
Packit f92f8e
    );
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
Now, right after you've just written the above lines, you'd
Packit f92f8e
normally have to go open the file 
Packit f92f8e
ThisProject/I18N/en.pm, and immediately add an entry:
Packit f92f8e
Packit f92f8e
  "Couldn't find file \"[_1]\"!\n"
Packit f92f8e
  => "Couldn't find file \"[_1]\"!\n",
Packit f92f8e
Packit f92f8e
But I consider that somewhat of a distraction from the work
Packit f92f8e
of getting the main code working -- to say nothing of the fact
Packit f92f8e
that I often have to play with the program a few times before
Packit f92f8e
I can decide exactly what wording I want in the messages (which
Packit f92f8e
in this case would require me to go changing three lines of code:
Packit f92f8e
the call to maketext with that key, and then the two lines in
Packit f92f8e
ThisProject/I18N/en.pm).
Packit f92f8e
Packit f92f8e
However, if you set "_AUTO => 1" in the %Lexicon in,
Packit f92f8e
ThisProject/I18N/en.pm (assuming that English (en) is
Packit f92f8e
the language that all your programmers will be using for this
Packit f92f8e
project's internal message keys), then you don't ever have to
Packit f92f8e
go adding lines like this
Packit f92f8e
Packit f92f8e
  "Couldn't find file \"[_1]\"!\n"
Packit f92f8e
  => "Couldn't find file \"[_1]\"!\n",
Packit f92f8e
Packit f92f8e
to ThisProject/I18N/en.pm, because if _AUTO is true there,
Packit f92f8e
then just looking for an entry with the key "Couldn't find
Packit f92f8e
file \"[_1]\"!\n" in that lexicon will cause it to be added,
Packit f92f8e
with that value!
Packit f92f8e
Packit f92f8e
Note that the reason that keys that start with "_"
Packit f92f8e
are immune to _AUTO isn't anything generally magical about
Packit f92f8e
the underscore character -- I just wanted a way to have most
Packit f92f8e
lexicon keys be autoable, except for possibly a few, and I
Packit f92f8e
arbitrarily decided to use a leading underscore as a signal
Packit f92f8e
to distinguish those few.
Packit f92f8e
Packit f92f8e
=head1 READONLY LEXICONS
Packit f92f8e
Packit f92f8e
If your lexicon is a tied hash the simple act of caching the compiled value can be fatal.
Packit f92f8e
Packit f92f8e
For example a L<GDBM_File> GDBM_READER tied hash will die with something like:
Packit f92f8e
Packit f92f8e
   gdbm store returned -1, errno 2, key "..." at ...
Packit f92f8e
Packit f92f8e
All you need to do is turn on caching outside of the lexicon hash itself like so:
Packit f92f8e
Packit f92f8e
   sub init {
Packit f92f8e
       my ($lh) = @_;
Packit f92f8e
       ...
Packit f92f8e
       $lh->{'use_external_lex_cache'} = 1;
Packit f92f8e
       ...
Packit f92f8e
   }
Packit f92f8e
Packit f92f8e
And then instead of storing the compiled value in the lexicon hash it will store it in $lh->{'_external_lex_cache'}
Packit f92f8e
Packit f92f8e
=head1 CONTROLLING LOOKUP FAILURE
Packit f92f8e
Packit f92f8e
If you call $lh->maketext(I<key>, ...parameters...),
Packit f92f8e
and there's no entry I<key> in $lh's class's %Lexicon, nor
Packit f92f8e
in the superclass %Lexicon hash, I<and> if we can't auto-make
Packit f92f8e
I<key> (because either it starts with a "_", or because none
Packit f92f8e
of its lexicons have C<_AUTO =E<gt> 1,>), then we have
Packit f92f8e
failed to find a normal way to maketext I<key>.  What then
Packit f92f8e
happens in these failure conditions, depends on the $lh object's
Packit f92f8e
"fail" attribute.
Packit f92f8e
Packit f92f8e
If the language handle has no "fail" attribute, maketext
Packit f92f8e
will simply throw an exception (i.e., it calls C<die>, mentioning
Packit f92f8e
the I<key> whose lookup failed, and naming the line number where
Packit f92f8e
the calling $lh->maketext(I<key>,...) was.
Packit f92f8e
Packit f92f8e
If the language handle has a "fail" attribute whose value is a
Packit f92f8e
coderef, then $lh->maketext(I<key>,...params...) gives up and calls:
Packit f92f8e
Packit f92f8e
  return $that_subref->($lh, $key, @params);
Packit f92f8e
Packit f92f8e
Otherwise, the "fail" attribute's value should be a string denoting
Packit f92f8e
a method name, so that $lh->maketext(I<key>,...params...) can
Packit f92f8e
give up with:
Packit f92f8e
Packit f92f8e
  return $lh->$that_method_name($phrase, @params);
Packit f92f8e
Packit f92f8e
The "fail" attribute can be accessed with the C<fail_with> method:
Packit f92f8e
Packit f92f8e
  # Set to a coderef:
Packit f92f8e
  $lh->fail_with( \&failure_handler );
Packit f92f8e
Packit f92f8e
  # Set to a method name:
Packit f92f8e
  $lh->fail_with( 'failure_method' );
Packit f92f8e
Packit f92f8e
  # Set to nothing (i.e., so failure throws a plain exception)
Packit f92f8e
  $lh->fail_with( undef );
Packit f92f8e
Packit f92f8e
  # Get the current value
Packit f92f8e
  $handler = $lh->fail_with();
Packit f92f8e
Packit f92f8e
Now, as to what you may want to do with these handlers:  Maybe you'd
Packit f92f8e
want to log what key failed for what class, and then die.  Maybe
Packit f92f8e
you don't like C<die> and instead you want to send the error message
Packit f92f8e
to STDOUT (or wherever) and then merely C<exit()>.
Packit f92f8e
Packit f92f8e
Or maybe you don't want to C<die> at all!  Maybe you could use a
Packit f92f8e
handler like this:
Packit f92f8e
Packit f92f8e
  # Make all lookups fall back onto an English value,
Packit f92f8e
  #  but only after we log it for later fingerpointing.
Packit f92f8e
  my $lh_backup = ThisProject->get_handle('en');
Packit f92f8e
  open(LEX_FAIL_LOG, ">>wherever/lex.log") || die "GNAARGH $!";
Packit f92f8e
  sub lex_fail {
Packit f92f8e
    my($failing_lh, $key, $params) = @_;
Packit f92f8e
    print LEX_FAIL_LOG scalar(localtime), "\t",
Packit f92f8e
       ref($failing_lh), "\t", $key, "\n";
Packit f92f8e
    return $lh_backup->maketext($key,@params);
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
Some users have expressed that they think this whole mechanism of
Packit f92f8e
having a "fail" attribute at all, seems a rather pointless complication.
Packit f92f8e
But I want Locale::Maketext to be usable for software projects of I<any>
Packit f92f8e
scale and type; and different software projects have different ideas
Packit f92f8e
of what the right thing is to do in failure conditions.  I could simply
Packit f92f8e
say that failure always throws an exception, and that if you want to be
Packit f92f8e
careful, you'll just have to wrap every call to $lh->maketext in an
Packit f92f8e
S<eval { }>.  However, I want programmers to reserve the right (via
Packit f92f8e
the "fail" attribute) to treat lookup failure as something other than
Packit f92f8e
an exception of the same level of severity as a config file being
Packit f92f8e
unreadable, or some essential resource being inaccessible.
Packit f92f8e
Packit f92f8e
One possibly useful value for the "fail" attribute is the method name
Packit f92f8e
"failure_handler_auto".  This is a method defined in the class
Packit f92f8e
Locale::Maketext itself.  You set it with:
Packit f92f8e
Packit f92f8e
  $lh->fail_with('failure_handler_auto');
Packit f92f8e
Packit f92f8e
Then when you call $lh->maketext(I<key>, ...parameters...) and
Packit f92f8e
there's no I<key> in any of those lexicons, maketext gives up with
Packit f92f8e
Packit f92f8e
  return $lh->failure_handler_auto($key, @params);
Packit f92f8e
Packit f92f8e
But failure_handler_auto, instead of dying or anything, compiles
Packit f92f8e
$key, caching it in
Packit f92f8e
Packit f92f8e
    $lh->{'failure_lex'}{$key} = $compiled
Packit f92f8e
Packit f92f8e
and then calls the compiled value, and returns that.  (I.e., if
Packit f92f8e
$key looks like bracket notation, $compiled is a sub, and we return
Packit f92f8e
&{$compiled}(@params); but if $key is just a plain string, we just
Packit f92f8e
return that.)
Packit f92f8e
Packit f92f8e
The effect of using "failure_auto_handler"
Packit f92f8e
is like an AUTO lexicon, except that it 1) compiles $key even if
Packit f92f8e
it starts with "_", and 2) you have a record in the new hashref
Packit f92f8e
$lh->{'failure_lex'} of all the keys that have failed for
Packit f92f8e
this object.  This should avoid your program dying -- as long
Packit f92f8e
as your keys aren't actually invalid as bracket code, and as
Packit f92f8e
long as they don't try calling methods that don't exist.
Packit f92f8e
Packit f92f8e
"failure_auto_handler" may not be exactly what you want, but I
Packit f92f8e
hope it at least shows you that maketext failure can be mitigated
Packit f92f8e
in any number of very flexible ways.  If you can formalize exactly
Packit f92f8e
what you want, you should be able to express that as a failure
Packit f92f8e
handler.  You can even make it default for every object of a given
Packit f92f8e
class, by setting it in that class's init:
Packit f92f8e
Packit f92f8e
  sub init {
Packit f92f8e
    my $lh = $_[0];  # a newborn handle
Packit f92f8e
    $lh->SUPER::init();
Packit f92f8e
    $lh->fail_with('my_clever_failure_handler');
Packit f92f8e
    return;
Packit f92f8e
  }
Packit f92f8e
  sub my_clever_failure_handler {
Packit f92f8e
    ...you clever things here...
Packit f92f8e
  }
Packit f92f8e
Packit f92f8e
=head1 HOW TO USE MAKETEXT
Packit f92f8e
Packit f92f8e
Here is a brief checklist on how to use Maketext to localize
Packit f92f8e
applications:
Packit f92f8e
Packit f92f8e
=over
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Decide what system you'll use for lexicon keys.  If you insist,
Packit f92f8e
you can use opaque IDs (if you're nostalgic for C<catgets>),
Packit f92f8e
but I have better suggestions in the
Packit f92f8e
section "Entries in Each Lexicon", above.  Assuming you opt for
Packit f92f8e
meaningful keys that double as values (like "Minimum ([_1]) is
Packit f92f8e
larger than maximum ([_2])!\n"), you'll have to settle on what
Packit f92f8e
language those should be in.  For the sake of argument, I'll
Packit f92f8e
call this English, specifically American English, "en-US".
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Create a class for your localization project.  This is
Packit f92f8e
the name of the class that you'll use in the idiom:
Packit f92f8e
Packit f92f8e
  use Projname::L10N;
Packit f92f8e
  my $lh = Projname::L10N->get_handle(...) || die "Language?";
Packit f92f8e
Packit f92f8e
Assuming you call your class Projname::L10N, create a class
Packit f92f8e
consisting minimally of:
Packit f92f8e
Packit f92f8e
  package Projname::L10N;
Packit f92f8e
  use base qw(Locale::Maketext);
Packit f92f8e
  ...any methods you might want all your languages to share...
Packit f92f8e
Packit f92f8e
  # And, assuming you want the base class to be an _AUTO lexicon,
Packit f92f8e
  # as is discussed a few sections up:
Packit f92f8e
Packit f92f8e
  1;
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Create a class for the language your internal keys are in.  Name
Packit f92f8e
the class after the language-tag for that language, in lowercase,
Packit f92f8e
with dashes changed to underscores.  Assuming your project's first
Packit f92f8e
language is US English, you should call this Projname::L10N::en_us.
Packit f92f8e
It should consist minimally of:
Packit f92f8e
Packit f92f8e
  package Projname::L10N::en_us;
Packit f92f8e
  use base qw(Projname::L10N);
Packit f92f8e
  %Lexicon = (
Packit f92f8e
    '_AUTO' => 1,
Packit f92f8e
  );
Packit f92f8e
  1;
Packit f92f8e
Packit f92f8e
(For the rest of this section, I'll assume that this "first
Packit f92f8e
language class" of Projname::L10N::en_us has
Packit f92f8e
_AUTO lexicon.)
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Go and write your program.  Everywhere in your program where 
Packit f92f8e
you would say:
Packit f92f8e
Packit f92f8e
  print "Foobar $thing stuff\n";
Packit f92f8e
Packit f92f8e
instead do it thru maketext, using no variable interpolation in
Packit f92f8e
the key:
Packit f92f8e
Packit f92f8e
  print $lh->maketext("Foobar [_1] stuff\n", $thing);
Packit f92f8e
Packit f92f8e
If you get tired of constantly saying C<print $lh-E<gt>maketext>,
Packit f92f8e
consider making a functional wrapper for it, like so:
Packit f92f8e
Packit f92f8e
  use Projname::L10N;
Packit f92f8e
  use vars qw($lh);
Packit f92f8e
  $lh = Projname::L10N->get_handle(...) || die "Language?";
Packit f92f8e
  sub pmt (@) { print( $lh->maketext(@_)) }
Packit f92f8e
   # "pmt" is short for "Print MakeText"
Packit f92f8e
  $Carp::Verbose = 1;
Packit f92f8e
   # so if maketext fails, we see made the call to pmt
Packit f92f8e
Packit f92f8e
Besides whole phrases meant for output, anything language-dependent
Packit f92f8e
should be put into the class Projname::L10N::en_us,
Packit f92f8e
whether as methods, or as lexicon entries -- this is discussed
Packit f92f8e
in the section "Entries in Each Lexicon", above.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Once the program is otherwise done, and once its localization for
Packit f92f8e
the first language works right (via the data and methods in
Packit f92f8e
Projname::L10N::en_us), you can get together the data for translation.
Packit f92f8e
If your first language lexicon isn't an _AUTO lexicon, then you already
Packit f92f8e
have all the messages explicitly in the lexicon (or else you'd be
Packit f92f8e
getting exceptions thrown when you call $lh->maketext to get
Packit f92f8e
messages that aren't in there).  But if you were (advisedly) lazy and are
Packit f92f8e
using an _AUTO lexicon, then you've got to make a list of all the phrases
Packit f92f8e
that you've so far been letting _AUTO generate for you.  There are very
Packit f92f8e
many ways to assemble such a list.  The most straightforward is to simply
Packit f92f8e
grep the source for every occurrence of "maketext" (or calls
Packit f92f8e
to wrappers around it, like the above C<pmt> function), and to log the
Packit f92f8e
following phrase.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
You may at this point want to consider whether your base class 
Packit f92f8e
(Projname::L10N), from which all lexicons inherit from (Projname::L10N::en,
Packit f92f8e
Projname::L10N::es, etc.), should be an _AUTO lexicon.  It may be true
Packit f92f8e
that in theory, all needed messages will be in each language class;
Packit f92f8e
but in the presumably unlikely or "impossible" case of lookup failure,
Packit f92f8e
you should consider whether your program should throw an exception,
Packit f92f8e
emit text in English (or whatever your project's first language is),
Packit f92f8e
or some more complex solution as described in the section
Packit f92f8e
"Controlling Lookup Failure", above.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Submit all messages/phrases/etc. to translators.
Packit f92f8e
Packit f92f8e
(You may, in fact, want to start with localizing to I<one> other language
Packit f92f8e
at first, if you're not sure that you've properly abstracted the
Packit f92f8e
language-dependent parts of your code.)
Packit f92f8e
Packit f92f8e
Translators may request clarification of the situation in which a
Packit f92f8e
particular phrase is found.  For example, in English we are entirely happy
Packit f92f8e
saying "I<n> files found", regardless of whether we mean "I looked for files,
Packit f92f8e
and found I<n> of them" or the rather distinct situation of "I looked for
Packit f92f8e
something else (like lines in files), and along the way I saw I<n>
Packit f92f8e
files."  This may involve rethinking things that you thought quite clear:
Packit f92f8e
should "Edit" on a toolbar be a noun ("editing") or a verb ("to edit")?  Is
Packit f92f8e
there already a conventionalized way to express that menu option, separate
Packit f92f8e
from the target language's normal word for "to edit"?
Packit f92f8e
Packit f92f8e
In all cases where the very common phenomenon of quantification
Packit f92f8e
(saying "I<N> files", for B<any> value of N)
Packit f92f8e
is involved, each translator should make clear what dependencies the
Packit f92f8e
number causes in the sentence.  In many cases, dependency is
Packit f92f8e
limited to words adjacent to the number, in places where you might
Packit f92f8e
expect them ("I found the-?PLURAL I<N>
Packit f92f8e
empty-?PLURAL directory-?PLURAL"), but in some cases there are
Packit f92f8e
unexpected dependencies ("I found-?PLURAL ..."!) as well as long-distance
Packit f92f8e
dependencies "The I<N> directory-?PLURAL could not be deleted-?PLURAL"!).
Packit f92f8e
Packit f92f8e
Remind the translators to consider the case where N is 0:
Packit f92f8e
"0 files found" isn't exactly natural-sounding in any language, but it
Packit f92f8e
may be unacceptable in many -- or it may condition special
Packit f92f8e
kinds of agreement (similar to English "I didN'T find ANY files").
Packit f92f8e
Packit f92f8e
Remember to ask your translators about numeral formatting in their
Packit f92f8e
language, so that you can override the C<numf> method as
Packit f92f8e
appropriate.  Typical variables in number formatting are:  what to
Packit f92f8e
use as a decimal point (comma? period?); what to use as a thousands
Packit f92f8e
separator (space? nonbreaking space? comma? period? small
Packit f92f8e
middot? prime? apostrophe?); and even whether the so-called "thousands
Packit f92f8e
separator" is actually for every third digit -- I've heard reports of
Packit f92f8e
two hundred thousand being expressible as "2,00,000" for some Indian
Packit f92f8e
(Subcontinental) languages, besides the less surprising "S<200 000>",
Packit f92f8e
"200.000", "200,000", and "200'000".  Also, using a set of numeral
Packit f92f8e
glyphs other than the usual ASCII "0"-"9" might be appreciated, as via
Packit f92f8e
C for getting digits in Devanagari script
Packit f92f8e
(for Hindi, Konkani, others).
Packit f92f8e
Packit f92f8e
The basic C<quant> method that Locale::Maketext provides should be
Packit f92f8e
good for many languages.  For some languages, it might be useful
Packit f92f8e
to modify it (or its constituent C<numerate> method)
Packit f92f8e
to take a plural form in the two-argument call to C<quant>
Packit f92f8e
(as in "[quant,_1,files]") if
Packit f92f8e
it's all-around easier to infer the singular form from the plural, than
Packit f92f8e
to infer the plural form from the singular.
Packit f92f8e
Packit f92f8e
But for other languages (as is discussed at length
Packit f92f8e
in L<Locale::Maketext::TPJ13|Locale::Maketext::TPJ13>), simple
Packit f92f8e
C<quant>/C<numf> is not enough.  For the particularly problematic
Packit f92f8e
Slavic languages, what you may need is a method which you provide
Packit f92f8e
with the number, the citation form of the noun to quantify, and
Packit f92f8e
the case and gender that the sentence's syntax projects onto that
Packit f92f8e
noun slot.  The method would then be responsible for determining
Packit f92f8e
what grammatical number that numeral projects onto its noun phrase,
Packit f92f8e
and what case and gender it may override the normal case and gender
Packit f92f8e
with; and then it would look up the noun in a lexicon providing
Packit f92f8e
all needed inflected forms.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
You may also wish to discuss with the translators the question of
Packit f92f8e
how to relate different subforms of the same language tag,
Packit f92f8e
considering how this reacts with C<get_handle>'s treatment of
Packit f92f8e
these.  For example, if a user accepts interfaces in "en, fr", and
Packit f92f8e
you have interfaces available in "en-US" and "fr", what should
Packit f92f8e
they get?  You may wish to resolve this by establishing that "en"
Packit f92f8e
and "en-US" are effectively synonymous, by having one class
Packit f92f8e
zero-derive from the other.
Packit f92f8e
Packit f92f8e
For some languages this issue may never come up (Danish is rarely
Packit f92f8e
expressed as "da-DK", but instead is just "da").  And for other
Packit f92f8e
languages, the whole concept of a "generic" form may verge on
Packit f92f8e
being uselessly vague, particularly for interfaces involving voice
Packit f92f8e
media in forms of Arabic or Chinese.
Packit f92f8e
Packit f92f8e
=item *
Packit f92f8e
Packit f92f8e
Once you've localized your program/site/etc. for all desired
Packit f92f8e
languages, be sure to show the result (whether live, or via
Packit f92f8e
screenshots) to the translators.  Once they approve, make every
Packit f92f8e
effort to have it then checked by at least one other speaker of
Packit f92f8e
that language.  This holds true even when (or especially when) the
Packit f92f8e
translation is done by one of your own programmers.  Some
Packit f92f8e
kinds of systems may be harder to find testers for than others,
Packit f92f8e
depending on the amount of domain-specific jargon and concepts
Packit f92f8e
involved -- it's easier to find people who can tell you whether
Packit f92f8e
they approve of your translation for "delete this message" in an
Packit f92f8e
email-via-Web interface, than to find people who can give you
Packit f92f8e
an informed opinion on your translation for "attribute value"
Packit f92f8e
in an XML query tool's interface.
Packit f92f8e
Packit f92f8e
=back
Packit f92f8e
Packit f92f8e
=head1 SEE ALSO
Packit f92f8e
Packit f92f8e
I recommend reading all of these:
Packit f92f8e
Packit f92f8e
L<Locale::Maketext::TPJ13|Locale::Maketext::TPJ13> -- my I
Packit f92f8e
Journal> article about Maketext.  It explains many important concepts
Packit f92f8e
underlying Locale::Maketext's design, and some insight into why
Packit f92f8e
Maketext is better than the plain old approach of having 
Packit f92f8e
message catalogs that are just databases of sprintf formats.
Packit f92f8e
Packit f92f8e
L<File::Findgrep|File::Findgrep> is a sample application/module
Packit f92f8e
that uses Locale::Maketext to localize its messages.  For a larger
Packit f92f8e
internationalized system, see also L<Apache::MP3>.
Packit f92f8e
Packit f92f8e
L<I18N::LangTags|I18N::LangTags>.
Packit f92f8e
Packit f92f8e
L<Win32::Locale|Win32::Locale>.
Packit f92f8e
Packit f92f8e
RFC 3066, I<Tags for the Identification of Languages>,
Packit f92f8e
as at http://sunsite.dk/RFC/rfc/rfc3066.html
Packit f92f8e
Packit f92f8e
RFC 2277, I<IETF Policy on Character Sets and Languages>
Packit f92f8e
is at http://sunsite.dk/RFC/rfc/rfc2277.html -- much of it is
Packit f92f8e
just things of interest to protocol designers, but it explains
Packit f92f8e
some basic concepts, like the distinction between locales and
Packit f92f8e
language-tags.
Packit f92f8e
Packit f92f8e
The manual for GNU C<gettext>.  The gettext dist is available in
Packit f92f8e
C<ftp://prep.ai.mit.edu/pub/gnu/> -- get
Packit f92f8e
a recent gettext tarball and look in its "doc/" directory, there's
Packit f92f8e
an easily browsable HTML version in there.  The
Packit f92f8e
gettext documentation asks lots of questions worth thinking
Packit f92f8e
about, even if some of their answers are sometimes wonky,
Packit f92f8e
particularly where they start talking about pluralization.
Packit f92f8e
Packit f92f8e
The Locale/Maketext.pm source.  Observe that the module is much
Packit f92f8e
shorter than its documentation!
Packit f92f8e
Packit f92f8e
=head1 COPYRIGHT AND DISCLAIMER
Packit f92f8e
Packit f92f8e
Copyright (c) 1999-2004 Sean M. Burke.  All rights reserved.
Packit f92f8e
Packit f92f8e
This library is free software; you can redistribute it and/or modify
Packit f92f8e
it under the same terms as Perl itself.
Packit f92f8e
Packit f92f8e
This program is distributed in the hope that it will be useful, but
Packit f92f8e
without any warranty; without even the implied warranty of
Packit f92f8e
merchantability or fitness for a particular purpose.
Packit f92f8e
Packit f92f8e
=head1 AUTHOR
Packit f92f8e
Packit f92f8e
Sean M. Burke C<sburke@cpan.org>
Packit f92f8e
Packit f92f8e
=cut