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

(-)a/C4/Installer/PerlDependencies.pm (-6 / +6 lines)
Lines 727-741 our $PERL_DEPS = { Link Here
727
        required   => 1,
727
        required   => 1,
728
        min_ver    => '2.125',
728
        min_ver    => '2.125',
729
    },
729
    },
730
    'Locale::Maketext' => {
730
    'Locale::Messages' => {
731
        'usage'    => 'Core',
731
        'usage'    => 'Core',
732
        'required' => '1',
732
        'required' => '1',
733
        'min_ver'  => '1.19',
733
        'min_ver'  => '1.20',
734
    },
734
    },
735
    'Locale::Maketext::Lexicon' => {
735
    'PPI' => {
736
        'usage'    => 'Core',
736
        'usage'    => 'I18N',
737
        'required' => '1',
737
        'required' => '0',
738
        'min_ver'  => '0.91',
738
        'min_ver'  => '1.215',
739
    },
739
    },
740
    'LWP::Protocol::https' => {
740
    'LWP::Protocol::https' => {
741
        'usage'    => 'OverDrive integration',
741
        '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 456-485 sub create_tmpl { Link Here
456
    }
467
    }
457
}
468
}
458
469
470
sub locale_name {
471
    my $self = shift;
472
473
    my ($language, $region, $country) = split /-/, $self->{lang};
474
    $country //= $region;
475
    my $locale = $language;
476
    if ($country && length($country) == 2) {
477
        $locale .= '_' . $country;
478
    }
479
480
    return $locale;
481
}
482
459
sub create_messages {
483
sub create_messages {
460
    my $self = shift;
484
    my $self = shift;
461
485
462
    print "Create messages ($self->{lang})\n" if $self->{verbose};
486
    my $pot = "$self->{domain}.pot";
463
    system
487
    my $po = "$self->{path_po}/$self->{lang}-messages.po";
464
        "$self->{cp} $self->{domain}.pot " .
488
465
        "$self->{path_po}/$self->{lang}-$self->{domain}.po";
489
    unless ( -f $pot ) {
490
        $self->extract_messages();
491
    }
492
493
    say "Create messages ($self->{lang})" if $self->{verbose};
494
    my $locale = $self->locale_name();
495
    system "$self->{msginit} -i $pot -o $po -l $locale --no-translator";
496
497
    # If msginit failed to correctly set Plural-Forms, set a default one
498
    system "$self->{sed} --in-place $po "
499
        . "--expression='s/Plural-Forms: nplurals=INTEGER; plural=EXPRESSION/Plural-Forms: nplurals=2; plural=(n != 1)/'";
466
}
500
}
467
501
468
sub update_messages {
502
sub update_messages {
469
    my $self = shift;
503
    my $self = shift;
470
504
471
    my $pofile = "$self->{path_po}/$self->{lang}-$self->{domain}.po";
505
    my $pot = "$self->{domain}.pot";
472
    print "Update messages ($self->{lang})\n" if $self->{verbose};
506
    my $po = "$self->{path_po}/$self->{lang}-messages.po";
473
    if ( not -f $pofile ) {
507
474
        print "File $pofile does not exist\n" if $self->{verbose};
508
    unless ( -f $pot ) {
509
        $self->extract_messages();
510
    }
511
512
    if ( -f $po ) {
513
        say "Update messages ($self->{lang})" if $self->{verbose};
514
        system "$self->{msgmerge} -U $po $pot";
515
    } else {
475
        $self->create_messages();
516
        $self->create_messages();
476
    }
517
    }
477
    system "$self->{msgmerge} -U $pofile $self->{domain}.pot";
518
}
519
520
sub extract_messages_from_templates {
521
    my ($self, $tempdir, @files) = @_;
522
523
    my $intranetdir = $self->{context}->config('intranetdir');
524
    my @keywords = qw(t tx tn txn tnx tp tpx tnp tnpx);
525
    my $parser = Template::Parser->new();
526
527
    foreach my $file (@files) {
528
        say "Extract messages from $file" if $self->{verbose};
529
        my $template = read_file("$intranetdir/$file");
530
        my $data = $parser->parse($template);
531
        unless ($data) {
532
            warn "Error at $file : " . $parser->error();
533
            next;
534
        }
535
536
        make_path(dirname("$tempdir/$file"));
537
        open my $fh, '>', "$tempdir/$file";
538
539
        my @blocks = ($data->{BLOCK}, values %{ $data->{DEFBLOCKS} });
540
        foreach my $block (@blocks) {
541
            my $document = PPI::Document->new(\$block);
542
543
            # [% t('foo') %] is compiled to
544
            # $output .= $stash->get(['t', ['foo']]);
545
            # We try to find all nodes corresponding to keyword (here 't')
546
            my $nodes = $document->find(sub {
547
                my ($topnode, $element) = @_;
548
549
                # Filter out non-valid keywords
550
                return 0 unless ($element->isa('PPI::Token::Quote::Single'));
551
                return 0 unless (grep {$element->content eq qq{'$_'}} @keywords);
552
553
                # keyword (e.g. 't') should be the first element of the arrayref
554
                # passed to $stash->get()
555
                return 0 if $element->sprevious_sibling;
556
557
                return 0 unless $element->snext_sibling
558
                    && $element->snext_sibling->snext_sibling
559
                    && $element->snext_sibling->snext_sibling->isa('PPI::Structure::Constructor');
560
561
                # Check that it's indeed a call to $stash->get()
562
                my $statement = $element->statement->parent->statement->parent->statement;
563
                return 0 unless grep { $_->isa('PPI::Token::Symbol') && $_->content eq '$stash' } $statement->children;
564
                return 0 unless grep { $_->isa('PPI::Token::Operator') && $_->content eq '->' } $statement->children;
565
                return 0 unless grep { $_->isa('PPI::Token::Word') && $_->content eq 'get' } $statement->children;
566
567
                return 1;
568
            });
569
570
            next unless $nodes;
571
572
            # Write the Perl equivalent of calls to t* functions family, so
573
            # xgettext can extract the strings correctly
574
            foreach my $node (@$nodes) {
575
                my @args = map {
576
                    $_->significant && !$_->isa('PPI::Token::Operator') ? $_->content : ()
577
                } $node->snext_sibling->snext_sibling->find_first('PPI::Statement')->children;
578
579
                my $keyword = $node->content;
580
                $keyword =~ s/^'t(.*)'$/__$1/;
581
582
                say $fh "$keyword(" . join(', ', @args) . ");";
583
            }
584
585
        }
586
587
        close $fh;
588
    }
589
590
    return $tempdir;
478
}
591
}
479
592
480
sub extract_messages {
593
sub extract_messages {
481
    my $self = shift;
594
    my $self = shift;
482
595
596
    say "Extract messages into POT file" if $self->{verbose};
597
483
    my $intranetdir = $self->{context}->config('intranetdir');
598
    my $intranetdir = $self->{context}->config('intranetdir');
484
    my @files_to_scan;
599
    my @files_to_scan;
485
    my @directories_to_scan = ('.');
600
    my @directories_to_scan = ('.');
Lines 499-524 sub extract_messages { Link Here
499
        }
614
        }
500
    }
615
    }
501
616
502
    my $xgettext_cmd = "$self->{xgettext} -L Perl --from-code=UTF-8 " .
617
    my @tt_files;
503
        "-o $Bin/$self->{domain}.pot -D $intranetdir";
618
    find(sub {
619
        if ($File::Find::dir =~ m|/en/| && $_ =~ m/\.(tt|inc)$/) {
620
            my $filename = $File::Find::name;
621
            $filename =~ s|^$intranetdir/||;
622
            push @tt_files, $filename;
623
        }
624
    }, "$intranetdir/koha-tmpl");
625
626
    my $tempdir = tempdir('Koha-translate-XXXX', TMPDIR => 1, CLEANUP => 1);
627
    $self->extract_messages_from_templates($tempdir, @tt_files);
628
    push @files_to_scan, @tt_files;
629
630
    my $xgettext_cmd = "$self->{xgettext} -L Perl --from-code=UTF-8 "
631
        . "--package-name=Koha --package-version='' "
632
        . "-k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 -k__p:1c,2 "
633
        . "-k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ -kN__n:1,2 "
634
        . "-kN__p:1c,2 -kN__np:1c,2,3 "
635
        . "-o $Bin/$self->{domain}.pot -D $tempdir -D $intranetdir";
504
    $xgettext_cmd .= " $_" foreach (@files_to_scan);
636
    $xgettext_cmd .= " $_" foreach (@files_to_scan);
505
637
506
    if (system($xgettext_cmd) != 0) {
638
    if (system($xgettext_cmd) != 0) {
507
        die "system call failed: $xgettext_cmd";
639
        die "system call failed: $xgettext_cmd";
508
    }
640
    }
509
641
510
    if ( -f "$Bin/$self->{domain}.pot" ) {
642
    my $replace_charset_cmd = "$self->{sed} --in-place " .
511
        my $replace_charset_cmd = "$self->{sed} --in-place " .
643
        "$Bin/$self->{domain}.pot " .
512
            "$Bin/$self->{domain}.pot " .
644
        "--expression='s/charset=CHARSET/charset=UTF-8/'";
513
            "--expression='s/charset=CHARSET/charset=UTF-8/'";
645
    if (system($replace_charset_cmd) != 0) {
514
        if (system($replace_charset_cmd) != 0) {
646
        die "system call failed: $replace_charset_cmd";
515
            die "system call failed: $replace_charset_cmd";
516
        }
517
    } else {
518
        print "No messages found\n" if $self->{verbose};
519
        return;
520
    }
647
    }
521
    return 1;
648
}
649
650
sub install_messages {
651
    my ($self) = @_;
652
653
    my $locale = $self->locale_name();
654
    my $modir = "$self->{path_po}/$locale/LC_MESSAGES";
655
    my $pofile = "$self->{path_po}/$self->{lang}-messages.po";
656
    my $mofile = "$modir/$self->{domain}.mo";
657
658
    if ( not -f $pofile ) {
659
        $self->create_messages();
660
    }
661
    say "Install messages ($locale)" if $self->{verbose};
662
    make_path($modir);
663
    system "$self->{msgfmt} -o $mofile $pofile";
522
}
664
}
523
665
524
sub remove_pot {
666
sub remove_pot {
Lines 532-537 sub install { Link Here
532
    return unless $self->{lang};
674
    return unless $self->{lang};
533
    $self->install_tmpl($files) unless $self->{pref_only};
675
    $self->install_tmpl($files) unless $self->{pref_only};
534
    $self->install_prefs();
676
    $self->install_prefs();
677
    $self->install_messages();
678
    $self->remove_pot();
535
}
679
}
536
680
537
681
Lines 547-560 sub get_all_langs { Link Here
547
sub update {
691
sub update {
548
    my ($self, $files) = @_;
692
    my ($self, $files) = @_;
549
    my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs();
693
    my @langs = $self->{lang} ? ($self->{lang}) : $self->get_all_langs();
550
    my $extract_ok = $self->extract_messages();
551
    for my $lang ( @langs ) {
694
    for my $lang ( @langs ) {
552
        $self->set_lang( $lang );
695
        $self->set_lang( $lang );
553
        $self->update_tmpl($files) unless $self->{pref_only};
696
        $self->update_tmpl($files) unless $self->{pref_only};
554
        $self->update_prefs();
697
        $self->update_prefs();
555
        $self->update_messages() if $extract_ok;
698
        $self->update_messages();
556
    }
699
    }
557
    $self->remove_pot() if $extract_ok;
700
    $self->remove_pot();
558
}
701
}
559
702
560
703
Lines 563-572 sub create { Link Here
563
    return unless $self->{lang};
706
    return unless $self->{lang};
564
    $self->create_tmpl($files) unless $self->{pref_only};
707
    $self->create_tmpl($files) unless $self->{pref_only};
565
    $self->create_prefs();
708
    $self->create_prefs();
566
    if ($self->extract_messages()) {
709
    $self->create_messages();
567
        $self->create_messages();
710
    $self->remove_pot();
568
        $self->remove_pot();
569
    }
570
}
711
}
571
712
572
713
(-)a/t/Koha/I18N.t (+62 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
# If you need to modify the MO file to add new tests
20
# 1) msgunfmt -o Koha.po t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo
21
# 2) Edit Koha.po
22
# 3) msgfmt -o t/Koha/I18N/po/xx_XX/LC_MESSAGES/Koha.mo Koha.po
23
my @tests = (
24
    [ __('test') => 'test ツ' ],
25
    [ __x('Hello {name}', name => 'World') => 'Hello World ツ' ],
26
    [ __n('Singular', 'Plural', 0) => 'Zero ツ' ],
27
    [ __n('Singular', 'Plural', 1) => 'Singular ツ' ],
28
    [ __n('Singular', 'Plural', 2) => 'Plural ツ' ],
29
    [ __n('Singular', 'Plural', 3) => 'Plural ツ' ],
30
    [ __nx('one item', '{count} items', 0, count => 0) => 'no item ツ' ],
31
    [ __nx('one item', '{count} items', 1, count => 1) => 'one item ツ' ],
32
    [ __nx('one item', '{count} items', 2, count => 2) => '2 items ツ' ],
33
    [ __nx('one item', '{count} items', 3, count => 3) => '3 items ツ' ],
34
    [ __xn('one item', '{count} items', 0, count => 0) => 'no item ツ' ],
35
    [ __xn('one item', '{count} items', 1, count => 1) => 'one item ツ' ],
36
    [ __xn('one item', '{count} items', 2, count => 2) => '2 items ツ' ],
37
    [ __xn('one item', '{count} items', 3, count => 3) => '3 items ツ' ],
38
    [ __p('biblio', 'title') => 'title (biblio) ツ' ],
39
    [ __p('patron', 'title') => 'title (patron) ツ' ],
40
    [ __px('biblio', 'Remove item {id}', id => 42) => 'Remove item 42 (biblio) ツ' ],
41
    [ __px('list', 'Remove item {id}', id => 42) => 'Remove item 42 (list) ツ' ],
42
    [ __np('ctxt1', 'singular', 'plural', 0) => 'zero (ctxt1) ツ' ],
43
    [ __np('ctxt1', 'singular', 'plural', 1) => 'singular (ctxt1) ツ' ],
44
    [ __np('ctxt1', 'singular', 'plural', 2) => 'plural (ctxt1) ツ' ],
45
    [ __np('ctxt1', 'singular', 'plural', 3) => 'plural (ctxt1) ツ' ],
46
    [ __np('ctxt2', 'singular', 'plural', 0) => 'zero (ctxt2) ツ' ],
47
    [ __np('ctxt2', 'singular', 'plural', 1) => 'singular (ctxt2) ツ' ],
48
    [ __np('ctxt2', 'singular', 'plural', 2) => 'plural (ctxt2) ツ' ],
49
    [ __np('ctxt2', 'singular', 'plural', 3) => 'plural (ctxt2) ツ' ],
50
    [ __npx('biblio', 'one item', '{count} items', 0, count => 0) => 'no item (biblio) ツ' ],
51
    [ __npx('biblio', 'one item', '{count} items', 1, count => 1) => 'one item (biblio) ツ' ],
52
    [ __npx('biblio', 'one item', '{count} items', 2, count => 2) => '2 items (biblio) ツ' ],
53
    [ __npx('biblio', 'one item', '{count} items', 3, count => 3) => '3 items (biblio) ツ' ],
54
    [ __npx('list', 'one item', '{count} items', 0, count => 0) => 'no item (list) ツ' ],
55
    [ __npx('list', 'one item', '{count} items', 1, count => 1) => 'one item (list) ツ' ],
56
    [ __npx('list', 'one item', '{count} items', 2, count => 2) => '2 items (list) ツ' ],
57
    [ __npx('list', 'one item', '{count} items', 3, count => 3) => '3 items (list) ツ' ],
58
);
59
60
foreach my $test (@tests) {
61
    is($test->[0], decode_utf8($test->[1]), $test->[1]);
62
}

Return to bug 15395