File Coverage

blib/lib/Locale/TextDomain.pm
Criterion Covered Total %
statement 72 138 52.1
branch 13 46 28.2
condition 5 36 13.8
subroutine 18 34 52.9
pod 7 7 100.0
total 115 261 44.0


line stmt bran cond sub pod time code
1             #! /bin/false
2              
3             # vim: set autoindent shiftwidth=4 tabstop=4:
4              
5             # High-level interface to Perl i18n.
6             # Copyright (C) 2002-2017 Guido Flohr ,
7             # all rights reserved.
8              
9             # This program is free software: you can redistribute it and/or modify
10             # it under the terms of the GNU General Public License as published by
11             # the Free Software Foundation; either version 3 of the License, or
12             # (at your option) any later version.
13              
14             # This program is distributed in the hope that it will be useful,
15             # but WITHOUT ANY WARRANTY; without even the implied warranty of
16             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17             # GNU General Public License for more details.
18              
19             # You should have received a copy of the GNU General Public License
20             # along with this program. If not, see .
21              
22             package __TiedTextDomain;
23              
24 3     3   6500 use strict;
  3         12  
  3         702  
25              
26             sub TIEHASH
27             {
28 3     3   11 my ($class, $function) = @_;
29 3         26 bless {
30             __function => $function,
31             }, $class;
32             }
33              
34             sub FETCH
35             {
36 0     0   0 my ($self, $msg) = @_;
37            
38 0         0 &{$self->{__function}} ($msg);
  0         0  
39             }
40              
41             sub FIRSTKEY
42             {
43 0     0   0 my $self = shift;
44 0         0 my $reset_iterator = keys %$self;
45 0         0 return scalar each %$self;
46             }
47              
48             sub NEXTKEY
49             {
50 0     0   0 my $self = shift;
51 0         0 return scalar each %$self;
52             }
53              
54       0     sub CLEAR {}
55       0     sub STORE {}
56       0     sub DELETE {}
57              
58             1;
59              
60             package Locale::TextDomain;
61              
62 3     3   22 use strict;
  3         6  
  3         121  
63              
64 3     3   988 use Locale::Messages qw (textdomain bindtextdomain dgettext dngettext dpgettext dnpgettext);
  3         7  
  3         285  
65 3     3   20 use Cwd qw (abs_path);
  3         6  
  3         149  
66              
67 3     3   19 use vars qw ($VERSION);
  3         6  
  3         159  
68              
69             $VERSION = '1.32';
70              
71             require Exporter;
72              
73 3     3   16 use vars qw (@ISA @EXPORT %__ $__);
  3         6  
  3         670  
74              
75             @ISA = ('Exporter');
76             @EXPORT = qw (__ __x __n __nx __xn __p __px __np __npx $__ %__
77             N__ N__n N__p N__np);
78              
79             my %textdomains = ();
80             my %bound_dirs = ();
81             my @default_dirs = ();
82              
83             sub __ ($);
84            
85             sub __find_domain ($);
86             sub __expand ($%);
87             sub __tied_gettext ($$);
88              
89             BEGIN {
90             # Tie the hash to gettext().
91 3     3   36 tie %__, '__TiedTextDomain', \&__tied_gettext;
92 3         8 $__ = \%__;
93              
94             # Add default search directories, but only if they exist.
95 3         8 for my $dir (qw (/usr/share/locale /usr/local/share/locale)) {
96 3 50       72 if (-d $dir) {
97 3         11 @default_dirs = ($dir);
98 3         5700 last;
99             }
100             }
101             }
102              
103             # Class methods.
104             sub keywords {
105 2     2 1 13 join ' ', (
106             '--keyword=__',
107             '--keyword=%__',
108             '--keyword=$__',
109             '--keyword=__x',
110             '--keyword=__n:1,2',
111             '--keyword=__nx:1,2',
112             '--keyword=__xn:1,2',
113             '--keyword=__p:1c,2',
114             '--keyword=__px:1c,2',
115             '--keyword=__np:1c,2,3',
116             '--keyword=__npx:1c,2,3',
117             '--keyword=N__',
118             '--keyword=N__n:1,2',
119             '--keyword=N__p:1c,2',
120             '--keyword=N__np:1c,2,3',
121             );
122             }
123              
124             sub flags {
125 2     2 1 126 join ' ', (
126             '--flag=__:1:pass-perl-format',
127             '--flag=%__:1:pass-perl-format',
128             '--flag=$__:1:pass-perl-format',
129             '--flag=__x:1:perl-brace-format',
130             '--flag=__x:1:pass-perl-format',
131             '--flag=__n:1:pass-perl-format',
132             '--flag=__n:2:pass-perl-format',
133             '--flag=__nx:1:perl-brace-format',
134             '--flag=__nx:1:pass-perl-format',
135             '--flag=__nx:2:perl-brace-format',
136             '--flag=__nx:2:pass-perl-format',
137             '--flag=__xn:1:perl-brace-format',
138             '--flag=__xn:1:pass-perl-format',
139             '--flag=__xn:2:perl-brace-format',
140             '--flag=__xn:2:pass-perl-format',
141             '--flag=__p:2:pass-perl-format',
142             '--flag=__px:2:perl-brace-format',
143             '--flag=__px:2:pass-perl-format',
144             '--flag=__np:2:pass-perl-format',
145             '--flag=__np:3:pass-perl-format',
146             '--flag=__npx:2:perl-brace-format',
147             '--flag=__npx:2:pass-perl-format',
148             '--flag=__npx:3:perl-brace-format',
149             '--flag=__npx:3:pass-perl-format',
150             '--flag=N__:1:pass-perl-format',
151             '--flag=N__n:1:pass-perl-format',
152             '--flag=N__n:2:pass-perl-format',
153             '--flag=N__p:2:pass-perl-format',
154             '--flag=N__np:2:pass-perl-format',
155             '--flag=N__np:3:pass-perl-format',
156             );
157             }
158              
159             sub options {
160 1     1 1 53 my ($class) = @_;
161              
162 1         3 join ' ', $class->keywords, $class->flags;
163             }
164              
165             # Normal gettext.
166             sub __ ($)
167             {
168 2     2   15 my $msgid = shift;
169            
170 2         5 my $package = caller;
171            
172 2         7 my $textdomain = $textdomains{$package};
173            
174             __find_domain $textdomain if
175 2 100 66     15 defined $textdomain && defined $bound_dirs{$textdomain};
176            
177 2         7 return dgettext $textdomain => $msgid;
178             }
179              
180             # Called from tied hash.
181             sub __tied_gettext ($$)
182             {
183 0     0   0 my ($msgid) = @_;
184            
185 0         0 my ($package) = caller (1);
186            
187 0         0 my $textdomain = $textdomains{$package};
188 0 0       0 unless (defined $textdomain) {
189 0         0 my ($maybe_package, $filename, $line) = caller (2);
190 0 0       0 if (exists $textdomains{$maybe_package}) {
191 0         0 warn <
192             Probable use of \$__ or \%__ where __() should be used at $filename:$line.
193             EOF
194             }
195             }
196             __find_domain $textdomain if
197 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
198            
199 0         0 return dgettext $textdomain => $msgid;
200             }
201              
202             # With interpolation.
203             sub __x ($@)
204             {
205 0     0   0 my ($msgid, %vars) = @_;
206            
207 0         0 my $package = caller;
208            
209 0         0 my $textdomain = $textdomains{$package};
210            
211             __find_domain $textdomain if
212 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
213            
214 0         0 return __expand ((dgettext $textdomain => $msgid), %vars);
215             }
216              
217             # Plural.
218             sub __n ($$$)
219             {
220 0     0   0 my ($msgid, $msgid_plural, $count) = @_;
221            
222 0         0 my $package = caller;
223            
224 0         0 my $textdomain = $textdomains{$package};
225            
226             __find_domain $textdomain if
227 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
228            
229 0         0 return dngettext $textdomain, $msgid, $msgid_plural, $count;
230             }
231              
232             # Plural with interpolation.
233             sub __nx ($$$@)
234             {
235 0     0   0 my ($msgid, $msgid_plural, $count, %args) = @_;
236            
237 0         0 my $package = caller;
238            
239 0         0 my $textdomain = $textdomains{$package};
240            
241             __find_domain $textdomain if
242 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
243            
244 0         0 return __expand ((dngettext $textdomain, $msgid, $msgid_plural, $count),
245             %args);
246             }
247              
248             # Plural with interpolation.
249             sub __xn ($$$@)
250             {
251 0     0   0 my ($msgid, $msgid_plural, $count, %args) = @_;
252            
253 0         0 my $package = caller;
254            
255 0         0 my $textdomain = $textdomains{$package};
256            
257             __find_domain $textdomain if
258 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
259            
260 0         0 return __expand ((dngettext $textdomain, $msgid, $msgid_plural, $count),
261             %args);
262             }
263              
264             # Context. (p is for particular or special)
265             sub __p ($$)
266             {
267 0     0   0 my $msgctxt = shift;
268 0         0 my $msgid = shift;
269            
270 0         0 my $package = caller;
271            
272 0         0 my $textdomain = $textdomains{$package};
273            
274             __find_domain $textdomain if
275 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
276            
277 0         0 return dpgettext $textdomain => $msgctxt, $msgid;
278             }
279              
280             # With interpolation.
281             sub __px ($$@)
282             {
283 0     0   0 my ($msgctxt, $msgid, %vars) = @_;
284            
285 0         0 my $package = caller;
286            
287 0         0 my $textdomain = $textdomains{$package};
288            
289             __find_domain $textdomain if
290 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
291            
292 0         0 return __expand ((dpgettext $textdomain => $msgctxt, $msgid), %vars);
293             }
294              
295             # Context + Plural.
296             sub __np ($$$$)
297             {
298 0     0   0 my ($msgctxt, $msgid, $msgid_plural, $count) = @_;
299            
300 0         0 my $package = caller;
301            
302 0         0 my $textdomain = $textdomains{$package};
303            
304             __find_domain $textdomain if
305 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
306            
307 0         0 return dnpgettext $textdomain, $msgctxt, $msgid, $msgid_plural, $count;
308             }
309              
310             # Plural with interpolation.
311             sub __npx ($$$$@)
312             {
313 0     0   0 my ($msgctxt, $msgid, $msgid_plural, $count, %args) = @_;
314            
315 0         0 my $package = caller;
316            
317 0         0 my $textdomain = $textdomains{$package};
318            
319             __find_domain $textdomain if
320 0 0 0     0 defined $textdomain && defined $bound_dirs{$textdomain};
321            
322 0         0 return __expand ((dnpgettext $textdomain, $msgctxt, $msgid, $msgid_plural, $count),
323             %args);
324             }
325              
326             # Dummy functions for string marking.
327             sub N__($)
328             {
329 2     2 1 127 return shift;
330             }
331              
332             sub N__n($$$)
333             {
334 1     1 1 54 return @_;
335             }
336              
337             sub N__p($$) {
338 1     1 1 47 return @_;
339             }
340              
341             sub N__np($$$$) {
342 1     1 1 44 return @_;
343             }
344              
345             sub import
346             {
347 2     2   21 my ($self, $textdomain, @search_dirs) = @_;
348            
349             # Check our caller.
350 2         5 my $package = caller;
351 2 50       9 return if exists $textdomains{$package};
352            
353             # Was a textdomain specified?
354 2 100 66     15 $textdomain = textdomain unless defined $textdomain && length $textdomain;
355            
356             # Remember the textdomain of that package.
357 2         5 $textdomains{$package} = $textdomain;
358            
359             # Remember that we still have to bind that textdomain to
360             # a directory.
361 2 50       7 unless (exists $bound_dirs{$textdomain}) {
362 2 50       7 unless (@search_dirs) {
363 2 50       25 @search_dirs = ((map $_ . '/LocaleData', @INC), @default_dirs)
364             unless @search_dirs;
365 2 50       5 if (my $share = eval {
366 2         1049 require File::ShareDir;
367 2         58215 File::ShareDir::dist_dir ($textdomain);
368             }) {
369             unshift @search_dirs,
370 0         0 map { "$share/$_" }
  0         0  
371             qw (locale LocaleData);
372             }
373             }
374 2         1849 $bound_dirs{$textdomain} = [grep { -d $_ } @search_dirs];
  25         317  
375             }
376            
377 2         412 Locale::TextDomain->export_to_level (1, $package, @EXPORT);
378            
379 2         1909 return;
380             }
381              
382             # Private functions.
383             sub __find_domain ($)
384             {
385 1     1   4 my $domain = shift;
386            
387 1         3 my $try_dirs = $bound_dirs{$domain};
388            
389 1 50       4 if (defined $try_dirs) {
390 1         4 my $found_dir = '';
391            
392 1         3 TRYDIR: foreach my $dir (grep { -d $_ } @$try_dirs) {
  1         23  
393             # Is there a message catalog? We have to search recursively
394             # for it. Since globbing is reported to be buggy under
395             # MS-DOS, we roll our own version.
396 1         6 local *DIR;
397 1 50       44 if (opendir DIR, $dir) {
398 4         17 my @files = map { "$dir/$_/LC_MESSAGES/$domain.mo" }
399 1         38 grep { ! /^\.\.?$/ } readdir DIR;
  6         23  
400              
401 1         6 foreach my $file (@files) {
402 1 50 33     27 if (-f $file || -l $file) {
403             # If we find a non-readable file on our way,
404             # we access has been disabled on purpose.
405             # Therefore no -r check here.
406 1         4 $found_dir = $dir;
407 1         23 last TRYDIR;
408             }
409             }
410             }
411             }
412            
413             # If there was no success, this will fall back to the default search
414             # directories.
415 1         42 bindtextdomain $domain => abs_path $found_dir;
416             }
417            
418             # The search has completed.
419 1         3 undef $bound_dirs{$domain};
420            
421 1         3 return 1;
422             }
423              
424             sub __expand ($%)
425             {
426 0     0     my ($translation, %args) = @_;
427            
428 0           my $re = join '|', map { quotemeta $_ } keys %args;
  0            
429 0 0         $translation =~ s/\{($re)\}/defined $args{$1} ? $args{$1} : "{$1}"/ge;
  0            
430            
431 0           return $translation;
432             }
433              
434             1;
435              
436             __END__