NAME
Locale::TextDomain::OO - Perl OO Interface to Uniforum Message Translation
$Id: OO.pm 264 2009-12-29 19:41:11Z steffenw $
$HeadURL: https://perl-gettext-oo.svn.sourceforge.net/svnroot/perl-gettext-oo/trunk/lib/Locale/TextDomain/OO.pm $
VERSION
0.04
DESCRIPTION
This module provides a high-level interface to Perl message translation.
Why a new module?
This module is very similar to Locale::TextDomain.
The most important problem of Locale::TextDomain is the functional interface and consequently the use of the caller to figure out the text domain. That is why is not possible to wrap Locale::TextDomain functions into a new package.
Locale::TextDomain::OO has a flexible object oriented interface.
Why to write a wrapper?
Locale::TextDomain depends on Locale::Messages and Locale::Messages depends on gettext mo files. This is a very good idea to do this. It is a standard.
But if the data are not saved in mo files and the project is not a new project, how to bind a database or anything else to the Locale::TextDomain API?
Now it is possible to change the source of data.
Why to inherit?
In case of change a project from Locale::Maketext or Locale::Maketext::Simple use an extended API Locale::TextDomain::OO::Maketext.
Why bind late to a more simple functional interface?
Locale::TextDomain::OO::FunctionalInterface is a wrapper to have functions like Locale::TextDomain for the application interface and all the benefit from the binded object too.
When tie the object to a hash?
Fetch a hash is similar to call a subroutine. Fetch hash is interpolated in a string but call a subroutine not. This is a reason, to use the tied interface.
How to extract?
Inside this distribution is module Locale::TextDomain::OO::Extract. This is a base class for all source scanner to create pot files. Use this base class and give this module the rules or use one of the already exteded classes. Locale::TextDomain::OO::Extract::Perl is a extension for Perl code and so on.
Do not follow the dead end of Locale::Maketext!
What is the problem of?
Locale::Maketext allows 2 plural forms (and zero) only. This is changable, but the developer has to control the plural forms. He is not an omniscient translator.
'quant' inside a phrase is the end of the automatic translation because quant is an 'or'-construct.
begin of phrase [quant,_1,singular,plural,zero] end of phrase
The plural form is allowed after a number, followed by a whitespace, not a non-breaking whitespace.
1 book 2 books
A plural form can not be before a number.
It is 1 book. These are 2 books.
There is no plural form without a number in the phrase.
I like this book. I like these books.
Placeholders are numbered serially. It is difficult to translate this because the sense of the phrase could be lost.
[_1] is a [_2] in [_3]. Erlangen is a town in Bavaria.
But there are a lot of modules around Locale::Maketext.
This is the reason for another module to have:
Endless (real: up to 6) plural forms controlled by the translater and not by the developer.
Named placeholders.
Locale::TextDomain::OO can bind gettext subroutines or gettext methods.
An example for binding subroutines is the default Locale::gettext_xs or Locale::gettext_pp.
An example for object binding is Locale::TextDomain::OO::MessagesStruct.
What is the difference?
As default this module calls the subroutines like module Locale::Messages.
This behaviour is changeable. Choose a functional or object oriented module. Locale::Messages is an functional module. Locale::TextDomain::OO::MessagesStruct is an object oriented module.
Locale::TextDomain::OO::MessagesStruct implements the idea to read the full data into a data structure for fast access.
More informations
Read the documentation of Locale::TextDoamin to learn more about the translation subroutines.
Run the examples of this distribution (folder example).
Overview
Application calls Application calls Application calls
TextDomain subs .------ TextDomain subs method maketext
(the goal) | and Maketext methods -. (the beginning)
| | (the changeover) | |
| | | |
| | '------------. |
| v | |
| .---------------------------------------------. | |
| | functional interface | | |
| |---------------------------------------------| | |
| | Locale::TextDomain::OO::FunctionalInterface | | |
| |---------------------------------------------| | |
| | calls methods | | |
| `---------------------------------------------' | |
| | | |
| v v v
| .------------------------------------------------.
| | OO interface like |
| | Locale::Maketext::Simple |
| | and (!) |
| | Locale::TextDomain::OO |
| |------------------------------------------------|
| | Locale::TextDomain::OO::Maketext |
| `------------------------------------------------'
| |
v v
.----------------------------.
| interface like |
| Locale::TextDomain |
|----------------------------|
| Locale::TextDomain::OO |
`----------------------------'
| |
v v
.-------------------------. .----------------------------------------.
| Locale::gettext_(xs|pp) | | Locale::TextDomain::OO::MessagesStruct |
| (the default) | | (a possibility) |
`-------------------------' `----------------------------------------'
| |
| v
| .-------------------.
| | data struct |
| |-------------------|
| | | |
| | +--[text domain] |
| | | |
| | `--[...] |
| `-------------------'
| ^ ^
| | |
| .------------------------. |
| | build using | .-------------.
| | po extrction tools | | build using |
| | like DBD::PO | | DBI |
| | or DBD::PO::Locale::PO | `-------------'
| `------------------------' ^
| ^ |
v | _____|_____
.----------. .----------. /_ _ _ _ _ _\
| mo files |-. | po files |-. | |
`----------' | `----------' | | Database |
`----------' `----------' `-----------'
^ ^ ^
| | |
build using build using existing data
gettext tools gettext tools
SYNOPSIS
require Locale::TextDomain::OO;
SUBROUTINES/METHODS
method new
optional parameter text_domain
Set the default text domian __PACKAGE__.
my $loc = Locale::TextDoamin::OO->new(
...
);
Set the text domain.
my $loc = Locale::TextDoamin::OO->new(
...
text_domain => 'exapmle',
...
);
optional parameter search_dirs
Set the search dirs.
my $loc = Locale::TextDoamin::OO->new(
...
search_dirs => \@local_dirs,
...
);
The default for the search_dirs is:
my @locale_dirs = map {
-d "$_/LocaleData"
? "$_/LocaleData"
: ();
} (
@INC,
qw(/usr/share/locale /usr/local/share/locale),
);
optional parameter gettext_package or alternative gettext_object
Note, that the default of gettest_package is Locale::gettext_xs or Locale::gettext_pp. This package has to implement the subroutines 'dgettext', 'dngettext', 'dpgettext', 'dnpgettext' and can implement the subroutine 'bindtextdomain'.
my $loc = Locale::TextDoamin::OO->new(
gettext_package => 'Package::With::Subroutines',
...
);
Or alternative the package which has to implement the methods 'dgettext', 'dngettext', 'dpgettext', 'dnpgettext'.
my $loc = Locale::TextDoamin::OO->new(
gettext_object => Locale::TextDomain::OO::MessagesStruct->new(\my %struct),
...
);
optional parameter language_detect
Describe as code, how to detect the language. This example code describes the default in list context.
my $loc = Locale::TextDoamin::OO->new(
...
language_detect => sub {
my @languages_want = I18N::LangTags::Detect::detect();
my @languages_all = implicate_supers(@languages_want);
return @languages_all, panic_languages(@languages_all);
},
...
);
Read I18N::LangTags, panic_languages for more informations.
method get_default_language_detect
This method returns a code reference. Run this code reference to find the needed language from the environment and all the fallbacks.
get the code reference
my $code_ref = Locale::TextDomain::OO->get_default_language_detect();
or
my $code_ref = $loc->get_default_language_detect();
run the code in list context
@langauges = $code_ref->();
or in scalar context typical for
local $ENV{LANGUAGE} = $code_ref->(); # result joined by :
method get_file_path
my $file_suffix = '.mo';
my $file_path = $loc->get_file_path($text_domain, $file_suffix);
If a file based database system not exists, create an extra file system. Write down for which language and which text domain a database exists. Instead of an "$text_domain$suffix" database file create some empty dummy files.
If possible, extract this informations automaticly from the database.
Than the method get_file_path checks the wanted languages and matches the existing langauges.
my ($dir, $language) = $loc->get_file_path($text_domain, $file_suffix);
Another way to use this module with a none file based database system is to implement the language selection self.
object or class method get_nplurals
How many plurals has the translation? This is one-time interesting to read the translation data.
$nplurals = $self->get_nplurals(
'nplurals=2; plural=n != 1;' # look at the po/mo file header
);
or
$nplurals = Locale::Text::Domain::OO->get_nplurals(
'nplurals=2; plural=n != 1;' # look at the po/mo file header
);
object or class method get_function_ref_plural
Which plural form sould be used? The code runs during every plural tranlation.
$code_ref = $self->get_function_ref_plural(
'nplurals=2; plural=n != 1;' # look at the po/mo file header
);
or
$code_ref = Locale::Text::Domain::OO->get_function_ref_plural(
'nplurals=2; plural=n != 1;' # look at the po/mo file header
);
Translating methods
How to build the method name?
Use __ and append this with 'n', 'p' and/or 'x' in alphabetic order.
.----------------------------------------------------------------.
| Snippet | Description |
|---------+------------------------------------------------------|
| __ | Special marked for extraction. |
| x | Last parameters are the hash for named placeholders. |
| n | Using plural forms. |
| p | Context is the first parameter. |
'----------------------------------------------------------------'
__ Translate only
print $loc->__(
'Hello World!',
);
__x Named placeholders
print $loc->__x(
'Hello {name}!',
name => 'Steffen',
);
__n Plural
print $loc->__n(
'one file read',
'a lot of files read',
$num_files,
);
__nx Plural and named placeholders
print $loc->__nx(
'one file read',
'{num} files read',
$num_files,
num => $num_files,
);
The sub __xn is the same like sub __nx at Locale::TextDomain.
Method __xn is not implemented because it is the same like method __nx.
__p Context
print $loc->__p (
'time',
'to',
);
print $loc->__p (
'destination',
'to',
);
__px Context and named placeholders
print $loc->__px (
'destination',
'from {town_from} to {town_to}',
town_from => 'Chemnitz',
town_to => 'Erlangen',
);
__np Context and plural
print $loc->__np (
'maskulin',
'Dear friend',
'Dear friends',
$friends,
);
__npx Context, plural and named placeholders
print $loc->__npx(
'maskulin',
'Mr. {name} has {num} book.',
'Mr. {name} has {num} books.',
$books,
name => $name,
);
Methods to mark the translation for extraction only
How to build the method name?
Use N__ and append this with 'n', 'p' and/or 'x' in alphabetic order.
.----------------------------------------------------------------.
| Snippet | Description |
|---------+------------------------------------------------------|
| N__ | Special marked for extraction. |
| x | Last parameters are the hash for named placeholders. |
| n | Using plural forms. |
| p | Context is the first parameter. |
'----------------------------------------------------------------'
N__, N__x, N__n, N__nx, N__p, N__px, N__np, N__npx
The extractor looks for __('...')
and has no problem with <$loc-
N__('...')>>.
This is the idea of the N-Methods.
$loc->N__('...');
EXAMPLE
Inside of this distribution is a directory named example. Read the file README there. Then run the *.pl files.
DIAGNOSTICS
Error message in case of unknown parameters.
Unknown parameter: ...
Error message during load of the implementation package.
Can't locate ...
Error message during load of the default implementation package.
gettext_xs_version ... is to old.
Error message at calculation plural forms.
Plural-Forms are not defined
Code of Plural-Forms ... is not safe, ...
Code ... is not safe, ...
CONFIGURATION AND ENVIRONMENT
none
DEPENDENCIES
Carp
Cwd
English
Safe
dynamic require
INCOMPATIBILITIES
not known
BUGS AND LIMITATIONS
none
SEE ALSO
At version 1.20:
The bug L<http://rt.cpan.org/Public/Bug/Display.html?id=49744>
"gettext_pp.pm can not parse Russian plural forms"
was fixed in module Locale::Text::Domain::OO.
The bug L<http://rt.cpan.org/Public/Bug/Display.html?id=49758>
"no perl unicode support or the half one only"
was fixed in module Locale::Text::Domain::OO.
The problem is in detail if you have an UTF-8 Perl source
and a non ASCII msgid, msgid_plural or msgctxt.
Because English is not ASCII, you can have
the English currency symbol, the paragraph symbol
and so on.
http://www.gnu.org/software/gettext/manual/gettext.html
http://en.wikipedia.org/wiki/Gettext
http://translate.sourceforge.net/wiki/l10n/pluralforms
http://rassie.org/archives/247 The choice of the right module for the translation.
AUTHOR
Steffen Winkler
LICENSE AND COPYRIGHT
Copyright (c) 2009, Steffen Winkler <steffenw at cpan.org>
. All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.