File Coverage

blib/lib/Log/Report/Lexicon/PO.pm
Criterion Covered Total %
statement 166 196 84.6
branch 78 108 72.2
condition 31 45 68.8
subroutine 24 27 88.8
pod 20 21 95.2
total 319 397 80.3


line stmt bran cond sub pod time code
1             # Copyrights 2007-2018 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution Log-Report-Lexicon. Meta-POD processed
6             # with OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Log::Report::Lexicon::PO;
10 4     4   1211 use vars '$VERSION';
  4         6  
  4         176  
11             $VERSION = '1.11';
12              
13              
14 4     4   18 use warnings;
  4         6  
  4         75  
15 4     4   16 use strict;
  4         5  
  4         62  
16              
17 4     4   811 use Log::Report 'log-report-lexicon';
  4         165794  
  4         20  
18              
19             # steal from cheaper module, we have no ::Util for this (yet)
20 4     4   2253 use Log::Report::Lexicon::POTcompact ();
  4         7  
  4         8660  
21             *_escape = \&Log::Report::Lexicon::POTcompact::_escape;
22             *_unescape = \&Log::Report::Lexicon::POTcompact::_unescape;
23              
24              
25             sub new(@)
26 15     15 1 24 { my $class = shift;
27 15         77 (bless {}, $class)->init( {@_} );
28             }
29              
30             sub init($)
31 15     15 0 27 { my ($self, $args) = @_;
32             defined($self->{msgid} = delete $args->{msgid})
33 15 50       44 or error "no msgid defined for PO";
34              
35 15         27 $self->{plural} = delete $args->{msgid_plural};
36 15         22 $self->{msgstr} = delete $args->{msgstr};
37 15         21 $self->{msgctxt} = delete $args->{msgctxt};
38              
39 15         34 $self->addComment(delete $args->{comment});
40 15         30 $self->addAutomatic(delete $args->{automatic});
41 15         29 $self->fuzzy(delete $args->{fuzzy});
42              
43 15         38 $self->{refs} = {};
44             $self->addReferences(delete $args->{references})
45 15 100       46 if defined $args->{references};
46              
47 15         32 $self;
48             }
49              
50             # only for internal usage
51 0     0   0 sub _fast_new($) { bless $_[1], $_[0] }
52              
53             #--------------------
54              
55 67     67 1 8440 sub msgid() {shift->{msgid}}
56 0     0 1 0 sub msgctxt() {shift->{msgctxt}}
57              
58              
59             sub plural(;$)
60 3     3 1 5 { my $self = shift;
61 3 100       12 @_ or return $self->{plural};
62            
63 1 50       3 if(my $m = $self->{msgstr})
64             { # prepare msgstr list for multiple translations.
65 1 50 33     9 $self->{msgstr} = [ $m ] if defined $m && !ref $m;
66             }
67              
68 1         4 $self->{plural} = shift;
69             }
70              
71              
72             sub msgstr($;$)
73 40     40 1 2432 { my $self = shift;
74 40         58 my $m = $self->{msgstr};
75              
76 40 100       64 unless($self->{plural})
77 27 100       64 { $self->{msgstr} = $_[1] if @_==2;
78 27         92 return $m;
79             }
80              
81 13   100     30 my $index = shift || 0;
82 13 100       63 @_ ? $m->[$index] = shift : $m->[$index];
83             }
84              
85              
86             sub comment(@)
87 26     26 1 33 { my $self = shift;
88 26 50       57 @_ or return $self->{comment};
89 0         0 $self->{comment} = '';
90 0         0 $self->addComment(@_);
91             }
92              
93              
94             sub addComment(@)
95 22     22 1 23 { my $self = shift;
96 22         29 my $comment = $self->{comment};
97 22 50       44 foreach my $line (ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_)
  0         0  
98 22 100       36 { defined $line or next;
99 7         13 $line =~ s/[\r\n]+/\n/; # cleanup line-endings
100 7         10 $comment .= $line;
101             }
102              
103             # be sure there is a \n at the end
104 22 100       51 $comment =~ s/\n?\z/\n/ if defined $comment;
105 22         36 $self->{comment} = $comment;
106             }
107              
108              
109             sub automatic(@)
110 26     26 1 31 { my $self = shift;
111 26 50       52 @_ or return $self->{automatic};
112 0         0 $self->{automatic} = '';
113 0         0 $self->addAutomatic(@_);
114             }
115              
116              
117             sub addAutomatic(@)
118 20     20 1 22 { my $self = shift;
119 20         36 my $auto = $self->{automatic};
120 20 50       40 foreach my $line (ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_)
  0         0  
121 20 100       37 { defined $line or next;
122 5         21 $line =~ s/[\r\n]+/\n/; # cleanup line-endings
123 5         12 $auto .= $line;
124             }
125              
126 20 100       48 $auto =~ s/\n?\z/\n/ if defined $auto; # be sure there is a \n at the end
127 20         39 $self->{automatic} = $auto;
128             }
129              
130              
131             sub references(@)
132 39     39 1 41 { my $self = shift;
133 39 50       61 if(@_)
134 0         0 { $self->{refs} = {};
135 0         0 $self->addReferences(@_);
136             }
137              
138 39         38 keys %{$self->{refs}};
  39         151  
139             }
140              
141              
142             sub addReferences(@)
143 52     52 1 54 { my $self = shift;
144 52   100     148 my $refs = $self->{refs} ||= {};
145 52 50       93 @_ or return $refs;
146              
147             $refs->{$_}++
148 52 100       189 for @_ > 1 ? @_ # list
    100          
149 12         34 : ref $_[0] eq 'ARRAY' ? @{$_[0]} # array
150             : split " ",$_[0]; # scalar
151 52         72 $refs;
152             }
153              
154              
155             sub removeReferencesTo($)
156 3     3 1 7 { my $refs = $_[0]->{refs};
157 3         43 my $match = qr/^\Q$_[1]\E\:[0-9]+$/;
158             $_ =~ $match && delete $refs->{$_}
159 3   66     23 for keys %$refs;
160              
161 3         21 scalar keys %$refs;
162             }
163              
164              
165             sub keepReferencesTo($)
166 0     0 1 0 { my $refs = shift->{refs};
167 0         0 my $keep = shift;
168              
169 0         0 foreach my $ref (keys %$refs)
170 0         0 { (my $fn = $ref) =~ s/\:[0-9]+$//;
171 0 0       0 $keep->{$fn} or delete $refs->{$ref};
172             }
173              
174 0         0 scalar keys %$refs;
175             }
176              
177              
178 13 100   13 1 25 sub isActive() { $_[0]->{msgid} eq '' || keys %{$_[0]->{refs}} }
  12         31  
179              
180              
181 29 50   29 1 38 sub fuzzy(;$) {my $self = shift; @_ ? $self->{fuzzy} = shift : $self->{fuzzy}}
  29         62  
182              
183              
184             sub format(@)
185 2     2 1 3 { my $format = shift->{format};
186 2 50 33     5 return $format->{ (shift) }
187             if @_==1 && !ref $_[0]; # language
188              
189 2 0       5 my @pairs = @_ > 1 ? @_ : ref $_[0] eq 'ARRAY' ? @{$_[0]} : %{$_[0]};
  0 50       0  
  0         0  
190 2         4 while(@pairs)
191 2         3 { my($k, $v) = (shift @pairs, shift @pairs);
192 2         6 $format->{$k} = $v;
193             }
194 2         5 $format;
195             }
196              
197              
198             sub addFlags($)
199 14     14 1 15 { my $self = shift;
200 14         17 local $_ = shift;
201 14         15 my $where = shift;
202              
203 14         24 s/^\s+//;
204 14         44 s/\s*$//;
205 14         34 foreach my $flag (split /\s*\,\s*/)
206 14 100       42 { if($flag eq 'fuzzy') { $self->fuzzy(1) }
  12 50       20  
    50          
207 0         0 elsif($flag =~ m/^no-(.*)-format$/) { $self->format($1, 0) }
208 2         13 elsif($flag =~ m/^(.*)-format$/) { $self->format($1, 1) }
209             else
210 0         0 { warning __x"unknown flag {flag} ignored", flag => $flag;
211             }
212             }
213 14         21 $_;
214             }
215              
216             sub fromText($$)
217 40     40 1 43 { my $class = shift;
218 40         185 my @lines = split /[\r\n]+/, shift;
219 40   50     61 my $where = shift || ' unknown location';
220              
221 40         56 my $self = bless {}, $class;
222              
223             # translations which are not used anymore are escaped with #~
224             # however, we just say: no references found.
225 40         116 s/^\#\~\s+// for @lines;
226              
227 40         38 my $last; # used for line continuations
228 40         41 foreach (@lines)
229 179         463 { s/\r?\n$//;
230 179 100       602 if( s/^\#(.)\s?// )
    100          
    100          
    50          
231 61 100       160 { if($1 =~ /\s/) { $self->addComment($_) }
  7 100       10  
    100          
    50          
232 3         25 elsif($1 eq '.' ) { $self->addAutomatic($_) }
233 37         54 elsif($1 eq ':' ) { $self->addReferences($_) }
234 14         26 elsif($1 eq ',' ) { $self->addFlags($_) }
235             else
236 0         0 { warning __x"unknown comment type '{cmd}' at {where}"
237             , cmd => "#$1", where => $where;
238             }
239 61         76 undef $last;
240             }
241             elsif( s/^\s*(\w+)\s+// )
242 80         119 { my $cmd = $1;
243 80         121 my $string = _unescape($_,$where);
244              
245 80 100       446 if($cmd eq 'msgid')
    100          
    50          
    0          
246 40         51 { $self->{msgid} = $string;
247 40         60 $last = \($self->{msgid});
248             }
249             elsif($cmd eq 'msgid_plural')
250 2         3 { $self->{plural} = $string;
251 2         4 $last = \($self->{plural});
252             }
253             elsif($cmd eq 'msgstr')
254 38         50 { $self->{msgstr} = $string;
255 38         61 $last = \($self->{msgstr});
256             }
257             elsif($cmd eq 'msgctxt')
258 0         0 { $self->{msgctxt} = $string;
259 0         0 $last = \($self->{msgctxt});
260             }
261             else
262 0         0 { warning __x"do not understand command '{cmd}' at {where}"
263             , cmd => $cmd, where => $where;
264 0         0 undef $last;
265             }
266             }
267             elsif( s/^\s*msgstr\[(\d+)\]\s*// )
268 6         10 { my $nr = $1;
269 6         10 $self->{msgstr}[$nr] = _unescape($_,$where);
270             }
271             elsif( m/^\s*\"/ )
272 32 50       38 { if(defined $last) { $$last .= _unescape($_,$where) }
  32         60  
273             else
274 0         0 { warning __x"quoted line is not a continuation at {where}"
275             , where => $where;
276             }
277             }
278             else
279 0         0 { warning __x"do not understand line at {where}:\n {line}"
280             , where => $where, line => $_;
281             }
282             }
283              
284             defined $self->{msgid}
285 40 50       101 or warning __x"no msgid in block {where}", where => $where;
286              
287 40         65 $self;
288             }
289              
290              
291             sub toString(@)
292 25     25 1 56 { my ($self, %args) = @_;
293 25         37 my $nplurals = $args{nr_plurals};
294 25         42 my @record;
295              
296 25         76 my $comment = $self->comment;
297 25 100 66     53 if(defined $comment && length $comment)
298 1         6 { $comment =~ s/^/# /gm;
299 1         3 push @record, $comment;
300             }
301              
302 25         38 my $auto = $self->automatic;
303 25 100 66     56 if(defined $auto && length $auto)
304 5         22 { $auto =~ s/^/#. /gm;
305 5         9 push @record, $auto;
306             }
307              
308 25         41 my @refs = sort $self->references;
309 25   100     59 my $msgid = $self->{msgid} || '';
310 25 100 100     79 my $active = $msgid eq '' || @refs ? '' : '#~ ';
311              
312 25         50 while(@refs)
313 20         24 { my $line = '#:';
314 20   66     108 $line .= ' '.shift @refs
315             while @refs && length($line) + length($refs[0]) < 80;
316 20         47 push @record, "$line\n";
317             }
318              
319 25 100       51 my @flags = $self->{fuzzy} ? 'fuzzy' : ();
320              
321             push @flags, ($self->{format}{$_} ? '' : 'no-') . $_ . '-format'
322 25 0       29 for sort keys %{$self->{format}};
  25         60  
323              
324 25 100       56 push @record, "#, ". join(", ", @flags) . "\n"
325             if @flags;
326              
327 25         41 my $msgctxt = $self->{msgctxt};
328 25 50 33     51 if(defined $msgctxt && length $msgctxt)
329 0         0 { push @record, "${active}msgctxt "._escape($msgctxt, "\n$active")."\n";
330             }
331 25         75 push @record, "${active}msgid "._escape($msgid, "\n$active")."\n";
332              
333 25   100     78 my $msgstr = $self->{msgstr} || [];
334 25 100       52 my @msgstr = ref $msgstr ? @$msgstr : $msgstr;
335 25         36 my $plural = $self->{plural};
336 25 100       40 if(defined $plural)
337 7         18 { push @record
338             , "${active}msgid_plural " . _escape($plural, "\n$active") . "\n";
339              
340 7   100     38 push @msgstr, ''
341             while defined $nplurals && @msgstr < $nplurals;
342              
343 7 50 66     22 if(defined $nplurals && @msgstr > $nplurals)
344 0         0 { warning __x"too many plurals for '{msgid}'", msgid => $msgid;
345 0         0 $#msgstr = $nplurals -1;
346             }
347              
348 7   100     17 $nplurals ||= 2;
349 7         13 for(my $nr = 0; $nr < $nplurals; $nr++)
350 14         37 { push @record, "${active}msgstr[$nr] "
351             . _escape($msgstr[$nr], "\n$active") . "\n";
352             }
353             }
354             else
355 18 50       29 { warning __x"no plurals for '{msgid}'", msgid => $msgid
356             if @msgstr > 1;
357              
358 18         49 push @record
359             , "${active}msgstr " . _escape($msgstr[0], "\n$active") . "\n";
360             }
361              
362 25         129 join '', @record;
363             }
364              
365              
366             sub useless()
367 13     13 1 15 { my $self = shift;
368 13   33     20 ! $self->references && ! $self->msgstr(0);
369             }
370             *unused = \&useless; # before <1.02
371              
372             1;