Bugzilla – Attachment 77475 Details for
Bug 15395
Internationalization: plural forms, context, and more
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 15395: Allow correct handling of plural translation
Bug-15395-Allow-correct-handling-of-plural-transla.patch (text/plain), 29.96 KB, created by
Julian Maurice
on 2018-08-03 15:24:26 UTC
(
hide
)
Description:
Bug 15395: Allow correct handling of plural translation
Filename:
MIME Type:
Creator:
Julian Maurice
Created:
2018-08-03 15:24:26 UTC
Size:
29.96 KB
patch
obsolete
>From 397660b5ae90f73e674350b446e0af1ba3999547 Mon Sep 17 00:00:00 2001 >From: Julian Maurice <julian.maurice@biblibre.com> >Date: Tue, 15 Dec 2015 02:31:03 +0100 >Subject: [PATCH] Bug 15395: Allow correct handling of plural translation >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >Locale::Maketext does not allow correct handling of plural translation >for languages that have more than one plural forms. >Locale::Messages does. > >So Koha::I18N is now a wrapper around Locale::Messages, just like >Locale::TextDomain, and export the same symbols as Locale::TextDomain. >You can refer to documentation of Locale::TextDomain to know how to use >exported subroutines. > >Example usage: > __("Hi") > __x("Hi {name}", name => 'Bob') > __n("item", "items", $num_items) > __nx("one item", "{count} items", $num_items, count => $num_items) > __p("Bibliographic record", "item") > >This patch also brings Koha::I18N power to Template::Toolkit templates >by adding a TT plugin. >This plugin can be used like this: > >[%# USE the plugin and define some macros %] >[% PROCESS 'i18n.inc' %] > >[%# tn is the equivalent of __n %] >[%# macro names can't start with underscore, t is for "translate" %] >[% tn('item', 'items', num_items) %] >Extraction of strings from templates is a bit complicated and use >Template::Parser and PPI. Template is compiled into Perl code and then >analyzed by PPI. It is slow, but should be correct even with complex >constructions. > >Remove dependency to Locale::Maketext and Locale::Maketext::Lexicon >Add dependency to Locale::Messages and PPI > >Test plan for translation in Perl code: >1. Open a .pl script or .pm module with your favorite text editor >2. Add 'use Koha::I18N;' in the beginning of file >3. Use one of the subroutines exported by Koha::I18N and be sure to have > a way to visualize the result (pass result to the template for > example, or simply warn and watch the log file) >4. cd misc/translator && ./translate update fr-FR # try other languages >5. Open misc/translator/po/fr-FR-messages.po and translate your > string(s) > You may need to change the "Plural-Forms" header. See > https://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html >6. ./translate install fr-FR >7. Use your web browser to go to the page that should display the > translation, change language and verify the translation is correct >8. prove t/Koha/I18N.t > >Test plan for translation in templates: >1. Open a template file (.tt or .inc) with your favorite text editor >2. Add the PROCESS directive mentioned above in the beginning of file >3. Use one of the t* macros defined in i18n.inc. They are used like > their "__" equivalent, with one difference: the 'x' variants take a > hashref instead of a hash as last parameter >4. cd misc/translator && ./translate update fr-FR >5. Open misc/translator/po/fr-FR-messages.po and translate your > string(s) >6. ./translate install fr-FR >7. Use your web browser to go to the page that should display the > translation, change language and verify the translation is > correct > >Signed-off-by: Marc Véron <veron@veron.ch> >--- > C4/Installer/PerlDependencies.pm | 12 +- > Koha/I18N.pm | 191 +++++++++++++++-- > Koha/Template/Plugin/I18N.pm | 72 +++++++ > .../intranet-tmpl/prog/en/includes/i18n.inc | 39 ++++ > .../opac-tmpl/bootstrap/en/includes/i18n.inc | 39 ++++ > misc/translator/LangInstaller.pm | 201 +++++++++++++++--- > t/Koha/I18N.t | 58 +++++ > t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo | Bin 0 -> 1370 bytes > t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po | 81 +++++++ > 9 files changed, 634 insertions(+), 59 deletions(-) > create mode 100644 Koha/Template/Plugin/I18N.pm > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc > create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/includes/i18n.inc > create mode 100755 t/Koha/I18N.t > create mode 100644 t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo > create mode 100644 t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po > >diff --git a/C4/Installer/PerlDependencies.pm b/C4/Installer/PerlDependencies.pm >index 73721a640d..f108c79ea5 100644 >--- a/C4/Installer/PerlDependencies.pm >+++ b/C4/Installer/PerlDependencies.pm >@@ -722,15 +722,15 @@ our $PERL_DEPS = { > required => 1, > min_ver => '2.125', > }, >- 'Locale::Maketext' => { >+ 'Locale::Messages' => { > 'usage' => 'Core', > 'required' => '1', >- 'min_ver' => '1.19', >+ 'min_ver' => '1.20', > }, >- 'Locale::Maketext::Lexicon' => { >- 'usage' => 'Core', >- 'required' => '1', >- 'min_ver' => '0.91', >+ 'PPI' => { >+ 'usage' => 'I18N', >+ 'required' => '0', >+ 'min_ver' => '1.215', > }, > 'LWP::Protocol::https' => { > 'usage' => 'OverDrive integration', >diff --git a/Koha/I18N.pm b/Koha/I18N.pm >index 33730f99b8..44c47132ff 100644 >--- a/Koha/I18N.pm >+++ b/Koha/I18N.pm >@@ -18,41 +18,186 @@ package Koha::I18N; > # along with Koha; if not, see <http://www.gnu.org/licenses>. > > use Modern::Perl; >-use base qw(Locale::Maketext Exporter); > > use CGI; > use C4::Languages; >+use C4::Context; > >-use Locale::Maketext::Lexicon { >- 'en' => ['Auto'], >- '*' => [ >- Gettext => >- C4::Context->config('intranetdir') >- . '/misc/translator/po/*-messages.po' >- ], >- '_AUTO' => 1, >- '_style' => 'gettext', >-}; >+use Encode; >+use Locale::Messages qw(:locale_h nl_putenv setlocale LC_MESSAGES); >+use Koha::Cache::Memory::Lite; > >-our @EXPORT = qw( gettext ); >+use parent 'Exporter'; >+our @EXPORT = qw( >+ __ >+ __x >+ __n >+ __nx >+ __xn >+ __p >+ __px >+ __np >+ __npx >+ N__ >+ N__n >+ N__p >+ N__np >+); > >-my %language_handles; >+our $textdomain = 'Koha'; > >-sub get_language_handle { >- my $cgi = new CGI; >- my $language = C4::Languages::getlanguage; >+sub init { >+ my $cache = Koha::Cache::Memory::Lite->get_instance(); >+ my $cache_key = 'i18n:initialized'; >+ unless ($cache->get_from_cache($cache_key)) { >+ my @system_locales = grep { chomp; not (/^C/ || $_ eq 'POSIX') } qx/locale -a/; >+ if (@system_locales) { >+ # LANG needs to be set to a valid locale, >+ # otherwise LANGUAGE is ignored >+ nl_putenv('LANG=' . $system_locales[0]); >+ setlocale(LC_MESSAGES, ''); > >- if (not exists $language_handles{$language}) { >- $language_handles{$language} = __PACKAGE__->get_handle($language) >- or die "No language handle for '$language'"; >+ my $langtag = C4::Languages::getlanguage; >+ my @subtags = split /-/, $langtag; >+ my ($language, $region) = @subtags; >+ if ($region && length $region == 4) { >+ $region = $subtags[2]; >+ } >+ my $locale = $language; >+ if ($region) { >+ $locale .= '_' . $region; >+ } >+ >+ nl_putenv("LANGUAGE=$locale"); >+ nl_putenv('OUTPUT_CHARSET=UTF-8'); >+ >+ my $directory = _base_directory(); >+ textdomain($textdomain); >+ bindtextdomain($textdomain, $directory); >+ } else { >+ warn "No locale installed. Localization cannot work and is therefore disabled"; >+ } >+ >+ $cache->set_in_cache($cache_key, 1); >+ } >+} >+ >+sub __ { >+ my ($msgid) = @_; >+ >+ $msgid = Encode::encode_utf8($msgid); >+ >+ return _gettext(\&gettext, [ $msgid ]); >+} >+ >+sub __x { >+ my ($msgid, %vars) = @_; >+ >+ $msgid = Encode::encode_utf8($msgid); >+ >+ return _gettext(\&gettext, [ $msgid ], %vars); >+} >+ >+sub __n { >+ my ($msgid, $msgid_plural, $count) = @_; >+ >+ $msgid = Encode::encode_utf8($msgid); >+ $msgid_plural = Encode::encode_utf8($msgid_plural); >+ >+ return _gettext(\&ngettext, [ $msgid, $msgid_plural, $count ]); >+} >+ >+sub __nx { >+ my ($msgid, $msgid_plural, $count, %vars) = @_; >+ >+ $msgid = Encode::encode_utf8($msgid); >+ $msgid_plural = Encode::encode_utf8($msgid_plural); >+ >+ return _gettext(\&ngettext, [ $msgid, $msgid_plural, $count ], %vars); >+} >+ >+sub __xn { >+ return __nx(@_); >+} >+ >+sub __p { >+ my ($msgctxt, $msgid) = @_; >+ >+ $msgctxt = Encode::encode_utf8($msgctxt); >+ $msgid = Encode::encode_utf8($msgid); >+ >+ return _gettext(\&pgettext, [ $msgctxt, $msgid ]); >+} >+ >+sub __px { >+ my ($msgctxt, $msgid, %vars) = @_; >+ >+ $msgctxt = Encode::encode_utf8($msgctxt); >+ $msgid = Encode::encode_utf8($msgid); >+ >+ return _gettext(\&pgettext, [ $msgctxt, $msgid ], %vars); >+} >+ >+sub __np { >+ my ($msgctxt, $msgid, $msgid_plural, $count) = @_; >+ >+ $msgctxt = Encode::encode_utf8($msgctxt); >+ $msgid = Encode::encode_utf8($msgid); >+ $msgid_plural = Encode::encode_utf8($msgid_plural); >+ >+ return _gettext(\&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ]); >+} >+ >+sub __npx { >+ my ($msgctxt, $msgid, $msgid_plural, $count, %vars) = @_; >+ >+ $msgctxt = Encode::encode_utf8($msgctxt); >+ $msgid = Encode::encode_utf8($msgid); >+ $msgid_plural = Encode::encode_utf8($msgid_plural); >+ >+ return _gettext(\&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count], %vars); >+} >+ >+sub N__ { >+ return @_; >+} >+ >+sub N__n { >+ return @_; >+} >+ >+sub N__p { >+ return @_; >+} >+ >+sub N__np { >+ return @_; >+} >+ >+sub _base_directory { >+ return C4::Context->config('intranetdir') . '/misc/translator/po'; >+} >+ >+sub _gettext { >+ my ($sub, $args, %vars) = @_; >+ >+ init(); >+ >+ my $text = Encode::decode_utf8($sub->(@$args)); >+ if (%vars) { >+ $text = _expand($text, %vars); > } > >- return $language_handles{$language}; >+ return $text; > } > >-sub gettext { >- my $lh = get_language_handle; >- $lh->maketext(@_); >+sub _expand { >+ my ($text, %vars) = @_; >+ >+ my $re = join '|', map { quotemeta $_ } keys %vars; >+ $text =~ s/\{($re)\}/defined $vars{$1} ? $vars{$1} : "{$1}"/ge; >+ >+ return $text; > } > > 1; >diff --git a/Koha/Template/Plugin/I18N.pm b/Koha/Template/Plugin/I18N.pm >new file mode 100644 >index 0000000000..8c95ea6d72 >--- /dev/null >+++ b/Koha/Template/Plugin/I18N.pm >@@ -0,0 +1,72 @@ >+package Koha::Template::Plugin::I18N; >+ >+# Copyright BibLibre 2015 >+# >+# This file is part of Koha. >+# >+# Koha 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. >+# >+# Koha 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 Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw( Template::Plugin ); >+ >+use C4::Context; >+use Koha::I18N; >+ >+sub t { >+ my ($self, $msgid) = @_; >+ return __($msgid); >+} >+ >+sub tx { >+ my ($self, $msgid, $vars) = @_; >+ return __x($msgid, %$vars); >+} >+ >+sub tn { >+ my ($self, $msgid, $msgid_plural, $count) = @_; >+ return __n($msgid, $msgid_plural, $count); >+} >+ >+sub tnx { >+ my ($self, $msgid, $msgid_plural, $count, $vars) = @_; >+ return __nx($msgid, $msgid_plural, $count, %$vars); >+} >+ >+sub txn { >+ my ($self, $msgid, $msgid_plural, $count, $vars) = @_; >+ return __xn($msgid, $msgid_plural, $count, %$vars); >+} >+ >+sub tp { >+ my ($self, $msgctxt, $msgid) = @_; >+ return __p($msgctxt, $msgid); >+} >+ >+sub tpx { >+ my ($self, $msgctxt, $msgid, $vars) = @_; >+ return __px($msgctxt, $msgid, %$vars); >+} >+ >+sub tnp { >+ my ($self, $msgctxt, $msgid, $msgid_plural, $count) = @_; >+ return __np($msgctxt, $msgid, $msgid_plural, $count); >+} >+ >+sub tnpx { >+ my ($self, $msgctxt, $msgid, $msgid_plural, $count, $vars) = @_; >+ return __np($msgctxt, $msgid, $msgid_plural, $count, %$vars); >+} >+ >+1; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc >new file mode 100644 >index 0000000000..4b5da9231b >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc >@@ -0,0 +1,39 @@ >+[% >+ USE I18N; >+ >+ MACRO t(msgid) BLOCK; >+ I18N.t(msgid); >+ END; >+ >+ MACRO tx(msgid, vars) BLOCK; >+ I18N.tx(msgid, vars); >+ END; >+ >+ MACRO tn(msgid, msgid_plural, count) BLOCK; >+ I18N.tn(msgid, msgid_plural, count); >+ END; >+ >+ MACRO tnx(msgid, msgid_plural, count, vars) BLOCK; >+ I18N.tnx(msgid, msgid_plural, count, vars); >+ END; >+ >+ MACRO txn(msgid, msgid_plural, count, vars) BLOCK; >+ I18N.txn(msgid, msgid_plural, count, vars); >+ END; >+ >+ MACRO tp(msgctxt, msgid) BLOCK; >+ I18N.tp(msgctxt, msgid); >+ END; >+ >+ MACRO tpx(msgctxt, msgid, vars) BLOCK; >+ I18N.tpx(msgctxt, msgid, vars); >+ END; >+ >+ MACRO tnp(msgctxt, msgid, msgid_plural, count) BLOCK; >+ I18N.tnp(msgctxt, msgid, msgid_plural, count); >+ END; >+ >+ MACRO tnpx(msgctxt, msgid, msgid_plural, count, vars) BLOCK; >+ I18N.tnpx(msgctxt, msgid, msgid_plural, count, vars); >+ END; >+%] >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/i18n.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/i18n.inc >new file mode 100644 >index 0000000000..4b5da9231b >--- /dev/null >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/i18n.inc >@@ -0,0 +1,39 @@ >+[% >+ USE I18N; >+ >+ MACRO t(msgid) BLOCK; >+ I18N.t(msgid); >+ END; >+ >+ MACRO tx(msgid, vars) BLOCK; >+ I18N.tx(msgid, vars); >+ END; >+ >+ MACRO tn(msgid, msgid_plural, count) BLOCK; >+ I18N.tn(msgid, msgid_plural, count); >+ END; >+ >+ MACRO tnx(msgid, msgid_plural, count, vars) BLOCK; >+ I18N.tnx(msgid, msgid_plural, count, vars); >+ END; >+ >+ MACRO txn(msgid, msgid_plural, count, vars) BLOCK; >+ I18N.txn(msgid, msgid_plural, count, vars); >+ END; >+ >+ MACRO tp(msgctxt, msgid) BLOCK; >+ I18N.tp(msgctxt, msgid); >+ END; >+ >+ MACRO tpx(msgctxt, msgid, vars) BLOCK; >+ I18N.tpx(msgctxt, msgid, vars); >+ END; >+ >+ MACRO tnp(msgctxt, msgid, msgid_plural, count) BLOCK; >+ I18N.tnp(msgctxt, msgid, msgid_plural, count); >+ END; >+ >+ MACRO tnpx(msgctxt, msgid, msgid_plural, count, vars) BLOCK; >+ I18N.tnpx(msgctxt, msgid, msgid_plural, count, vars); >+ END; >+%] >diff --git a/misc/translator/LangInstaller.pm b/misc/translator/LangInstaller.pm >index cd947fc3b3..78c006267a 100644 >--- a/misc/translator/LangInstaller.pm >+++ b/misc/translator/LangInstaller.pm >@@ -25,6 +25,13 @@ use C4::Context; > use YAML::Syck qw( Dump LoadFile ); > use Locale::PO; > use FindBin qw( $Bin ); >+use File::Basename; >+use File::Find; >+use File::Path qw( make_path ); >+use File::Slurp; >+use File::Temp qw( tempdir ); >+use Template::Parser; >+use PPI; > > $YAML::Syck::ImplicitTyping = 1; > >@@ -66,13 +73,17 @@ sub new { > $self->{process} = "$Bin/tmpl_process3.pl " . ($verbose ? '' : '-q'); > $self->{path_po} = "$Bin/po"; > $self->{po} = { '' => $default_pref_po_header }; >- $self->{domain} = 'messages'; >+ $self->{domain} = 'Koha'; > $self->{cp} = `which cp`; > $self->{msgmerge} = `which msgmerge`; >+ $self->{msgfmt} = `which msgfmt`; >+ $self->{msginit} = `which msginit`; > $self->{xgettext} = `which xgettext`; > $self->{sed} = `which sed`; > chomp $self->{cp}; > chomp $self->{msgmerge}; >+ chomp $self->{msgfmt}; >+ chomp $self->{msginit}; > chomp $self->{xgettext}; > chomp $self->{sed}; > >@@ -468,30 +479,134 @@ sub create_tmpl { > } > } > >+sub locale_name { >+ my $self = shift; >+ >+ my ($language, $region, $country) = split /-/, $self->{lang}; >+ $country //= $region; >+ my $locale = $language; >+ if ($country && length($country) == 2) { >+ $locale .= '_' . $country; >+ } >+ >+ return $locale; >+} >+ > sub create_messages { > my $self = shift; > >- print "Create messages ($self->{lang})\n" if $self->{verbose}; >- system >- "$self->{cp} $self->{domain}.pot " . >- "$self->{path_po}/$self->{lang}-$self->{domain}.po"; >+ my $pot = "$self->{domain}.pot"; >+ my $po = "$self->{path_po}/$self->{lang}-messages.po"; >+ >+ unless ( -f $pot ) { >+ $self->extract_messages(); >+ } >+ >+ say "Create messages ($self->{lang})" if $self->{verbose}; >+ my $locale = $self->locale_name(); >+ system "$self->{msginit} -i $pot -o $po -l $locale --no-translator"; >+ >+ # If msginit failed to correctly set Plural-Forms, set a default one >+ system "$self->{sed} --in-place $po " >+ . "--expression='s/Plural-Forms: nplurals=INTEGER; plural=EXPRESSION/Plural-Forms: nplurals=2; plural=(n != 1)/'"; > } > > sub update_messages { > my $self = shift; > >- my $pofile = "$self->{path_po}/$self->{lang}-$self->{domain}.po"; >- print "Update messages ($self->{lang})\n" if $self->{verbose}; >- if ( not -f $pofile ) { >- print "File $pofile does not exist\n" if $self->{verbose}; >+ my $pot = "$self->{domain}.pot"; >+ my $po = "$self->{path_po}/$self->{lang}-messages.po"; >+ >+ unless ( -f $pot ) { >+ $self->extract_messages(); >+ } >+ >+ if ( -f $po ) { >+ say "Update messages ($self->{lang})" if $self->{verbose}; >+ system "$self->{msgmerge} -U $po $pot"; >+ } else { > $self->create_messages(); > } >- system "$self->{msgmerge} -U $pofile $self->{domain}.pot"; >+} >+ >+sub extract_messages_from_templates { >+ my ($self, $tempdir, @files) = @_; >+ >+ my $intranetdir = $self->{context}->config('intranetdir'); >+ my @keywords = qw(t tx tn txn tnx tp tpx tnp tnpx); >+ my $parser = Template::Parser->new(); >+ >+ foreach my $file (@files) { >+ say "Extract messages from $file" if $self->{verbose}; >+ my $template = read_file("$intranetdir/$file"); >+ my $data = $parser->parse($template); >+ unless ($data) { >+ warn "Error at $file : " . $parser->error(); >+ next; >+ } >+ >+ make_path(dirname("$tempdir/$file")); >+ open my $fh, '>', "$tempdir/$file"; >+ >+ my @blocks = ($data->{BLOCK}, values %{ $data->{DEFBLOCKS} }); >+ foreach my $block (@blocks) { >+ my $document = PPI::Document->new(\$block); >+ >+ # [% t('foo') %] is compiled to >+ # $output .= $stash->get(['t', ['foo']]); >+ # We try to find all nodes corresponding to keyword (here 't') >+ my $nodes = $document->find(sub { >+ my ($topnode, $element) = @_; >+ >+ # Filter out non-valid keywords >+ return 0 unless ($element->isa('PPI::Token::Quote::Single')); >+ return 0 unless (grep {$element->content eq qq{'$_'}} @keywords); >+ >+ # keyword (e.g. 't') should be the first element of the arrayref >+ # passed to $stash->get() >+ return 0 if $element->sprevious_sibling; >+ >+ return 0 unless $element->snext_sibling >+ && $element->snext_sibling->snext_sibling >+ && $element->snext_sibling->snext_sibling->isa('PPI::Structure::Constructor'); >+ >+ # Check that it's indeed a call to $stash->get() >+ my $statement = $element->statement->parent->statement->parent->statement; >+ return 0 unless grep { $_->isa('PPI::Token::Symbol') && $_->content eq '$stash' } $statement->children; >+ return 0 unless grep { $_->isa('PPI::Token::Operator') && $_->content eq '->' } $statement->children; >+ return 0 unless grep { $_->isa('PPI::Token::Word') && $_->content eq 'get' } $statement->children; >+ >+ return 1; >+ }); >+ >+ next unless $nodes; >+ >+ # Write the Perl equivalent of calls to t* functions family, so >+ # xgettext can extract the strings correctly >+ foreach my $node (@$nodes) { >+ my @args = map { >+ $_->significant && !$_->isa('PPI::Token::Operator') ? $_->content : () >+ } $node->snext_sibling->snext_sibling->find_first('PPI::Statement')->children; >+ >+ my $keyword = $node->content; >+ $keyword =~ s/^'t(.*)'$/__$1/; >+ >+ say $fh "$keyword(" . join(', ', @args) . ");"; >+ } >+ >+ } >+ >+ close $fh; >+ } >+ >+ return $tempdir; > } > > sub extract_messages { > my $self = shift; > >+ say "Extract messages into POT file" if $self->{verbose}; >+ > my $intranetdir = $self->{context}->config('intranetdir'); > my @files_to_scan; > my @directories_to_scan = ('.'); >@@ -511,26 +626,53 @@ sub extract_messages { > } > } > >- my $xgettext_cmd = "$self->{xgettext} -L Perl --from-code=UTF-8 " . >- "-o $Bin/$self->{domain}.pot -D $intranetdir"; >+ my @tt_files; >+ find(sub { >+ if ($File::Find::dir =~ m|/en/| && $_ =~ m/\.(tt|inc)$/) { >+ my $filename = $File::Find::name; >+ $filename =~ s|^$intranetdir/||; >+ push @tt_files, $filename; >+ } >+ }, "$intranetdir/koha-tmpl"); >+ >+ my $tempdir = tempdir('Koha-translate-XXXX', TMPDIR => 1, CLEANUP => 1); >+ $self->extract_messages_from_templates($tempdir, @tt_files); >+ push @files_to_scan, @tt_files; >+ >+ my $xgettext_cmd = "$self->{xgettext} -L Perl --from-code=UTF-8 " >+ . "--package-name=Koha --package-version='' " >+ . "-k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 -k__p:1c,2 " >+ . "-k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ -kN__n:1,2 " >+ . "-kN__p:1c,2 -kN__np:1c,2,3 " >+ . "-o $Bin/$self->{domain}.pot -D $tempdir -D $intranetdir"; > $xgettext_cmd .= " $_" foreach (@files_to_scan); > > if (system($xgettext_cmd) != 0) { > die "system call failed: $xgettext_cmd"; > } > >- if ( -f "$Bin/$self->{domain}.pot" ) { >- my $replace_charset_cmd = "$self->{sed} --in-place " . >- "$Bin/$self->{domain}.pot " . >- "--expression='s/charset=CHARSET/charset=UTF-8/'"; >- if (system($replace_charset_cmd) != 0) { >- die "system call failed: $replace_charset_cmd"; >- } >- } else { >- print "No messages found\n" if $self->{verbose}; >- return; >+ my $replace_charset_cmd = "$self->{sed} --in-place " . >+ "$Bin/$self->{domain}.pot " . >+ "--expression='s/charset=CHARSET/charset=UTF-8/'"; >+ if (system($replace_charset_cmd) != 0) { >+ die "system call failed: $replace_charset_cmd"; > } >- return 1; >+} >+ >+sub install_messages { >+ my ($self) = @_; >+ >+ my $locale = $self->locale_name(); >+ my $modir = "$self->{path_po}/$locale/LC_MESSAGES"; >+ my $pofile = "$self->{path_po}/$self->{lang}-messages.po"; >+ my $mofile = "$modir/$self->{domain}.mo"; >+ >+ if ( not -f $pofile ) { >+ $self->create_messages(); >+ } >+ say "Install messages ($locale)" if $self->{verbose}; >+ make_path($modir); >+ system "$self->{msgfmt} -o $mofile $pofile"; > } > > sub remove_pot { >@@ -544,6 +686,8 @@ sub install { > return unless $self->{lang}; > $self->install_tmpl($files) unless $self->{pref_only}; > $self->install_prefs(); >+ $self->install_messages(); >+ $self->remove_pot(); > } > > >@@ -559,14 +703,13 @@ sub get_all_langs { > sub update { > my ($self, $files) = @_; > my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs(); >- my $extract_ok = $self->extract_messages(); > for my $lang ( @langs ) { > $self->set_lang( $lang ); > $self->update_tmpl($files) unless $self->{pref_only}; > $self->update_prefs(); >- $self->update_messages() if $extract_ok; >+ $self->update_messages(); > } >- $self->remove_pot() if $extract_ok; >+ $self->remove_pot(); > } > > >@@ -575,10 +718,8 @@ sub create { > return unless $self->{lang}; > $self->create_tmpl($files) unless $self->{pref_only}; > $self->create_prefs(); >- if ($self->extract_messages()) { >- $self->create_messages(); >- $self->remove_pot(); >- } >+ $self->create_messages(); >+ $self->remove_pot(); > } > > >diff --git a/t/Koha/I18N.t b/t/Koha/I18N.t >new file mode 100755 >index 0000000000..a657b1e75c >--- /dev/null >+++ b/t/Koha/I18N.t >@@ -0,0 +1,58 @@ >+#!/usr/bin/perl >+ >+use Modern::Perl; >+use Test::More tests => 35; >+use Test::MockModule; >+use FindBin qw($Bin); >+use Encode; >+ >+BEGIN { >+ use_ok('Koha::I18N'); >+} >+ >+my $koha_i18n = Test::MockModule->new('Koha::I18N'); >+$koha_i18n->mock('_base_directory', sub { "$Bin/I18N/po" }); >+ >+my $c4_languages = Test::MockModule->new('C4::Languages'); >+$c4_languages->mock('getlanguage', sub { 'xx-XX' }); >+ >+my @tests = ( >+ [ __('test') => 'test ã' ], >+ [ __x('Hello {name}', name => 'World') => 'Hello World ã' ], >+ [ __n('Singular', 'Plural', 0) => 'Zero ã' ], >+ [ __n('Singular', 'Plural', 1) => 'Singular ã' ], >+ [ __n('Singular', 'Plural', 2) => 'Plural ã' ], >+ [ __n('Singular', 'Plural', 3) => 'Plural ã' ], >+ [ __nx('one item', '{count} items', 0, count => 0) => 'no item ã' ], >+ [ __nx('one item', '{count} items', 1, count => 1) => 'one item ã' ], >+ [ __nx('one item', '{count} items', 2, count => 2) => '2 items ã' ], >+ [ __nx('one item', '{count} items', 3, count => 3) => '3 items ã' ], >+ [ __xn('one item', '{count} items', 0, count => 0) => 'no item ã' ], >+ [ __xn('one item', '{count} items', 1, count => 1) => 'one item ã' ], >+ [ __xn('one item', '{count} items', 2, count => 2) => '2 items ã' ], >+ [ __xn('one item', '{count} items', 3, count => 3) => '3 items ã' ], >+ [ __p('biblio', 'title') => 'title (biblio) ã' ], >+ [ __p('patron', 'title') => 'title (patron) ã' ], >+ [ __px('biblio', 'Remove item {id}', id => 42) => 'Remove item 42 (biblio) ã' ], >+ [ __px('list', 'Remove item {id}', id => 42) => 'Remove item 42 (list) ã' ], >+ [ __np('ctxt1', 'singular', 'plural', 0) => 'zero (ctxt1) ã' ], >+ [ __np('ctxt1', 'singular', 'plural', 1) => 'singular (ctxt1) ã' ], >+ [ __np('ctxt1', 'singular', 'plural', 2) => 'plural (ctxt1) ã' ], >+ [ __np('ctxt1', 'singular', 'plural', 3) => 'plural (ctxt1) ã' ], >+ [ __np('ctxt2', 'singular', 'plural', 0) => 'zero (ctxt2) ã' ], >+ [ __np('ctxt2', 'singular', 'plural', 1) => 'singular (ctxt2) ã' ], >+ [ __np('ctxt2', 'singular', 'plural', 2) => 'plural (ctxt2) ã' ], >+ [ __np('ctxt2', 'singular', 'plural', 3) => 'plural (ctxt2) ã' ], >+ [ __npx('biblio', 'one item', '{count} items', 0, count => 0) => 'no item (biblio) ã' ], >+ [ __npx('biblio', 'one item', '{count} items', 1, count => 1) => 'one item (biblio) ã' ], >+ [ __npx('biblio', 'one item', '{count} items', 2, count => 2) => '2 items (biblio) ã' ], >+ [ __npx('biblio', 'one item', '{count} items', 3, count => 3) => '3 items (biblio) ã' ], >+ [ __npx('list', 'one item', '{count} items', 0, count => 0) => 'no item (list) ã' ], >+ [ __npx('list', 'one item', '{count} items', 1, count => 1) => 'one item (list) ã' ], >+ [ __npx('list', 'one item', '{count} items', 2, count => 2) => '2 items (list) ã' ], >+ [ __npx('list', 'one item', '{count} items', 3, count => 3) => '3 items (list) ã' ], >+); >+ >+foreach my $test (@tests) { >+ is($test->[0], decode_utf8($test->[1]), $test->[1]); >+} >diff --git a/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo b/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo >new file mode 100644 >index 0000000000000000000000000000000000000000..99be22f268b3e872ac10b2d2e3e538b1fd8293d6 >GIT binary patch >literal 1370 >zcmaKr&rcLF6vwNssG~-O_#;u19ziqf%t9g=SRmpOQC1OGL%f*oZdpoZ+fAoSRta9< >zAMoJKyC=_{{1?1%_u`erf5C63yNm-ulb28XeXp;5oj1RR3!e$bW!N3qF6<g?A7)2z >zijYfS2wnkaz?<N6@GiL4_wRz&vHsNO*FJxMx1sNWH^4uj%{O$KkODXY?m)W*Ud8&$ >z8A2%d4Yc`xf)#Kd90p6+GznJv{0Q3o-@#dM54;DCoOSUt;CZZHf;i@2*m{RxNSofr >z191g%5oX5)hxCrvO3uM-9`c08v7%d&wfQ!AC1k4`GfkG`u4XZ*i+U_nVTHHVCa1#i >zHr*1<?SZbO6UkOXb*0%(Ba(sF2orPCFg+6%l0o8*OKjEB5j7TxIeJ*C9&^+&a)%kM >zWWn9Q@Wha1tu}bW_=`>d4cCcKGNRAaI`dX|M``0%la^@ukGieIuc?T7%P(sFyynax >z(tpehk7y|fANs+B9|km>h=TF^K`96j=&$fiVKbf>x5N_T*EEw!%#6|zecFu$leEgZ >zS~NJF-bmNQwp-7oPhZ!(*ih{mZ;9c<uom}goV6p`>(S|6Z!Q)^rCXWTI^M0Wdey~h >zB`Y&52Hw1qhD%)FeFv2o-ZKw6F%xo%HrAO=xGBG`E%=jJoW1B8*M3DdR1+UBqLX!D >zJl7Zgg3|3IqSAfMq&z-F-LyO^%jJO1(tyHUE{B+gn3l$-yj-i%-#feHE!QfY4Eioz >zxHHn};l`t*t|P}%uT-ujbK;;Q@}ao~&AZYj$x9#Xy++f{btetlHz&G^b5gdk^uLXL >w@!ySo>1d(0>kpNgNm-GZcBsV6F?c5X;s+b;BEQ2b{kL$xk2~G|J?U%z1(g|z>;M1& > >literal 0 >HcmV?d00001 > >diff --git a/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po b/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po >new file mode 100644 >index 0000000000..1b956aa0c8 >--- /dev/null >+++ b/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po >@@ -0,0 +1,81 @@ >+# PO file for tests >+# Copyright (C) 2017 BibLibre >+# This file is distributed under the same license as the Koha package. >+# Julian Maurice <julian.maurice@biblibre.com, 2017. >+# >+#, fuzzy >+msgid "" >+msgstr "" >+"Project-Id-Version: Koha\n" >+"Report-Msgid-Bugs-To: \n" >+"POT-Creation-Date: 2017-04-10 14:03+0200\n" >+"PO-Revision-Date: 2017-04-10 14:03+0200\n" >+"Last-Translator: Julian Maurice <julian.maurice@biblibre.com>\n" >+"Language-Team: xx <xx@li.org>\n" >+"Language: xx_XX\n" >+"MIME-Version: 1.0\n" >+"Content-Type: text/plain; charset=UTF-8\n" >+"Content-Transfer-Encoding: 8bit\n" >+"Plural-Forms: nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);\n" >+ >+msgid "test" >+msgstr "test ã" >+ >+msgid "Hello {name}" >+msgstr "Hello {name} ã" >+ >+msgid "Singular" >+msgid_plural "Plural" >+msgstr[0] "Zero ã" >+msgstr[1] "Singular ã" >+msgstr[2] "Plural ã" >+ >+msgid "one item" >+msgid_plural "{count} items" >+msgstr[0] "no item ã" >+msgstr[1] "one item ã" >+msgstr[2] "{count} items ã" >+ >+msgctxt "biblio" >+msgid "title" >+msgstr "title (biblio) ã" >+ >+msgctxt "patron" >+msgid "title" >+msgstr "title (patron) ã" >+ >+msgctxt "biblio" >+msgid "Remove item {id}" >+msgstr "Remove item {id} (biblio) ã" >+ >+msgctxt "list" >+msgid "Remove item {id}" >+msgstr "Remove item {id} (list) ã" >+ >+msgctxt "ctxt1" >+msgid "singular" >+msgid_plural "plural" >+msgstr[0] "zero (ctxt1) ã" >+msgstr[1] "singular (ctxt1) ã" >+msgstr[2] "plural (ctxt1) ã" >+ >+msgctxt "ctxt2" >+msgid "singular" >+msgid_plural "plural" >+msgstr[0] "zero (ctxt2) ã" >+msgstr[1] "singular (ctxt2) ã" >+msgstr[2] "plural (ctxt2) ã" >+ >+msgctxt "biblio" >+msgid "one item" >+msgid_plural "{count} items" >+msgstr[0] "no item (biblio) ã" >+msgstr[1] "one item (biblio) ã" >+msgstr[2] "{count} items (biblio) ã" >+ >+msgctxt "list" >+msgid "one item" >+msgid_plural "{count} items" >+msgstr[0] "no item (list) ã" >+msgstr[1] "one item (list) ã" >+msgstr[2] "{count} items (list) ã" >-- >2.17.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 15395
:
45797
|
45798
|
45799
|
46337
|
50023
|
50024
|
50025
|
50026
|
61873
|
61874
|
61875
|
61876
|
61934
|
61935
|
61977
|
62008
|
62041
|
62326
|
62327
|
62328
|
62329
|
62330
|
62331
|
62332
|
62333
|
62335
|
62709
|
65894
|
70974
|
70975
|
70976
|
71940
|
71941
|
71942
|
71943
|
71944
|
71945
|
71946
|
71947
|
71948
|
71949
|
71950
|
71951
|
71952
|
73901
|
73902
|
73903
|
73904
|
73905
|
73906
|
73907
|
73908
|
73909
|
73910
|
73911
|
73912
|
73913
|
76328
|
76329
|
76330
|
77475
|
77476
|
81152
|
81153
|
81154
|
81155
|
81156
|
81157
|
81158
|
81159
|
81167
|
81734
|
81735
|
81736
|
81737
|
81738
|
81739
|
81741
|
81742
|
81743