File Coverage

blib/lib/Locale/Utils/Autotranslator.pm
Criterion Covered Total %
statement 270 272 99.2
branch 90 116 77.5
condition 18 31 58.0
subroutine 44 44 100.0
pod 5 5 100.0
total 427 468 91.2


line stmt bran cond sub pod time code
1             package Locale::Utils::Autotranslator; ## no critic (TidyCode)
2            
3 9     9   165382 use strict;
  9         33  
  9         203  
4 9     9   42 use warnings;
  9         16  
  9         202  
5 9     9   38 use Carp qw(confess);
  9         16  
  9         351  
6 9     9   3577 use Encode qw(decode encode_utf8 find_encoding);
  9         62033  
  9         480  
7 9     9   3632 use Locale::PO;
  9         29257  
  9         233  
8 9     9   3536 use Locale::TextDomain::OO::Util::ExtractHeader;
  9         554518  
  9         240  
9 9     9   1349 use Moo;
  9         16277  
  9         66  
10 9     9   7726 use MooX::StrictConstructor;
  9         84703  
  9         64  
11 9     9   149654 use MooX::Types::MooseLike::Base qw(CodeRef Str);
  9         46146  
  9         569  
12 9     9   3419 use MooX::Types::MooseLike::Numeric qw(PositiveInt);
  9         9552  
  9         400  
13 9     9   53 use Try::Tiny;
  9         16  
  9         336  
14 9     9   41 use namespace::autoclean;
  9         16  
  9         137  
15            
16             our $VERSION = '1.008';
17            
18             # plural_ref e.g. ru
19             # The key is the plural form 0, 1 or 2.
20             # The value is the first number 0 .. that is resulting in that plural form.
21             # 0 => 1, # singular
22             # 1 => 2, # 2 .. 4 plural
23             # 2 => 5, # 5 .. plural
24             has _plural_ref => (
25             is => 'ro',
26             init_arg => undef,
27             default => sub { {} },
28             );
29            
30             sub _clear_plural_ref {
31 5     5   10 my $self = shift;
32            
33 5         8 %{ $self->_plural_ref } = ();
  5         23  
34            
35 5         9 return;
36             }
37            
38             # to store the original gettext parts by placeholder number
39             # e.g.
40             # %*(%1,singular,plural,zero)
41             # 1 => [ '*', 'singular', 'plural'. 'zero' ],
42             # %quant(%2,singular,plural)
43             # 2 => [ 'quant', 'singular', 'plural' ],
44             has _gettext_ref => (
45             is => 'ro',
46             init_arg => undef,
47             default => sub { {} },
48             );
49            
50             sub _clear_gettext_ref {
51 7     7   12 my $self = shift;
52            
53 7         8 %{ $self->_gettext_ref } = ();
  7         26  
54            
55 7         14 return;
56             }
57            
58             has _num_ref => (
59             is => 'ro',
60             init_arg => undef,
61             default => sub { {} },
62             );
63            
64             sub _clear_num_ref {
65 6     6   8 my $self = shift;
66            
67 6         7 %{ $self->_num_ref } = ();
  6         18  
68            
69 6         10 return;
70             }
71            
72             has error => (
73             is => 'rw',
74             init_arg => undef,
75             writer => '_error',
76             clearer => '_clear_error',
77             );
78            
79             has translation_count => (
80             is => 'rw',
81             init_arg => undef,
82             default => 0,
83             writer => '_translation_count',
84             );
85            
86             has item_translation_count => (
87             is => 'rw',
88             init_arg => undef,
89             default => 0,
90             writer => '_item_translation_count',
91             );
92            
93             sub _translation_count_increment {
94 26     26   33 my $self = shift;
95            
96 26         72 $self->_translation_count( $self->translation_count + 1 );
97            
98 26         33 return;
99             }
100            
101             sub _item_translation_count_increment {
102 33     33   41 my $self = shift;
103            
104 33         80 $self->_item_translation_count( $self->item_translation_count + 1 );
105            
106 33         47 return;
107             }
108            
109             has developer_language => (
110             is => 'ro',
111             isa => Str,
112             default => 'en',
113             );
114            
115             has language => (
116             is => 'ro',
117             isa => Str,
118             required => 1,
119             );
120            
121             has [ qw( before_translation_code after_translation_code ) ] => (
122             is => 'ro',
123             isa => CodeRef,
124             );
125            
126             has bytes_max => (
127             is => 'ro',
128             isa => PositiveInt,
129             );
130            
131             has comment => (
132             is => 'rw',
133             isa => Str,
134             clearer => '_clear_comment',
135             );
136            
137             # Coding schema:
138             # a .. w, z => A .. W, Z
139             # A .. W, Z => YA .. YW, YZ
140             # space => YX
141             # open, e.g. { => XX
142             # : => XY
143             # close, e.g. } => XZ
144             # other => XAA .. XPP
145             # like hex but
146             # 0123456789ABCDEF is
147             # ABCDEFGHIJKLMNOP
148             # not valid => XQ .. XW, YY
149            
150             my $encode_az = sub {
151             my $inner = shift;
152             local *__ANON__ = '$encode_az->'; ## no critic (InterpolationOfMetachars LocalVars)
153            
154             my $encode_inner = sub {
155             my ( $lc, $uc, $space, $colon, $other ) = @_;
156             local *__ANON__ = '$encode_inner->'; ## no critic (InterpolationOfMetachars LocalVars)
157            
158             defined $lc
159             and return uc $lc;
160             defined $uc
161             and return q{Y} . $uc;
162             defined $space
163             and return 'YX';
164             defined $colon
165             and return 'XY';
166            
167             $other = ord $other;
168             $other > 255 ## no critic (MagicNumbers)
169             and confess 'encode error Xnn overflow';
170             my $digit2 = int $other / 16; ## no critic (MagicNumbers)
171             my $digit1 = $other % 16; ## no critic (MagicNumbers)
172             for my $digit ( $digit2, $digit1 ) {
173             $digit = [ q{A} .. q{P} ]->[$digit];
174             }
175            
176             return q{X} . $digit2 . $digit1;
177             };
178            
179             $inner =~ s{
180             ( [a-wz] )
181             | ( [A-WZ] )
182             | ( [ ] )
183             | ( [:] )
184             | ( . )
185             }
186             {
187             $encode_inner->($1, $2, $3, $4, $5, $6)
188             }xmsge;
189            
190             return 'XX'. $inner . 'XZ';
191             };
192            
193 6     6   9 sub _encode_named_placeholder {
194             my ( $self, $placeholder ) = @_;
195            
196 6         25 ## no critic (EscapedMetacharacters)
197             $placeholder =~ s{
198             ( \\ \{ )
199             | \{ ( [^\{\}]* ) \}
200 6 50       19 }
201             {
202             $1
203             || $encode_az->($2)
204             }xmsge;
205 6         34 ## use critic (EscapedMetacharacters)
206            
207             return $placeholder;
208             }
209            
210             my $decode_inner = sub {
211             my $inner = shift;
212             local *__ANON__ = '$decode_inner->'; ## no critic (InterpolationOfMetachars LocalVars)
213            
214             my @chars = $inner =~ m{ (.) }xmsg;
215             my $decoded = q{};
216             CHAR:
217             while ( @chars ) {
218             my $char = shift @chars;
219             if ( $char =~ m{ \A [A-WZ] \z }xms ) {
220             $decoded .= lc $char;
221             next CHAR;
222             }
223             if ( $char eq q{Y} ) {
224             @chars
225             or return "DECODE_ERROR_Y($inner)";
226             my $char2 = shift @chars;
227             $decoded .= $char2 eq q{X}
228             ? q{ }
229             : uc $char2;
230             next CHAR;
231             }
232             if ( $char eq q{X} ) {
233             @chars
234             or return "DECODE_ERROR_Xn($inner)";
235             my $char2 = shift @chars;
236             if ( $char2 eq q{Y} ) {
237             $decoded .= q{:};
238             next CHAR;
239             }
240             @chars
241             or return "DECODE_ERROR_Xnn($inner)";
242             my $char3 = shift @chars;
243             my $decode_string = 'ABCDEFGHIJKLMNOP';
244             my $index2 = index $decode_string, $char2;
245             $index2 == -1 ## no critic (MagicNumbers)
246             and return "DECODE_ERROR_X?($inner)";
247             my $index1 = index $decode_string, $char3;
248             $index1 == -1 ## no critic (MagicNumbers)
249             and return "DECODE_ERROR_Xn?($inner)";
250             $decoded .= chr $index2 * 16 + $index1; ## no critic (MagicNumbers)
251             next CHAR;
252             }
253             return "DECODE_ERROR($inner)";
254             }
255            
256             return $decoded;
257             };
258 6     6   13
259             sub _decode_named_placeholder {
260 6         20 my ( $self, $placeholder ) = @_;
261            
262             $placeholder =~ s{
263             XX
264             ( [[:upper:]]+ )
265 3         5 XZ
266             }
267             {
268 6         11 q[{] . $decode_inner->($1) . q[}]
269             }xmsge;
270            
271             return $placeholder;
272 2     2   4 }
273            
274 2 50       7 sub _prepare_plural {
275             my ( $self, $nplurals, $plural_code ) = @_;
276            
277             exists $self->_plural_ref->{0}
278             and return;
279 2         4
280 4         10 ## no critic (MagicNumbers)
281 4 50       1152 NUMBER:
282 0         0 for ( 0 .. 1000 ) {
283             my $plural = $plural_code->($_);
284             if ( $plural > ( $nplurals - 1 ) ) {
285             confess sprintf
286             'Using plural formula with value %s. Got index %s. nplurals is %s. Then the maximal expected index is %s',
287             $_,
288             $plural,
289 4 50       26 $nplurals,
290 4         10 $nplurals - 1;
291             }
292 4 100       5 if ( ! exists $self->_plural_ref->{$plural} ) {
  4         22  
293             $self->_plural_ref->{$plural} = $_;
294             }
295             $nplurals == ( keys %{ $self->_plural_ref } )
296             and last NUMBER;
297 2         4 }
298             ## use critic (MagicNumbers)
299            
300             return;
301 5     5 1 476 }
302            
303 5 50       17 sub translate { ## no critic (ExcessComplexity)
304             my ( $self, $name_read, $name_write ) = @_;
305 5 50       15
306             defined $name_read
307 5 50       37 or confess 'Undef is not a name of a po/pot file';
308             defined $name_write
309             or confess 'Undef is not a name of a po file';
310 5   33     6694 my $pos_ref = Locale::PO->load_file_asarray($name_read)
311             or confess "$name_read is not a valid po/pot file";
312            
313             my $header = Locale::TextDomain::OO::Util::ExtractHeader
314             ->instance
315             ->extract_header_msgstr(
316             Locale::PO->dequote(
317             $pos_ref->[0]->msgstr
318 5         14711 || confess "No header found in file $name_read",
319 5         23 ),
320 5         721 );
321 5         9 my $charset = $header->{charset};
322 5         107 my $encode_obj = find_encoding($charset);
323 5         48 my $nplurals = $header->{nplurals};
324 5         10 my $plural_code = $header->{plural_code};
325             $self->_clear_error;
326 5         33 $self->_clear_plural_ref;
327 5         17 my $entry_ref = { encode_obj => $encode_obj };
328            
329             $self->_translation_count(0);
330 5     5   245 $self->_item_translation_count(0);
  5         20  
  5         10  
331 13         243 try {
332             MESSAGE:
333 13   33     78 for my $po ( @{$pos_ref}[ 1 .. $#{$pos_ref} ] ) { # without 0 = header
334             $self->_clear_comment;
335             $entry_ref->{msgid}
336 13   66     421 = $po->msgid
337             && $encode_obj->decode( $po->dequote( $po->msgid ) );
338             $entry_ref->{msgid_plural}
339 13   66     123 = defined $po->msgid_plural
340             && $encode_obj->decode( $po->dequote( $po->msgid_plural ) );
341             $entry_ref->{msgstr}
342 13 50       242 = defined $po->msgstr
343 13         27 && $po->dequote( $po->msgstr );
344 13   100     31 length $entry_ref->{msgstr}
345 13         82 and next MESSAGE;
346 13         31 $entry_ref->{msgstr_n} = {};
347             my $msgstr_n = $po->msgstr_n || {};
348             my $is_all_translated = 1;
349 26   66     83 for my $index ( 0 .. ( $nplurals - 1 ) ) {
350             $entry_ref->{msgstr_n}->{$index}
351             = defined $msgstr_n->{$index}
352 26   33     152 && $po->dequote( $msgstr_n->{$index} );
353 26   66     79 my $is_translated
354             = defined $entry_ref->{msgstr_n}->{$index}
355             && length $entry_ref->{msgstr_n}->{$index};
356 13 50       22 $is_all_translated &&= $is_translated;
357 13 100       79 }
    100          
    100          
358 2 50       7 $is_all_translated
359 2         10 and next MESSAGE;
360             if ( length $entry_ref->{msgid_plural} ) {
361 2         23 if ( $nplurals ) {
362             $self->_prepare_plural($nplurals, $plural_code);
363             }
364             $self->_translate_named_plural($entry_ref, $po);
365 2         9 }
366             ## no critic (EscapedMetacharacters)
367             elsif ( $entry_ref->{msgid} =~ m{ \{ [^\{\}]+ \} }xms ) {
368             $self->_translate_named($entry_ref, $po);
369 7         34 }
370             ## use critic (EscapedMetacharacters)
371             elsif ( $entry_ref->{msgid} =~ m{ [%] (?: \d | [*] | quant ) }xms ) {
372 2         10 $self->_translate_gettext($entry_ref, $po);
373             }
374 10         23 else {
375             $self->_translate_simple($entry_ref, $po);
376 2 50       13 }
377 2         10 $self->_update_comment($po);
378             }
379             if ( $self->translation_count ) {
380             Locale::PO->save_file_fromarray($name_write, $pos_ref);
381 3 100   3   2400 }
382 1         5 }
383             catch {
384             if ( $self->translation_count ) {
385             Locale::PO->save_file_fromarray($name_write, $pos_ref);
386             }
387             ## no critic (ComplexRegexes)
388             m{
389             \A \QAPI error: \E
390             |
391             \A \QByte limit exceeded, \E
392             |
393 3 50       914 \A (?: Before | After )
394             (?: \Q paragraph\E | \Q line\E )?
395 5         40 \Q translation break\E \b
396             }xms or confess $_;
397 5         2052 ## use critic (ComplexRegexes)
398             };
399            
400             return $self;
401 6     6   11 }
402            
403 6 100       30 sub _encode_named {
404 6         27 my ( $self, $msgid, $num ) = @_;
405            
406 12     12   31 $num = defined $num ? $num : 1;
407 12         34 $self->_clear_num_ref;
408 12 100       24 my $encode_placeholder = sub {
409 6         16 my ( $placeholder, $is_num ) = @_;
410 6         33 local *__ANON__ = '$encode_placeholder->'; ## no critic (InterpolationOfMetachars LocalVars)
411             if ( $is_num ) {
412 6         18 $self->_num_ref->{$num} = $placeholder;
413 6         27 return $num++;
414             }
415 6         53 return $self->_encode_named_placeholder($placeholder);
416             };
417             ## no critic (EscapedMetacharacters)
418             $msgid =~ s{
419             ( \\ \{ )
420             | (
421             \{
422             [^\{\}:]+
423             ( [:] ( num )? [^\{\}]* )?
424 12 50       48 \}
425             )
426             }
427             {
428             $1
429 6         21 || $encode_placeholder->($2, $3)
430             }xmsge;
431             ## use critic (EscapedMetacharacters)
432            
433 6     6   10 return $msgid;
434             }
435 6         23
436 6 100       39 sub _decode_named {
437             my ( $self, $msgstr ) = @_;
438 6         17
439             $msgstr =~ s{ ( \d+ ) }{
440 6         12 exists $self->_num_ref->{$1} ? $self->_num_ref->{$1} : $1
441             }xmsge;
442             $msgstr = $self->_decode_named_placeholder($msgstr);
443            
444 2     2   4 return $msgstr;
445             }
446 2         21
447 2         6 sub _translate_named {
448 2         9 my ( $self, $entry_ref, $po ) = @_;
449 2         11
450             my $msgid = $self->_encode_named( $entry_ref->{msgid} );
451 2         24 my $msgstr = $self->translate_any_msgid($msgid, 1);
452             $msgstr = $self->_decode_named($msgstr);
453             $po->msgstr( $entry_ref->{encode_obj}->encode($msgstr) );
454            
455 2     2   5 return;
456             }
457 2         4
458 2         4 sub _translate_named_plural {
459             my ( $self, $entry_ref, $po ) = @_;
460 2         4
  2         10  
461             my $msgid = $entry_ref->{msgid};
462 4 50 33     44 my $msgid_plural = $entry_ref->{msgid_plural};
463             MSGSTR_N:
464             for my $index ( sort keys %{ $self->_plural_ref } ) {
465             defined $entry_ref->{msgstr_n}->{$index}
466 4 100       29 and length $entry_ref->{msgstr_n}->{$index}
467             and next MSGSTR_N;
468             my $any_msgid = $self->_encode_named(
469 4         12 $index
470 4         10 ? ( $msgid_plural, $self->_plural_ref->{$index} )
471             : $msgid,
472 4         17 );
473             my $any_msgstr = $self->translate_any_msgid($any_msgid, 1);
474             $any_msgstr = $self->_decode_named($any_msgstr);
475 2         29 $po->msgstr_n->{$index}
476             = $po->quote( $entry_ref->{encode_obj}->encode($any_msgstr) );
477             }
478            
479 6     6   24 return;
480             }
481            
482             sub _encode_gettext_inner { ## no critic (ManyArgs)
483 6   100     31 my ( $self, $quant, $number, $inner, $singular, $plural, $zero ) = @_;
484 12 100 66     50
485             $self->_gettext_ref->{$inner} ||= [
486             map {
487             ( defined && length )
488             ? $self->translate_any_msgid($_, 1)
489 6         28 : undef;
490             } $singular, $plural, $zero
491             ];
492            
493 7     7   15 return $encode_az->("$quant,$number,$inner");
494             }
495            
496 7         59 sub _encode_gettext {
497             my ( $self, $msgid ) = @_;
498            
499             ## no critic (ComplexRegexes)
500             $msgid =~ s{
501             ( %% ) # escaped
502             |
503             [%] ( [*] | quant ) [(] # quant
504             [%] ( \d+ ) [,] # number
505             ( # inner
506             ( [^,)]* ) # singular
507             [,] ( [^,)]* ) # plural
508             (?: [,] ( [^,)]* ) )? # zero
509             )
510 18 100       95 [)]
    50          
511             |
512             [%] ( \d+ ) # simple
513             }
514             {
515             $1
516             ? $1
517             : $2
518 7         20 ? $self->_encode_gettext_inner($2, $3, $4, $5, $6, $7)
519             : $encode_az->($8)
520             }xmsge;
521             ## use critic (ComplexRegexes)
522 6     6   13
523             return $msgid;
524 6         10 }
525 6 100       25
526 3         13 sub _decode_gettext_inner {
527             my ( $self, $inner ) = @_;
528 3 50       11
529 3         12 $inner = $decode_inner->($inner);
530             if ( $inner =~ m{ \A ( \d+ ) \z }xms ) {
531             return q{%} . $1;
532             }
533             if ( $inner =~ m{ \A ( [*] | quant ) [,] ( \d+ ) [,] ( .* ) \z }xms ) {
534 3         5 my ( $quant, $number, $plural ) = ( $1, $2, $self->_gettext_ref->{$3} );
  9         26  
  3         5  
535             return join q{},
536             q{%},
537             $quant,
538 0         0 q{(},
539             ( join q{,}, "%$number", grep { defined } @{$plural}[ 0 .. 2 ] ),
540             q{)};
541             }
542 4     4   7
543             return "DECODE_ERROR($inner)";
544 4         13 }
545            
546             sub _decode_gettext {
547             my ( $self, $msgstr ) = @_;
548            
549 6         14 $msgstr =~ s{
550             XX
551             ( [[:upper:]]+? )
552 4         8 XZ
553             }
554             {
555             $self->_decode_gettext_inner($1)
556 7     7   17 }xmsge;
557            
558 7         38 return $msgstr;
559 7         19 }
560 7         31
561 4         11 sub _translate_gettext {
562 4         38 my ( $self, $entry_ref, $po ) = @_;
563            
564 4         51 $self->_clear_gettext_ref;
565             my $msgid = $self->_encode_gettext( $entry_ref->{msgid} );
566             my $msgstr = $self->translate_any_msgid($msgid, 1);
567             $msgstr = $self->_decode_gettext($msgstr);
568 2     2   6 $po->msgstr( $entry_ref->{encode_obj}->encode($msgstr) );
569            
570             return;
571             }
572 2         4
573             sub _translate_simple {
574             my ( $self, $entry_ref, $po ) = @_;
575            
576 2         26 $po->msgstr(
577             $entry_ref->{encode_obj}->encode(
578             $self->translate_any_msgid( $entry_ref->{msgid}, 1 ),
579             ),
580 10     10   16 );
581            
582 10 100       170 return;
583             }
584 5 50       78
585             sub _update_comment {
586 5 100       28 my ( $self, $po ) = @_;
587 4         59
588 4         31 defined $self->comment
589             or return;
590             length $self->comment
591             or return;
592 1         5 if ( ! defined $po->comment ) {
  2         32  
593             $po->comment( $self->comment );
594             return;
595 1         16 }
596             my @lines
597 1         10 = grep {
598             $_ ne $self->comment;
599             }
600             $po->comment =~ m{ [\n]* ( [^\n]+ ) }xmsg;
601 6     6 1 80 $po->comment( join "\n", $self->comment, @lines );
602            
603 6 50       17 return;
604             }
605 6 50       14
606             sub with_paragraphs {
607             my ( $self, $msgid, $callback ) = @_;
608 6         16
609 6         9 ref $callback eq 'CODE'
610             or confess 'Code reference expected';
611             defined $msgid
612             or return $msgid;
613 12         34
614             my $has_network_line_endings = $msgid =~ s{ \r\n }{\n}xmsg;
615 13 100       38 my ( @prefix, @suffix );
616             my $msgstr
617 6         35 = join "\n\n",
  13         21  
618 13         34 map {
  13         26  
  13         24  
619 13         69 ( shift @prefix ) . $_ . ( shift @suffix );
  13         25  
  13         21  
620 13         29 }
621             map { length $_ ? $callback->($_) : $_ }
622             map { ## no critic (ComplexMappings)
623 5 100       16 my $paragraph = $_;
624             $paragraph =~ s{ \A ( \s* ) }{ push @prefix, $1; q{} }xmse; # left
625             $paragraph =~ s{ ( \s* ) \z }{ push @suffix, $1; q{} }xmse; # right
626 5         19 $paragraph;
627             }
628             split m{ \n [^\S\n]* \n }xms, $msgid;
629             $has_network_line_endings
630 6     6 1 75 and $msgstr =~ s{ \n }{\r\n}xmsg;
631            
632 6 50       16 return $msgstr;
633             }
634 6 50       14
635             sub with_lines {
636             my ( $self, $msgid, $callback ) = @_;
637 6         16
638 6         7 ref $callback eq 'CODE'
639             or confess 'Code reference expected';
640             defined $msgid
641             or return $msgid;
642            
643 14         52 my $has_network_line_endings = $msgid =~ s{ \r\n }{\n}xmsg;
644             my ( @prefix, @suffix );
645 15 100       39
646             return
647 6 100       23 join $has_network_line_endings ? ( "\r\n" ) : ( "\n" ),
  15         22  
648 15         38 map {
  15         28  
  15         27  
649 15         58 ( shift @prefix ) . $_ . ( shift @suffix );
  15         27  
  15         25  
650 15         33 }
651 15         29 map { length $_ ? $callback->($_) : $_ }
652             map { ## no critic (ComplexMappings)
653             my $line = $_;
654             $line =~ s{ \A ( \s* ) }{ push @prefix, $1; q{} }xmse; # left
655             $line =~ s{ ( \s* ) \z }{ push @suffix, $1; q{} }xmse; # right
656             $line =~ s{ \s+ }{ }xmsg; # inner
657 67     67   93 $line;
658             }
659 67 100       158 split m{ \n }xms, $msgid;
660             }
661 19         37
662             sub _bytes_max_fail_message {
663             my ( $self, $msgid ) = @_;
664 19 100       135
665 9         25 $self->bytes_max
666 9         38 or return 0;
667             my $msgid_bytes = length encode_utf8($msgid);
668            
669             return $msgid_bytes > $self->bytes_max
670             ? do {
671             ( my $msgid_short = $msgid ) =~ s{ \A ( .{80} ) .* \z }{$1 ...}xms;
672 31     31   50 "Byte limit exceeded, $msgid_bytes bytes at: $msgid_short";
673             }
674             : q{};
675             }
676            
677             sub _translate_paragraph {
678 8     8   35 my ( $self, $msgid ) = @_;
679 8 100       27
680             return $self->_bytes_max_fail_message($msgid)
681 7         12 ? $self->with_lines(
682 7 50       31 $msgid,
683             sub {
684 7         11 my $fail_message = $self->_bytes_max_fail_message($_);
685 7         9 $fail_message
686 7 50       13 and die $fail_message, "\n";
687             my $msgstr = $self->translate_text($_);
688 7         14 defined $msgstr
689             or $msgstr = q{};
690             $self->_item_translation_count_increment;
691 31 100       55 $msgstr =~ s{ [\x{0}\x{4}] }{}xmsg; # because of mo file conflicts
692 27         72 length $msgstr
693 26 100       98 or die "No translation break\n";
694             return $msgstr;
695 26         68 },
696 26         48 )
697 26 100       63 : do {
698             my $msgstr = $self->translate_text($msgid);
699 25         61 defined $msgstr
700             or $msgstr = q{};
701             $self->_item_translation_count_increment;
702             $msgstr =~ s{ [\x{0}\x{4}] }{}xmsg; # because of mo file conflicts
703             length $msgstr
704 29     29 1 493 or die "No translation break\n";
705             $msgstr;
706 29 0       85 };
    50          
707             }
708            
709             sub translate_any_msgid {
710 29 100       69 my ( $self, $msgid, $is_called_indirectly ) = @_;
711 23 100       76
712             $self->error
713             and $is_called_indirectly
714 28         214 ? ( die $self->error, "\n" )
715             : return q{};
716             if ( $self->before_translation_code ) {
717             $self->before_translation_code->($self, $msgid)
718 7         12 or die "Before translation break\n";
719 28 100   28   1004 }
720             my $fail_message = $self->_bytes_max_fail_message($msgid);
721             my $msgstr
722 3 100   3   666 = try {
    100          
723             $fail_message
724             ? $self->with_paragraphs($msgid, sub { $self->_translate_paragraph($_) })
725             : $self->_translate_paragraph($msgid, 'is_not_paragraph');
726 1         3 }
727 1         7 catch {
728             if ( m{ \A \QNo translation break\E \b }xms ) {
729             ;
730 1         9 }
731             elsif ( m{ \A \QByte limit exceeded, \E }xms ) {
732 3         13 chomp;
733 28         167 $self->_error($_);
734 28 100       387 }
    100          
735             else {
736             $self->_error("API error: $_")
737             }
738 26         71 q{};
739 26 100       59 };
740 23 100       52 $self->error
741             and $is_called_indirectly
742             ? ( die $self->error, "\n" )
743             : return q{};
744 25         197 $self->_translation_count_increment;
745             if ( $self->after_translation_code ) {
746             $self->after_translation_code->($self, $msgid, $msgstr)
747             or die "After translation break\n";
748 1     1 1 1 }
749            
750 1         2 return $msgstr;
751             }
752            
753             sub translate_text {
754             my ( $self, $msgid ) = @_;
755            
756             return;
757             }
758            
759             __PACKAGE__->meta->make_immutable;
760            
761             1;
762            
763             __END__