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

(-)a/Koha/I18N.pm (+121 lines)
Lines 29-38 our @EXPORT = qw( Link Here
29
    __px
29
    __px
30
    __np
30
    __np
31
    __npx
31
    __npx
32
33
    __d
34
    __dn
35
    __dp
36
    __dnp
37
    __dx
38
    __dnx
39
    __dpx
40
    __dnpx
41
32
    N__
42
    N__
33
    N__n
43
    N__n
34
    N__p
44
    N__p
35
    N__np
45
    N__np
46
47
    N__d
48
    N__dn
49
    N__dp
50
    N__dnp
36
);
51
);
37
52
38
our @EXPORT_OK = qw(
53
our @EXPORT_OK = qw(
Lines 46-51 use Encode; Link Here
46
use List::Util       qw( first );
61
use List::Util       qw( first );
47
use Locale::Messages qw(
62
use Locale::Messages qw(
48
    bindtextdomain
63
    bindtextdomain
64
    dgettext
65
    dngettext
66
    dnpgettext
67
    dpgettext
49
    gettext
68
    gettext
50
    LC_MESSAGES
69
    LC_MESSAGES
51
    ngettext
70
    ngettext
Lines 54-60 use Locale::Messages qw( Link Here
54
    textdomain
73
    textdomain
55
);
74
);
56
use POSIX qw();
75
use POSIX qw();
76
use Try::Tiny;
57
use Koha::Cache::Memory::Lite;
77
use Koha::Cache::Memory::Lite;
78
use Koha::Plugins;
58
79
59
our $textdomain = 'Koha';
80
our $textdomain = 'Koha';
60
81
Lines 126-131 sub init { Link Here
126
            my $directory = _base_directory();
147
            my $directory = _base_directory();
127
            textdomain($textdomain);
148
            textdomain($textdomain);
128
            bindtextdomain( $textdomain, $directory );
149
            bindtextdomain( $textdomain, $directory );
150
151
            my @plugins = Koha::Plugins->get_enabled_plugins();
152
            foreach my $plugin (@plugins) {
153
                try {
154
                    my $locale_dir = $plugin->locale_dir;
155
                    if ( $locale_dir && -d $locale_dir && -r $locale_dir ) {
156
                        bindtextdomain($plugin->locale_textdomain, $locale_dir);
157
                    }
158
                } catch {
159
                    warn sprintf('Failed to initialize translations for plugin %s: %s', ref $plugin, $_);
160
                }
161
            }
129
        } else {
162
        } else {
130
            warn "No locale installed. Localization cannot work and is therefore disabled";
163
            warn "No locale installed. Localization cannot work and is therefore disabled";
131
        }
164
        }
Lines 282-287 sub __npx { Link Here
282
    return _gettext( \&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ], %vars );
315
    return _gettext( \&npgettext, [ $msgctxt, $msgid, $msgid_plural, $count ], %vars );
283
}
316
}
284
317
318
sub __d {
319
    my ( $textdomain, $msgid ) = @_;
320
321
    $msgid = Encode::encode_utf8($msgid);
322
323
    return _gettext( \&dgettext, [ $textdomain, $msgid ] );
324
}
325
326
sub __dn {
327
    my ( $textdomain, $msgid, $msgid_plural, $count ) = @_;
328
329
    $msgid        = Encode::encode_utf8($msgid);
330
    $msgid_plural = Encode::encode_utf8($msgid_plural);
331
332
    return _gettext( \&dngettext, [ $textdomain, $msgid, $msgid_plural, $count ] );
333
}
334
335
sub __dp {
336
    my ( $textdomain, $msgctxt, $msgid ) = @_;
337
338
    $msgctxt = Encode::encode_utf8($msgctxt);
339
    $msgid   = Encode::encode_utf8($msgid);
340
341
    return _gettext( \&dpgettext, [ $textdomain, $msgctxt, $msgid ] );
342
}
343
344
sub __dnp {
345
    my ( $textdomain, $msgctxt, $msgid, $msgid_plural, $count ) = @_;
346
347
    $msgctxt      = Encode::encode_utf8($msgctxt);
348
    $msgid        = Encode::encode_utf8($msgid);
349
    $msgid_plural = Encode::encode_utf8($msgid_plural);
350
351
    return _gettext( \&dnpgettext, [ $textdomain, $msgctxt, $msgid, $msgid_plural, $count ] );
352
}
353
354
sub __dx {
355
    my ( $textdomain, $msgid, %vars ) = @_;
356
357
    $msgid = Encode::encode_utf8($msgid);
358
359
    return _gettext( \&dgettext, [ $textdomain, $msgid ], %vars );
360
}
361
362
sub __dnx {
363
    my ( $textdomain, $msgid, $msgid_plural, $count, %vars ) = @_;
364
365
    $msgid        = Encode::encode_utf8($msgid);
366
    $msgid_plural = Encode::encode_utf8($msgid_plural);
367
368
    return _gettext( \&dngettext, [ $textdomain, $msgid, $msgid_plural, $count ], %vars );
369
}
370
371
sub __dpx {
372
    my ( $textdomain, $msgctxt, $msgid, %vars ) = @_;
373
374
    $msgctxt = Encode::encode_utf8($msgctxt);
375
    $msgid   = Encode::encode_utf8($msgid);
376
377
    return _gettext( \&dpgettext, [ $textdomain, $msgctxt, $msgid ], %vars );
378
}
379
380
sub __dnpx {
381
    my ( $textdomain, $msgctxt, $msgid, $msgid_plural, $count, %vars ) = @_;
382
383
    $msgctxt      = Encode::encode_utf8($msgctxt);
384
    $msgid        = Encode::encode_utf8($msgid);
385
    $msgid_plural = Encode::encode_utf8($msgid_plural);
386
387
    return _gettext( \&dnpgettext, [ $textdomain, $msgctxt, $msgid, $msgid_plural, $count ], %vars );
388
}
389
285
=head2 N__
390
=head2 N__
286
391
287
    my $msgid = N__('Text for later translation');
392
    my $msgid = N__('Text for later translation');
Lines 331-336 sub N__np { Link Here
331
    return $_[1];
436
    return $_[1];
332
}
437
}
333
438
439
sub N__d {
440
    return $_[1];
441
}
442
443
sub N__dn {
444
    return $_[1];
445
}
446
447
sub N__dp {
448
    return $_[2];
449
}
450
451
sub N__dnp {
452
    return $_[2];
453
}
454
334
sub _base_directory {
455
sub _base_directory {
335
456
336
    # Directory structure is not the same for dev and standard installs
457
    # Directory structure is not the same for dev and standard installs
(-)a/Koha/Plugins/Base.pm (+29 lines)
Lines 305-310 sub bundle_path { Link Here
305
    return $self->{_bundle_path};
305
    return $self->{_bundle_path};
306
}
306
}
307
307
308
=head2 locale_dir
309
310
Returns the directory where locale files (.mo) are stored
311
312
=cut
313
314
sub locale_dir {
315
    my ($self) = @_;
316
317
    return $self->mbf_exists('locale') ? $self->mbf_path('locale') : undef;
318
}
319
320
=head2 locale_dir
321
322
Returns the locale textdomain for this plugin. The textdomain needs to be
323
passed to functions __d, __dn, __dp, __dnp, __dx, __dnx, __dpx, __dnpx.
324
It is also the name of the .mo file that should be present in locale_dir
325
326
By default it's the plugin name with '::' replaced by '-', so for a plugin
327
named Koha::Plugin::Foo, the text domain will be Koha-Plugin-Foo
328
329
=cut
330
331
sub locale_textdomain {
332
    my ($self) = @_;
333
334
    return ( ref $self ) =~ s/::/-/gr;
335
}
336
308
=head2 output
337
=head2 output
309
338
310
   $self->output( $data, $content_type[, $status[, $extra_options]]);
339
   $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