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