File Coverage

blib/lib/Locale/Maketext/Gettext/Functions.pm
Criterion Covered Total %
statement 306 306 100.0
branch 62 80 77.5
condition 15 21 71.4
subroutine 102 102 100.0
pod 13 13 100.0
total 498 522 95.4


line stmt bran cond sub pod time code
1             # Locale::Maketext::Gettext::Functions - Functional interface to Locale::Maketext::Gettext
2              
3             # Copyright (c) 2003-2008 imacat. All rights reserved. This program is free
4             # software; you can redistribute it and/or modify it under the same terms
5             # as Perl itself.
6             # First written: 2003-04-28
7              
8             package Locale::Maketext::Gettext::Functions;
9 8     8   75828 use 5.008;
  8         57  
10 8     8   236 use strict;
  8         26  
  8         133  
11 8     8   130 use warnings;
  8         28  
  8         158  
12 8     8   248 use base qw(Exporter);
  8         26  
  8         453  
13 8     8   131 use vars qw($VERSION @EXPORT @EXPORT_OK);
  8         23  
  8         1237  
14             $VERSION = 0.13;
15             @EXPORT = qw();
16             push @EXPORT, qw(bindtextdomain textdomain get_handle maketext __ N_);
17             push @EXPORT, qw(dmaketext pmaketext dpmaketext);
18             push @EXPORT, qw(reload_text read_mo encoding key_encoding encode_failure);
19             push @EXPORT, qw(die_for_lookup_failures);
20             @EXPORT_OK = @EXPORT;
21             # Prototype declaration
22             sub bindtextdomain($;$);
23             sub textdomain(;$);
24             sub get_handle(@);
25             sub maketext(@);
26             sub __(@);
27             sub N_(@);
28             sub dmaketext($$@);
29             sub pmaketext($$@);
30             sub dpmaketext($$$@);
31             sub reload_text();
32             sub encoding(;$);
33             sub key_encoding(;$);
34             sub encode_failure(;$);
35             sub die_for_lookup_failures(;$);
36             sub _declare_class($);
37             sub _catclass(@);
38             sub _init_textdomain($);
39             sub _get_langs($$);
40             sub _get_handle();
41             sub _get_empty_handle();
42             sub _reset();
43             sub _new_rid();
44             sub _k($);
45             sub _lang($);
46              
47 8     8   1225 use Encode qw(encode decode from_to FB_DEFAULT);
  8         19200  
  8         588  
48 8     8   131 use File::Spec::Functions qw(catdir catfile);
  8         25  
  8         296  
49 8     8   5524 use Locale::Maketext::Gettext qw(read_mo);
  8         28  
  8         281  
50 8     8   131 use vars qw(%LOCALEDIRS %RIDS %CLASSES %LANGS);
  8         29  
  8         251  
51 8     8   341 use vars qw(%LHS $_EMPTY $LH $DOMAIN $CATEGORY $CLASSBASE @LANGS %PARAMS);
  8         26  
  8         317  
52 8     8   123 use vars qw(@SYSTEM_LOCALEDIRS);
  8         26  
  8         415  
53             %LHS = qw();
54             # The category is always LC_MESSAGES
55             $CATEGORY = "LC_MESSAGES";
56             $CLASSBASE = "Locale::Maketext::Gettext::_runtime";
57             # Current language parameters
58             @LANGS = qw();
59             @SYSTEM_LOCALEDIRS = @Locale::Maketext::Gettext::SYSTEM_LOCALEDIRS;
60             %PARAMS = qw();
61             $PARAMS{"KEY_ENCODING"} = "US-ASCII";
62             $PARAMS{"ENCODE_FAILURE"} = FB_DEFAULT;
63             $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = 0;
64             # Parameters for random class IDs
65 8     8   194 use vars qw($RID_LEN @RID_CHARS);
  8         24  
  8         10131  
66             $RID_LEN = 8;
67             @RID_CHARS = split //,
68             "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
69              
70             # bindtextdomain: Bind a text domain to a locale directory
71             sub bindtextdomain($;$) {
72 59     59 1 335 local ($_, %_);
73 59         86 my ($domain, $LOCALEDIR);
74 59         189 ($domain, $LOCALEDIR) = @_;
75             # Return the current registry
76 59 50       138 return (exists $LOCALEDIRS{$domain}? $LOCALEDIRS{$domain}: undef)
    100          
77             if !defined $LOCALEDIR;
78             # Register the locale directory
79 58         116 $LOCALEDIRS{$domain} = $LOCALEDIR;
80             # Reinitialize the text domain
81 58         238 _init_textdomain($domain);
82             # Reset the current language handle
83 58 100 100     191 _get_handle() if defined $DOMAIN && $domain eq $DOMAIN;
84             # Return the locale directory
85 58         131 return $LOCALEDIR;
86             }
87              
88             # textdomain: Set the current text domain
89             sub textdomain(;$) {
90 70     70 1 27303 local ($_, %_);
91 70         100 my ($new_domain);
92 70         105 $new_domain = $_[0];
93             # Return the current text domain
94 70 100       264 return $DOMAIN if !defined $new_domain;
95             # Set the current text domain
96 69         102 $DOMAIN = $new_domain;
97             # Reinitialize the text domain
98 69         147 _init_textdomain($DOMAIN);
99             # Reset the current language handle
100 69         268 _get_handle();
101 69         148 return $DOMAIN;
102             }
103              
104             # get_handle: Get a language handle
105             sub get_handle(@) {
106 73     73 1 263 local ($_, %_);
107             # Register the current get_handle arguments
108 73         259 @LANGS = @_;
109             # Reset and return the current language handle
110 73         130 return _get_handle();
111             }
112              
113             # maketext: Maketext, in its long name
114             # Use @ instead of $@ in prototype, so that we can pass @_ to it.
115             sub maketext(@) {
116 41     41 1 138 return __($_[0], @_[1..$#_]);
117             }
118              
119             # __: Maketext, in its shortcut name
120             # Use @ instead of $@ in prototype, so that we can pass @_ to it.
121             sub __(@) {
122 104     104   687 local ($_, %_);
123 104         156 my ($key, @param, $keyd);
124 104         176 ($key, @param) = @_;
125             # Reset the current language handle if it is not set yet
126 104 100       319 _get_handle() if !defined $LH;
127            
128             # Decode the source text
129 104         159 $keyd = $key;
130             $keyd = decode($PARAMS{"KEY_ENCODING"}, $keyd, $PARAMS{"ENCODE_FAILURE"})
131 104 50 33     580 if exists $PARAMS{"KEY_ENCODING"} && !Encode::is_utf8($key);
132             # Maketext
133 104         4358 $_ = $LH->maketext($keyd, @param);
134             # Output to the requested encoding
135 103 100 66     501 if (exists $PARAMS{"ENCODING"}) {
    100 33        
136 92         288 $_ = encode($PARAMS{"ENCODING"}, $_, $PARAMS{"ENCODE_FAILURE"});
137             # Pass through the empty/invalid lexicon
138 12         124 } elsif ( scalar(keys %{$LH->{"Lexicon"}}) == 0
139             && exists $PARAMS{"KEY_ENCODING"}
140             && !Encode::is_utf8($key)) {
141 10         32 $_ = encode($PARAMS{"KEY_ENCODING"}, $_, $PARAMS{"ENCODE_FAILURE"});
142             }
143            
144 102         11178 return $_;
145             }
146              
147             # N_: Return the original text untouched, so that it can be catched
148             # with xgettext
149             # Use @ instead of $@ in prototype, so that we can pass @_ to it.
150             sub N_(@) {
151             # Watch out for this Perl magic! :p
152 6 100   7 1 55 return $_[0] unless wantarray;
153 3         12 return @_;
154             }
155              
156             # dmaketext: Maketext in another text domain temporarily,
157             # an equivalent to dgettext().
158             sub dmaketext($$@) {
159 5     6 1 14 local ($_, %_);
160 5         53 my ($domain, $key, @param, $lh0, $domain0, $text);
161 5         11 ($domain, $key, @param) = @_;
162             # Preserve the current status
163 5         10 ($lh0, $domain0) = ($LH, $DOMAIN);
164             # Reinitialize the text domain
165 5         35 textdomain($domain);
166             # Maketext
167 5         29 $text = maketext($key, @param);
168             # Return the current status
169 5         18 ($LH, $DOMAIN) = ($lh0, $domain0);
170             # Return the "made text"
171 5         78 return $text;
172             }
173              
174             # pmaketext: Maketext with context,
175             # an equivalent to pgettext().
176             sub pmaketext($$@) {
177 20     21 1 72 local ($_, %_);
178 20         30 my ($ctxt, $key, @param);
179 20         66 ($ctxt, $key, @param) = @_;
180             # This is actually a wrapper to the maketext() function
181 20         55 return maketext("$ctxt\x04$key", @param);
182             }
183              
184             # dpmaketext: Maketext with context in another text domain temporarily,
185             # an equivalent to dpgettext().
186             sub dpmaketext($$$@) {
187 3     3 1 10 local ($_, %_);
188 3         57 my ($domain, $ctxt, $key, @param);
189 3         10 ($domain, $ctxt, $key, @param) = @_;
190             # This is actually a wrapper to the dmaketext() function
191 3         9 return dmaketext($domain, "$ctxt\x04$key", @param);
192             }
193              
194             # reload_text: Purge the lexicon cache
195             sub reload_text() {
196             # reload_text is static.
197 2     2 1 36 Locale::Maketext::Gettext->reload_text;
198             }
199              
200             # encoding: Set the output encoding
201             sub encoding(;$) {
202 17     17 1 57 local ($_, %_);
203 17         29 $_ = $_[0];
204            
205             # Set the output encoding
206 17 100       92 if (@_ > 0) {
207 13 100       26 if (defined $_) {
208 11         18 $PARAMS{"ENCODING"} = $_;
209             } else {
210 3         33 delete $PARAMS{"ENCODING"};
211             }
212 13         23 $PARAMS{"USERSET_ENCODING"} = $_;
213             }
214            
215             # Return the encoding
216 17 100       40 return exists $PARAMS{"ENCODING"}? $PARAMS{"ENCODING"}: undef;
217             }
218              
219             # key_encoding: Set the encoding of the original text
220             sub key_encoding(;$) {
221 3     3 1 54 local ($_, %_);
222 3         8 $_ = $_[0];
223            
224             # Set the encoding used in the keys
225 3 50       9 if (@_ > 0) {
226 3 50       32 if (defined $_) {
227 3         9 $PARAMS{"KEY_ENCODING"} = $_;
228             } else {
229 1         2 delete $PARAMS{"KEY_ENCODING"};
230             }
231             }
232            
233             # Return the encoding
234 3 50       60 return exists $PARAMS{"KEY_ENCODING"}? $PARAMS{"KEY_ENCODING"}: undef;
235             }
236              
237             # encode_failure: What to do if the text is out of your output encoding
238             # Refer to Encode on possible values of this check
239             sub encode_failure(;$) {
240 3     3 1 13 local ($_, %_);
241 3         5 $_ = $_[0];
242             # Set and return the current setting
243 3 50       34 $PARAMS{"ENCODE_FAILURE"} = $_ if @_ > 0;
244             # Return the current setting
245 3         8 return $PARAMS{"ENCODE_FAILURE"};
246             }
247              
248             # die_for_lookup_failures: Whether we should die for lookup failure
249             # The default is no. GNU gettext never fails.
250             sub die_for_lookup_failures(;$) {
251 1     1 1 2 local ($_, %_);
252 1         54 $_ = $_[0];
253             # Set the current setting
254 1 0       6 if (@_ > 0) {
255 1 0       1 $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = $_? 1: 0;
256 1         29 $LH->die_for_lookup_failures($PARAMS{"DIE_FOR_LOOKUP_FAILURES"});
257             }
258             # Return the current setting
259             # Resetting the current language handle is not required
260             # Lookup failures are handled by the fail handler directly
261 1         5 return $PARAMS{"DIE_FOR_LOOKUP_FAILURES"};
262             }
263              
264             # _declare_class: Declare a class
265             sub _declare_class($) {
266 94     94   154 local ($_, %_);
267 94         205 $_ = $_[0];
268 94     5   6126 eval << "EOT";
  5     5   33  
  5     5   10  
  5     5   305  
  5     5   29  
  5     5   8  
  5     5   239  
  5     5   27  
  5     5   9  
  5     5   258  
  5     1   27  
  5     1   6  
  5     1   183  
  5     1   33  
  5     1   10  
  5     1   265  
  5     1   27  
  5     1   8  
  5     1   181  
  5     1   27  
  5     1   8  
  5     1   278  
  5     1   28  
  5     1   14  
  5     1   182  
  5     1   28  
  5     1   9  
  5     1   277  
  5     1   30  
  5     1   19  
  5     1   174  
  1     1   5  
  1     1   9  
  1     1   30  
  1     1   5  
  1     1   2  
  1     1   53  
  1     1   7  
  1     1   2  
  1     1   34  
  1     1   5  
  1     1   2  
  1     1   82  
  1     1   6  
  1     1   2  
  1     1   29  
  1     1   6  
  1     1   2  
  1     1   60  
  1     1   12  
  1     1   2  
  1     1   30  
  1     1   5  
  1     1   2  
  1     1   53  
  1     1   5  
  1         2  
  1         28  
  1         5  
  1         2  
  1         56  
  1         6  
  1         1  
  1         36  
269             package $_[0];
270             use base qw(Locale::Maketext::Gettext);
271             use vars qw(\@ISA %Lexicon);
272             EOT
273             }
274              
275             # _catclass: Catenate the class name
276             sub _catclass(@) {
277 94     94   326 return join("::", @_);;
278             }
279              
280             # _init_textdomain: Initialize a text domain
281             sub _init_textdomain($) {
282 124     124   252 local ($_, %_);
283 124         194 my ($domain, $k, @langs, $langs);
284 124         168 $domain = $_[0];
285            
286             # Return if text domain not specified yet
287 124 50       295 return if !defined $domain;
288            
289             # Obtain the available locales
290             # A binded domain
291 124 100       253 if (exists $LOCALEDIRS{$domain}) {
292 118         236 @langs = _get_langs($LOCALEDIRS{$domain}, $domain);
293             # Not binded
294             } else {
295 7         40 @langs = qw();
296             # Search the system locale directories
297 7         22 foreach (@SYSTEM_LOCALEDIRS) {
298 22         45 @langs = _get_langs($_, $domain);
299             # Domain not found in this directory
300 22 100       118 next if @langs == 0;
301 2         9 $LOCALEDIRS{$domain} = $_;
302 2         4 last;
303             }
304             # Not found at last
305 7 100       52 return if !exists $LOCALEDIRS{$domain};
306             }
307 119         543 $langs = join ",", sort @langs;
308            
309             # Obtain the registry key
310 119         246 $k = _k($domain);
311            
312             # Available language list remains for this domain
313 119 100 100     649 return if exists $LANGS{$k} && $LANGS{$k} eq $langs;
314             # Register this new language list
315 20         49 $LANGS{$k} = $langs;
316            
317 20         34 my ($rid, $class);
318             # Garbage collection - drop abandoned language handles
319 20 100       83 if (exists $CLASSES{$k}) {
320 5         123 delete $LHS{$_} foreach grep /^$CLASSES{$k}/, keys %LHS;
321             }
322             # Get a new class ID
323 20         54 $rid = _new_rid();
324             # Obtain the class name
325 20         106 $class = _catclass($CLASSBASE, $rid);
326             # Register the domain with this class
327 20         41 $CLASSES{$k} = $class;
328             # Declare this class
329 20         56 _declare_class($class);
330             # Declare its language subclasses
331             _declare_class(_catclass($class, $_))
332 20         103 foreach @langs;
333            
334 20         67 return;
335             }
336              
337             # _get_langs: Search a locale directory and return the available languages
338             sub _get_langs($$) {
339 139     139   207 local ($_, %_);
340 139         241 my ($dir, $domain, $DH, $entry, $MOfile);
341 139         234 ($dir, $domain) = @_;
342            
343 139         186 @_ = qw();
344             {
345 139 100       211 opendir $DH, $dir or last;
  139         3576  
346 123         1946 while (defined($entry = readdir $DH)) {
347             # Skip hidden entries
348 976 100       2953 next if $entry =~ /^\./;
349             # Skip non-directories
350 732 50       8773 next unless -d catdir($dir, $entry);
351             # Skip locales with dot "." (trailing encoding)
352 732 100       2592 next if $entry =~ /\./;
353             # Get the MO file name
354 727         3154 $MOfile = catfile($dir, $entry, $CATEGORY, "$domain.mo");
355             # Skip if MO file is not available for this locale
356 727 50 66     26447 next if ! -f $MOfile && ! -r $MOfile;
357             # Map C to i_default
358 404 100       1008 $entry = "i_default" if $entry eq "C";
359             # Add this language
360 404         1745 push @_, lc $entry;
361             }
362 123 50       394 close $DH or last;
363             }
364 139         1821 return @_;
365             }
366              
367             # _get_handle: Set the language handle with the current DOMAIN and @LANGS
368             sub _get_handle() {
369 143     143   251 local ($_, %_);
370 143         213 my ($k, $class, $subclass);
371            
372             # Lexicon empty if text domain not specified, or not binded yet
373 143 100 100     474 return _get_empty_handle if !defined $DOMAIN || !exists $LOCALEDIRS{$DOMAIN};
374             # Obtain the registry key
375 125         306 $k = _k($DOMAIN);
376             # Lexicon empty if text domain was not properly set yet
377 125 50       296 return _get_empty_handle if !exists $CLASSES{$k};
378            
379             # Get the localization class name
380 125         198 $class = $CLASSES{$k};
381             # Get the language handle
382 125         600 $LH = $class->get_handle(@LANGS);
383             # Lexicon empty if failed get_handle()
384 125 100       11218 return _get_empty_handle if !defined $LH;
385            
386             # Obtain the subclass name of the got language handle
387 109         188 $subclass = ref($LH);
388             # Use the existing language handle whenever possible, to reduce
389             # the initialization overhead
390 109 100       263 if (exists $LHS{$subclass}) {
391 84         358 $LH = $LHS{$subclass};
392 84 50       176 if (!exists $PARAMS{"USERSET_ENCODING"}) {
393 84 50       185 if (exists $LH->{"MO_ENCODING"}) {
394 84         141 $PARAMS{"ENCODING"} = $LH->{"MO_ENCODING"};
395             } else {
396 1         2 delete $PARAMS{"ENCODING"};
397             }
398             }
399 84         188 return _lang($LH)
400             }
401            
402             # Initialize it
403 26         141 $LH->bindtextdomain($DOMAIN, $LOCALEDIRS{$DOMAIN});
404 26         116 $LH->textdomain($DOMAIN);
405             # Respect the MO file encoding unless there is a user preferrence
406 26 50       106 if (!exists $PARAMS{"USERSET_ENCODING"}) {
407 26 100       62 if (exists $LH->{"MO_ENCODING"}) {
408 25         50 $PARAMS{"ENCODING"} = $LH->{"MO_ENCODING"};
409             } else {
410 2         47 delete $PARAMS{"ENCODING"};
411             }
412             }
413             # We handle the encoding() and key_encoding() ourselves.
414 26         119 $LH->key_encoding(undef);
415 26         158 $LH->encoding(undef);
416             # Register it
417 26         70 $LHS{$subclass} = $LH;
418            
419 26         61 return _lang($LH);
420             }
421              
422             # _get_empty_handle: Obtain the empty language handle
423             sub _get_empty_handle() {
424 35     35   67 local ($_, %_);
425 35 100       132 if (!defined $_EMPTY) {
426 3         34 $_EMPTY = Locale::Maketext::Gettext::Functions::_EMPTY->get_handle;
427 3         13 $_EMPTY->key_encoding(undef);
428 3         36 $_EMPTY->encoding(undef);
429             }
430 35         55 $LH = $_EMPTY;
431 35         128 $LH->die_for_lookup_failures($PARAMS{"DIE_FOR_LOOKUP_FAILURES"});
432 35         120 return _lang($LH);
433             }
434              
435             # _reset: Initialize everything
436             sub _reset() {
437 56     56   9626 local ($_, %_);
438            
439 56         134 %LOCALEDIRS = qw();
440 56         134 undef $LH;
441 56         83 undef $DOMAIN;
442 56         97 @LANGS = qw();
443 56         176 %PARAMS = qw();
444 56         117 $PARAMS{"KEY_ENCODING"} = "US-ASCII";
445 56         91 $PARAMS{"ENCODE_FAILURE"} = FB_DEFAULT;
446 56         115 $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = 0;
447            
448 56         122 return;
449             }
450              
451             # _new_rid: Generate a new random ID
452             sub _new_rid() {
453 20     20   47 local ($_, %_);
454 20         72 my ($id);
455            
456             do {
457 20         72 for ($id = "", $_ = 0; $_ < $RID_LEN; $_++) {
458 153         605 $id .= $RID_CHARS[int rand scalar @RID_CHARS];
459             }
460 20         31 } while exists $RIDS{$id};
461 20         48 $RIDS{$id} = 1;
462            
463 20         57 return $id;
464             }
465              
466             # _k: Build the key for the domain registry
467             sub _k($) {
468 243     243   732 return join "\n", $LOCALEDIRS{$_[0]}, $CATEGORY, $_[0];
469             }
470              
471             # _lang: The langage from a language handle. language_tag is not quite sane.
472             sub _lang($) {
473 143     143   213 local ($_, %_);
474 143         190 $_ = $_[0];
475 143         270 $_ = ref($_);
476 143         598 s/^.+:://;
477 143         312 s/_/-/g;
478 143         424 return $_;
479             }
480              
481             # Public empty lexicon
482             package Locale::Maketext::Gettext::Functions::_EMPTY;
483 8     8   248 use 5.008;
  8         36  
484 8     8   34 use strict;
  8         194  
  8         134  
485 8     7   30 use warnings;
  8         103  
  7         234  
486 7     7   43 use base qw(Locale::Maketext::Gettext);
  7         216  
  7         403  
487 7     7   32 use vars qw($VERSION @ISA %Lexicon);
  7         91  
  7         344  
488             $VERSION = 0.01;
489              
490             package Locale::Maketext::Gettext::Functions::_EMPTY::i_default;
491 7     7   68 use 5.008;
  7         124  
492 7     7   40 use strict;
  7         12  
  7         186  
493 7     7   33 use warnings;
  7         14  
  7         266  
494 7     7   41 use base qw(Locale::Maketext::Gettext);
  7         14  
  7         418  
495 7     7   70 use vars qw($VERSION @ISA %Lexicon);
  7         20  
  7         457  
496             $VERSION = 0.01;
497              
498             return 1;
499              
500             __END__