NAME

Locale::VersionedMessages - handle all aspects of the localization process

SYNOPSIS

use Locale::VersionedMessages

$obj     = new Locale::VersionedMessages;
$vers    = $obj->version();
$obj->search(LOCALE_LIST);

DESCRIPTION

Although several modules exist for handling internationalization and localization of a program, they are missing functionality for dealing with all of the functions to perform the task fully. Existing modules work fine with respect to a programmer who wants to access localized messages, but the modules do not contain the necessary functions to maintain those messages.

This module (and the programs that are distributed with it) form a complete package for handling the localization problem.

At the heart of the localization problem, you have a set of messages. These messages may be translated into any number of locales, and a programmer can then look up the appropriate message, as it would appear in the desired locale.

Any number of sets of messages may be used by a program. A set of messages is best thought of as a set of related messages which might be used by any number of programs. For example, one set of messages might be I/O error messages encountered when working with files. Another set might be the messages specific to one particular program.

Each set of messages can be translated into the language for any number of locales. Ideally, all messages would be translated into all locales, but that is often not the case. However, every set of messages has a default locale (which may not be the same across all sets of messages) which MUST have all of the messages in it.

This allows you to look up a message in either the locale you are working with, or the default locale if it is not available there.

Unlike most existing localization modules, this module keeps track of the version of every message in every locale. Although this information is not typically useful to those programmers who are simply looking up messages in the table, it is extremely useful to those who are responsible for translating and maintaining the messages.

METHODS

The following methods are available:

new
$obj = new Locale::VersionedMessages;

This creates a new Locale::VersionedMessages object.

version
use Locale::VersionedMessages;
$obj = new Locale::VersionedMessages;
$vers = $obj->version();

Check the module version.

err
$err = $obj->err();

This returns any error message set during the previous operation.

set
$obj->set(SET0 [,SET1,SET2,...]);

This loads any number of message sets into the object. You can only look up messages from sets that have been loaded.

@set = $obj->set();

This returns the names of the message sets that have been loaded. SETi is a string containing alphanumeric and underscore characters.

query_set_default
query_set_locales
query_set_msgid

These return information about a message set.

$locale = $obj->query_set_default();

returns the default locale.

@locale = $obj->query_set_locales();

returns the list of all locales for which this message set is defined.

@msgid = $obj->query_set_msgid();

returns a list of all message IDs in this set of messages. The message ID is the label used in the program to access a message.

This method is used to specify the search order of the locales.

By default, all messages are returned in the default locale of the message set (which may not be the same across message sets), but you can change this using these methods.

$obj->search(LOCALE0 [,LOCALE1,LOCALE2,...]);

This specifies the global search order. This is the default search order of locales used for all message sets. This specifies that, in all message sets, the default is to use LOCALE0, followed by LOCALE1, LOCALE2, etc.

If the message is not found in any of the locales, the default locale for the set will be used.

The global search order can be overridden on a per-set basis.

$obj->search();

This erases the global default search order.

$obj->search(SET, LOCALE0 [,LOCALE1,LOCALE2,...]);

This specifies the search order for the given set, overriding the global default search order. It is not necessary that all of the locales actually have translations. Any that don't will be silently ignored.

If a message is not found in any of these locales, the default locale for the message set will be used.

$obj->search(SET);

This erases the search order for the given set, so the global search order will be used.

For example:

$obj->search('BUTTONS','en','en_US','en_GB');

would look for a messages in the 'BUTTONS' set in the locales: 'en', 'en_US', and 'en_GB' in that order. If a message were not found in any of those three, the default locale would be used.

@locale = $obj->query_search();
@locale = $obj->query_search(SET);

This returns the global search order or the search order for the set.

In the second call, the global search order is NOT returned if the set-specific search order is not set.

message
$text           = $obj(SET, MSGID [,LOCALE] [,VALUES]);
($text,$locale) = $obj(SET, MSGID [,LOCALE] [,VALUES]);

This method is used to look up a message in the given set and substitutes in VALUES (described below in MESSAGE SUBSTITUTIONS). If LOCALE is present, it only looks up the message in that locale. By default, it will look in the search order for that set.

If the message is not found, $text will be the empty string.

In list context, the text will be returned along with the name of the locale in which the message was found.

If any error occurs, an empty string will be returned for $text.

query_msg_locales
query_msg_vers

These return information about a specific message.

@locale = $obj->query_msg_locales(SET, MSGID);

returns a list of all locales for which this message is defined. The first one will be the default locale.

$vers = $obj->query_msg_vers(SET, MSGID [,LOCALE]);

returns the version of the message in the given locale. If LOCALE is not given, it defaults to the default locale. If the message is not defined in the locale, a version of 0 is returned.

MESSAGE SUBSTITUTIONS

When a message is defined, it can have values that will be passed in which can be inserted into the message.

Unlike most localization tool kits, the values are passed in by name, rather than position in a list, so the substitution looks a bit different than other tool kits.

When a message is initially defined, two pieces of information are required: the message ID and a list of substitution variables. Other information (such as a description of the message) may also be provided, but is not relevant to message substitution.

If one of the substitution variables is named 'foo', then anywhere in the message, I can include '[foo...]' and that portion of the message will be replaced based on the value of foo that is passed in.

The syntax of the substitution string can be any of the following:

[foo]

In it's simplest form, the value is simply inserted into the string. For example, if the text is defined (in the default lexicon) as:

Set:        Set1
Message ID: Foo value [foo]
Text:       The value of foo is [foo].

Then:

$obj->message('Set1','Foo value [foo]',
              'foo' => 'bar');
   => 'The value of foo is bar.
[foo:FORMAT]

The value can be formatted using standard sprintf formats. Only simple formats are allowed, so FORMAT can be something like '%.3f' but not 'the number %3d'. To be exact, FORMAT must be any valid format that can be handled by sprintf that takes exactly one argument, so something like %% is not allowed, but any %d format is.

Set:        Set1
Message ID: Foo value [foo]
Text:       The value of foo is >[foo:%5s]<.

$obj->message('Set1','Foo value [foo]',
              'foo' => 'bar');
   => 'The value of foo is >  bar<.
[foo:quant ...] or [foo:quant:FORMAT ...]

This is for handling plural elements. These two forms are identical except that the number will be formatted using a sprintf format in the second case.

The general form of this is:

[VAL:quant COND1 STRING1 COND2 STRING2 ... DEFAULT_STRING]

Each of the tokens in this form (CONDi, STRINGi, and DEFAULT_STRING) can be a string that is wrapped in square brackets, single, or double quotes. So, the string foobar can appear as foobar, [foobar], 'foobar', or "foobar". If a token starts with a square bracket, or a single or double quote, it must be wrapped in one of the others.

Each CONDi is a string involving a correctly specified numerical test. It should be noted that the tests are NOT passed to the perl interpreter, so the full perl syntax is not supported. Since tests are specified in user (i.e. translator) defined files, passing portions of these files to eval would represent a security risk, so instead, the condition strings are manually parsed, and only a limited syntax is supported. However, the syntax should be flexible enough to handle all real-life cases.

A condition can be a simple test. Simple tests are all of one of the forms:

NUM <  NUM
NUM <= NUM
NUM == NUM
NUM >= NUM
NUM >  NUM
NUM != NUM

and each NUM can be:

_VAL
_VAL % DIGITS
DIGITS

Note that there are no signs allowed... only positive integers are allowed. Also, parentheses are not allowed inside a simple test (though the simple test can be enclosed in parentheses).

Simple tests can also be combined using parentheses and the two operators '&&' and '||'.

If COND1 is met, then STRING1 is used. If COND2 is met, then STRING2 is used. If none of the conditions are met, then the DEFAULT_STRING is met.

The DEFAULT_STRING is required, and at least one COND and STRING should be included (though they are not strictly required... if they are not present,t hen the DEFAULT_STRING will always be used.

An actual example might be:

Set:        Set1
Message ID: Number of oranges [n]
Text:       I have [n:quant [_n>1] '_n oranges' _n==1 "1 orange" [no oranges]].

and:

$obj->message('Set1','Number of oranges [n]',1);
   => 'I have 1 orange.'

$obj->message('Set1','Number of oranges [n]',3);
   => 'I have 3 oranges.'

$obj->message('Set1','Number of oranges [n]',0);
   => 'I have no oranges.'

Note that it is not required that '_VAL' be included in every one of the strings. It can be included zero, one, or even multiple times if desired.

Most whitespace is ignored, so the following are equivalent:

[foo]
[ foo ]

as are:

[foo:FORMAT]
[ foo : FORMAT ]

KNOWN PROBLEMS

None at this point.

SEE ALSO

Locale::VersionedMessages::Overview An overview of the internationalization problem with a comparison to other Locale::* modules

lm_gui A GUI tool for creating and maintaining sets of messages and their translation tables.

LICENSE

This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHOR

Sullivan Beck (sbeck@cpan.org)