File Coverage

blib/lib/Log/Report/Lexicon/PO.pm
Criterion Covered Total %
statement 165 192 85.9
branch 72 98 73.4
condition 34 48 70.8
subroutine 24 27 88.8
pod 20 22 90.9
total 315 387 81.4


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