File Coverage

blib/lib/Locale/XGettext.pm
Criterion Covered Total %
statement 301 552 54.5
branch 119 236 50.4
condition 27 54 50.0
subroutine 36 57 63.1
pod 29 29 100.0
total 512 928 55.1


line stmt bran cond sub pod time code
1             #! /bin/false
2             # vim: ts=4:et
3              
4             # Copyright (C) 2016-2017 Guido Flohr ,
5             # all rights reserved.
6              
7             # This program is free software; you can redistribute it and/or modify it
8             # under the terms of the GNU Library General Public License as published
9             # by the Free Software Foundation; either version 2, or (at your option)
10             # any later version.
11              
12             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warrant y of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15             # Library General Public License for more details.
16              
17             # You should have received a copy of the GNU Library General Public
18             # License along with this program; if not, write to the Free Software
19             # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20             # USA.
21              
22             # ABSTRACT: Extract Strings To PO Files
23              
24             package Locale::XGettext;
25             $Locale::XGettext::VERSION = '0.7';
26 15     15   165 use strict;
  15         32  
  15         577  
27              
28 15     15   95 use Locale::TextDomain 1.20 qw(Locale-XGettext);
  15         320  
  15         100  
29 15     15   2190 use File::Spec;
  15         38  
  15         517  
30 15     15   7706 use Locale::PO 0.27;
  15         63097  
  15         514  
31 15     15   113 use Scalar::Util qw(reftype blessed);
  15         37  
  15         836  
32 15     15   6841 use Locale::Recode;
  15         43089  
  15         690  
33 15     15   10817 use Getopt::Long 2.36 qw(GetOptionsFromArray);
  15         154178  
  15         355  
34 15     15   2385 use Encode;
  15         36  
  15         1146  
35              
36 15     15   6782 use Locale::XGettext::Util::POEntries;
  15         45  
  15         506  
37 15     15   6361 use Locale::XGettext::Util::Keyword;
  15         43  
  15         510  
38 15     15   6893 use Locale::XGettext::Util::Flag;
  15         48  
  15         49472  
39              
40             # Helper method, not exported!
41             sub __empty($) {
42 439     439   825 my ($what) = @_;
43              
44 439 100 66     1527 return if defined $what && length $what;
45              
46 222         630 return 1;
47             }
48              
49             sub new {
50 52     52 1 26962 my ($class, $options, @files) = @_;
51              
52 52         101 my $self;
53 52 50       143 if (ref $class) {
54 0         0 $self = $class;
55             } else {
56 52         123 $self = bless {}, $class;
57             }
58              
59 52         178 $self->{__options} = $options;
60 52         107 $self->{__comment_tag} = undef;
61 52         132 $self->{__files} = [@files];
62 52         112 $self->{__exclude} = {};
63              
64 52 50       172 if (__PACKAGE__ eq ref $self) {
65 0         0 require Carp;
66 0         0 Carp::croak(__x("{package} is an abstract base class and must not"
67             . " be instantiated directly",
68             package => __PACKAGE__));
69             }
70            
71 52 100       192 $options->{default_domain} = 'messages' if __empty $options->{default_domain};
72 52 50       156 $options->{from_code} = 'ASCII' if __empty $options->{default_domain};
73 52 100       148 $options->{output_dir} = '.' if __empty $options->{output_dir};
74              
75 52 50       183 if (exists $options->{add_location}) {
76 0         0 my $option = $options->{add_location};
77 0 0       0 if (__empty $option) {
78 0         0 $option = 'full';
79             }
80 0 0 0     0 die __"The argument to '--add-location' must be 'full', 'file', or 'never'.\n"
      0        
81             if $option ne 'full' && $option ne 'file' && $option ne 'never';
82             }
83              
84 52 100       132 if (exists $options->{add_comments}) {
85 4 50 33     13 if (!ref $options->{add_comments}
86             && 'ARRAY' ne $options->{add_comments}) {
87 0         0 die __"Option 'add_comments' must be an array reference.\n";
88             }
89            
90 4         8 foreach my $comment (@{$options->{add_comments}}) {
  4         9  
91 5         16 $comment =~ s/^[ \t\n\f\r\013]+//;
92 5         12 $comment = quotemeta $comment;
93             }
94             }
95            
96 52 50       134 $options->{from_code} = 'ASCII' if __empty $options->{from_code};
97              
98 52         111 my $from_code = $options->{from_code};
99 52         244 my $cd = Locale::Recode->new(from => $from_code,
100             to => 'utf-8');
101 52 50       80357 if ($cd->getError) {
102 0         0 warn __x("warning: '{from_code}' is not a valid encoding name. "
103             . "Using ASCII as fallback.",
104             from_code => $from_code);
105 0         0 $options->{from_code} = 'ASCII';
106             } else {
107             $options->{from_code} =
108 52         421 Locale::Recode->resolveAlias($options->{from_code});
109             }
110            
111 52         1169 $self->__readFilesFrom($options->{files_from});
112 52 100       200 if ($self->needInputFiles) {
113             $self->__usageError(__"no input file given")
114 39 0 33     57 if !@{$self->{__files}} && !@{$options->{files_from}};
  39         112  
  0         0  
115             }
116            
117 52         234 $self->{__keywords} = $self->__setKeywords($options->{keyword});
118 52         198 $self->{__flags} = $self->__setFlags($options->{flag});
119              
120 52 100 66     176 if (exists $options->{exclude_file} && !ref $options->{exclude_file}) {
121 1         3 $options->{exclude_file} = [$options->{exclude_file}];
122             }
123              
124 52         216 $self->__readExcludeFiles($options->{exclude_file});
125              
126 52         577 return $self;
127             }
128              
129             sub newFromArgv {
130 0     0 1 0 my ($class, $argv) = @_;
131              
132 0         0 my $self;
133 0 0       0 if (ref $class) {
134 0         0 $self = $class;
135             } else {
136 0         0 $self = bless {}, $class;
137             }
138              
139 0         0 my %options = eval { $self->__getOptions($argv) };
  0         0  
140 0 0       0 if ($@) {
141 0         0 $self->__usageError($@);
142             }
143            
144 0 0       0 $self->__displayUsage if $options{help};
145            
146 0 0       0 if ($options{version}) {
147 0         0 print $self->versionInformation;
148 0         0 exit 0;
149             }
150            
151 0         0 return $class->new(\%options, @$argv);
152             }
153              
154             sub defaultKeywords {
155 52     52 1 156 return [];
156             }
157              
158             sub defaultFlags {
159 52     52 1 120 return [];
160             }
161              
162             sub run {
163 45     45 1 301 my ($self) = @_;
164              
165 45 50       150 if ($self->{__run}++) {
166 0         0 require Carp;
167 0         0 Carp::croak(__"Attempt to re-run extractor");
168             }
169              
170 45         273 my $po = $self->{__po} = Locale::XGettext::Util::POEntries->new;
171            
172 45 100       170 if ($self->option('join_existing')) {
173 1         4 my $output_file = $self->__outputFilename;
174 1 50       4 if ('-' eq $output_file) {
175 0         0 $self->__usageError(__"--join-existing cannot be used when output"
176             . " is written to stdout");
177             }
178 1         8 $self->readPO($output_file);
179             }
180            
181 45         84 foreach my $filename (@{$self->{__files}}) {
  45         124  
182 40 50       138 my $path = $self->resolveFilename($filename)
183             or die __x("Error resolving file name '{filename}': {error}!\n",
184             filename => $filename, error => $!);
185 40 50       205 if ($path =~ /\.pot?$/i) {
186 0         0 $self->readPO($path);
187             } else {
188 40         163 $self->readFile($path);
189             }
190             }
191              
192 45         258 $self->extractFromNonFiles;
193            
194             # FIXME! Sort po!
195            
196 45 100 33     157 if (($po->entries || $self->{__options}->{force_po})
      66        
197             && !$self->{__options}->{omit_header}) {
198 44         161 $po->prepend($self->__poHeader);
199             }
200              
201 45         113 foreach my $entry ($po->entries) {
202 98         279 $self->recodeEntry($entry);
203             }
204              
205 45         246 return $self;
206             }
207              
208 32     32 1 56 sub extractFromNonFiles { shift }
209              
210             sub resolveFilename {
211 40     40 1 91 my ($self, $filename) = @_;
212            
213 40   100     198 my $directories = $self->{__options}->{directory} || [''];
214 40         86 foreach my $directory (@$directories) {
215 41 100       131 my $path = length $directory
216             ? File::Spec->catfile($directory, $filename) : $filename;
217 41 100       866 stat $path && return $path;
218             }
219            
220 0         0 return;
221             }
222              
223             sub po {
224 36     36 1 130 shift->{__po}->entries;
225             }
226              
227             sub readPO {
228 1     1 1 3 my ($self, $path) = @_;
229            
230 1 50       6 my $entries = Locale::PO->load_file_asarray($path)
231             or die __x("error reading '{filename}': {error}!\n",
232             filename => $path, error => $!);
233            
234 1         921 foreach my $entry (@$entries) {
235 2 100 66     7 if ('""' eq $entry->msgid
236             && __empty $entry->dequote($entry->msgctxt)) {
237 1         3 next;
238             }
239 1         8 $self->addEntry($entry);
240             }
241            
242 1         4 return $self;
243             }
244              
245             sub addEntry {
246 55     55 1 255 my ($self, $entry) = @_;
247              
248 55 50       167 if (!$self->{__run}) {
249 0         0 require Carp;
250             # TRANSLATORS: run() is a method that should be invoked first.
251 0         0 Carp::croak(__"Attempt to add entries before run");
252             }
253              
254             # Simplify calling from languages that do not have hashes.
255 55 50       162 if (!ref $entry) {
256 0         0 $entry = {splice @_, 1};
257             }
258              
259 55         120 my $comment = delete $entry->{automatic};
260 55         177 $entry = $self->__promoteEntry($entry);
261              
262 55 100       131 if (defined $comment) {
263             # Does it contain an "xgettext:" comment? The original implementation
264             # is quite relaxed here, even recogizing comments like "exgettext:".
265 6         11 my $cleaned = '';
266 6         31 $comment =~ s{
267             (.*?)xgettext:(.*?(?:\n|\Z))
268             }{
269 2         9 my ($lead, $string) = ($1, $2);
270 2         4 my $valid;
271            
272 2         14 my @tokens = split /[ \x09-\x0d]+/, $string;
273            
274 2         6 foreach my $token (@tokens) {
275 13 50       47 if ($token eq 'fuzzy') {
    50          
    50          
    100          
276 0         0 $entry->fuzzy(1);
277 0         0 $valid = 1;
278             } elsif ($token eq 'no-wrap') {
279 0         0 $entry->add_flag('no-wrap');
280 0         0 $valid = 1;
281             } elsif ($token eq 'wrap') {
282 0         0 $entry->add_flag('wrap');
283 0         0 $valid = 1;
284             } elsif ($token =~ /^[a-z]+-(?:format|check)$/) {
285 1         5 $entry->add_flag($token);
286 1         20 $valid = 1;
287             }
288             }
289            
290 2 100       16 $cleaned .= "${lead}xgettext:${string}" if !$valid;
291             }exg;
292              
293 6         15 $cleaned .= $comment;
294 6         11 $comment = $cleaned;
295              
296 6         12 my $comment_keywords = $self->option('add_comments');
297 6 100 66     12 if (!__empty $comment && defined $comment_keywords) {
298 5         9 my @automatic;
299 5         10 foreach my $keyword (@$comment_keywords) {
300 6 100       71 if ($comment =~ /($keyword.*)/s) {
301 5         17 push @automatic, $1;
302 5         11 last;
303             }
304             }
305            
306 5         13 my $old_automatic = $entry->automatic;
307 5 100       28 push @automatic, $entry->dequote($old_automatic) if !__empty $old_automatic;
308 5 50       75 $entry->automatic(join "\n", @automatic) if @automatic;
309             }
310             }
311            
312 55         159 my ($msgid) = $entry->msgid;
313 55 50       343 if (!__empty $msgid) {
314 55         154 my $ctx = $entry->msgctxt;
315 55 50       296 $ctx = '' if __empty $ctx;
316            
317 55 100       263 return $self if exists $self->{__exclude}->{$msgid}->{$ctx};
318             }
319            
320 54         257 $self->{__po}->add($entry);
321              
322 54         135 return $self;
323             }
324              
325             sub keywords {
326 7     7 1 14 my ($self) = @_;
327              
328 7         15 my %keywords = %{$self->{__keywords}};
  7         27  
329              
330 7         18 return \%keywords;
331             }
332              
333             sub keywordOptionStrings {
334 0     0 1 0 my ($self) = @_;
335              
336 0         0 my @keywords;
337 0         0 my $keywords = $self->keywords;
338 0         0 foreach my $function (keys %$keywords) {
339 0         0 push @keywords, $keywords->{$function}->dump;
340             }
341              
342 0         0 return \@keywords;
343             }
344              
345             sub flags {
346 7     7 1 13 my ($self) = @_;
347              
348 7         16 my @flags = @{$self->{__flags}};
  7         19  
349              
350 7         16 return \@flags;
351             }
352              
353             sub flagOptionStrings {
354 0     0 1 0 my ($self) = @_;
355              
356 0         0 my @flags;
357 0         0 my $flags = $self->flags;
358 0         0 foreach my $flag (@$flags) {
359 0         0 push @flags, $flag->dump;
360             }
361              
362 0         0 return \@flags;
363             }
364              
365             sub recodeEntry {
366 98     98 1 176 my ($self, $entry) = @_;
367            
368 98         193 my $from_code = $self->option('from_code');
369 98 100       240 $from_code = 'US-ASCII' if __empty $from_code;
370 98         394 $from_code = Locale::Recode->resolveAlias($from_code);
371            
372 98         1160 my $cd;
373 98 50 33     264 if ($from_code ne 'US-ASCII' && $from_code ne 'UTF-8') {
374 0         0 $cd = Locale::Recode->new(from => $from_code, to => 'utf-8');
375 0 0       0 die $cd->getError if defined $cd->getError;
376             }
377              
378             my $toString = sub {
379 98     98   179 my ($entry) = @_;
380              
381 98 100       228 return join '', map { defined $_ ? $_ : '' }
  392         2094  
382             $entry->msgid, $entry->msgid_plural,
383             $entry->msgctxt, $entry->comment;
384 98         418 };
385            
386 98 50       256 if ($from_code eq 'US-ASCII') {
    0          
387             # Check that everything is 7 bit.
388 98         181 my $flesh = $toString->($entry);
389 98 50       564 if ($flesh !~ /^[\000-\177]*$/) {
390 0         0 die __x("Non-ASCII string at '{reference}'.\n"
391             . " Please specify the source encoding through "
392             . "'--from-code'.\n",
393             reference => $entry->reference);
394             }
395             } elsif ($from_code eq 'UTF-8') {
396             # Check that utf-8 is valid.
397 0         0 require utf8; # [SIC!]
398            
399 0         0 my $flesh = $toString->($entry);
400 0 0       0 if (!utf8::valid($flesh)) {
401 0         0 die __x("{reference}: invalid multibyte sequence\n",
402             reference => $entry->reference);
403             }
404             } else {
405             # Convert.
406 0         0 my $msgid = Locale::PO->dequote($entry->msgid);
407 0 0       0 if (!__empty $msgid) {
408 0 0       0 $cd->recode($msgid)
409             or $self->__conversionError($entry->reference, $cd);
410 0         0 $entry->msgid($msgid);
411             }
412            
413 0         0 my $msgid_plural = Locale::PO->dequote($entry->msgid_plural);
414 0 0       0 if (!__empty $msgid_plural) {
415 0 0       0 $cd->recode($msgid_plural)
416             or $self->__conversionError($entry->reference, $cd);
417 0         0 $entry->msgid($msgid_plural);
418             }
419            
420 0         0 my $msgstr = Locale::PO->dequote($entry->msgstr);
421 0 0       0 if (!__empty $msgstr) {
422 0 0       0 $cd->recode($msgstr)
423             or $self->__conversionError($entry->reference, $cd);
424 0         0 $entry->msgid($msgstr);
425             }
426            
427 0         0 my $msgstr_n = Locale::PO->dequote($entry->msgstr_n);
428 0 0       0 if ($msgstr_n) {
429 0         0 my $msgstr_0 = Locale::PO->dequote($msgstr_n->{0});
430 0 0       0 $cd->recode($msgstr_0)
431             or $self->__conversionError($entry->reference, $cd);
432 0         0 my $msgstr_1 = Locale::PO->dequote($msgstr_n->{1});
433 0 0       0 $cd->recode($msgstr_1)
434             or $self->__conversionError($entry->reference, $cd);
435 0         0 $entry->msgstr_n({
436             0 => $msgstr_0,
437             1 => $msgstr_1,
438             })
439             }
440            
441 0         0 my $comment = $entry->comment;
442 0 0       0 $cd->recode($comment)
443             or $self->__conversionError($entry->reference, $cd);
444 0         0 $entry->comment($comment);
445             }
446              
447 98         442 return $self;
448             }
449              
450             sub options {
451 0     0 1 0 shift->{__options};
452             }
453              
454             sub option {
455 149     149 1 323 my ($self, $key) = @_;
456              
457 149 100       412 return if !exists $self->{__options}->{$key};
458            
459 98         215 return $self->{__options}->{$key};
460             }
461              
462             sub setOption {
463 0     0 1 0 my ($self, $key, $value) = @_;
464              
465 0         0 $self->{__options}->{$key} = $value;
466              
467 0         0 return $self;
468             }
469              
470             sub output {
471 9     9 1 21 my ($self) = @_;
472            
473 9 50       23 if (!$self->{__run}) {
474 0         0 require Carp;
475 0         0 Carp::croak(__"Attempt to output from extractor before run");
476             }
477            
478 9 50       22 if (!$self->{__po}) {
479 0         0 require Carp;
480 0         0 Carp::croak(__"No PO data");
481             }
482            
483 9 0 33     22 return if !$self->{__po}->entries && !$self->{__options}->{force_po};
484              
485 9         17 my $options = $self->{__options};
486 9         26 my $filename = $self->__outputFilename;
487              
488 9 50       579 open my $fh, '>', $filename
489             or die __x("Error writing '{file}': {error}.\n",
490             file => $filename, error => $!);
491            
492 9         55 foreach my $entry ($self->{__po}->entries) {
493 18         62 my $dump = $entry->dump;
494             # We have no idea about the encoding.
495 18         4027 Encode::_utf8_off($dump);
496              
497 18 50       162 print $fh $dump
498             or die __x("Error writing '{file}': {error}.\n",
499             file => $filename, error => $!);
500             }
501 9 50       399 close $fh
502             or die __x("Error writing '{file}': {error}.\n",
503             file => $filename, error => $!);
504            
505 9         87 return $self;
506             }
507              
508       0 1   sub languageSpecificOptions {}
509              
510             # In order to simplify the code in other languages, we allow returning
511             # a flat list instead of an array of arrays. This wrapper checks the
512             # return value and converts it accordingly.
513             sub __languageSpecificOptions {
514 0     0   0 my ($self) = @_;
515              
516 0         0 my @options = $self->languageSpecificOptions;
517 0 0       0 return $options[0] if @options & 0x3;
518              
519             # Number of items is a multiple of 4.
520 0         0 my @retval;
521 0         0 while (@options) {
522 0         0 push @retval, [splice @options, 0, 4];
523             }
524              
525 0         0 return \@retval;
526             }
527              
528             sub printLanguageSpecificUsage {
529 0     0 1 0 my ($self) = @_;
530            
531 0         0 my $options = $self->__languageSpecificOptions;
532            
533 0 0       0 foreach my $optspec (@{$options || []}) {
  0         0  
534 0         0 my ($optstring, $optvar,
535             $usage, $description) = @$optspec;
536            
537 0         0 print " $usage ";
538 0         0 my $pos = 3 + length $usage;
539            
540 0         0 my @description = split /[ \x09-\x0d]+/, $description;
541 0         0 my $lineno = 0;
542 0         0 while (@description) {
543 0 0       0 my $limit = $lineno ? 31 : 29;
544 0 0       0 if ($pos < $limit) {
545 0         0 print ' ' x ($limit - $pos);
546 0         0 $pos = $limit;
547             }
548            
549 0         0 while (@description) {
550 0         0 my $word = shift @description;
551 0         0 print " $word";
552 0         0 $pos += 1 + length $word;
553 0 0 0     0 if (@description && $pos > 77 - length $description[-1]) {
554 0         0 ++$lineno;
555 0         0 print "\n";
556 0         0 $pos = 0;
557 0         0 last;
558             }
559             }
560             }
561 0         0 print "\n";
562             }
563            
564 0         0 return $self;
565             }
566              
567       0 1   sub fileInformation {}
568              
569       0 1   sub bugTrackingAddress {}
570              
571             sub versionInformation {
572 0     0 1 0 my ($self) = @_;
573            
574 0         0 my $package = ref $self;
575              
576 0         0 my $version;
577             {
578             ## no critic
579 15     15   146 no strict 'refs';
  15         34  
  15         47061  
  0         0  
580              
581 0         0 my $varname = "${package}::VERSION";
582 0         0 $version = ${$varname};
  0         0  
583             };
584              
585 0 0       0 $version = '' if !defined $version;
586            
587 0         0 $package =~ s/::/-/g;
588            
589 0         0 return __x('{program} ({package}) {version}
590             Please see the source for copyright information!
591             ', program => $self->programName, package => $package, version => $version);
592             }
593              
594             sub canExtractAll {
595 0     0 1 0 return;
596             }
597              
598             sub canKeywords {
599 0     0 1 0 shift;
600             }
601              
602             sub canFlags {
603 0     0 1 0 shift;
604             }
605              
606             sub needInputFiles {
607 39     39 1 117 shift;
608             }
609              
610             sub __readExcludeFiles {
611 52     52   130 my ($self, $files) = @_;
612            
613 52 100       143 return $self if !$files;
614            
615 1         3 foreach my $file (@$files) {
616 1 50       10 my $entries = Locale::PO->load_file_asarray($file)
617             or die __x("error reading '{filename}': {error}!\n",
618             filename => $file, error => $!);
619            
620 1         498 foreach my $entry (@$entries) {
621 2         6 my $msgid = $entry->msgid;
622 2 50       12 next if __empty $msgid;
623            
624 2         5 my $ctx = $entry->msgctxt;
625 2 100       11 $ctx = '' if __empty $ctx;
626            
627 2         11 $self->{__exclude}->{$msgid}->{$ctx} = $entry;
628             }
629             }
630            
631 1         2 return $self;
632             }
633              
634             sub __promoteEntry {
635 55     55   108 my ($self, $entry) = @_;
636            
637 55 100       194 if (!blessed $entry) {
638 54         232 my %entry = %$entry;
639 54         288 my $po_entry = Locale::PO->new;
640              
641 54         2488 my $keyword = delete $entry{keyword};
642 54 100       177 if (defined $keyword) {
643 7         21 my $keywords = $self->keywords;
644 7 50       18 if (exists $keywords->{$keyword}) {
645 7         20 my $comment = $keywords->{$keyword}->comment;
646 7 100       18 $entry{automatic} = $comment if !__empty $comment;
647              
648 7         33 my $flags = $self->flags;
649 7         20 my $sg_arg = $keywords->{$keyword}->singular;
650 7   100     18 my $pl_arg = $keywords->{$keyword}->plural || 0;
651 7         19 foreach my $flag (@$flags) {
652 9 100       80 next if $keyword ne $flag->function;
653 7 100 100     15 next if $flag->arg != $sg_arg && $flag->arg != $pl_arg;
654 6         13 my $flag_name = $flag->flag;
655 6 50       13 $flag_name = 'no-' . $flag_name if $flag->no;
656 6         17 $po_entry->add_flag($flag_name);
657             }
658             }
659             }
660              
661 54         155 my $flags = delete $entry{flags};
662 54 100       120 if (defined $flags) {
663 2         13 my @flags = split /[ \t\r\n]*,[ \t\r\n]*/, $flags;
664 2         5 foreach my $flag (@flags) {
665 3 50       36 $po_entry->add_flag($flag)
666             if !$po_entry->has_flag($flag);
667             }
668             }
669              
670 54         255 foreach my $method (keys %entry) {
671 101         167 eval { $po_entry->$method($entry{$method}) };
  101         325  
672             warn __x("error calling method '{method}' with value '{value}'"
673             . " on Locale::PO instance: {error}.\n",
674 101 50       1325 method => $method, value => $entry{$method},
675             error => $@) if $@;
676             }
677              
678 54         151 $entry = $po_entry;
679             }
680              
681 55         111 return $entry;
682             }
683              
684             sub __conversionError {
685 0     0   0 my ($self, $reference, $cd) = @_;
686            
687 0         0 die __x("{reference}: {conversion_error}\n",
688             reference => $reference,
689             conversion_error => $cd->getError);
690             }
691              
692             sub __outputFilename {
693 10     10   16 my ($self) = @_;
694            
695 10         28 my $options = $self->{__options};
696 10 100       51 if (exists $options->{output}) {
    100          
697 3 100 66     37 if (File::Spec->file_name_is_absolute($options->{output})
698             || '-' eq $options->{output}) {
699 1         3 return $options->{output};
700             } else {
701             return File::Spec->catfile($options->{output_dir},
702             $options->{output})
703 2         22 }
704             } elsif ('-' eq $options->{default_domain}) {
705 1         3 return '-';
706             } else {
707             return File::Spec->catfile($options->{output_dir},
708 6         92 $options->{default_domain} . '.po');
709             }
710            
711             # NOT REACHED!
712             }
713             sub __poHeader {
714 44     44   99 my ($self) = @_;
715              
716 44         78 my $options = $self->{__options};
717            
718 44         72 my $user_info;
719 44 100       107 if ($options->{foreign_user}) {
720 3         5 $user_info = <
721             This file is put in the public domain.
722             EOF
723             } else {
724 41         70 my $copyright = $options->{copyright_holder};
725 41 100       107 $copyright = "THE PACKAGE'S COPYRIGHT HOLDER" if !defined $copyright;
726            
727 41         130 $user_info = <
728             Copyright (C) YEAR $copyright
729             This file is distributed under the same license as the PACKAGE package.
730             EOF
731             }
732 44         96 chomp $user_info;
733            
734 44         124 my $entry = Locale::PO->new;
735 44         1567 $entry->fuzzy(1);
736 44         1691 $entry->comment(<
737             SOME DESCRIPTIVE TITLE.
738             $user_info
739             FIRST AUTHOR , YEAR.
740             EOF
741 44         276 $entry->msgid('');
742              
743 44         589 my @fields;
744            
745 44         83 my $package_name = $options->{package_name};
746 44 100       113 if (defined $package_name) {
747 2         4 my $package_version = $options->{package_version};
748 2 100 66     11 $package_name .= ' ' . $package_version
749             if defined $package_version && length $package_version;
750             } else {
751 42         100 $package_name = 'PACKAGE VERSION'
752             }
753            
754 44         111 push @fields, "Project-Id-Version: $package_name";
755              
756 44         85 my $msgid_bugs_address = $options->{msgid_bugs_address};
757 44 100       151 $msgid_bugs_address = '' if !defined $msgid_bugs_address;
758 44         102 push @fields, "Report-Msgid-Bugs-To: $msgid_bugs_address";
759            
760 44         89 push @fields, 'Last-Translator: FULL NAME ';
761 44         69 push @fields, 'Language-Team: LANGUAGE ';
762 44         72 push @fields, 'Language: ';
763 44         85 push @fields, 'MIME-Version: ';
764             # We always write utf-8.
765 44         130 push @fields, 'Content-Type: text/plain; charset=UTF-8';
766 44         82 push @fields, 'Content-Transfer-Encoding: 8bit';
767            
768 44         245 $entry->msgstr(join "\n", @fields);
769 44         989 return $entry;
770             }
771              
772             sub __getEntriesFromFile {
773 0     0   0 my ($self, $filename) = @_;
774              
775 0 0       0 open my $fh, '<', $filename
776             or die __x("Error reading '{filename}': {error}!\n",
777             filename => $filename, error => $!);
778            
779 0         0 my @entries;
780 0         0 my $chunk = '';
781 0         0 my $last_lineno = 1;
782 0         0 while (my $line = <$fh>) {
783 0 0       0 if ($line =~ /^[\x09-\x0d ]*$/) {
784 0 0       0 if (length $chunk) {
785 0         0 my $entry = Locale::PO->new;
786 0         0 chomp $chunk;
787 0         0 $entry->msgid($chunk);
788 0         0 $entry->reference("$filename:$last_lineno");
789 0         0 push @entries, $entry;
790             }
791 0         0 $last_lineno = $. + 1;
792 0         0 $chunk = '';
793             } else {
794 0         0 $chunk .= $line;
795             }
796             }
797            
798 0 0       0 if (length $chunk) {
799 0         0 my $entry = Locale::PO->new;
800 0         0 $entry->msgid($chunk);
801 0         0 chomp $chunk;
802 0         0 $entry->reference("$filename:$last_lineno");
803 0         0 push @entries, $entry;
804             }
805              
806 0         0 return @entries;
807             }
808              
809             sub __readFilesFrom {
810 52     52   170 my ($self, $list) = @_;
811            
812 52         99 my %seen;
813             my @files;
814 52         80 foreach my $file (@{$self->{__files}}) {
  52         142  
815 42         248 my $canonical = File::Spec->canonpath($file);
816 42 50       242 push @files, $file if !$seen{$canonical}++;
817             }
818            
819             # This matches the format expected by GNU xgettext. Lines where the
820             # first non-whitespace character is a hash sign, are ignored. So are
821             # empty lines (after whitespace stripping). All other lines are treated
822             # as filenames with trailing (not leading!) space stripped off.
823 52         131 foreach my $potfile (@$list) {
824 3 50       109 open my $fh, '<', $potfile
825             or die __x("Error opening '{file}': {error}!\n",
826             file => $potfile, error => $!);
827 3         47 while (my $file = <$fh>) {
828 6 50       20 next if $file =~ /^[ \x09-\x0d]*#/;
829 6         33 $file =~ s/[ \x09-\x0d]+$//;
830 6 50       17 next if !length $file;
831            
832 6         26 my $canonical = File::Spec->canonpath($file);
833 6 100       26 next if $seen{$canonical}++;
834              
835 5         63 push @files, $file;
836             }
837             }
838            
839 52         117 $self->{__files} = \@files;
840            
841 52         126 return $self;
842             }
843              
844             sub __getOptions {
845 0     0   0 my ($self, $argv) = @_;
846            
847 0         0 my %options;
848            
849 0         0 my $lang_options = $self->__languageSpecificOptions;
850 0         0 my %lang_options;
851            
852 0         0 foreach my $optspec (@$lang_options) {
853 0         0 my ($optstring, $optvar,
854             $usage, $description) = @$optspec;
855 0         0 $lang_options{$optstring} = \$options{$optvar};
856             }
857            
858 0         0 Getopt::Long::Configure('bundling');
859             $SIG{__WARN__} = sub {
860 0     0   0 $SIG{__WARN__} = 'DEFAULT';
861 0         0 die shift;
862 0         0 };
863             GetOptionsFromArray($argv,
864             # Are always overridden by standard options.
865             %lang_options,
866            
867             # Input file location:
868             'f|files-from=s@' => \$options{files_from},
869             'D|directory=s@' => \$options{directory},
870              
871             # Output file location:
872             'd|default-domain=s' => \$options{default_domain},
873             'o|output=s' => \$options{output},
874             'p|output-dir=s' => \$options{output_dir},
875              
876             # Input file interpretation.
877             'from-code=s' => \$options{from_code},
878            
879             # Operation mode:
880             'j|join-existing' => \$options{join_existing},
881            
882             # We allow multiple files.
883             'x|exclude-file=s@' => \$options{exclude_file},
884             'c|add-comments:s@' => \$options{add_comments},
885              
886             # Language specific options:
887             'a|extract-all' => \$options{extract_all},
888             'k|keyword:s@' => \$options{keyword},
889             'flag:s@' => \$options{flag},
890            
891             # Output details:
892             'force-po' => \$options{force_po},
893             'no-location' => \$options{no_location},
894             'n|add-location' => \$options{add_location},
895             's|sort-output' => \$options{sort_output},
896             'F|sort-by-file' => \$options{sort_by_file},
897             'omit-header' => \$options{omit_header},
898             'copyright-holder=s' => \$options{copyright_holder},
899             'foreign-user' => \$options{foreign_user},
900             'package_name=s' => \$options{package_name},
901             'package_version=s' => \$options{package_version},
902             'msgid-bugs-address=s' => \$options{msgid_bugs_address},
903             'm|msgstr-prefix:s' => \$options{msgid_str_prefix},
904             'M|msgstr-suffix:s' => \$options{msgid_str_suffix},
905              
906             # Informative output.
907             'h|help' => \$options{help},
908             'V|version' => \$options{version},
909 0         0 );
910 0         0 $SIG{__WARN__} = 'DEFAULT';
911            
912 0         0 foreach my $key (keys %options) {
913 0 0       0 delete $options{$key} if !defined $options{$key};
914             }
915            
916 0         0 return %options;
917             }
918              
919             sub __setKeywords {
920 52     52   182 my ($self, $options) = @_;
921              
922 52   50     166 my $defaults = $self->defaultKeywords || [];
923            
924 52         93 my $keywords = {};
925 52         131 foreach my $option (@$defaults, @$options) {
926 7 50       21 if ('' eq $option) {
927 0         0 $keywords = {};
928 0         0 next;
929             }
930              
931 7         10 my $keyword;
932 7 50       17 if (ref $option) {
933 0         0 $keyword = $option;
934             } else {
935 7         31 $keyword = Locale::XGettext::Util::Keyword->newFromString($option);
936             }
937 7         23 $keywords->{$keyword->function} = $keyword;
938             }
939              
940 52         135 return $keywords;
941             }
942              
943             sub __setFlags {
944 52     52   142 my ($self, $options) = @_;
945            
946 52         81 my @defaults = @{$self->defaultFlags};
  52         143  
947              
948 52         105 my %flags;
949             my @flags;
950              
951 52         111 foreach my $spec (@defaults, @$options) {
952 10 50       28 my $obj = Locale::XGettext::Util::Flag->newFromString($spec)
953             or die __x("A --flag argument doesn't have the"
954             . " ::[pass-] syntax: {flag}",
955             $spec);
956 10         27 my $function = $obj->function;
957 10         18 my $arg = $obj->arg;
958 10         21 my $flag = $obj->flag;
959              
960             # First one wins.
961 10 100       32 next if $flags{$function}->{$flag}->{$arg};
962              
963 9         20 $flags{$function}->{$flag}->{$arg} = $obj;
964 9         21 push @flags, $obj;
965             }
966            
967 52         145 return \@flags;
968             }
969              
970 0     0 1   sub programName { $0 }
971              
972             sub __displayUsage {
973 0     0     my ($self) = @_;
974            
975 0 0         if ($self->needInputFiles) {
976 0           print __x("Usage: {program} [OPTION] [INPUTFILE]...\n",
977             program => $self->programName);
978 0           print "\n";
979            
980 0           print __(<
981             Extract translatable strings from given input files.
982             EOF
983             } else {
984 0           print __x("Usage: {program} [OPTION]\n", program => $self->programName);
985 0           print "\n";
986            
987 0           print __(<
988             Extract translatable strings.
989             EOF
990             }
991            
992 0 0         if (defined $self->fileInformation) {
993 0           print "\n";
994 0           my $description = $self->fileInformation;
995 0           chomp $description;
996 0           print "$description\n";
997             }
998              
999 0           print "\n";
1000            
1001 0           print __(<
1002             Mandatory arguments to long options are mandatory for short options too.
1003             Similarly for optional arguments.
1004             EOF
1005              
1006 0           print "\n";
1007              
1008 0           print __(<
1009             Input file location:
1010             EOF
1011              
1012 0           print __(<
1013             INPUTFILE ... input files
1014             EOF
1015              
1016 0           print __(<
1017             -f, --files-from=FILE get list of input files from FILE\
1018             EOF
1019              
1020 0           print __(<
1021             -D, --directory=DIRECTORY add DIRECTORY to list for input files search
1022             EOF
1023              
1024 0           printf __(<
1025             If input file is -, standard input is read.
1026             EOF
1027              
1028 0           print "\n";
1029            
1030 0           printf __(<
1031             Output file location:
1032             EOF
1033              
1034 0           printf __(<
1035             -d, --default-domain=NAME use NAME.po for output (instead of messages.po)
1036             EOF
1037              
1038 0           print __(<
1039             -o, --output=FILE write output to specified file
1040             EOF
1041              
1042 0           print __(<
1043             -p, --output-dir=DIR output files will be placed in directory DIR
1044             EOF
1045              
1046 0           print __(<
1047             If output file is -, output is written to standard output.
1048             EOF
1049              
1050 0           print "\n";
1051              
1052 0           print __(<
1053             Input file interpretation:
1054             EOF
1055              
1056 0           print __(<
1057             --from-code=NAME encoding of input files
1058             EOF
1059 0           print __(<
1060             By default the input files are assumed to be in ASCII.
1061             EOF
1062              
1063 0           printf "\n";
1064              
1065 0           print __(<
1066             Operation mode:
1067             EOF
1068              
1069 0           print __(<
1070             -j, --join-existing join messages with existing file
1071             EOF
1072              
1073 0           print __(<
1074             -x, --exclude-file=FILE.po entries from FILE.po are not extracted
1075             EOF
1076              
1077 0           print __(<
1078             -cTAG, --add-comments=TAG place comment blocks starting with TAG and
1079             preceding keyword lines in output file
1080             -c, --add-comments place all comment blocks preceding keyword lines
1081             in output file
1082             EOF
1083              
1084 0           print "\n";
1085              
1086 0           print __(<
1087             Language specific options:
1088             EOF
1089              
1090 0 0         if ($self->canExtractAll) {
1091 0           print __(<
1092             -a, --extract-all extract all strings
1093             EOF
1094             }
1095              
1096 0 0         if ($self->canKeywords) {
1097 0           print __(<
1098             -kWORD, --keyword=WORD look for WORD as an additional keyword
1099             -k, --keyword do not to use default keywords"));
1100             --flag=WORD:ARG:FLAG additional flag for strings inside the argument
1101             number ARG of keyword WORD
1102             EOF
1103             }
1104              
1105 0           $self->printLanguageSpecificUsage;
1106              
1107 0           print "\n";
1108              
1109 0           print __(<
1110             Output details:
1111             EOF
1112              
1113 0           print __(<
1114             --force-po write PO file even if empty
1115             EOF
1116              
1117 0           print __(<
1118             --no-location do not write '#: filename:line' lines
1119             EOF
1120              
1121 0           print __(<
1122             -n, --add-location generate '#: filename:line' lines (default)
1123             EOF
1124              
1125 0           print __(<
1126             -s, --sort-output generate sorted output
1127             EOF
1128              
1129 0           print __(<
1130             -F, --sort-by-file sort output by file location
1131             EOF
1132              
1133 0           print __(<
1134             --omit-header don't write header with 'msgid ""' entry
1135             EOF
1136              
1137 0           print __(<
1138             --copyright-holder=STRING set copyright holder in output
1139             EOF
1140              
1141 0           print __(<
1142             --foreign-user omit FSF copyright in output for foreign user
1143             EOF
1144              
1145 0           print __(<
1146             --package-name=PACKAGE set package name in output
1147             EOF
1148              
1149 0           print __(<
1150             --package-version=VERSION set package version in output
1151             EOF
1152              
1153 0           print __(<
1154             --msgid-bugs-address=EMAIL\@ADDRESS set report address for msgid bugs
1155             EOF
1156              
1157 0           print __(<
1158             -m[STRING], --msgstr-prefix[=STRING] use STRING or "" as prefix for msgstr
1159             values
1160             EOF
1161              
1162 0           print __(<
1163             -M[STRING], --msgstr-suffix[=STRING] use STRING or "" as suffix for msgstr
1164             values
1165             EOF
1166              
1167 0           printf "\n";
1168              
1169 0           print __(<
1170             Informative output:
1171             EOF
1172              
1173 0           print __(<
1174             -h, --help display this help and exit
1175             EOF
1176              
1177 0           print __(<
1178             -V, --version output version information and exit
1179             EOF
1180              
1181 0           my $url = $self->bugTrackingAddress;
1182              
1183 0           printf "\n";
1184              
1185 0 0         if (defined $url) {
1186             # TRANSLATORS: The placeholder indicates the bug-reporting address
1187             # for this package. Please add _another line_ saying
1188             # "Report translation bugs to <...>\n" with the address for translation
1189             # bugs (typically your translation team's web or email address).
1190 0           print __x("Report bugs at <{URL}>!\n", URL => $url);
1191             }
1192              
1193 0           exit 0;
1194             }
1195              
1196             sub __usageError {
1197 0     0     my ($self, $message) = @_;
1198              
1199 0 0         if ($message) {
1200 0           $message =~ s/\s+$//;
1201 0           $message = __x("{program_name}: {error}\n",
1202             program_name => $self->programName, error => $message);
1203             } else {
1204 0           $message = '';
1205             }
1206            
1207 0           die $message . __x("Try '{program_name} --help' for more information!\n",
1208             program_name => $self->programName);
1209             }
1210              
1211             1;