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