File Coverage

blib/lib/Log/Report/Lexicon/POT.pm
Criterion Covered Total %
statement 136 153 88.8
branch 42 76 55.2
condition 14 37 37.8
subroutine 26 29 89.6
pod 15 16 93.7
total 233 311 74.9


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   159206 use warnings;
  4         15  
  4         167  
6 4     4   32 use strict;
  4         12  
  4         173  
7              
8             package Log::Report::Lexicon::POT;
9 4     4   29 use vars '$VERSION';
  4         12  
  4         246  
10             $VERSION = '1.08';
11              
12 4     4   32 use base 'Log::Report::Lexicon::Table';
  4         14  
  4         1246  
13              
14 4     4   38 use Log::Report 'log-report-lexicon';
  4         12  
  4         30  
15 4     4   2269 use Log::Report::Lexicon::PO ();
  4         12  
  4         120  
16              
17 4     4   35 use POSIX qw/strftime/;
  4         12  
  4         35  
18 4     4   351 use List::Util qw/sum/;
  4         10  
  4         268  
19 4     4   32 use Scalar::Util qw/blessed/;
  4         11  
  4         242  
20              
21 4     4   30 use constant MSGID_HEADER => '';
  4         17  
  4         7876  
22              
23              
24             sub init($)
25 2     2 0 13 { my ($self, $args) = @_;
26              
27 2         17 $self->{LRLP_fn} = $args->{filename};
28 2   50     19 $self->{LRLP_index} = $args->{index} || {};
29             $self->{LRLP_charset} = $args->{charset}
30             or error __x"charset parameter is required for {fn}"
31 2 50 0     13 , fn => ($args->{filename} || __"unnamed file");
32              
33 2         7 my $version = $args->{version};
34             my $domain = $args->{textdomain}
35 2 50       14 or error __"textdomain parameter is required";
36              
37 2         7 my $forms = $args->{plural_forms};
38 2 50       14 unless($forms)
39 2   50     20 { my $nrplurals = $args->{nr_plurals} || 2;
40 2   50     13 my $algo = $args->{plural_alg} || 'n!=1';
41 2         11 $forms = "nplurals=$nrplurals; plural=($algo);";
42             }
43              
44             $self->_createHeader
45             ( project => $domain . (defined $version ? " $version" : '')
46             , forms => $forms
47             , charset => $args->{charset}
48             , date => $args->{date}
49 2 50       33 );
50              
51 2         25 $self->setupPluralAlgorithm;
52 2         8 $self;
53             }
54              
55              
56             sub read($@)
57 2     2 1 3056 { my ($class, $fn, %args) = @_;
58              
59 2         9 my $self = bless {LRLP_index => {}}, $class;
60              
61             my $charset = $self->{LRLP_charset} = $args{charset}
62 2 50       18 or error __x"charset parameter is required for {fn}", fn => $fn;
63              
64 2 50   1   80 open my $fh, "<:encoding($charset):crlf", $fn
  1         6  
  1         2  
  1         6  
65             or fault __x"cannot read in {cs} from file {fn}"
66             , cs => $charset, fn => $fn;
67              
68 2         1332 local $/ = "\n\n";
69 2         6 my $linenr = 1; # $/ frustrates $fh->input_line_number
70 2         5 while(1)
71 20         65 { my $location = "$fn line $linenr";
72 20         155 my $block = <$fh>;
73 20 100       163 defined $block or last;
74              
75 19         83 $linenr += $block =~ tr/\n//;
76              
77 19         197 $block =~ s/\s+\z//s;
78 19 100       93 length $block or last;
79              
80 18         88 my $po = Log::Report::Lexicon::PO->fromText($block, $location);
81 18 50       87 $self->add($po) if $po;
82             }
83              
84 2 50       36 close $fh
85             or failure __x"failed reading from file {fn}", fn => $fn;
86              
87 2         17 $self->{LRLP_fn} = $fn;
88 2         17 $self->setupPluralAlgorithm;
89 2         18 $self;
90             }
91              
92              
93             sub write($@)
94 2     2 1 852 { my $self = shift;
95 2 100       17 my $file = @_%2 ? shift : $self->filename;
96 2         11 my %args = @_;
97              
98 2 50       12 defined $file
99             or error __"no filename or file-handle specified for PO";
100              
101 2         7 my $need_refs = $args{only_active};
102 2         18 my @opt = (nr_plurals => $self->nrPlurals);
103              
104 2         6 my $fh;
105 2 100       18 if(ref $file) { $fh = $file }
  1         3  
106             else
107 1         6 { my $layers = '>:encoding('.$self->charset.')';
108 1 50       134 open $fh, $layers, $file
109             or fault __x"cannot write to file {fn} with {layers}"
110             , fn => $file, layers => $layers;
111             }
112              
113 2         122 $fh->print($self->msgid(MSGID_HEADER)->toString(@opt));
114 2         36 my $index = $self->index;
115 2         20 foreach my $msgid (sort keys %$index)
116 15 100       294 { next if $msgid eq MSGID_HEADER;
117              
118 13         43 my $rec = $index->{$msgid};
119             my @recs = blessed $rec ? $rec # one record with $msgid
120 13 50       85 : @{$rec}{sort keys %$rec}; # multiple records, msgctxt
  0         0  
121              
122 13         44 foreach my $po (@recs)
123 13 50       52 { next if $po->useless;
124 13 50 33     52 next if $need_refs && !$po->references;
125 13         53 $fh->print("\n", $po->toString(@opt));
126             }
127             }
128              
129             $fh->close
130 2 50       40 or failure __x"write errors for file {fn}", fn => $file;
131              
132 2         68 $self;
133             }
134              
135             #-----------------------
136              
137 1     1 1 6 sub charset() {shift->{LRLP_charset}}
138 86     86 1 507 sub index() {shift->{LRLP_index}}
139 1     1 1 5 sub filename() {shift->{LRLP_fn}}
140              
141              
142 0 0   0 1 0 sub language() { shift->filename =~ m![/\\](\w+)[^/\\]*$! ? $1 : undef }
143              
144             #-----------------------
145              
146             sub msgid($;$)
147 48     48 1 130 { my ($self, $msgid, $msgctxt) = @_;
148 48 100       137 my $msgs = $self->index->{$msgid} or return;
149              
150 35 50 33     355 return $msgs
      33        
151             if blessed $msgs
152             && (!$msgctxt || $msgctxt eq $msgs->msgctxt);
153              
154 0         0 $msgs->{$msgctxt};
155             }
156              
157              
158             sub msgstr($;$$)
159 15     15 1 55 { my ($self, $msgid, $count, $msgctxt) = @_;
160 15 50       42 my $po = $self->msgid($msgid, $msgctxt)
161             or return undef;
162              
163 15   100     51 $count //= 1;
164 15         57 $po->msgstr($self->pluralIndex($count));
165             }
166              
167              
168             sub add($)
169 31     31 1 101 { my ($self, $po) = @_;
170 31         128 my $msgid = $po->msgid;
171 31         100 my $index = $self->index;
172              
173 31         91 my $h = $index->{$msgid};
174 31 50       229 $h or return $index->{$msgid} = $po;
175              
176 0 0 0     0 $h = $index->{$msgid} = +{ ($h->msgctxt // '') => $h }
177             if blessed $h;
178              
179 0   0     0 my $ctxt = $po->msgctxt // '';
180             error __x"translation already exists for '{msgid}' with '{ctxt}"
181             , msgid => $msgid, ctxt => $ctxt
182 0 0       0 if $h->{$ctxt};
183              
184 0         0 $h->{$ctxt} = $po;
185             }
186              
187              
188             sub translations(;$)
189 4     4 1 2579 { my $self = shift;
190             @_ or return map +(blessed $_ ? $_ : values %$_)
191 4 50       18 , values %{$self->index};
  3 100       14  
192              
193 1 50       7 error __x"the only acceptable parameter is 'ACTIVE', not '{p}'", p => $_[0]
194             if $_[0] ne 'ACTIVE';
195              
196 1         8 grep $_->isActive, $self->translations;
197             }
198              
199              
200 2     2   306 sub _now() { strftime "%Y-%m-%d %H:%M%z", localtime }
201              
202             sub header($;$)
203 12     12 1 1056 { my ($self, $field) = (shift, shift);
204 12 50       42 my $header = $self->msgid(MSGID_HEADER)
205             or error __x"no header defined in POT for file {fn}"
206             , fn => $self->filename;
207              
208 12 100       47 if(!@_)
209 7   50     32 { my $text = $header->msgstr(0) || '';
210 7 50       252 return $text =~ m/^\Q$field\E\:\s*([^\n]*?)\;?\s*$/im ? $1 : undef;
211             }
212              
213 5         14 my $content = shift;
214 5         23 my $text = $header->msgstr(0);
215              
216 5         18 for($text)
217 5 100       16 { if(defined $content)
218 4 100       114 { s/^\Q$field\E\:([^\n]*)/$field: $content/im # change
219             || s/\z/$field: $content\n/; # new
220             }
221             else
222 1         18 { s/^\Q$field\E\:[^\n]*\n?//im; # remove
223             }
224             }
225              
226 5         338 $header->msgstr(0, $text);
227 5         21 $content;
228             }
229              
230              
231             sub updated(;$)
232 2     2 1 9 { my $self = shift;
233 2   66     16 my $date = shift || _now;
234 2         14 $self->header('PO-Revision-Date', $date);
235 2         8 $date;
236             }
237              
238             ### internal
239             sub _createHeader(%)
240 2     2   16 { my ($self, %args) = @_;
241 2   66     16 my $date = $args{date} || _now;
242              
243 2         46 my $header = Log::Report::Lexicon::PO->new
244             ( msgid => MSGID_HEADER, msgstr => <<__CONFIG);
245             Project-Id-Version: $args{project}
246             Report-Msgid-Bugs-To:
247             POT-Creation-Date: $date
248             PO-Revision-Date: $date
249             Last-Translator:
250             Language-Team:
251             MIME-Version: 1.0
252             Content-Type: text/plain; charset=$args{charset}
253             Content-Transfer-Encoding: 8bit
254             Plural-Forms: $args{forms}
255             __CONFIG
256              
257 2   50     13 my $version = $Log::Report::VERSION || '0.0';
258 2         15 $header->addAutomatic("Header generated with ".__PACKAGE__." $version\n");
259              
260 2 50       15 $self->index->{&MSGID_HEADER} = $header
261             if $header;
262              
263 2         8 $header;
264             }
265              
266              
267             sub removeReferencesTo($)
268 1     1 1 5 { my ($self, $filename) = @_;
269 1         5 sum map $_->removeReferencesTo($filename), $self->translations;
270             }
271              
272              
273             sub keepReferencesTo($)
274 0     0 1 0 { my ($self, $keep) = @_;
275 0         0 sum map $_->keepReferencesTo($keep), $self->translations;
276             }
277              
278              
279             sub stats()
280 0     0 1 0 { my $self = shift;
281 0         0 my %stats = (msgids => 0, fuzzy => 0, inactive => 0);
282 0         0 foreach my $po ($self->translations)
283 0 0       0 { next if $po->msgid eq MSGID_HEADER;
284 0         0 $stats{msgids}++;
285 0 0       0 $stats{fuzzy}++ if $po->fuzzy;
286 0 0 0     0 $stats{inactive}++ if !$po->isActive && !$po->useless;
287             }
288 0         0 \%stats;
289             }
290              
291             1;