View | Details | Raw Unified | Return to bug 15395
Collapse All | Expand All

(-)a/C4/Installer/PerlDependencies.pm (-6 / +6 lines)
Lines 722-736 our $PERL_DEPS = { Link Here
722
        required   => 1,
722
        required   => 1,
723
        min_ver    => '2.125',
723
        min_ver    => '2.125',
724
    },
724
    },
725
    'Locale::Maketext' => {
725
    'Locale::Messages' => {
726
        'usage'    => 'Core',
726
        'usage'    => 'Core',
727
        'required' => '1',
727
        'required' => '1',
728
        'min_ver'  => '1.19',
728
        'min_ver'  => '1.20',
729
    },
729
    },
730
    'Locale::Maketext::Lexicon' => {
730
    'PPI' => {
731
        'usage'    => 'Core',
731
        'usage'    => 'I18N',
732
        'required' => '1',
732
        'required' => '0',
733
        'min_ver'  => '0.91',
733
        'min_ver'  => '1.215',
734
    },
734
    },
735
    'LWP::Protocol::https' => {
735
    'LWP::Protocol::https' => {
736
        'usage'    => 'OverDrive integration',
736
        'usage'    => 'OverDrive integration',
(-)a/Koha/I18N.pm (-23 / +168 lines)
Lines 18-58 package Koha::I18N; Link Here
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
use base qw(Locale::Maketext Exporter);
22
21
23
use CGI;
22
use CGI;
24
use C4::Languages;
23
use C4::Languages;
24
use C4::Context;
25
25
26
use Locale::Maketext::Lexicon {
26
use Encode;
27
    'en' => ['Auto'],
27
use Locale::Messages qw(:locale_h nl_putenv setlocale LC_MESSAGES);
28
    '*' => [
28
use Koha::Cache::Memory::Lite;
29
        Gettext =>
30
            C4::Context->config('intranetdir')
31
            . '/misc/translator/po/*-messages.po'
32
    ],
33
    '_AUTO' => 1,
34
    '_style' => 'gettext',
35
};
36
29
37
our @EXPORT = qw( gettext );
30
use parent 'Exporter';
31
our @EXPORT = qw(
32
    __
33
    __x
34
    __n
35
    __nx
36
    __xn
37
    __p
38
    __px
39
    __np
40
    __npx
41
    N__
42
    N__n
43
    N__p
44
    N__np
45
);
38
46
39
my %language_handles;
47
our $textdomain = 'Koha';
40
48
41
sub get_language_handle {
49
sub init {
42
    my $cgi = new CGI;
50
    my $cache = Koha::Cache::Memory::Lite->get_instance();
43
    my $language = C4::Languages::getlanguage;
51
    my $cache_key = 'i18n:initialized';
52
    unless ($cache->get_from_cache($cache_key)) {
53
        my @system_locales = grep { chomp; not (/^C/ || $_ eq 'POSIX') } qx/locale -a/;
54
        if (@system_locales) {
55
            # LANG needs to be set to a valid locale,
56
            # otherwise LANGUAGE is ignored
57
            nl_putenv('LANG=' . $system_locales[0]);
58
            setlocale(LC_MESSAGES, '');
44
59
45
    if (not exists $language_handles{$language}) {
60
            my $langtag = C4::Languages::getlanguage;
46
        $language_handles{$language} = __PACKAGE__->get_handle($language)
61
            my @subtags = split /-/, $langtag;
47
            or die "No language handle for '$language'";
62
            my ($language, $region) = @subtags;
63
            if ($region && length $region == 4) {
64
                $region = $subtags[2];
65
            }
66
            my $locale = $language;
67
            if ($region) {
68
                $locale .= '_' . $region;
69
            }
70
71
            nl_putenv("LANGUAGE=$locale");
72
            nl_putenv('OUTPUT_CHARSET=UTF-8');
73
74
            my $directory = _base_directory();
75
            textdomain($textdomain);
76
            bindtextdomain($textdomain, $directory);
77
        } else {
78
            warn "No locale installed. Localization cannot work and is therefore disabled";
79
        }
80
81
        $cache->set_in_cache($cache_key, 1);
82
    }
83
}
84
85
sub __ {
86
    my ($msgid) = @_;
87
88
    $msgid = Encode::encode_utf8($msgid);
89
90
    return _gettext(\&gettext, [ $msgid ]);
91
}
92
93
sub __x {
94
    my ($msgid, %vars) = @_;
95
96
    $msgid = Encode::encode_utf8($msgid);
97
98
    return _gettext(\&gettext, [ $msgid ], %vars);
99
}
100
101
sub __n {
102
    my ($msgid, $msgid_plural, $count) = @_;
103
104
    $msgid = Encode::encode_utf8($msgid);
105
    $msgid_plural = Encode::encode_utf8($msgid_plural);
106
107
    return _gettext(\&ngettext, [ $msgid, $msgid_plural, $count ]);
108
}
109
110
sub __nx {
111
    my ($msgid, $msgid_plural, $count, %vars) = @_;
112
113
    $msgid = Encode::encode_utf8($msgid);
114
    $msgid_plural = Encode::encode_utf8($msgid_plural);
115
116
    return _gettext(\&ngettext, [ $msgid, $msgid_plural, $count ], %vars);
117
}
118
119
sub __xn {
120
    return __nx(@_);
121
}
122
123
sub __p {
124
    my ($msgctxt, $msgid) = @_;
125
126
    $msgctxt = Encode::encode_utf8($msgctxt);
127
    $msgid = Encode::encode_utf8($msgid);
128
129
    return _gettext(\&pgettext, [ $msgctxt, $msgid ]);
130
}
131
132
sub __px {
133
    my ($msgctxt, $msgid, %vars) = @_;
134
135
    $msgctxt = Encode::encode_utf8($msgctxt);
136
    $msgid = Encode::encode_utf8($msgid);
137
138
    return _gettext(\&pgettext, [ $msgctxt, $msgid ], %vars);
139
}
140
141
sub __np {
142
    my ($msgctxt, $msgid, $msgid_plural, $count) = @_;
143
144
    $msgctxt = Encode::encode_utf8($msgctxt);
145
    $msgid = Encode::encode_utf8($msgid);
146
    $msgid_plural = Encode::encode_utf8($msgid_plural);
147
148
    return _gettext(\&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ]);
149
}
150
151
sub __npx {
152
    my ($msgctxt, $msgid, $msgid_plural, $count, %vars) = @_;
153
154
    $msgctxt = Encode::encode_utf8($msgctxt);
155
    $msgid = Encode::encode_utf8($msgid);
156
    $msgid_plural = Encode::encode_utf8($msgid_plural);
157
158
    return _gettext(\&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count], %vars);
159
}
160
161
sub N__ {
162
    return @_;
163
}
164
165
sub N__n {
166
    return @_;
167
}
168
169
sub N__p {
170
    return @_;
171
}
172
173
sub N__np {
174
    return @_;
175
}
176
177
sub _base_directory {
178
    return C4::Context->config('intranetdir') . '/misc/translator/po';
179
}
180
181
sub _gettext {
182
    my ($sub, $args, %vars) = @_;
183
184
    init();
185
186
    my $text = Encode::decode_utf8($sub->(@$args));
187
    if (%vars) {
188
        $text = _expand($text, %vars);
48
    }
189
    }
49
190
50
    return $language_handles{$language};
191
    return $text;
51
}
192
}
52
193
53
sub gettext {
194
sub _expand {
54
    my $lh = get_language_handle;
195
    my ($text, %vars) = @_;
55
    $lh->maketext(@_);
196
197
    my $re = join '|', map { quotemeta $_ } keys %vars;
198
    $text =~ s/\{($re)\}/defined $vars{$1} ? $vars{$1} : "{$1}"/ge;
199
200
    return $text;
56
}
201
}
57
202
58
1;
203
1;
(-)a/Koha/Template/Plugin/I18N.pm (+72 lines)
Line 0 Link Here
1
package Koha::Template::Plugin::I18N;
2
3
# Copyright BibLibre 2015
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use base qw( Template::Plugin );
23
24
use C4::Context;
25
use Koha::I18N;
26
27
sub t {
28
    my ($self, $msgid) = @_;
29
    return __($msgid);
30
}
31
32
sub tx {
33
    my ($self, $msgid, $vars) = @_;
34
    return __x($msgid, %$vars);
35
}
36
37
sub tn {
38
    my ($self, $msgid, $msgid_plural, $count) = @_;
39
    return __n($msgid, $msgid_plural, $count);
40
}
41
42
sub tnx {
43
    my ($self, $msgid, $msgid_plural, $count, $vars) = @_;
44
    return __nx($msgid, $msgid_plural, $count, %$vars);
45
}
46
47
sub txn {
48
    my ($self, $msgid, $msgid_plural, $count, $vars) = @_;
49
    return __xn($msgid, $msgid_plural, $count, %$vars);
50
}
51
52
sub tp {
53
    my ($self, $msgctxt, $msgid) = @_;
54
    return __p($msgctxt, $msgid);
55
}
56
57
sub tpx {
58
    my ($self, $msgctxt, $msgid, $vars) = @_;
59
    return __px($msgctxt, $msgid, %$vars);
60
}
61
62
sub tnp {
63
    my ($self, $msgctxt, $msgid, $msgid_plural, $count) = @_;
64
    return __np($msgctxt, $msgid, $msgid_plural, $count);
65
}
66
67
sub tnpx {
68
    my ($self, $msgctxt, $msgid, $msgid_plural, $count, $vars) = @_;
69
    return __np($msgctxt, $msgid, $msgid_plural, $count, %$vars);
70
}
71
72
1;
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/i18n.inc (+39 lines)
Line 0 Link Here
1
[%
2
  USE I18N;
3
4
  MACRO t(msgid) BLOCK;
5
    I18N.t(msgid);
6
  END;
7
8
  MACRO tx(msgid, vars) BLOCK;
9
    I18N.tx(msgid, vars);
10
  END;
11
12
  MACRO tn(msgid, msgid_plural, count) BLOCK;
13
    I18N.tn(msgid, msgid_plural, count);
14
  END;
15
16
  MACRO tnx(msgid, msgid_plural, count, vars) BLOCK;
17
    I18N.tnx(msgid, msgid_plural, count, vars);
18
  END;
19
20
  MACRO txn(msgid, msgid_plural, count, vars) BLOCK;
21
    I18N.txn(msgid, msgid_plural, count, vars);
22
  END;
23
24
  MACRO tp(msgctxt, msgid) BLOCK;
25
    I18N.tp(msgctxt, msgid);
26
  END;
27
28
  MACRO tpx(msgctxt, msgid, vars) BLOCK;
29
    I18N.tpx(msgctxt, msgid, vars);
30
  END;
31
32
  MACRO tnp(msgctxt, msgid, msgid_plural, count) BLOCK;
33
    I18N.tnp(msgctxt, msgid, msgid_plural, count);
34
  END;
35
36
  MACRO tnpx(msgctxt, msgid, msgid_plural, count, vars) BLOCK;
37
    I18N.tnpx(msgctxt, msgid, msgid_plural, count, vars);
38
  END;
39
%]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/i18n.inc (+39 lines)
Line 0 Link Here
1
[%
2
  USE I18N;
3
4
  MACRO t(msgid) BLOCK;
5
    I18N.t(msgid);
6
  END;
7
8
  MACRO tx(msgid, vars) BLOCK;
9
    I18N.tx(msgid, vars);
10
  END;
11
12
  MACRO tn(msgid, msgid_plural, count) BLOCK;
13
    I18N.tn(msgid, msgid_plural, count);
14
  END;
15
16
  MACRO tnx(msgid, msgid_plural, count, vars) BLOCK;
17
    I18N.tnx(msgid, msgid_plural, count, vars);
18
  END;
19
20
  MACRO txn(msgid, msgid_plural, count, vars) BLOCK;
21
    I18N.txn(msgid, msgid_plural, count, vars);
22
  END;
23
24
  MACRO tp(msgctxt, msgid) BLOCK;
25
    I18N.tp(msgctxt, msgid);
26
  END;
27
28
  MACRO tpx(msgctxt, msgid, vars) BLOCK;
29
    I18N.tpx(msgctxt, msgid, vars);
30
  END;
31
32
  MACRO tnp(msgctxt, msgid, msgid_plural, count) BLOCK;
33
    I18N.tnp(msgctxt, msgid, msgid_plural, count);
34
  END;
35
36
  MACRO tnpx(msgctxt, msgid, msgid_plural, count, vars) BLOCK;
37
    I18N.tnpx(msgctxt, msgid, msgid_plural, count, vars);
38
  END;
39
%]
(-)a/misc/translator/LangInstaller.pm (-30 / +171 lines)
Lines 25-30 use C4::Context; Link Here
25
use YAML::Syck qw( Dump LoadFile );
25
use YAML::Syck qw( Dump LoadFile );
26
use Locale::PO;
26
use Locale::PO;
27
use FindBin qw( $Bin );
27
use FindBin qw( $Bin );
28
use File::Basename;
29
use File::Find;
30
use File::Path qw( make_path );
31
use File::Slurp;
32
use File::Temp qw( tempdir );
33
use Template::Parser;
34
use PPI;
28
35
29
$YAML::Syck::ImplicitTyping = 1;
36
$YAML::Syck::ImplicitTyping = 1;
30
37
Lines 66-78 sub new { Link Here
66
    $self->{process}         = "$Bin/tmpl_process3.pl " . ($verbose ? '' : '-q');
73
    $self->{process}         = "$Bin/tmpl_process3.pl " . ($verbose ? '' : '-q');
67
    $self->{path_po}         = "$Bin/po";
74
    $self->{path_po}         = "$Bin/po";
68
    $self->{po}              = { '' => $default_pref_po_header };
75
    $self->{po}              = { '' => $default_pref_po_header };
69
    $self->{domain}          = 'messages';
76
    $self->{domain}          = 'Koha';
70
    $self->{cp}              = `which cp`;
77
    $self->{cp}              = `which cp`;
71
    $self->{msgmerge}        = `which msgmerge`;
78
    $self->{msgmerge}        = `which msgmerge`;
79
    $self->{msgfmt}          = `which msgfmt`;
80
    $self->{msginit}         = `which msginit`;
72
    $self->{xgettext}        = `which xgettext`;
81
    $self->{xgettext}        = `which xgettext`;
73
    $self->{sed}             = `which sed`;
82
    $self->{sed}             = `which sed`;
74
    chomp $self->{cp};
83
    chomp $self->{cp};
75
    chomp $self->{msgmerge};
84
    chomp $self->{msgmerge};
85
    chomp $self->{msgfmt};
86
    chomp $self->{msginit};
76
    chomp $self->{xgettext};
87
    chomp $self->{xgettext};
77
    chomp $self->{sed};
88
    chomp $self->{sed};
78
89
Lines 468-497 sub create_tmpl { Link Here
468
    }
479
    }
469
}
480
}
470
481
482
sub locale_name {
483
    my $self = shift;
484
485
    my ($language, $region, $country) = split /-/, $self->{lang};
486
    $country //= $region;
487
    my $locale = $language;
488
    if ($country && length($country) == 2) {
489
        $locale .= '_' . $country;
490
    }
491
492
    return $locale;
493
}
494
471
sub create_messages {
495
sub create_messages {
472
    my $self = shift;
496
    my $self = shift;
473
497
474
    print "Create messages ($self->{lang})\n" if $self->{verbose};
498
    my $pot = "$self->{domain}.pot";
475
    system
499
    my $po = "$self->{path_po}/$self->{lang}-messages.po";
476
        "$self->{cp} $self->{domain}.pot " .
500
477
        "$self->{path_po}/$self->{lang}-$self->{domain}.po";
501
    unless ( -f $pot ) {
502
        $self->extract_messages();
503
    }
504
505
    say "Create messages ($self->{lang})" if $self->{verbose};
506
    my $locale = $self->locale_name();
507
    system "$self->{msginit} -i $pot -o $po -l $locale --no-translator";
508
509
    # If msginit failed to correctly set Plural-Forms, set a default one
510
    system "$self->{sed} --in-place $po "
511
        . "--expression='s/Plural-Forms: nplurals=INTEGER; plural=EXPRESSION/Plural-Forms: nplurals=2; plural=(n != 1)/'";
478
}
512
}
479
513
480
sub update_messages {
514
sub update_messages {
481
    my $self = shift;
515
    my $self = shift;
482
516
483
    my $pofile = "$self->{path_po}/$self->{lang}-$self->{domain}.po";
517
    my $pot = "$self->{domain}.pot";
484
    print "Update messages ($self->{lang})\n" if $self->{verbose};
518
    my $po = "$self->{path_po}/$self->{lang}-messages.po";
485
    if ( not -f $pofile ) {
519
486
        print "File $pofile does not exist\n" if $self->{verbose};
520
    unless ( -f $pot ) {
521
        $self->extract_messages();
522
    }
523
524
    if ( -f $po ) {
525
        say "Update messages ($self->{lang})" if $self->{verbose};
526
        system "$self->{msgmerge} -U $po $pot";
527
    } else {
487
        $self->create_messages();
528
        $self->create_messages();
488
    }
529
    }
489
    system "$self->{msgmerge} -U $pofile $self->{domain}.pot";
530
}
531
532
sub extract_messages_from_templates {
533
    my ($self, $tempdir, @files) = @_;
534
535
    my $intranetdir = $self->{context}->config('intranetdir');
536
    my @keywords = qw(t tx tn txn tnx tp tpx tnp tnpx);
537
    my $parser = Template::Parser->new();
538
539
    foreach my $file (@files) {
540
        say "Extract messages from $file" if $self->{verbose};
541
        my $template = read_file("$intranetdir/$file");
542
        my $data = $parser->parse($template);
543
        unless ($data) {
544
            warn "Error at $file : " . $parser->error();
545
            next;
546
        }
547
548
        make_path(dirname("$tempdir/$file"));
549
        open my $fh, '>', "$tempdir/$file";
550
551
        my @blocks = ($data->{BLOCK}, values %{ $data->{DEFBLOCKS} });
552
        foreach my $block (@blocks) {
553
            my $document = PPI::Document->new(\$block);
554
555
            # [% t('foo') %] is compiled to
556
            # $output .= $stash->get(['t', ['foo']]);
557
            # We try to find all nodes corresponding to keyword (here 't')
558
            my $nodes = $document->find(sub {
559
                my ($topnode, $element) = @_;
560
561
                # Filter out non-valid keywords
562
                return 0 unless ($element->isa('PPI::Token::Quote::Single'));
563
                return 0 unless (grep {$element->content eq qq{'$_'}} @keywords);
564
565
                # keyword (e.g. 't') should be the first element of the arrayref
566
                # passed to $stash->get()
567
                return 0 if $element->sprevious_sibling;
568
569
                return 0 unless $element->snext_sibling
570
                    && $element->snext_sibling->snext_sibling
571
                    && $element->snext_sibling->snext_sibling->isa('PPI::Structure::Constructor');
572
573
                # Check that it's indeed a call to $stash->get()
574
                my $statement = $element->statement->parent->statement->parent->statement;
575
                return 0 unless grep { $_->isa('PPI::Token::Symbol') && $_->content eq '$stash' } $statement->children;
576
                return 0 unless grep { $_->isa('PPI::Token::Operator') && $_->content eq '->' } $statement->children;
577
                return 0 unless grep { $_->isa('PPI::Token::Word') && $_->content eq 'get' } $statement->children;
578
579
                return 1;
580
            });
581
582
            next unless $nodes;
583
584
            # Write the Perl equivalent of calls to t* functions family, so
585
            # xgettext can extract the strings correctly
586
            foreach my $node (@$nodes) {
587
                my @args = map {
588
                    $_->significant && !$_->isa('PPI::Token::Operator') ? $_->content : ()
589
                } $node->snext_sibling->snext_sibling->find_first('PPI::Statement')->children;
590
591
                my $keyword = $node->content;
592
                $keyword =~ s/^'t(.*)'$/__$1/;
593
594
                say $fh "$keyword(" . join(', ', @args) . ");";
595
            }
596
597
        }
598
599
        close $fh;
600
    }
601
602
    return $tempdir;
490
}
603
}
491
604
492
sub extract_messages {
605
sub extract_messages {
493
    my $self = shift;
606
    my $self = shift;
494
607
608
    say "Extract messages into POT file" if $self->{verbose};
609
495
    my $intranetdir = $self->{context}->config('intranetdir');
610
    my $intranetdir = $self->{context}->config('intranetdir');
496
    my @files_to_scan;
611
    my @files_to_scan;
497
    my @directories_to_scan = ('.');
612
    my @directories_to_scan = ('.');
Lines 511-536 sub extract_messages { Link Here
511
        }
626
        }
512
    }
627
    }
513
628
514
    my $xgettext_cmd = "$self->{xgettext} -L Perl --from-code=UTF-8 " .
629
    my @tt_files;
515
        "-o $Bin/$self->{domain}.pot -D $intranetdir";
630
    find(sub {
631
        if ($File::Find::dir =~ m|/en/| && $_ =~ m/\.(tt|inc)$/) {
632
            my $filename = $File::Find::name;
633
            $filename =~ s|^$intranetdir/||;
634
            push @tt_files, $filename;
635
        }
636
    }, "$intranetdir/koha-tmpl");
637
638
    my $tempdir = tempdir('Koha-translate-XXXX', TMPDIR => 1, CLEANUP => 1);
639
    $self->extract_messages_from_templates($tempdir, @tt_files);
640
    push @files_to_scan, @tt_files;
641
642
    my $xgettext_cmd = "$self->{xgettext} -L Perl --from-code=UTF-8 "
643
        . "--package-name=Koha --package-version='' "
644
        . "-k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 -k__p:1c,2 "
645
        . "-k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ -kN__n:1,2 "
646
        . "-kN__p:1c,2 -kN__np:1c,2,3 "
647
        . "-o $Bin/$self->{domain}.pot -D $tempdir -D $intranetdir";
516
    $xgettext_cmd .= " $_" foreach (@files_to_scan);
648
    $xgettext_cmd .= " $_" foreach (@files_to_scan);
517
649
518
    if (system($xgettext_cmd) != 0) {
650
    if (system($xgettext_cmd) != 0) {
519
        die "system call failed: $xgettext_cmd";
651
        die "system call failed: $xgettext_cmd";
520
    }
652
    }
521
653
522
    if ( -f "$Bin/$self->{domain}.pot" ) {
654
    my $replace_charset_cmd = "$self->{sed} --in-place " .
523
        my $replace_charset_cmd = "$self->{sed} --in-place " .
655
        "$Bin/$self->{domain}.pot " .
524
            "$Bin/$self->{domain}.pot " .
656
        "--expression='s/charset=CHARSET/charset=UTF-8/'";
525
            "--expression='s/charset=CHARSET/charset=UTF-8/'";
657
    if (system($replace_charset_cmd) != 0) {
526
        if (system($replace_charset_cmd) != 0) {
658
        die "system call failed: $replace_charset_cmd";
527
            die "system call failed: $replace_charset_cmd";
528
        }
529
    } else {
530
        print "No messages found\n" if $self->{verbose};
531
        return;
532
    }
659
    }
533
    return 1;
660
}
661
662
sub install_messages {
663
    my ($self) = @_;
664
665
    my $locale = $self->locale_name();
666
    my $modir = "$self->{path_po}/$locale/LC_MESSAGES";
667
    my $pofile = "$self->{path_po}/$self->{lang}-messages.po";
668
    my $mofile = "$modir/$self->{domain}.mo";
669
670
    if ( not -f $pofile ) {
671
        $self->create_messages();
672
    }
673
    say "Install messages ($locale)" if $self->{verbose};
674
    make_path($modir);
675
    system "$self->{msgfmt} -o $mofile $pofile";
534
}
676
}
535
677
536
sub remove_pot {
678
sub remove_pot {
Lines 544-549 sub install { Link Here
544
    return unless $self->{lang};
686
    return unless $self->{lang};
545
    $self->install_tmpl($files) unless $self->{pref_only};
687
    $self->install_tmpl($files) unless $self->{pref_only};
546
    $self->install_prefs();
688
    $self->install_prefs();
689
    $self->install_messages();
690
    $self->remove_pot();
547
}
691
}
548
692
549
693
Lines 559-572 sub get_all_langs { Link Here
559
sub update {
703
sub update {
560
    my ($self, $files) = @_;
704
    my ($self, $files) = @_;
561
    my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs();
705
    my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs();
562
    my $extract_ok = $self->extract_messages();
563
    for my $lang ( @langs ) {
706
    for my $lang ( @langs ) {
564
        $self->set_lang( $lang );
707
        $self->set_lang( $lang );
565
        $self->update_tmpl($files) unless $self->{pref_only};
708
        $self->update_tmpl($files) unless $self->{pref_only};
566
        $self->update_prefs();
709
        $self->update_prefs();
567
        $self->update_messages() if $extract_ok;
710
        $self->update_messages();
568
    }
711
    }
569
    $self->remove_pot() if $extract_ok;
712
    $self->remove_pot();
570
}
713
}
571
714
572
715
Lines 575-584 sub create { Link Here
575
    return unless $self->{lang};
718
    return unless $self->{lang};
576
    $self->create_tmpl($files) unless $self->{pref_only};
719
    $self->create_tmpl($files) unless $self->{pref_only};
577
    $self->create_prefs();
720
    $self->create_prefs();
578
    if ($self->extract_messages()) {
721
    $self->create_messages();
579
        $self->create_messages();
722
    $self->remove_pot();
580
        $self->remove_pot();
581
    }
582
}
723
}
583
724
584
725
(-)a/t/Koha/I18N.t (+58 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
use Test::More tests => 35;
5
use Test::MockModule;
6
use FindBin qw($Bin);
7
use Encode;
8
9
BEGIN {
10
    use_ok('Koha::I18N');
11
}
12
13
my $koha_i18n = Test::MockModule->new('Koha::I18N');
14
$koha_i18n->mock('_base_directory', sub { "$Bin/I18N/po" });
15
16
my $c4_languages = Test::MockModule->new('C4::Languages');
17
$c4_languages->mock('getlanguage', sub { 'xx-XX' });
18
19
my @tests = (
20
    [ __('test') => 'test ツ' ],
21
    [ __x('Hello {name}', name => 'World') => 'Hello World ツ' ],
22
    [ __n('Singular', 'Plural', 0) => 'Zero ツ' ],
23
    [ __n('Singular', 'Plural', 1) => 'Singular ツ' ],
24
    [ __n('Singular', 'Plural', 2) => 'Plural ツ' ],
25
    [ __n('Singular', 'Plural', 3) => 'Plural ツ' ],
26
    [ __nx('one item', '{count} items', 0, count => 0) => 'no item ツ' ],
27
    [ __nx('one item', '{count} items', 1, count => 1) => 'one item ツ' ],
28
    [ __nx('one item', '{count} items', 2, count => 2) => '2 items ツ' ],
29
    [ __nx('one item', '{count} items', 3, count => 3) => '3 items ツ' ],
30
    [ __xn('one item', '{count} items', 0, count => 0) => 'no item ツ' ],
31
    [ __xn('one item', '{count} items', 1, count => 1) => 'one item ツ' ],
32
    [ __xn('one item', '{count} items', 2, count => 2) => '2 items ツ' ],
33
    [ __xn('one item', '{count} items', 3, count => 3) => '3 items ツ' ],
34
    [ __p('biblio', 'title') => 'title (biblio) ツ' ],
35
    [ __p('patron', 'title') => 'title (patron) ツ' ],
36
    [ __px('biblio', 'Remove item {id}', id => 42) => 'Remove item 42 (biblio) ツ' ],
37
    [ __px('list', 'Remove item {id}', id => 42) => 'Remove item 42 (list) ツ' ],
38
    [ __np('ctxt1', 'singular', 'plural', 0) => 'zero (ctxt1) ツ' ],
39
    [ __np('ctxt1', 'singular', 'plural', 1) => 'singular (ctxt1) ツ' ],
40
    [ __np('ctxt1', 'singular', 'plural', 2) => 'plural (ctxt1) ツ' ],
41
    [ __np('ctxt1', 'singular', 'plural', 3) => 'plural (ctxt1) ツ' ],
42
    [ __np('ctxt2', 'singular', 'plural', 0) => 'zero (ctxt2) ツ' ],
43
    [ __np('ctxt2', 'singular', 'plural', 1) => 'singular (ctxt2) ツ' ],
44
    [ __np('ctxt2', 'singular', 'plural', 2) => 'plural (ctxt2) ツ' ],
45
    [ __np('ctxt2', 'singular', 'plural', 3) => 'plural (ctxt2) ツ' ],
46
    [ __npx('biblio', 'one item', '{count} items', 0, count => 0) => 'no item (biblio) ツ' ],
47
    [ __npx('biblio', 'one item', '{count} items', 1, count => 1) => 'one item (biblio) ツ' ],
48
    [ __npx('biblio', 'one item', '{count} items', 2, count => 2) => '2 items (biblio) ツ' ],
49
    [ __npx('biblio', 'one item', '{count} items', 3, count => 3) => '3 items (biblio) ツ' ],
50
    [ __npx('list', 'one item', '{count} items', 0, count => 0) => 'no item (list) ツ' ],
51
    [ __npx('list', 'one item', '{count} items', 1, count => 1) => 'one item (list) ツ' ],
52
    [ __npx('list', 'one item', '{count} items', 2, count => 2) => '2 items (list) ツ' ],
53
    [ __npx('list', 'one item', '{count} items', 3, count => 3) => '3 items (list) ツ' ],
54
);
55
56
foreach my $test (@tests) {
57
    is($test->[0], decode_utf8($test->[1]), $test->[1]);
58
}
(-)a/t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.po (-1 / +81 lines)
Line 0 Link Here
0
- 
1
# PO file for tests
2
# Copyright (C) 2017 BibLibre
3
# This file is distributed under the same license as the Koha package.
4
# Julian Maurice <julian.maurice@biblibre.com, 2017.
5
#
6
#, fuzzy
7
msgid ""
8
msgstr ""
9
"Project-Id-Version: Koha\n"
10
"Report-Msgid-Bugs-To: \n"
11
"POT-Creation-Date: 2017-04-10 14:03+0200\n"
12
"PO-Revision-Date: 2017-04-10 14:03+0200\n"
13
"Last-Translator: Julian Maurice <julian.maurice@biblibre.com>\n"
14
"Language-Team: xx <xx@li.org>\n"
15
"Language: xx_XX\n"
16
"MIME-Version: 1.0\n"
17
"Content-Type: text/plain; charset=UTF-8\n"
18
"Content-Transfer-Encoding: 8bit\n"
19
"Plural-Forms: nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);\n"
20
21
msgid "test"
22
msgstr "test ツ"
23
24
msgid "Hello {name}"
25
msgstr "Hello {name} ツ"
26
27
msgid "Singular"
28
msgid_plural "Plural"
29
msgstr[0] "Zero ツ"
30
msgstr[1] "Singular ツ"
31
msgstr[2] "Plural ツ"
32
33
msgid "one item"
34
msgid_plural "{count} items"
35
msgstr[0] "no item ツ"
36
msgstr[1] "one item ツ"
37
msgstr[2] "{count} items ツ"
38
39
msgctxt "biblio"
40
msgid "title"
41
msgstr "title (biblio) ツ"
42
43
msgctxt "patron"
44
msgid "title"
45
msgstr "title (patron) ツ"
46
47
msgctxt "biblio"
48
msgid "Remove item {id}"
49
msgstr "Remove item {id} (biblio) ツ"
50
51
msgctxt "list"
52
msgid "Remove item {id}"
53
msgstr "Remove item {id} (list) ツ"
54
55
msgctxt "ctxt1"
56
msgid "singular"
57
msgid_plural "plural"
58
msgstr[0] "zero (ctxt1) ツ"
59
msgstr[1] "singular (ctxt1) ツ"
60
msgstr[2] "plural (ctxt1) ツ"
61
62
msgctxt "ctxt2"
63
msgid "singular"
64
msgid_plural "plural"
65
msgstr[0] "zero (ctxt2) ツ"
66
msgstr[1] "singular (ctxt2) ツ"
67
msgstr[2] "plural (ctxt2) ツ"
68
69
msgctxt "biblio"
70
msgid "one item"
71
msgid_plural "{count} items"
72
msgstr[0] "no item (biblio) ツ"
73
msgstr[1] "one item (biblio) ツ"
74
msgstr[2] "{count} items (biblio) ツ"
75
76
msgctxt "list"
77
msgid "one item"
78
msgid_plural "{count} items"
79
msgstr[0] "no item (list) ツ"
80
msgstr[1] "one item (list) ツ"
81
msgstr[2] "{count} items (list) ツ"

Return to bug 15395