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   90425 use 5.008;
  8         57  
10 8     8   297 use strict;
  8         33  
  8         136  
11 8     8   149 use warnings;
  8         35  
  8         179  
12 8     8   257 use base qw(Exporter);
  8         30  
  8         421  
13 8     8   143 use vars qw($VERSION @EXPORT @EXPORT_OK);
  8         31  
  8         1416  
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   1374 use Encode qw(encode decode from_to FB_DEFAULT);
  8         21565  
  8         549  
48 8     8   156 use File::Spec::Functions qw(catdir catfile);
  8         34  
  8         260  
49 8     8   6102 use Locale::Maketext::Gettext qw(read_mo);
  8         32  
  8         356  
50 8     8   170 use vars qw(%LOCALEDIRS %RIDS %CLASSES %LANGS);
  8         35  
  8         388  
51 8     8   357 use vars qw(%LHS $_EMPTY $LH $DOMAIN $CATEGORY $CLASSBASE @LANGS %PARAMS);
  8         30  
  8         369  
52 8     8   148 use vars qw(@SYSTEM_LOCALEDIRS);
  8         33  
  8         511  
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   250 use vars qw($RID_LEN @RID_CHARS);
  8         29  
  8         12517  
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 494 local ($_, %_);
73 59         114 my ($domain, $LOCALEDIR);
74 59         196 ($domain, $LOCALEDIR) = @_;
75             # Return the current registry
76 59 50       163 return (exists $LOCALEDIRS{$domain}? $LOCALEDIRS{$domain}: undef)
    100          
77             if !defined $LOCALEDIR;
78             # Register the locale directory
79 58         134 $LOCALEDIRS{$domain} = $LOCALEDIR;
80             # Reinitialize the text domain
81 58         278 _init_textdomain($domain);
82             # Reset the current language handle
83 58 100 100     219 _get_handle() if defined $DOMAIN && $domain eq $DOMAIN;
84             # Return the locale directory
85 58         229 return $LOCALEDIR;
86             }
87              
88             # textdomain: Set the current text domain
89             sub textdomain(;$) {
90 70     70 1 30104 local ($_, %_);
91 70         128 my ($new_domain);
92 70         126 $new_domain = $_[0];
93             # Return the current text domain
94 70 100       304 return $DOMAIN if !defined $new_domain;
95             # Set the current text domain
96 69         123 $DOMAIN = $new_domain;
97             # Reinitialize the text domain
98 69         173 _init_textdomain($DOMAIN);
99             # Reset the current language handle
100 69         291 _get_handle();
101 69         191 return $DOMAIN;
102             }
103              
104             # get_handle: Get a language handle
105             sub get_handle(@) {
106 73     73 1 339 local ($_, %_);
107             # Register the current get_handle arguments
108 73         323 @LANGS = @_;
109             # Reset and return the current language handle
110 73         154 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 189 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   867 local ($_, %_);
123 104         193 my ($key, @param, $keyd);
124 104         221 ($key, @param) = @_;
125             # Reset the current language handle if it is not set yet
126 104 100       363 _get_handle() if !defined $LH;
127            
128             # Decode the source text
129 104         189 $keyd = $key;
130             $keyd = decode($PARAMS{"KEY_ENCODING"}, $keyd, $PARAMS{"ENCODE_FAILURE"})
131 104 50 33     687 if exists $PARAMS{"KEY_ENCODING"} && !Encode::is_utf8($key);
132             # Maketext
133 104         5203 $_ = $LH->maketext($keyd, @param);
134             # Output to the requested encoding
135 103 100 66     242 if (exists $PARAMS{"ENCODING"}) {
    100 33        
136 92         342 $_ = encode($PARAMS{"ENCODING"}, $_, $PARAMS{"ENCODE_FAILURE"});
137             # Pass through the empty/invalid lexicon
138 12         137 } elsif ( scalar(keys %{$LH->{"Lexicon"}}) == 0
139             && exists $PARAMS{"KEY_ENCODING"}
140             && !Encode::is_utf8($key)) {
141 10         37 $_ = encode($PARAMS{"KEY_ENCODING"}, $_, $PARAMS{"ENCODE_FAILURE"});
142             }
143            
144 102         14232 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 75 return $_[0] unless wantarray;
153 3         25 return @_;
154             }
155              
156             # dmaketext: Maketext in another text domain temporarily,
157             # an equivalent to dgettext().
158             sub dmaketext($$@) {
159 5     6 1 16 local ($_, %_);
160 5         83 my ($domain, $key, @param, $lh0, $domain0, $text);
161 5         14 ($domain, $key, @param) = @_;
162             # Preserve the current status
163 5         15 ($lh0, $domain0) = ($LH, $DOMAIN);
164             # Reinitialize the text domain
165 5         49 textdomain($domain);
166             # Maketext
167 5         25 $text = maketext($key, @param);
168             # Return the current status
169 5         37 ($LH, $DOMAIN) = ($lh0, $domain0);
170             # Return the "made text"
171 5         96 return $text;
172             }
173              
174             # pmaketext: Maketext with context,
175             # an equivalent to pgettext().
176             sub pmaketext($$@) {
177 20     21 1 117 local ($_, %_);
178 20         39 my ($ctxt, $key, @param);
179 20         83 ($ctxt, $key, @param) = @_;
180             # This is actually a wrapper to the maketext() function
181 20         65 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 11 local ($_, %_);
188 3         75 my ($domain, $ctxt, $key, @param);
189 3         13 ($domain, $ctxt, $key, @param) = @_;
190             # This is actually a wrapper to the dmaketext() function
191 3         10 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 44 Locale::Maketext::Gettext->reload_text;
198             }
199              
200             # encoding: Set the output encoding
201             sub encoding(;$) {
202 17     17 1 83 local ($_, %_);
203 17         33 $_ = $_[0];
204            
205             # Set the output encoding
206 17 100       93 if (@_ > 0) {
207 13 100       38 if (defined $_) {
208 11         20 $PARAMS{"ENCODING"} = $_;
209             } else {
210 3         49 delete $PARAMS{"ENCODING"};
211             }
212 13         30 $PARAMS{"USERSET_ENCODING"} = $_;
213             }
214            
215             # Return the encoding
216 17 100       62 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 66 local ($_, %_);
222 3         12 $_ = $_[0];
223            
224             # Set the encoding used in the keys
225 3 50       8 if (@_ > 0) {
226 3 50       37 if (defined $_) {
227 3         13 $PARAMS{"KEY_ENCODING"} = $_;
228             } else {
229 1         2 delete $PARAMS{"KEY_ENCODING"};
230             }
231             }
232            
233             # Return the encoding
234 3 50       87 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 15 local ($_, %_);
241 3         9 $_ = $_[0];
242             # Set and return the current setting
243 3 50       39 $PARAMS{"ENCODE_FAILURE"} = $_ if @_ > 0;
244             # Return the current setting
245 3         12 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         65 $_ = $_[0];
253             # Set the current setting
254 1 0       7 if (@_ > 0) {
255 1 0       3 $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = $_? 1: 0;
256 1         32 $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   199 local ($_, %_);
267 94         247 $_ = $_[0];
268 94     5   7804 eval << "EOT";
  5     5   36  
  5     5   12  
  5     5   355  
  5     5   34  
  5     5   12  
  5     5   287  
  5     5   32  
  5     5   10  
  5     5   310  
  5     1   45  
  5     1   7  
  5     1   223  
  5     1   42  
  5     1   12  
  5     1   345  
  5     1   35  
  5     1   8  
  5     1   213  
  5     1   32  
  5     1   9  
  5     1   339  
  5     1   34  
  5     1   18  
  5     1   232  
  5     1   32  
  5     1   9  
  5     1   347  
  5     1   33  
  5     1   25  
  5     1   221  
  1     1   5  
  1     1   15  
  1     1   35  
  1     1   6  
  1     1   2  
  1     1   74  
  1     1   7  
  1     1   2  
  1     1   34  
  1     1   6  
  1     1   3  
  1     1   68  
  1     1   11  
  1     1   2  
  1     1   33  
  1     1   7  
  1     1   1  
  1     1   56  
  1     1   15  
  1     1   3  
  1     1   36  
  1     1   7  
  1     1   2  
  1     1   65  
  1     1   7  
  1         2  
  1         33  
  1         7  
  1         2  
  1         77  
  1         7  
  1         2  
  1         35  
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   380 return join("::", @_);;
278             }
279              
280             # _init_textdomain: Initialize a text domain
281             sub _init_textdomain($) {
282 124     124   298 local ($_, %_);
283 124         241 my ($domain, $k, @langs, $langs);
284 124         203 $domain = $_[0];
285            
286             # Return if text domain not specified yet
287 124 50       350 return if !defined $domain;
288            
289             # Obtain the available locales
290             # A binded domain
291 124 100       306 if (exists $LOCALEDIRS{$domain}) {
292 118         256 @langs = _get_langs($LOCALEDIRS{$domain}, $domain);
293             # Not binded
294             } else {
295 7         51 @langs = qw();
296             # Search the system locale directories
297 7         31 foreach (@SYSTEM_LOCALEDIRS) {
298 22         52 @langs = _get_langs($_, $domain);
299             # Domain not found in this directory
300 22 100       130 next if @langs == 0;
301 2         9 $LOCALEDIRS{$domain} = $_;
302 2         5 last;
303             }
304             # Not found at last
305 7 100       63 return if !exists $LOCALEDIRS{$domain};
306             }
307 119         608 $langs = join ",", sort @langs;
308            
309             # Obtain the registry key
310 119         291 $k = _k($domain);
311            
312             # Available language list remains for this domain
313 119 100 100     794 return if exists $LANGS{$k} && $LANGS{$k} eq $langs;
314             # Register this new language list
315 20         59 $LANGS{$k} = $langs;
316            
317 20         42 my ($rid, $class);
318             # Garbage collection - drop abandoned language handles
319 20 100       112 if (exists $CLASSES{$k}) {
320 5         136 delete $LHS{$_} foreach grep /^$CLASSES{$k}/, keys %LHS;
321             }
322             # Get a new class ID
323 20         63 $rid = _new_rid();
324             # Obtain the class name
325 20         119 $class = _catclass($CLASSBASE, $rid);
326             # Register the domain with this class
327 20         52 $CLASSES{$k} = $class;
328             # Declare this class
329 20         58 _declare_class($class);
330             # Declare its language subclasses
331             _declare_class(_catclass($class, $_))
332 20         128 foreach @langs;
333            
334 20         79 return;
335             }
336              
337             # _get_langs: Search a locale directory and return the available languages
338             sub _get_langs($$) {
339 139     139   249 local ($_, %_);
340 139         353 my ($dir, $domain, $DH, $entry, $MOfile);
341 139         314 ($dir, $domain) = @_;
342            
343 139         250 @_ = qw();
344             {
345 139 100       243 opendir $DH, $dir or last;
  139         4333  
346 123         2261 while (defined($entry = readdir $DH)) {
347             # Skip hidden entries
348 976 100       3169 next if $entry =~ /^\./;
349             # Skip non-directories
350 732 50       10385 next unless -d catdir($dir, $entry);
351             # Skip locales with dot "." (trailing encoding)
352 732 100       2790 next if $entry =~ /\./;
353             # Get the MO file name
354 727         3591 $MOfile = catfile($dir, $entry, $CATEGORY, "$domain.mo");
355             # Skip if MO file is not available for this locale
356 727 50 66     12465 next if ! -f $MOfile && ! -r $MOfile;
357             # Map C to i_default
358 404 100       1148 $entry = "i_default" if $entry eq "C";
359             # Add this language
360 404         2039 push @_, lc $entry;
361             }
362 123 50       452 close $DH or last;
363             }
364 139         2023 return @_;
365             }
366              
367             # _get_handle: Set the language handle with the current DOMAIN and @LANGS
368             sub _get_handle() {
369 143     143   299 local ($_, %_);
370 143         247 my ($k, $class, $subclass);
371            
372             # Lexicon empty if text domain not specified, or not binded yet
373 143 100 100     566 return _get_empty_handle if !defined $DOMAIN || !exists $LOCALEDIRS{$DOMAIN};
374             # Obtain the registry key
375 125         331 $k = _k($DOMAIN);
376             # Lexicon empty if text domain was not properly set yet
377 125 50       319 return _get_empty_handle if !exists $CLASSES{$k};
378            
379             # Get the localization class name
380 125         233 $class = $CLASSES{$k};
381             # Get the language handle
382 125         726 $LH = $class->get_handle(@LANGS);
383             # Lexicon empty if failed get_handle()
384 125 100       12549 return _get_empty_handle if !defined $LH;
385            
386             # Obtain the subclass name of the got language handle
387 109         213 $subclass = ref($LH);
388             # Use the existing language handle whenever possible, to reduce
389             # the initialization overhead
390 109 100       325 if (exists $LHS{$subclass}) {
391 84         430 $LH = $LHS{$subclass};
392 84 50       210 if (!exists $PARAMS{"USERSET_ENCODING"}) {
393 84 50       227 if (exists $LH->{"MO_ENCODING"}) {
394 84         179 $PARAMS{"ENCODING"} = $LH->{"MO_ENCODING"};
395             } else {
396 1         2 delete $PARAMS{"ENCODING"};
397             }
398             }
399 84         250 return _lang($LH)
400             }
401            
402             # Initialize it
403 26         170 $LH->bindtextdomain($DOMAIN, $LOCALEDIRS{$DOMAIN});
404 26         140 $LH->textdomain($DOMAIN);
405             # Respect the MO file encoding unless there is a user preferrence
406 26 50       123 if (!exists $PARAMS{"USERSET_ENCODING"}) {
407 26 100       68 if (exists $LH->{"MO_ENCODING"}) {
408 25         65 $PARAMS{"ENCODING"} = $LH->{"MO_ENCODING"};
409             } else {
410 2         110 delete $PARAMS{"ENCODING"};
411             }
412             }
413             # We handle the encoding() and key_encoding() ourselves.
414 26         152 $LH->key_encoding(undef);
415 26         194 $LH->encoding(undef);
416             # Register it
417 26         88 $LHS{$subclass} = $LH;
418            
419 26         69 return _lang($LH);
420             }
421              
422             # _get_empty_handle: Obtain the empty language handle
423             sub _get_empty_handle() {
424 35     35   82 local ($_, %_);
425 35 100       184 if (!defined $_EMPTY) {
426 3         33 $_EMPTY = Locale::Maketext::Gettext::Functions::_EMPTY->get_handle;
427 3         17 $_EMPTY->key_encoding(undef);
428 3         52 $_EMPTY->encoding(undef);
429             }
430 35         67 $LH = $_EMPTY;
431 35         149 $LH->die_for_lookup_failures($PARAMS{"DIE_FOR_LOOKUP_FAILURES"});
432 35         152 return _lang($LH);
433             }
434              
435             # _reset: Initialize everything
436             sub _reset() {
437 56     56   11333 local ($_, %_);
438            
439 56         162 %LOCALEDIRS = qw();
440 56         167 undef $LH;
441 56         109 undef $DOMAIN;
442 56         110 @LANGS = qw();
443 56         205 %PARAMS = qw();
444 56         139 $PARAMS{"KEY_ENCODING"} = "US-ASCII";
445 56         105 $PARAMS{"ENCODE_FAILURE"} = FB_DEFAULT;
446 56         131 $PARAMS{"DIE_FOR_LOOKUP_FAILURES"} = 0;
447            
448 56         140 return;
449             }
450              
451             # _new_rid: Generate a new random ID
452             sub _new_rid() {
453 20     20   54 local ($_, %_);
454 20         82 my ($id);
455            
456             do {
457 20         86 for ($id = "", $_ = 0; $_ < $RID_LEN; $_++) {
458 153         649 $id .= $RID_CHARS[int rand scalar @RID_CHARS];
459             }
460 20         43 } while exists $RIDS{$id};
461 20         59 $RIDS{$id} = 1;
462            
463 20         64 return $id;
464             }
465              
466             # _k: Build the key for the domain registry
467             sub _k($) {
468 243     243   931 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   288 local ($_, %_);
474 143         228 $_ = $_[0];
475 143         341 $_ = ref($_);
476 143         678 s/^.+:://;
477 143         385 s/_/-/g;
478 143         493 return $_;
479             }
480              
481             # Public empty lexicon
482             package Locale::Maketext::Gettext::Functions::_EMPTY;
483 8     8   225 use 5.008;
  8         47  
484 8     8   38 use strict;
  8         238  
  8         182  
485 8     7   35 use warnings;
  8         125  
  7         207  
486 7     7   50 use base qw(Locale::Maketext::Gettext);
  7         186  
  7         437  
487 7     7   39 use vars qw($VERSION @ISA %Lexicon);
  7         90  
  7         408  
488             $VERSION = 0.01;
489              
490             package Locale::Maketext::Gettext::Functions::_EMPTY::i_default;
491 7     7   81 use 5.008;
  7         146  
492 7     7   49 use strict;
  7         12  
  7         208  
493 7     7   40 use warnings;
  7         14  
  7         337  
494 7     7   44 use base qw(Locale::Maketext::Gettext);
  7         15  
  7         509  
495 7     7   50 use vars qw($VERSION @ISA %Lexicon);
  7         14  
  7         607  
496             $VERSION = 0.01;
497              
498             return 1;
499              
500             __END__