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

(-)a/Koha/I18N.pm (+122 lines)
Lines 26-31 use Encode; Link Here
26
use List::Util       qw( first );
26
use List::Util       qw( first );
27
use Locale::Messages qw(
27
use Locale::Messages qw(
28
    bindtextdomain
28
    bindtextdomain
29
    dgettext
30
    dngettext
31
    dnpgettext
32
    dpgettext
29
    gettext
33
    gettext
30
    LC_MESSAGES
34
    LC_MESSAGES
31
    ngettext
35
    ngettext
Lines 34-40 use Locale::Messages qw( Link Here
34
    textdomain
38
    textdomain
35
);
39
);
36
use POSIX qw();
40
use POSIX qw();
41
use Try::Tiny;
37
use Koha::Cache::Memory::Lite;
42
use Koha::Cache::Memory::Lite;
43
use Koha::Plugins;
38
44
39
use parent 'Exporter';
45
use parent 'Exporter';
40
our @EXPORT = qw(
46
our @EXPORT = qw(
Lines 47-56 our @EXPORT = qw( Link Here
47
    __px
53
    __px
48
    __np
54
    __np
49
    __npx
55
    __npx
56
57
    __d
58
    __dn
59
    __dp
60
    __dnp
61
    __dx
62
    __dnx
63
    __dpx
64
    __dnpx
65
50
    N__
66
    N__
51
    N__n
67
    N__n
52
    N__p
68
    N__p
53
    N__np
69
    N__np
70
71
    N__d
72
    N__dn
73
    N__dp
74
    N__dnp
54
);
75
);
55
76
56
our $textdomain = 'Koha';
77
our $textdomain = 'Koha';
Lines 84-89 sub init { Link Here
84
            my $directory = _base_directory();
105
            my $directory = _base_directory();
85
            textdomain($textdomain);
106
            textdomain($textdomain);
86
            bindtextdomain( $textdomain, $directory );
107
            bindtextdomain( $textdomain, $directory );
108
109
            my @plugins = Koha::Plugins->get_enabled_plugins();
110
            foreach my $plugin (@plugins) {
111
                try {
112
                    my $locale_dir = $plugin->locale_dir;
113
                    if ( $locale_dir && -d $locale_dir && -r $locale_dir ) {
114
                        bindtextdomain($plugin->locale_textdomain, $locale_dir);
115
                    }
116
                } catch {
117
                    warn sprintf('Failed to initialize translations for plugin %s: %s', ref $plugin, $_);
118
                }
119
            }
87
        } else {
120
        } else {
88
            warn "No locale installed. Localization cannot work and is therefore disabled";
121
            warn "No locale installed. Localization cannot work and is therefore disabled";
89
        }
122
        }
Lines 168-173 sub __npx { Link Here
168
    return _gettext( \&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ], %vars );
201
    return _gettext( \&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ], %vars );
169
}
202
}
170
203
204
sub __d {
205
    my ( $textdomain, $msgid ) = @_;
206
207
    $msgid = Encode::encode_utf8($msgid);
208
209
    return _gettext( \&dgettext, [ $textdomain, $msgid ] );
210
}
211
212
sub __dn {
213
    my ( $textdomain, $msgid, $msgid_plural, $count ) = @_;
214
215
    $msgid        = Encode::encode_utf8($msgid);
216
    $msgid_plural = Encode::encode_utf8($msgid_plural);
217
218
    return _gettext( \&dngettext, [ $textdomain, $msgid, $msgid_plural, $count ] );
219
}
220
221
sub __dp {
222
    my ( $textdomain, $msgctxt, $msgid ) = @_;
223
224
    $msgctxt = Encode::encode_utf8($msgctxt);
225
    $msgid   = Encode::encode_utf8($msgid);
226
227
    return _gettext( \&dpgettext, [ $textdomain, $msgctxt, $msgid ] );
228
}
229
230
sub __dnp {
231
    my ( $textdomain, $msgctxt, $msgid, $msgid_plural, $count ) = @_;
232
233
    $msgctxt      = Encode::encode_utf8($msgctxt);
234
    $msgid        = Encode::encode_utf8($msgid);
235
    $msgid_plural = Encode::encode_utf8($msgid_plural);
236
237
    return _gettext( \&dnpgettext, [ $textdomain, $msgctxt, $msgid, $msgid_plural, $count ] );
238
}
239
240
sub __dx {
241
    my ( $textdomain, $msgid, %vars ) = @_;
242
243
    $msgid = Encode::encode_utf8($msgid);
244
245
    return _gettext( \&dgettext, [ $textdomain, $msgid ], %vars );
246
}
247
248
sub __dnx {
249
    my ( $textdomain, $msgid, $msgid_plural, $count, %vars ) = @_;
250
251
    $msgid        = Encode::encode_utf8($msgid);
252
    $msgid_plural = Encode::encode_utf8($msgid_plural);
253
254
    return _gettext( \&dngettext, [ $textdomain, $msgid, $msgid_plural, $count ], %vars );
255
}
256
257
sub __dpx {
258
    my ( $textdomain, $msgctxt, $msgid, %vars ) = @_;
259
260
    $msgctxt = Encode::encode_utf8($msgctxt);
261
    $msgid   = Encode::encode_utf8($msgid);
262
263
    return _gettext( \&dpgettext, [ $textdomain, $msgctxt, $msgid ], %vars );
264
}
265
266
sub __dnpx {
267
    my ( $textdomain, $msgctxt, $msgid, $msgid_plural, $count, %vars ) = @_;
268
269
    $msgctxt      = Encode::encode_utf8($msgctxt);
270
    $msgid        = Encode::encode_utf8($msgid);
271
    $msgid_plural = Encode::encode_utf8($msgid_plural);
272
273
    return _gettext( \&dnpgettext, [ $textdomain, $msgctxt, $msgid, $msgid_plural, $count ], %vars );
274
}
275
276
171
sub N__ {
277
sub N__ {
172
    return $_[0];
278
    return $_[0];
173
}
279
}
Lines 184-189 sub N__np { Link Here
184
    return $_[1];
290
    return $_[1];
185
}
291
}
186
292
293
sub N__d {
294
    return $_[1];
295
}
296
297
sub N__dn {
298
    return $_[1];
299
}
300
301
sub N__dp {
302
    return $_[2];
303
}
304
305
sub N__dnp {
306
    return $_[2];
307
}
308
187
sub _base_directory {
309
sub _base_directory {
188
310
189
    # Directory structure is not the same for dev and standard installs
311
    # Directory structure is not the same for dev and standard installs
(-)a/Koha/Plugins/Base.pm (+29 lines)
Lines 273-278 sub bundle_path { Link Here
273
    return $self->{_bundle_path};
273
    return $self->{_bundle_path};
274
}
274
}
275
275
276
=head2 locale_dir
277
278
Returns the directory where locale files (.mo) are stored
279
280
=cut
281
282
sub locale_dir {
283
    my ($self) = @_;
284
285
    return $self->mbf_exists('locale') ? $self->mbf_path('locale') : undef;
286
}
287
288
=head2 locale_dir
289
290
Returns the locale textdomain for this plugin. The textdomain needs to be
291
passed to functions __d, __dn, __dp, __dnp, __dx, __dnx, __dpx, __dnpx.
292
It is also the name of the .mo file that should be present in locale_dir
293
294
By default it's the plugin name with '::' replaced by '-', so for a plugin
295
named Koha::Plugin::Foo, the text domain will be Koha-Plugin-Foo
296
297
=cut
298
299
sub locale_textdomain {
300
    my ($self) = @_;
301
302
    return ( ref $self ) =~ s/::/-/gr;
303
}
304
276
=head2 output
305
=head2 output
277
306
278
   $self->output( $data, $content_type[, $status[, $extra_options]]);
307
   $self->output( $data, $content_type[, $status[, $extra_options]]);
(-)a/Koha/Template/Plugin/I18N.pm (-1 / +114 lines)
Lines 22-28 use Modern::Perl; Link Here
22
use base qw( Template::Plugin );
22
use base qw( Template::Plugin );
23
23
24
use C4::Context;
24
use C4::Context;
25
use Koha::I18N qw( __ __n __np __npx __nx __p __px __x __xn );
25
use Koha::I18N qw( __ __n __np __npx __nx __p __px __x __xn __d __dn __dp __dnp __dx __dnx __dpx __dnpx);
26
26
27
=head1 NAME
27
=head1 NAME
28
28
Lines 42-47 Koha::Template::Plugin::I18N - Translate strings in templates Link Here
42
    [% I18N.tnp('bibliographic material', "item", "items", count) %]
42
    [% I18N.tnp('bibliographic material', "item", "items", count) %]
43
    [% I18N.tnpx('bibliographic material', "{count} item", "{count} items", count, { count = count }) %]
43
    [% I18N.tnpx('bibliographic material', "{count} item", "{count} items", count, { count = count }) %]
44
44
45
    [%# For plugins %]
46
    [% I18N.td("MyPlugin", "Hello!") %]
47
    [% I18N.tdx("MyPlugin", "Hello {name}", { name = name }) %]
48
    [% I18N.tdn("MyPlugin", "Hello friend", "Hello friends", count) %]
49
    [% I18N.tdnx("MyPlugin", "Hello my {count} friend", "Hello my {count} friends", count, { count = count }) %]
50
    [% I18N.tdp("MyPlugin", 'verb', 'Item') # to order %]
51
    [% I18N.tdnp("MyPlugin", 'bibliographic material', "item", "items", count) %]
52
    [% I18N.tdnpx("MyPlugin", 'bibliographic material', "{count} item", "{count} items", count, { count = count }) %]
53
45
Do not use this plugin directly. Add the following directive
54
Do not use this plugin directly. Add the following directive
46
55
47
    [% PROCESS 'i18n.inc' %]
56
    [% PROCESS 'i18n.inc' %]
Lines 183-186 sub tnpx { Link Here
183
    return __npx( $msgctxt, $msgid, $msgid_plural, $count, %$vars );
192
    return __npx( $msgctxt, $msgid, $msgid_plural, $count, %$vars );
184
}
193
}
185
194
195
=head2 td
196
197
    [% I18N.td("TextDomain", "hello") %]
198
199
Same as t, but with textdomain as first argument. Useful for plugins
200
201
=cut
202
203
sub td {
204
    my ($self, $textdomain, $msgid) = @_;
205
    return __d($textdomain, $msgid);
206
}
207
208
=head2 tdn
209
210
    [% I18N.tdn("TextDomain", "item", "items", count) %]
211
212
Same as tn, but with textdomain as first argument. Useful for plugins
213
214
=cut
215
216
sub tdn {
217
    my ( $self, $textdomain, $msgid, $msgid_plural, $count ) = @_;
218
    return __dn( $textdomain, $msgid, $msgid_plural, $count );
219
}
220
221
=head2 tdp
222
223
    [% I18N.tdp("TextDomain", "context", "hello") %]
224
225
Same as tp, but with textdomain as first argument. Useful for plugins
226
227
=cut
228
229
sub tdp {
230
    my ( $self, $textdomain, $msgctxt, $msgid ) = @_;
231
    return __p( $textdomain, $msgctxt, $msgid );
232
}
233
234
=head2 tdnp
235
236
    [% I18N.tdnp("TextDomain", "context", "item", "items", count) %]
237
238
Same as tnp, but with textdomain as first argument. Useful for plugins
239
240
=cut
241
242
sub tdnp {
243
    my ( $self, $textdomain, $msgctxt, $msgid, $msgid_plural, $count ) = @_;
244
    return __np( $textdomain, $msgctxt, $msgid, $msgid_plural, $count );
245
}
246
247
=head2 tdx
248
249
    [% I18N.tdx("TextDomain", "hello {name}", { name = name }) %]
250
251
Same as tx, but with textdomain as first argument. Useful for plugins
252
253
=cut
254
255
sub tdx {
256
    my ($self, $textdomain, $msgid, $vars) = @_;
257
    return __dx($textdomain, $msgid, %$vars);
258
}
259
260
=head2 tdnx
261
262
    [% I18N.tdnx("TextDomain", "{count} item", "{count} items", count, { count = count }) %]
263
264
Same as tnx, but with textdomain as first argument. Useful for plugins
265
266
=cut
267
268
sub tdnx {
269
    my ( $self, $textdomain, $msgid, $msgid_plural, $count, $vars ) = @_;
270
    return __dnx( $textdomain, $msgid, $msgid_plural, $count, %$vars );
271
}
272
273
=head2 tdpx
274
275
    [% I18N.tdpx("TextDomain", "context", "hello {name}", { name = name }) %]
276
277
Same as tpx, but with textdomain as first argument. Useful for plugins
278
279
=cut
280
281
sub tdpx {
282
    my ( $self, $textdomain, $msgctxt, $msgid, $vars ) = @_;
283
    return __p( $textdomain, $msgctxt, $msgid, %$vars );
284
}
285
286
=head2 tdnpx
287
288
    [% I18N.tdnpx("TextDomain", "context", "{count} item", "{count} items", count, { count = count }) %]
289
290
Same as tnpx, but with textdomain as first argument. Useful for plugins
291
292
=cut
293
294
sub tdnpx {
295
    my ( $self, $textdomain, $msgctxt, $msgid, $msgid_plural, $count, $vars ) = @_;
296
    return __npx( $textdomain, $msgctxt, $msgid, $msgid_plural, $count, %$vars );
297
}
298
186
1;
299
1;
(-)a/gulpfile.js (-10 / +2 lines)
Lines 235-248 function po_extract_opac() { Link Here
235
        .pipe(dest("misc/translator"));
235
        .pipe(dest("misc/translator"));
236
}
236
}
237
237
238
const xgettext_options =
239
    "--from-code=UTF-8 --package-name Koha " +
240
    "--package-version= -k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 " +
241
    "-k__p:1c,2 -k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ " +
242
    "-kN__n:1,2 -kN__p:1c,2 -kN__np:1c,2,3 " +
243
    "-k -k$__ -k$__x -k$__n:1,2 -k$__nx:1,2 -k$__xn:1,2 " +
244
    "--force-po";
245
246
function po_extract_messages_js() {
238
function po_extract_messages_js() {
247
    const globs = [
239
    const globs = [
248
        "koha-tmpl/intranet-tmpl/prog/js/vue/**/*.vue",
240
        "koha-tmpl/intranet-tmpl/prog/js/vue/**/*.vue",
Lines 253-259 function po_extract_messages_js() { Link Here
253
    return src(globs, { read: false, nocase: true })
245
    return src(globs, { read: false, nocase: true })
254
        .pipe(
246
        .pipe(
255
            xgettext(
247
            xgettext(
256
                `xgettext -L JavaScript ${xgettext_options}`,
248
                `misc/translator/xgettext-js`,
257
                "Koha-messages-js.pot"
249
                "Koha-messages-js.pot"
258
            )
250
            )
259
        )
251
        )
Lines 264-270 function po_extract_messages() { Link Here
264
    const perlStream = src(["**/*.pl", "**/*.pm"], {
256
    const perlStream = src(["**/*.pl", "**/*.pm"], {
265
        read: false,
257
        read: false,
266
        nocase: true,
258
        nocase: true,
267
    }).pipe(xgettext(`xgettext -L Perl ${xgettext_options}`, "Koha-perl.pot"));
259
    }).pipe(xgettext('misc/translator/xgettext-perl', "Koha-perl.pot"));
268
260
269
    const ttStream = src(
261
    const ttStream = src(
270
        [
262
        [
(-)a/misc/translator/xgettext-js (+19 lines)
Line 0 Link Here
1
#!/bin/sh
2
3
exec xgettext \
4
    -L JavaScript \
5
    --from-code=UTF-8 \
6
    --package-name Koha \
7
    --package-version= \
8
    --force-po \
9
    -k \
10
    -k'__'  -k'__n:1,2'  -k'__p:1c,2'  -k'__np:1c,2,3'  \
11
    -k'__x' -k'__nx:1,2' -k'__px:1c,2' -k'__npx:1c,2,3' \
12
            -k'__xn:1,2' \
13
    -k'__d:2'  -k'__dn:2,3'  -k'__dp:2c,3'  -k'__dnp:2c,3,4'  \
14
    -k'__dx:2' -k'__dnx:2,3' -k'__dpx:2c,3' -k'__dnpx:2c,3,4' \
15
    -k'$__'    -k'$__n:1,2'  -k'$__p:1c,2'  -k'$__np:1c,2,3'  \
16
    -k'$__d:2' -k'$__dn:2,3' -k'$__dp:2c,3' -k'$__dnp:2c,3,4' \
17
    -k'N__'    -k'N__n:1,2'  -k'N__p:1c,2'  -k'N__np:1c,2,3'  \
18
    -k'N__d:2' -k'N__dn:2,3' -k'N__dp:2c,3' -k'N__dnp:2c,3,4' \
19
    "$@"
(-)a/misc/translator/xgettext-perl (+17 lines)
Line 0 Link Here
1
#!/bin/sh
2
3
exec xgettext \
4
    -L Perl \
5
    --from-code=UTF-8 \
6
    --package-name Koha \
7
    --package-version= \
8
    --force-po \
9
    -k \
10
    -k'__'  -k'__n:1,2'  -k'__p:1c,2'  -k'__np:1c,2,3' \
11
    -k'__x' -k'__nx:1,2' -k'__px:1c,2' -k'__npx:1c,2,3' \
12
            -k'__xn:1,2' \
13
    -k'__d:2'  -k'__dn:2,3'  -k'__dp:2c,3'  -k'__dnp:2c,3,4' \
14
    -k'__dx:2' -k'__dnx:2,3' -k'__dpx:2c,3' -k'__dnpx:2c,3,4' \
15
    -k'N__'    -k'N__n:1,2'  -k'N__p:1c,2'  -k'N__np:1c,2,3' \
16
    -k'N__d:2' -k'N__dn:2,3' -k'N__dp:2c,3' -k'N__dnp:2c,3,4' \
17
    "$@"
(-)a/misc/translator/xgettext-tt2 (-1 / +17 lines)
Lines 37-42 sub defaultKeywords { Link Here
37
        'tpx:1c,2',
37
        'tpx:1c,2',
38
        'tnp:1c,2,3',
38
        'tnp:1c,2,3',
39
        'tnpx:1c,2,3',
39
        'tnpx:1c,2,3',
40
        'dt:2',
41
        'dtx:2',
42
        'dtn:2,3',
43
        'dtnx:2,3',
44
        'dtxn:2,3',
45
        'dtp:2c,3',
46
        'dtpx:2c,3',
47
        'dtnp:2c,3,4',
48
        'dtnpx:2c,3,4',
40
    ];
49
    ];
41
}
50
}
42
51
Lines 50-55 sub defaultFlags { Link Here
50
        'tpx:2:perl-brace-format',
59
        'tpx:2:perl-brace-format',
51
        'tnpx:2:perl-brace-format',
60
        'tnpx:2:perl-brace-format',
52
        'tnpx:3:perl-brace-format',
61
        'tnpx:3:perl-brace-format',
62
        'dtx:2:perl-brace-format',
63
        'dtnx:2:perl-brace-format',
64
        'dtnx:3:perl-brace-format',
65
        'dtxn:2:perl-brace-format',
66
        'dtxn:3:perl-brace-format',
67
        'dtpx:3:perl-brace-format',
68
        'dtnpx:3:perl-brace-format',
69
        'dtnpx:4:perl-brace-format',
53
    ],
70
    ],
54
}
71
}
55
72
56
- 

Return to bug 39564