The simplest and most common way of doing things is:
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.gettext("World Of Themes") %]
[% "Introduction" | gettext %]
[% FILTER gettext %]
The "World Of Themes" is the ultimate source of templates
for the Template Toolkit.
[% END %]
This shows three different ways of localizing strings. You can
use the function C, the filter C with pipe
syntax, or the same filter with block syntax. The result is always
the same. The string will be recognized as translatable by
C and it will be translated into the selected language,
when rendering the template.
=head4 Interpolating Strings Into Translations
One important thing to understand is that the argument to the
gettext functions or filters is the lookup key into the translation
database, when the template gets rendered. That implies that this
key has to be invariable and must not use any interpolated variables.
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.gettext("Hello, $firstname $lastname!") %]
This template code is syntactically correct and will also render
correctly. But C will bail out on it with an error
message like
templates.html:3: Illegal variable interpolation at "$"
The function C will receive the interpolated string
as its argument, and that is not the same as the string that
the extractor program C sees. And that means that
the translation cannot be found.
The correct way to interpolate strings uses C:
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.xgettext("Hello, {first} {last}!",
first => firstname, last => lastname) %]
[% "Hello, {first} {last}!" | xgettext(first => firstname,
last => lastname) %]
[% FILTER xgettext(first => firstname, last => lastname) %]
Hello, {first} {last}!
[% END %]
One additional benefit of this is that the extractor program
C will also mark these strings with the flag
"perl-brace-format". When the translation from the C<.po>
file gets compiled into an C<.mo> file, the compiler C
checks that the translated strings contains exactly the same
placeholders as the original.
One thing that you should also avoid is to assemble strings
in the template source code. Do I:
[% gtx.gettext("Please contact") %] [% name %]
[% gtx.gettext("for help about the") %] [% package %]
[% gtx.gettext("software.") %]
This will result in three translatable text snippets
"Please contact", "for help about the", and "software." that
are hard to translate without context. Besides it makes
illegal assumptions about the word order in translated sentences.
Instead, use C and write in complete sentences with
placeholders.
By the way, the C in the function C stands for I
while the C in the program C or GNU Gettext's
C program stands for I.
=head4 Plural Forms
Do I write this:
[% IF num != 1 %]
[% gtx.xgettext("{number} documents deleted!", number => num) %]
[% ELSE %]
[% gtx.gettext("One document deleted!") %]
[% END %]
This assumes that every language has one singular and one plural
(and no other forms) and that the condition that selects the correct
form is always C. But this is wrong for many languages
for example Russian (two plural forms), Chinese (no plural), French
(different condition), and many more.
Write instead:
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.nxgettext("One document deleted.",
"{count} documents deleted."
num,
count => num) %]
The function C receives the singular and plural
form as the first and second argument, followed by the number
of items, followed by an arbitrary number of key/value pairs
for interpolating variables in the strings.
There is also a function C that does not expand
its first two arguments. You will find out that you almost
never need that function.
You can also use C and C as filters.
But the necessary code is awkward, and their use is therefore
not recommended.
=head4 Ambiguous Strings (message contexts)
Sometimes an English string has different meanings in other
languages:
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.gettext("State:") %]
[% IF state == '1' %]
[% gtx.pgettext("state", "Open") %]
[% ELSE %]
[% gtx.gettext("Closed") %]
[% END %]
[% gtx.pgettext("action", "Open") %]
The function C works like gettext but has one
extra argument preceding the string, the so-called
message context. The string extractor C will now
create two distinct messages "Open", one with the context "state",
the other one with the context "action". The sole purpose of this
context is to disambiguate the string "Open" for languages where the
verb ("to open") and the adjective ("the door is I") has
two distinct translations.
You will normally use this function, when a translator asks you
to do so, but not on your own behalf.
There is also a function C that supports placeholder
interpolation, and C that has the following semantics:
npxgettext(CONTEXT, SINGULAR, PLURAL, COUNT,
KEY1 => VALUE1, KEY2 => VALUE2, ...)
=head4 More Esoteric Functions
The L contains
some more functions and filters that are available for completeness.
You will never need them in normal projects.
=head4 Translator Hints
You can add comments to the source code that are copied into the
C<.po> file as hints for the translators. This will look like
this:
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.gettext("Sun") %]
In order to make that work, you have to invoke the extractor
program C like this:
xgettext-tt2 --add-comments=TRANSLATORS: t1.html t2.html ...
=head4 Modifying Flags
In rare situations, you may need the following:
[% USE gtx = Gettext('com.mydomain.www', lang) %]
[% gtx.xgettext("Value: {value}", value => whatever) %]
Normally, the argument of C will be flagged in
the C<.po> file with "perl-brace-format", and a translation
will fail to compile if the translation does not contain exactly
the same placeholders as the original does.
You can override that default behavior for individual messages
by placing a comment containing the string "xgettext:" directly
in front of the string.
=head3 Translation Workflow
The translation workflow is the standard workflow known from GNU
Gettext. All files relevant for translations are conventionally
kept in a subdirectory C.
You can save time if you use the seed project
L
as a base. It contains a directory C ready for use,
with --- at your choice --- a Makefile or a script C
that automates the entire translation workflow. It is also
prepared for extracting strings from other sources than
template files. In that example, these are Perl source files,
but it will work in a similar fashion for other programming
languages.
But rolling your own version is also simple. Just read on.
=head4 Extracting Strings With C
Extracting translatable strings from templates for the Template
Toolkit 2 is as easy as:
$ xgettext-tt2 TEMPLATE....
This will scan all files given as arguments for translatable strings
and create a file C with the strings found.
The normal invocation of C is normally a little bit more
sophisticated:
$ xgettext-tt2 --files-from=POTFILES \
--output=com.mydomain.www.pot \
--add-comments=TRANSLATORS: --from-code=utf-8 \
--force-po
You can, of course, write everyting in one line and omit the backslashes.
Specifying all input files as arguments on the command-line can
quickly become unwieldy. It is more common to put the list of input
files into a text file, each input file on one line, and instruct
C to read it with the option C<--files-from>. The name
of the file is by convention C.
The output file is normally a file C, where
C is the identifier selected in the templates. The
reverse hostname of the server serving the rendered templates
is a good choice.
If you want to be able to give hints to translators in the source
files, you have to specify the trigger string --- normally
"TRANSLATORS:" --- with the option C<--add-comments>. Specifying
an empty string (C<--add-comments=''>) instructs C
to copy all comments into the C<.pot> file.
If your templates contain characters outside of US-ASCII, you should
specify the character set of the template files with the option
C<--from-code=CODESET>.
The option C<--force-po> instructs C to write an output
file even if no translatable strings had been found. But this
is a matter of taste. Omit the option, if you prefer it.
C has a lot more options. They are mostly compatible
with the ones of C from GNU gettext for C, Perl, and
a lot more languages. See the L
and the documentation for L
for more information.
By the way, why is the ouput file a C<.pot> file and not a C<.po>
file? It is the I for the C<.po> files for the individual
languages. You never edit that file, but re-generate it, whenever
the source files have changed. Hence, it only contains strings
in the original, in the base language.
=head4 Creating Translation Files
For each supported language (except for the base language) you
should create a file C, where C is the two-letter
language code for that language, for example C, C,
or C. You can also specify the combination of language
and country like in C or C.
One option for that is to simply copy the C<.pot> file and
edit the header accordingly. It is normally easier to do that
with the program C:
$ msginit --input=com.mydomain.www.pot --locale=fr
Replace C with the name of the C<.pot> file, and
C with the language in question. This will prefill a lot
of fields in the C<.po> file.
=head4 Compiling Translation Files
The translated C<.po> files are compiled with the program C:
$ msgfmt --check --statistics --verbose -o fr.mo fr.po
fr.po: 212 translated messages, 1 fuzzy translation, 3 untranslated messages.
This will compile the translation file C into a binary
file C. It also checks the translations for formal errors
and print statistics about the number of translated and
untranslated strings.
=head4 Installing Translation Files
The plugin does not use C<.po> files for looking up translations
but the binary C<.mo> files. But it has to find them.
You have to decide for one of the directories that
C searches for translations. The
default order is:
=over
=item *
C<@INC/LocaleData>
=item *
C
=item *
C
=back
The first line means that every directory C inside
Perl's include directories is searched for translation files.
Keep in mind, that for security reasons the current directory
(C<.>) is nowadays often I in Perl's C<@INC>.
Let's assume that C is in Perl's @INC. You would
then install the French translation file C as C.
C is a placeholder for the textdomain you have
selected (and C is I a placeholder but a real
directory name).
That is good except for the fact that C is usually
not in Perl's C<@INC>. But you can change that where you invoke
the template processor:
BEGIN {
unshift @INC, '/var/www/lib';
}
use Template;
Template->new->process('template.html', $data);
You can completely override the default search order in the
templates:
[% USE gtx = Gettext('com.mydomain.www', lang, 'utf-8',
'/var/www/locale', '/srv/www/locale')]
Now, the French translation would be searched in
C and
C.
=head4 Updating Translation Files
Translations may become obsolete, when the source templates
change. In this case, you have to merge the new set of
translatable strings into the existing translation files.
Fortunately, GNU Gettext makes this easy:
$ xgettext-tt2 --files-from=POTFILES \
--output=com.mydomain.www.pot \
--add-comments=TRANSLATORS: --from-code=utf-8 \
--force-po
$ cp fr.po fr.old.po
$ msgmerge fr.old.po com.mydomain.www.pot -o fr.po
....... done
You first update the C<.pot> file with C so that it
contains the current set of translatable strings. You then
make a backup of each C<.po> file and then invoke the program
C for merging the current translations from C
with the new set of strings from C into
the updated translation file C.
The file C will now contain the new strings as untranslated
entries. Strings that have only slightly change will retain their
translations but they will be marked as "fuzzy", so that they
can be reviewed by a translator. Entries for strings that are
no longer present in the sources are obsoleted.
=head4 Integrating With Other Programming Languages
The GNU Gettext framework is available for a lot of programming
languages and it is not uncommon that two or more of these
languages are mixed in a project. It is beneficial in these
cases to use a common translation base for all used
technologies.
C is based on L|https://github.com/gflohr/Locale-XGettext>
and therefore not only understands Template Toolkit templates
but also C<.po> and C<.pot> files as input. GNU Gettext's xgettext
has the same feature.
Accumulating all translatable strings from the different
technologies is therefore very easy. If you have a project
that uses Template Toolkit for rendering web pages and Perl
for the business logic you first extract strings from your
Perl files --- as usual --- with C from GNU gettext
into a temporary file, for example C. Then you
extract the strings from the templates with C
from this library, but you specify C as an
additional input file. And now the output file of C
contains all the strings from the template files I those
from the Perl files in C.
Of course, you can also do it the other way round, extract
with C into C, and then feed that as
an additional input file to GNU Gettext's C.
You can use the seed project L
as a fully functional starting point for such setups.
=head2 Bugs
Please report bugs at L
=head2 Author
Template-Plugin-Gettext was written by L.
Makefile.PL 100644 000765 000024 3544 14442777752 20515 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0 # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.030.
use strict;
use warnings;
use ExtUtils::MakeMaker;
my %WriteMakefileArgs = (
"ABSTRACT" => "Gettext Support For the Template Toolkit Version 2",
"AUTHOR" => "Guido Flohr ",
"BUILD_REQUIRES" => {
"Module::Build" => "0.28"
},
"CONFIGURE_REQUIRES" => {
"ExtUtils::MakeMaker" => 0,
"Module::Build" => "0.28"
},
"DISTNAME" => "Template-Plugin-Gettext",
"EXE_FILES" => [
"bin/xgettext-tt2"
],
"LICENSE" => "lgpl",
"NAME" => "Template::Plugin::Gettext",
"PREREQ_PM" => {
"Cwd" => 0,
"Encode" => 0,
"Getopt::Long" => 0,
"Locale::Messages" => 0,
"Locale::TextDomain" => "1.30",
"Locale::Util" => 0,
"Locale::XGettext" => "0.7",
"Template" => 0,
"Template::Parser" => 0,
"Template::Plugin" => 0,
"base" => 0,
"strict" => 0
},
"TEST_REQUIRES" => {
"File::Spec" => 0,
"Locale::XGettext::Util::Keyword" => 0,
"Test::More" => 0,
"vars" => 0
},
"VERSION" => "1.0",
"test" => {
"TESTS" => "t/*.t"
}
);
my %FallbackPrereqs = (
"Cwd" => 0,
"Encode" => 0,
"File::Spec" => 0,
"Getopt::Long" => 0,
"Locale::Messages" => 0,
"Locale::TextDomain" => "1.30",
"Locale::Util" => 0,
"Locale::XGettext" => "0.7",
"Locale::XGettext::Util::Keyword" => 0,
"Module::Build" => "0.28",
"Template" => 0,
"Template::Parser" => 0,
"Template::Plugin" => 0,
"Test::More" => 0,
"base" => 0,
"strict" => 0,
"vars" => 0
);
unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
delete $WriteMakefileArgs{TEST_REQUIRES};
delete $WriteMakefileArgs{BUILD_REQUIRES};
$WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
}
delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
unless eval { ExtUtils::MakeMaker->VERSION(6.52) };
WriteMakefile(%WriteMakefileArgs);
POTFILES1 100644 000765 000024 56 14442777752 20172 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t templates/template.tt
templates/additional.tt
POTFILES2 100644 000765 000024 53 14442777752 20170 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t templates/additional.tt
templates/extra.tt
TestLib.pm 100644 000765 000024 3476 14442777752 20716 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use vars qw(@ISA @EXPORT_OK);
use Locale::XGettext::TT2;
use Locale::XGettext::Util::Keyword;
@ISA = qw(Exporter);
@EXPORT_OK = qw(find_entries use_keywords);
sub find_entries {
my ($po, %args) = @_;
my @hits;
foreach my $entry (@$po) {
next if exists $args{msgid} && $entry->msgid ne $args{msgid};
next if exists $args{msgstr} && $entry->msgstr ne $args{msgstr};
next if exists $args{msgctxt} && $entry->msgctxt ne $args{msgctxt};
next if exists $args{comment} && $entry->comment ne $args{comment};
next if exists $args{automatic} && $entry->automatic ne $args{automatic};
push @hits, $entry;
}
return @hits;
}
sub use_keywords($) {
my ($keywords) = @_;
foreach my $method (keys %$keywords) {
$keywords->{$method} =
Locale::XGettext::Util::Keyword->new($method,
@{$keywords->{$method}});
}
Locale::XGettext::TT2->new({keywords => $keywords}, 'dummy');
}
perlcritic.rc 100644 000765 000024 1622 14442777752 21224 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0 ; Copyright (C) 2016-2018 Guido Flohr ,
; all rights reserved.
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 3 of the License, or
; (at your option) any later version.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with this program. If not, see .
; Prototypes are good and important, contrary to what Conway thinks.
[-Subroutines::ProhibitSubroutinePrototypes]
; Outsmarts automatic versioning.
[-TestingAndDebugging::RequireUseStrict]
automatic.t 100644 000765 000024 2617 14442777752 21161 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 1;
use Locale::XGettext::TT2;
BEGIN {
my $test_dir = __FILE__;
$test_dir =~ s/[-a-z0-9]+\.t$//i;
chdir $test_dir or die "cannot chdir to $test_dir: $!";
unshift @INC, '.';
}
use TestLib qw(find_entries);
my @po = Locale::XGettext::TT2->new({add_comments => ['TRANSLATORS:']},
'templates/template.tt')
->run->po;
is((scalar find_entries \@po,
msgid => qq{"Translator comment above.\\n"},
automatic => "TRANSLATORS: A translator comment.\n"), 1);
no-package.t 100644 000765 000024 3075 14442777752 21177 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More;
use File::Spec;
use Locale::XGettext::TT2;
BEGIN {
my $test_dir = __FILE__;
$test_dir =~ s/[-a-z0-9]+\.t$//i;
chdir $test_dir or die "cannot chdir to $test_dir: $!";
unshift @INC, '.';
}
my $relname = 'templates/no-package.tt';
my $absname = File::Spec->rel2abs('templates/no-package.tt');
my @po = eval { Locale::XGettext::TT2->new({plug_in => '',
keyword => ['', 'loc'],
flag => ['loc:1:perl-brace-format']
}, $absname)->run->po };
ok !$@;
ok scalar @po;
is 2, scalar @po;
if (2 == @po) {
is '"Price: {price}"', $po[1]->msgid;
ok $po[1]->has_flag('perl-brace-format');
}
done_testing;
filter-pipe.t 100644 000765 000024 11005 14442777752 21422 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 18;
use Template;
my $tt = Template->new or die Template->error;
my ($template, $output);
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Hello, world!' | gettext -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | ngettext('many files', 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | ngettext('many files', 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Context' | pgettext('Hello, world!') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Hello, world!' | gettextp('Context') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Context' | npgettext('one file', 'many files', 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Context' | npgettext('one file', 'many files', 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | ngettextp('many files', 1, 'Context') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | ngettextp('many files', 42, 'Context') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Hello, {who}!' | xgettext(who => 'world') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | nxgettext('{count} files', 1, count => 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | nxgettext('{count} files', 42, count => 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Context' | pxgettext('Hello, {who}!', who => 'world') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Hello, {who}!' | xgettextp('Context', who => 'world') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Context' | npxgettext('one file', '{count} files', 1, count => 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'Context' | npxgettext('one file', '{count} files', 42, count => 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | nxgettextp('{count} files', 1, 'Context', count => 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- 'one file' | nxgettextp('{count} files', 42, 'Context', count => 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
interpolate.t 100644 000765 000024 2507 14442777752 21517 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 3;
use Locale::XGettext::TT2;
BEGIN {
my $test_dir = __FILE__;
$test_dir =~ s/[-a-z0-9]+\.t$//i;
chdir $test_dir or die "cannot chdir to $test_dir: $!";
unshift @INC, '.';
}
eval { Locale::XGettext::TT2->new({}, 'templates/interpolate-1.tt')->run };
ok !$@, $@;
eval { Locale::XGettext::TT2->new({}, 'templates/interpolate-2.tt')->run };
ok !$@, $@;
eval { Locale::XGettext::TT2->new({}, 'templates/interpolate-3.tt')->run };
ok $@, "variable interpolation not detected";
bin 000755 000765 000024 0 14442777752 17145 5 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0 xgettext-tt2 100755 000765 000024 6010 14442777752 21613 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/bin #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
use strict;
use Getopt::Long;
use Locale::Messages qw(setlocale LC_MESSAGES);
use Locale::TextDomain qw(Template-Plugin-Gettext);
use Locale::XGettext::TT2;
Locale::Messages::setlocale(LC_MESSAGES, "");
my %options;
Locale::XGettext::TT2->newFromArgv(\@ARGV)->run->output;
=head1 NAME
xgettext-tt2 - Extract translatable strings from Template Toolkit 2 templates
=head1 SYNOPSIS
xgettext-tt2 [OPTIONS] INPUTFILES
xgettext-tt2 [OPTIONS] --files-from=FILELIST
=head1 DESCRIPTION
The program B extracts translatable strings from
templates for the Template Toolkit version 2 (see
L).
For an overview of all command line options try the command
"perldoc Locale::XGettext" or "man Locale::XGettext". The online
version of the manual page is available at L.
=head1 DEFAULT KEYWORDS
The program uses the following built-in default keywords supported by
L:
=over 4
=item *
gettext:1
=item *
ngettext:1,2
=item *
pgettext:1c,2
=item *
gettextp:1,2c
=item *
npgettext:1c,2,3
=item *
ngettextp:1,2,3c
=item *
xgettext:1
=item *
nxgettext:1,2
=item *
pxgettext:1c,2
=item *
xgettextp:1,2c
=item *
npxgettext:1c,2,3
=item *
nxgettextp:1,2,3c
=back
=head1 DEFAULT KEYWORDS
The program has the following built-in default flags supported by
L:
=over 4
=item *
xgettext:1:perl-brace-format
=item *
nxgettext:1:perl-brace-format
=item *
nxgettext:2:perl-brace-format
=item *
pxgettext:2:perl-brace-format
=item *
xgettextp:1:perl-brace-format
=item *
npxgettext:2:perl-brace-format
=item *
npxgettext:3:perl-brace-format
=item *
nxgettextp:1:perl-brace-format
=item *
nxgettextp:2:perl-brace-format
=back
=head1 COPYRIGHT
Copyright (C) 2016-2018 Guido Flohr (http://www.guido-flohr.net/).
License LGPLv3+: L.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Copyright (C) 2016-2018 Guido Flohr ,
all rights reserved.
=head1 SEE ALSO
L, L, L,
L, L
filter-filter.t 100644 000765 000024 11416 14442777752 21760 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 18;
use Template;
my $tt = Template->new or die Template->error;
my ($template, $output);
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[% FILTER gettext -%]
Hello, world!
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[% FILTER ngettext('many files', 1) -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[% FILTER ngettext('many files', 42) -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER pgettext('Hello, world!') -%]
Context
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER gettextp('Context') -%]
Hello, world!
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER npgettext('one file', 'many files', 1) -%]
Context
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER npgettext('one file', 'many files', 42) -%]
Context
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[% FILTER ngettextp('many files', 1, 'Context') -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER ngettextp('many files', 42, 'Context') -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[% FILTER xgettext(who => 'world') -%]
Hello, {who}!
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER nxgettext('{count} files', 1, count => 1) -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER nxgettext('{count} files', 42, count => 42) -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER pxgettext('Hello, {who}!', who => 'world') -%]
Context
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER xgettextp('Context', who => 'world') -%]
Hello, {who}!
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER npxgettext('one file', '{count} files', 1, count => 1) -%]
Context
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER npxgettext('one file', '{count} files', 42, count => 42) -%]
Context
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER nxgettextp('{count} files', 1, 'Context', count => 1) -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- FILTER nxgettextp('{count} files', 42, 'Context', count => 42) -%]
one file
[%- END -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
filter-method.t 100644 000765 000024 11203 14442777752 21745 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 18;
use Template;
my $tt = Template->new or die Template->error;
my ($template, $output);
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.gettext('Hello, world!') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.ngettext('one file', 'many files', 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.ngettext('one file', 'many files', 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.pgettext('Context', 'Hello, world!') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.gettextp('Hello, world!', 'Context') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.npgettext('Context', 'one file', 'many files', 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.npgettext('Context', 'one file', 'many files', 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.ngettextp('one file', 'many files', 1, 'Context') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.ngettextp('one file', 'many files', 42, 'Context') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'many files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.xgettext('Hello, {who}!', who => 'world') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.nxgettext('one file', '{count} files', 1, count => 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.nxgettext('one file', '{count} files', 42, count => 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.pxgettext('Context', 'Hello, {who}!', who => 'world') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.xgettextp('Hello, {who}!', 'Context', who => 'world') -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'Hello, world!';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.npxgettext('Context', 'one file', '{count} files', 1, count => 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.npxgettext('Context', 'one file', '{count} files', 42, count => 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.nxgettextp('one file', '{count} files', 1, 'Context', count => 1) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, 'one file';
$output = '';
$template = <<'EOF';
[%- USE Gettext -%]
[%- Gettext.nxgettextp('one file', '{count} files', 42, 'Context', count => 42) -%]
EOF
$tt->process(\$template, {}, \$output) or die $tt->error;
is $output, '42 files';
author-critic.t 100644 000765 000024 403 14442777752 21717 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #!perl
BEGIN {
unless ($ENV{AUTHOR_TESTING}) {
print qq{1..0 # SKIP these tests are for testing by the author\n};
exit
}
}
use strict;
use warnings;
use Test::Perl::Critic (-profile => "perlcritic.rc") x!! -e "perlcritic.rc";
all_critic_ok();
absolute-paths.t 100644 000765 000024 2354 14442777752 22124 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More;
use File::Spec;
use Locale::XGettext::TT2;
BEGIN {
my $test_dir = __FILE__;
$test_dir =~ s/[-a-z0-9]+\.t$//i;
chdir $test_dir or die "cannot chdir to $test_dir: $!";
unshift @INC, '.';
}
my $relname = 'templates/bugs-1.tt';
my $absname = File::Spec->rel2abs('templates/bugs-1.tt');
my @po = eval { Locale::XGettext::TT2->new({}, $absname)->run->po };
ok !$@;
ok scalar @po;
done_testing;
filter-xgettext.t 100644 000765 000024 2673 14442777752 22334 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 2;
use Locale::XGettext::TT2;
BEGIN {
my $test_dir = __FILE__;
$test_dir =~ s/[-a-z0-9]+\.t$//i;
chdir $test_dir or die "cannot chdir to $test_dir: $!";
unshift @INC, '.';
}
use TestLib qw(find_entries);
my @po = Locale::XGettext::TT2->new({},
'templates/template.tt')
->run->po;
is((scalar find_entries \@po,
msgid => qq{"Hello, {who}!"},
msgctxt => qq{"filter"}), 1);
is((scalar find_entries \@po,
msgid => qq{"Hello, {arg}!"},
msgctxt => qq{"pipe"}), 1);
method-xgettext.t 100644 000765 000024 2505 14442777752 22321 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #! /usr/bin/env perl
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
use strict;
use Test::More tests => 1;
use Locale::XGettext::TT2;
BEGIN {
my $test_dir = __FILE__;
$test_dir =~ s/[-a-z0-9]+\.t$//i;
chdir $test_dir or die "cannot chdir to $test_dir: $!";
unshift @INC, '.';
}
use TestLib qw(find_entries);
my @po = Locale::XGettext::TT2->new({},
'templates/template.tt')
->run->po;
is((scalar find_entries \@po,
msgid => qq{"Hello, {name}!"},
msgctxt => qq{"method"}), 1);
templates 000755 000765 000024 0 14442777752 20636 5 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t bugs-1.tt 100644 000765 000024 554 14442777752 22431 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% USE gtx = Gettext %]
[% "no parentheses around filter" | gettext %]
[% "no parentheses around filter and filter following" | gettext | html %]
The next one is debatable. The translation can never be a reference, and
therefore a translation cannot possibly be displayed in this case.
[% "no parentheses around filter and dot following" | gettext.something %]
nested.tt 100644 000765 000024 116 14442777752 22607 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% USE gtx = Gettext %]
[% q.strftime(gtx.gettext('%Y/%m/%d'), asset_date) %]
author-pod-syntax.t 100644 000765 000024 454 14442777752 22556 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #!perl
BEGIN {
unless ($ENV{AUTHOR_TESTING}) {
print qq{1..0 # SKIP these tests are for testing by the author\n};
exit
}
}
# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
use strict; use warnings;
use Test::More;
use Test::Pod 1.41;
all_pod_files_ok();
template.tt 100644 000765 000024 536 14442777752 23146 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% USE gtx = Gettext %]
[% gtx.gettext("Hello, world!\n") %]
[% # TRANSLATORS: A translator comment.
gtx.gettext("Translator comment above.\n") %]
[% gtx.pxgettext("method", "Hello, {name}!", name => "N. N.") %]
[% FILTER xgettextp("filter", who => 'world') %]
Hello, {who}!
[% END %]
[% "Hello, {arg}!" | xgettextp("pipe", arg => 'world') %]
author-pod-coverage.t 100644 000765 000024 567 14442777752 23030 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t #!perl
BEGIN {
unless ($ENV{AUTHOR_TESTING}) {
print qq{1..0 # SKIP these tests are for testing by the author\n};
exit
}
}
# This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests.
use strict;
use warnings;
use Test::Pod::Coverage 1.08;
use Pod::Coverage::TrustPod;
all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' });
release-cpan-changes.t 100644 000765 000024 553 14442777752 23115 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t
BEGIN {
unless ($ENV{RELEASE_TESTING}) {
print qq{1..0 # SKIP these tests are for release candidate testing\n};
exit
}
}
use strict;
use warnings;
# this test was generated with Dist::Zilla::Plugin::Test::CPAN::Changes 0.012
use Test::More 0.96 tests => 1;
use Test::CPAN::Changes;
subtest 'changes_ok' => sub {
changes_file_ok('Changes');
};
no-package.tt 100644 000765 000024 34 14442777752 23311 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% loc("Price: {price}") %]
XGettext 000755 000765 000024 0 14442777752 22116 5 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/lib/Locale TT2.pm 100644 000765 000024 30466 14442777752 23256 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/lib/Locale/XGettext #! /bin/false
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
package Locale::XGettext::TT2;
$Locale::XGettext::TT2::VERSION = '1.0';
use strict;
use Locale::TextDomain qw(Template-Plugin-Gettext);
use Template;
use Locale::XGettext 0.7;
use base qw(Locale::XGettext);
sub versionInformation {
return __x('{program} (Template-Plugin-Gettext) {version}
Copyright (C) {years} Cantanea EOOD (http://www.cantanea.com/).
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Guido Flohr (http://www.guido-flohr.net/).
',
program => $0, years => '2016-2018',
version => $Locale::XGettext::TT2::VERSION);
}
sub fileInformation {
return __(< 1,
# Needed for reading from POTFILES
RELATIVE => 1
);
my $parser = Locale::XGettext::TT2::Parser->new(\%options);
my $tt = Template->new({
%options,
PARSER => $parser,
});
my $sink;
$parser->{__xgettext} = $self;
$parser->{__xgettext_filename} = $filename;
$tt->process($filename, {}, \$sink) or die $tt->error;
return $self;
}
package Locale::XGettext::TT2::Parser;
$Locale::XGettext::TT2::Parser::VERSION = '1.0';
use strict;
use Locale::TextDomain qw(Template-Plugin-Gettext);
use base qw(Template::Parser);
sub split_text {
my ($self, $text) = @_;
my $chunks = $self->SUPER::split_text($text) or return;
my $keywords = $self->{__xgettext}->keywords;
my $plug_in = $self->{__xgettext}->option('plug_in');
$plug_in = 'Gettext' if !defined $plug_in;
my $ident;
my $lplug_in = length $plug_in;
while (my $chunk = shift @$chunks) {
if (!ref $chunk) {
shift @$chunks;
next;
}
my ($text, $lineno, $tokens) = @$chunk;
next if !ref $tokens;
if ($lplug_in) {
if ('USE' eq $tokens->[0] && 'IDENT' eq $tokens->[2]) {
if ($plug_in eq $tokens->[3]
&& (4 == @$tokens
|| '(' eq $tokens->[4])) {
$ident = $plug_in;
} elsif ('ASSIGN' eq $tokens->[4] && 'IDENT' eq $tokens->[6]
&& $plug_in eq $tokens->[7]) {
$ident = $tokens->[3];
}
next;
}
next if !defined $ident;
} else {
$ident = '';
}
for (my $i = 0; $i < @$tokens; $i += 2) {
# FIXME! It would be better to copy $tokens into an array
# @tokens because we modify the array reference $tokens.
# That implies that we iterate over tokens that do ot exist
# and that is an unnecessary risk.
if ($lplug_in
&& 'IDENT' eq $tokens->[$i] && $ident eq $tokens->[$i + 1]
&& 'DOT' eq $tokens->[$i + 2] && 'IDENT' eq $tokens->[$i + 4]
&& exists $keywords->{$tokens->[$i + 5]}) {
my $keyword = $keywords->{$tokens->[$i + 5]};
$self->__extractEntry($text, $lineno, $keyword,
@$tokens[$i + 6 .. $#$tokens]);
} elsif ('FILTER' eq $tokens->[$i]
&& 'IDENT' eq $tokens->[$i + 2]
&& exists $keywords->{$tokens->[$i + 3]}) {
my $keyword = $keywords->{$tokens->[$i + 3]};
# Inject the block contents as the first argument.
if ($i) {
my $first_arg;
if ($tokens->[$i - 2] eq 'LITERAL') {
$first_arg = $tokens->[$i - 1];
} else {
next;
}
# May have been called without parentheses, see
# https://github.com/gflohr/Template-Plugin-Gettext/issues/4
if (!defined $tokens->[4 + $i]) {
$tokens->[4 + $i] = $tokens->[5 + $i] = '(';
$tokens->[6 + $i] = $tokens->[7 + $i] = ')';
splice @$tokens, 6 + $i, 0, LITERAL => $first_arg;
# Or without parentheses and another filter is immediately
# following or the value gets dereferenced with a dot.
# The latter is kind of nonsense but we support it
# elsewhere as well and it is hard to catch.
} elsif ('FILTER' eq $tokens->[4 + $i]
|| 'DOT' eq $tokens->[4 + $i]) {
splice @$tokens, 4 + $i, 0,
'(', '(', LITERAL => $first_arg, ')', ')';
} else {
splice @$tokens, 6 + $i, 0,
LITERAL => $first_arg, COMMA => ',';
}
} else {
next if !@$chunks;
my $first_arg;
if (ref $chunks->[0]) {
next if $chunks->[0]->[2] ne 'ITEXT';
$first_arg = $chunks->[0]->[0];
} elsif ('TEXT' eq $chunks->[0]) {
$first_arg = $chunks->[1];
} else {
next;
}
splice @$tokens, 6, 0,
'LITERAL', $first_arg, 'COMMA', ',';
}
$self->__extractEntry($text, $lineno, $keyword,
@$tokens[$i + 4 .. $#$tokens]);
} elsif (!$lplug_in && 'IDENT' eq $tokens->[$i]
&& exists $keywords->{$tokens->[$i + 1]}) {
my $keyword = $keywords->{$tokens->[$i + 1]};
$self->__extractEntry($text, $lineno, $keyword,
@$tokens[$i + 2 .. $#$tokens]);
}
}
}
# Stop processing here, so that for example includes are ignored.
return [];
}
sub __extractEntry {
my ($self, $text, $lineno, $keyword, @tokens) = @_;
my $args = sub {
my (@tokens) = @_;
return if '(' ne $tokens[0];
splice @tokens, 0, 2;
my @values;
while (@tokens) {
if ('LITERAL' eq $tokens[0]) {
my $string = substr $tokens[1], 1, -1;
$string =~ s/\\([\\'])/$1/gs;
push @values, $string;
splice @tokens, 0, 2;
} elsif ('"' eq $tokens[0]) {
if ('TEXT' eq $tokens[2]
&& '"' eq $tokens[4]
&& ('COMMA' eq $tokens[6]
|| ')' eq $tokens[6])) {
push @values, $tokens[3];
splice @tokens, 6;
} else {
# String containing interpolated variables.
my $msg = __"Illegal variable interpolation at \"\$\"!";
push @values, \$msg;
while (@tokens) {
last if 'COMMA' eq $tokens[0];
last if ')' eq $tokens[0];
shift @tokens;
}
}
} elsif ('NUMBER' eq $tokens[0]) {
push @values, $tokens[1];
splice @tokens, 0, 2;
} elsif ('IDENT' eq $tokens[0]) {
# We store undef as the value because we cannot use it
# anyway.
push @values, undef;
splice @tokens, 0, 2;
} elsif ('(' eq $tokens[0]) {
splice @tokens, 0, 2;
my $nested = 1;
while (@tokens) {
if ('(' eq $tokens[0]) {
++$nested;
splice @tokens, 0, 2;
} elsif (')' eq $tokens[0]) {
--$nested;
splice @tokens, 0, 2;
if (!$nested) {
push @values, undef;
last;
}
} else {
splice @tokens, 0, 2;
}
}
} else {
return @values;
}
return @values if !@tokens;
my $next = shift @tokens;
if ('COMMA' eq $next) {
shift @tokens;
next;
} elsif ('ASSIGN' eq $next && '=>' eq $tokens[0]) {
shift @tokens;
next;
}
return @values;
}
return @values;
};
my $min_args = $keyword->singular;
my %forms = (msgid => $keyword->singular);
if ($keyword->plural) {
$min_args = $keyword->plural if $keyword->plural > $min_args;
$forms{msgid_plural} = $keyword->plural;
}
if ($keyword->context) {
$min_args = $keyword->context if $keyword->context > $min_args;
$forms{msgctxt} = $keyword->context;
}
my @args = $args->(@tokens);
# Do we have enough arguments?
return if $min_args > @args;
my $entry = {
keyword => $keyword->{function}
};
foreach my $prop (keys %forms) {
my $argno = $forms{$prop} - 1;
# We are only interested in literal values. Whatever is
# undefined is not parsable or not valid.
return if !defined $args[$argno];
if (ref $args[$argno]) {
my $filename = $self->{__xgettext_filename};
die "$filename:$lineno: ${$args[$argno]}\n" if ref $args[$argno];
}
$entry->{$prop} = $args[$argno];
}
my $reference = $self->{__xgettext_filename} . ':' . $lineno;
$reference =~ s/-[1-9][0-9]*$//;
$entry->{reference} = $reference;
if ($text =~ /^#/) {
my $comment = '';
my @lines = split /\n/, $text;
foreach my $line (@lines) {
last if $line !~ s/^[ \t\r\f\013]*#[ \t\r\f\013]?//;
$comment .= $line . "\n";
}
$entry->{automatic} = $comment;
}
$self->{__xgettext}->addEntry($entry);
return $self;
}
1;
TT2.pod 100644 000765 000024 2160 14442777752 23372 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/lib/Locale/XGettext =head1 NAME
Locale::XGettext::TT2 - Extract translatable strings from template files for the Template Toolkit version 2.
=head1 SYNOPSIS
xgettext-tt2 --help
=head1 DESCRIPTION
B is the parser backend for
L. It is based on L.
See L
for the big picture.
See L for an overview of all methods.
See L (or try "perldoc xgettext-tt2" or
"man xgettext-tt2") for information on invocation.
See L to learn more about how
to mark strings in templates as translatable.
=head1 COPYRIGHT
Copyright (C) 2016-2018 Guido Flohr (http://www.guido-flohr.net/).
License LGPLv3+: L.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Copyright (C) 2016-2018 Guido Flohr ,
all rights reserved.
=head1 SEE ALSO
L, L, L,
L, L
interpolate-1.tt 100644 000765 000024 130 14442777752 24005 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% USE gtx = Gettext %]
Okay, no variables inside.
[% gtx.gettext("Hello, world!\n") %]
interpolate-2.tt 100644 000765 000024 156 14442777752 24016 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% USE gtx = Gettext %]
Okay, single-quoted string without interpolation.
[% gtx.gettext('Hello, $world!') %]
interpolate-3.tt 100644 000765 000024 142 14442777752 24012 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/t/templates [% USE gtx = Gettext %]
Not okay, variable is interpolated.
[% gtx.gettext("Hello, $world!\n") %]
Plugin 000755 000765 000024 0 14442777752 22154 5 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/lib/Template Gettext.pm 100644 000765 000024 34067 14442777752 24330 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/lib/Template/Plugin #! /bin/false
# Copyright (C) 2016-2018 Guido Flohr ,
# all rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
# ABSTRACT: Gettext Support For the Template Toolkit Version 2
package Template::Plugin::Gettext;
$Template::Plugin::Gettext::VERSION = '1.0';
use strict;
use Locale::TextDomain 1.30 qw(com.cantanea.Template-Plugin-Gettext);
use Locale::Messages;
use Locale::Util qw(web_set_locale);
use Encode;
use Cwd qw(abs_path);
use base qw(Template::Plugin);
my %bound_dirs;
my %textdomains;
our @DEFAULT_DIRS;
our @LOCALE_DIRS;
sub __find_domain($);
sub __expand($%);
sub __fixup;
BEGIN {
foreach my $dir (qw('/usr/share/locale /usr/local/share/locale')) {
if (-d $dir) {
push @DEFAULT_DIRS, $dir;
last;
}
}
}
sub new {
my ($class, $ctx, $textdomain, $language, $charset, @search_dirs) = @_;
my $self = bless {}, $class;
$textdomain = 'textdomain' unless defined $textdomain && length $textdomain;
$charset = 'utf-8' unless defined $charset && length $charset;
my $template = $ctx->stash->get('component')->name;
if ('input text' eq $template || 'input file handle' eq $template) {
my $maybe_template = $ctx->stash->get('gettext_filename');
$template = $maybe_template
if defined $maybe_template && length $maybe_template;
}
$textdomains{$textdomain}->{$template} = 1;
unless (exists $bound_dirs{$textdomain}) {
unless (@search_dirs) {
@search_dirs = map $_ . '/LocaleData', @INC;
push @search_dirs, @DEFAULT_DIRS;
}
unshift @search_dirs, @LOCALE_DIRS;
$bound_dirs{$textdomain} = [@search_dirs];
}
$self->{__textdomain} = $textdomain;
$self->{__locale} = web_set_locale $language, $charset if defined $language;
$ctx->define_filter(gettext => sub {
my ($context) = @_;
return sub {
return __gettext($textdomain, shift);
};
}, 1);
$ctx->define_filter(ngettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __ngettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(pgettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __pgettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(gettextp => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __gettextp($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(npgettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __npgettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(ngettextp => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __ngettextp($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(xgettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __xgettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(nxgettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __nxgettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(pxgettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __pxgettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(xgettextp => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __xgettextp($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(npxgettext => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __npxgettext($textdomain, shift, @args);
};
}, 1);
$ctx->define_filter(nxgettextp => sub {
my ($context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return sub {
return __nxgettextp($textdomain, shift, @args);
};
}, 1);
return $self;
}
sub __fixup {
my $trans = $_[-1];
Encode::_utf8_on($trans);
return $trans;
}
sub __gettext {
my ($textdomain, $msgid) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __fixup $msgid, Locale::Messages::dgettext($textdomain => $msgid);
}
sub gettext {
my ($self, $msgid) = @_;
return __gettext $self->{__textdomain}, $msgid;
}
sub __ngettext {
my ($textdomain, $msgid, $msgid_plural, $count) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __fixup $msgid, $msgid_plural, $count,
Locale::Messages::dngettext($textdomain => $msgid,
$msgid_plural, $count);
}
sub ngettext {
my ($self, $msgid, $msgid_plural, $count) = @_;
return __ngettext $self->{__textdomain}, $msgid, $msgid_plural, $count;
}
sub __pgettext {
my ($textdomain, $context, $msgid) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __fixup $msgid,
Locale::Messages::dpgettext($textdomain => $context, $msgid);
}
sub pgettext {
my ($self, $context, $msgid) = @_;
return __pgettext $self->{__textdomain}, $context, $msgid;
}
sub __gettextp {
my ($textdomain, $msgid, $context) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __fixup $msgid,
Locale::Messages::dpgettext($textdomain => $context, $msgid);
}
sub gettextp {
my ($self, $msgid, $context) = @_;
return __gettextp $self->{__textdomain}, $msgid, $context;
}
sub __npgettext {
my ($textdomain, $context, $msgid, $msgid_plural, $count) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __fixup $msgid, $msgid_plural, $count,
Locale::Messages::dnpgettext($textdomain, $context, $msgid,
$msgid_plural, $count);
}
sub npgettext {
my ($self, $context, $msgid, $msgid_plural, $count) = @_;
return __npgettext $self->{__textdomain}, $context, $msgid, $msgid_plural,
$count;
}
sub __ngettextp {
my ($textdomain, $msgid, $msgid_plural, $count, $context) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __fixup $msgid, $msgid_plural, $count,
Locale::Messages::dnpgettext($textdomain => $context, $msgid,
$msgid_plural, $count);
}
sub ngettextp {
my ($self, $msgid, $msgid_plural, $count, $context) = @_;
return __ngettextp $self->{__textdomain}, $msgid, $msgid_plural, $count,
$context;
}
sub __xgettext {
my ($textdomain, $msgid, %vars) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __expand((__fixup $msgid,
Locale::Messages::dgettext($textdomain => $msgid)), %vars);
}
sub xgettext {
my ($self, $msgid, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return __xgettext $self->{__textdomain}, $msgid, @args;
}
sub __nxgettext {
my ($textdomain, $msgid, $msgid_plural, $count, %vars) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __expand((__fixup $msgid, $msgid_plural, $count,
Locale::Messages::dngettext($textdomain => $msgid,
$msgid_plural,
$count)),
%vars);
}
sub nxgettext {
my ($self, $msgid, $msgid_plural, $count, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return __nxgettext $self->{__textdomain}, $msgid, $msgid_plural, $count,
@args;
}
sub __pxgettext {
my ($textdomain, $context, $msgid, %vars) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __expand((__fixup $msgid,
Locale::Messages::dpgettext($textdomain => $context,
$msgid)),
%vars);
}
sub pxgettext {
my ($self, $context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return __pxgettext $self->{__textdomain}, $context, @args;
}
sub __xgettextp {
my ($textdomain, $msgid, $context, %vars) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __expand((__fixup $msgid,
Locale::Messages::dpgettext($textdomain => $context,
$msgid)),
%vars);
}
sub xgettextp {
my ($self, $msgid, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return __xgettextp $self->{__textdomain}, $msgid, @args;
}
sub __npxgettext {
my ($textdomain, $context, $msgid, $msgid_plural, $count, %vars) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __expand((__fixup $msgid, $msgid_plural, $count,
Locale::Messages::dnpgettext($textdomain => $context,
$msgid, $msgid_plural,
$count)),
%vars);
}
sub npxgettext {
my ($self, $context, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return __npxgettext $self->{__textdomain}, $context, @args;
}
sub __nxgettextp {
my ($textdomain, $msgid, $msgid_plural, $count, $context, %vars) = @_;
local %ENV = %ENV;
delete $ENV{LANGUAGE};
__find_domain $textdomain
if defined $textdomain && exists $bound_dirs{$textdomain};
return __expand((__fixup $msgid, $msgid_plural, $count,
Locale::Messages::dnpgettext($textdomain => $context,
$msgid, $msgid_plural,
$count)),
%vars);
}
sub nxgettextp {
my ($self, $msgid, @args) = @_;
my $pairs = ref $args[-1] eq 'HASH' ? pop(@args) : {};
push @args, %$pairs;
return __nxgettextp $self->{__textdomain}, $msgid, @args;
}
sub debug_locale {
shift->{__locale};
}
sub __expand($%) {
my ($str, %vars) = @_;
my $re = join '|', map { quotemeta } keys %vars;
$str =~ s/\{($re)\}/exists $vars{$1} ?
(defined $vars{$1} ? $vars{$1} : '') : "{$1}"/ge;
return $str;
}
sub __find_domain($) {
my ($domain) = @_;
my $try_dirs = $bound_dirs{$domain};
if (defined $try_dirs) {
my $found_dir = '';
TRYDIR: foreach my $dir (map {abs_path $_} grep { -d $_ } @$try_dirs) {
# Is there a message catalog?
local *DIR;
if (opendir DIR, $dir) {
my @files = map { "$dir/$_/LC_MESSAGES/$domain.mo" }
grep { ! /^\.\.?$/ } readdir DIR;
foreach my $file (@files) {
if (-f $file || -l $file) {
$found_dir = $dir;
last TRYDIR;
}
}
}
}
# If $found_dir is undef, the default search directories are
# used.
Locale::Messages::bindtextdomain($domain => $found_dir);
}
delete $bound_dirs{$domain};
return 1;
}
sub textdomains {
return %textdomains;
}
sub resetTextdomains {
undef %textdomains;
}
1;
Gettext.pod 100644 000765 000024 30326 14442777752 24470 0 ustar 00guidoflohr staff 000000 000000 Template-Plugin-Gettext-1.0/lib/Template/Plugin =for Pod::Coverage ngettextp new
=head1 NAME
Template::Plugin::Gettext - Gettext Support For the Template Toolkit Version 2
=head1 SYNOPSIS
Load the plug-in in templates:
[% USE Gettext('com.textdomain.my', 'fr', 'utf-8', 'DIRECTORIES'...) %]
[% Gettext.gettext('Hello, world!') %]
[% 'Hello, world!' | gettext %]
Or alias "Gettext":
[% USE gtx = Gettext('com.textdomain.my', 'fr', 'utf-8', 'DIRECTORIES'...) %]
[% gtx.gettext('Hello, world!') %]
Use method invocations:
[% Gettext.gettext("Hello, world!") %]
[% Gettext.xgettext("Hello, {name}!", name => 'John Doe') %]
Or filters (without the prefix):
[% FILTER gettext %]
Hello, world!
[% END %]
[% 'Hello, world!' | gettext %]
[% FILTER xgettext(name => 'John Doe') %]
Hello, {name}!
[% END %]
You have a multitude of methods available:
[% gtx.gettext("Hello, user!") %]
[% gtx.xgettext("Hello, {user}!", user => 'John Doe') %]
[% gtx.ngettext("One document deleted.",
"Multiple documents deleted."),
42) %]
[% gtx.nxgettext("One document deleted.",
"{num} documents deleted."),
42,
num => 42) %]
[% gtx.npgettext("context..."
"One document deleted.",
"Multiple documents deleted."),
42) %]
[% gtx.npxgettext("context...",
"One document deleted.",
"{num} documents deleted."),
42,
num => 42) %]
=head1 DESCRIPTION
The B plug-in makes the
L available for
documents using the
L. See
L for an overall
picture and the recommended tool-chain.
=head1 FUNCTIONS
The following methods produce translatable content:
=over 4
=item B<[% gtx.gettext(STRING) %]>
Retrieves the translation for B.
=item B<[% gtx.xgettext(STRING, PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2, ...) %]>
Gets the translation for a string with placeholders and interpolates values
into it. Placeholders have the format <{PLACEHOLDER}>. For a literal
left curly brace you can use this hack:
[% gtx.xgettext("String with {LBRACE}PLACEHOLDERS{RBRACE}",
LBRACE => "{", RBRACE => "}") %]
=item B<[% gtx.pgettext(CONTEXT, STRING) %]>
Retrieves the translation for B in context B. You
should use message context to disambiguate identical strings that
require different translations depending on the context. See this
example for an explanation:
[% gtx.gettext("State: ") %]
[% gtx.gettext("Open")] | [% gtx.gettext("Close") %]
[% gtx.gettext("Menu:") %]
[% gtx.pgettext("menu", "Open")]
[% gtx.gettext("Save")]
[% gtx.gettext("Save As")]
[% gtx.pgettext("menu", "Close")]
The strings "Open" and "Close" in line 2 are adjectives. As
menu entries they are verb forms and will have a different
translation in many languages.
In doubt: Only use contexts if one of your translators complains
about a message having multiple meanings.
=item B<[% gtx.pxgettext(CONTEXT, STRING,
PLACEHOLDER1 =E VALUE1,
PLACEHOLDER2 =E VALUE2) %]>
Get the translation for B with placeholders in context
B. This is a mixture of C and C
above.
=item B<[% gtx.nxgettext(SINGULAR, PLURAL, COUNT,
PLACEHOLDER1 =E VALUE1,
PLACEHOLDER2 =E VALUE2) %]>
Retrieves the translation for the string with the singular form
B and the plural from B, both possibly
containing placeholders. The correct form is picked based
upon the third argument B.
Example:
[% gtx.nxgettext("One document deleted",
"{num} documents deleted"),
count,
num => count) %]
In English this will produce "42 documents deleted" if the variable
count has the value 42. It will produce "One document deleted" if
the variable count has the value 1.
In other languages, the rules for plural forms may be a lot
simpler (for example Chinese, which has no plural) or a lot more
complicated (for example Russian with two or Slovenian with even
3 plural forms). Using ngettext() gives your translators the
chance to provide syntactically correct translations for these
cases.
=item B<[% gtx.npxgettext(CONTEXT, SINGULAR, PLURAL, COUNT,
PLACEHOLDER1 =E VALUE1,
PLACEHOLDER2 =E VALUE2) %]>
Putting it all together: For message context B
the translation for a message in B and B
is retrieved based on the argument B. Possible placeholders
are expanded.
The function is a mixture of xgettext(), ngettext(), and
pgettext(), see above!
=item B<[% gtx.ngettext(SINGULAR, PLURAL, COUNT) %]>
Useless function, provided for completeness. Use nxgettext()
instead, so that you can interpolate the value of B!
=item B<[% gtx.npgettext(CONTEXT, SINGULAR, PLURAL, COUNT) %]>
Useless function, provided for completeness. Use npxgettext()
instead, so that you can interpolate the value of B!
=back
In fact, you have also all the keywords used for L
available but those not listed here have such an odd ordering
of arguments that they are not listed here.
=head2 FILTERS
The entire gettext API is also exposed as a filter. There are
two things to note here:
=over 4
=item *
When used as filters, you don't prefix the method names. It'
s "gettext" not "Gettext.gettext" or "gtx.gettext()".
=item *
The filters with message contexts have rather strange names, for example:
[% FILTER gettextp("greeting") %]
Hello, world!
[% END %]
or 100 % equivalent:
[% 'Hello, world!' | gettextp("greeting") %]
Why? The text between B and B resp. the text
in front of the pipe symbol B<|> is always the first argument.
This plug-in therefore tries to make the first argument the
most significant one. Nobody stops you from writing the
following:
[% FILTER pgettext("Hello, world!") %]
greeting
[% END %]
or again 100 % equivalent:
[% 'greeting' | pgettext("Hello, world!") %]
It produces exactly the same results as above but it looks
a little bit odd, doesn't it?
It would have been arguably better understandable to silently
reorder the arguments, when using the plug-in as a filter.
But it would break extraction of your strings with
L (L) because the string
extractor would then confuse the arguments.
But stay relaxed! Message contexts are rarely needed, and when
you need them, you have to live with this little weirdness.
In order to avoid confusion, those filters that would not have
the translatable string (in the singular form) where one would
expect it, are not documented here.
=back
You can use the following filters:
=over 4
=item B<[% STRING | gettext %]>
=item B<[% FILTER gettext %]STRING[% FILTER %]>
Retrieves the translation for B.
=item B<[% STRING | xgettext(PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2, ...) %]>
=item B<[% FILTER xgettext(PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2, ...) %]STRING[% END %]>
Gets the translation for a string with placeholders and interpolates values
into it. Placeholders have the format <{PLACEHOLDER}>. For a literal
left curly brace you can use this hack:
[% "String with {LBRACE}PLACEHOLDERS{RBRACE}" | xgettext(LBRACE => "{", RBRACE => "}") %]
=item B<[% STRING | gettextp(CONTEXT) %]>
=item B<[% FILTER gettextp(CONTEXT) %]STRING[% END %]>
Retrieves the translation for B in context B. You
should use message context to disambiguate identical strings that
require different translations depending on the context. See the
docuemntation for pgettext() in L above for more details!
=item B<[% STRING | xgettextp(CONTEXT, STRING, PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2) %]>
=item B<[% FILTER xgettextp(CONTEXT, STRING, PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2) %]STRING[% END %]>
Get the translation for B with placeholders in context
B. This is a mixture of C and C
above.
=item B<[% SINGULAR | nxgettext(PLURAL, COUNT, PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2) %]>
=item B<[% FILTER nxgettext(PLURAL, COUNT, PLACEHOLDER1 =E VALUE1, PLACEHOLDER2 =E VALUE2) %]SINGULAR[% END %]>
Retrieves the translation for the string with the singular form
B and the plural from B, both possibly
containing placeholders. The correct form is picked based
upon the third argument B.
Example:
[% "One document deleted" | nxgettext("{num} documents deleted"),
count,
num => count) %]
In English this will produce "42 documents deleted" if the variable
count has the value 42. It will produce "One document deleted" if
the variable count has the value 1.
In other languages, the rules for plural forms may be a lot
simpler (for example Chinese, which has no plural) or a lot more
complicated (for example Russian with two or Slovenian with even
3 plural forms). Using ngettext() gives your translators the
chance to provide syntactically correct translations for these
cases.
=item B<[% SINGULAR | nxgettextp(PLURAL, COUNT, CONTEXT,
PLACEHOLDER1 =E VALUE1,
PLACEHOLDER2 =E VALUE2) %]>
=item B<[% FILTER nxgettextp(PLURAL, COUNT, CONTEXT,
PLACEHOLDER1 =E VALUE1,
PLACEHOLDER2 =E VALUE2) %]SINGULAR[% END %]>
Putting it all together: For message context B
the translation for a message in B and B
is retrieved based on the argument B. Possible placeholders
are expanded.
The filter is a mixture of xgettext(), ngettext(), and
pgettext(), see above!
=item B<[% SINGULAR | ngettext(PLURAL, COUNT) %]>
=item B<[% FILTER ngettext(PLURAL, COUNT) %]SINGULAR[% END %]>
Useless filter, provided for completeness. Use nxgettext()
instead, so that you can interpolate the value of B!
=item B<[% SINGULAR | ngettextp[(PLURAL, COUNT, CONTEXT) %]>
=item B<[% FILTER | ngettextp[(PLURAL, COUNT, CONTEXT) %]SINGULAR[% END %]>
Useless filter, provided for completeness. Use nxgettextp()
instead, so that you can interpolate the value of B!
=item B<[% debug_locale %]>
The plug-in implicitly calls B (see
L) if a language was specified in the B