This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Assimilate Locale-Codes-2.07
[perl5.git] / lib / Locale / Maketext.pm
1
2 # Time-stamp: "2004-03-30 16:33:31 AST"
3
4 require 5;
5 package Locale::Maketext;
6 use strict;
7 use vars qw( @ISA $VERSION $MATCH_SUPERS $USING_LANGUAGE_TAGS
8              $USE_LITERALS $MATCH_SUPERS_TIGHTLY);
9 use Carp ();
10 use I18N::LangTags 0.30 ();
11
12 #--------------------------------------------------------------------------
13
14 BEGIN { unless(defined &DEBUG) { *DEBUG = sub () {0} } }
15  # define the constant 'DEBUG' at compile-time
16
17 $VERSION = "1.09";
18 @ISA = ();
19
20 $MATCH_SUPERS = 1;
21 $MATCH_SUPERS_TIGHTLY = 1;
22 $USING_LANGUAGE_TAGS  = 1;
23  # Turning this off is somewhat of a security risk in that little or no
24  # checking will be done on the legality of tokens passed to the
25  # eval("use $module_name") in _try_use.  If you turn this off, you have
26  # to do your own taint checking.
27
28 $USE_LITERALS = 1 unless defined $USE_LITERALS;
29  # a hint for compiling bracket-notation things.
30
31 my %isa_scan = ();
32
33 ###########################################################################
34
35 sub quant {
36   my($handle, $num, @forms) = @_;
37
38   return $num if @forms == 0; # what should this mean?
39   return $forms[2] if @forms > 2 and $num == 0; # special zeroth case
40
41   # Normal case:
42   # Note that the formatting of $num is preserved.
43   return( $handle->numf($num) . ' ' . $handle->numerate($num, @forms) );
44    # Most human languages put the number phrase before the qualified phrase.
45 }
46
47
48 sub numerate {
49  # return this lexical item in a form appropriate to this number
50   my($handle, $num, @forms) = @_;
51   my $s = ($num == 1);
52
53   return '' unless @forms;
54   if(@forms == 1) { # only the headword form specified
55     return $s ? $forms[0] : ($forms[0] . 's'); # very cheap hack.
56   } else { # sing and plural were specified
57     return $s ? $forms[0] : $forms[1];
58   }
59 }
60
61 #--------------------------------------------------------------------------
62
63 sub numf {
64   my($handle, $num) = @_[0,1];
65   if($num < 10_000_000_000 and $num > -10_000_000_000 and $num == int($num)) {
66     $num += 0;  # Just use normal integer stringification.
67          # Specifically, don't let %G turn ten million into 1E+007
68   } else {
69     $num = CORE::sprintf("%G", $num);
70      # "CORE::" is there to avoid confusion with the above sub sprintf.
71   }
72   while( $num =~ s/^([-+]?\d+)(\d{3})/$1,$2/s ) {1}  # right from perlfaq5
73    # The initial \d+ gobbles as many digits as it can, and then we
74    #  backtrack so it un-eats the rightmost three, and then we
75    #  insert the comma there.
76
77   $num =~ tr<.,><,.> if ref($handle) and $handle->{'numf_comma'};
78    # This is just a lame hack instead of using Number::Format
79   return $num;
80 }
81
82 sub sprintf {
83   no integer;
84   my($handle, $format, @params) = @_;
85   return CORE::sprintf($format, @params);
86     # "CORE::" is there to avoid confusion with myself!
87 }
88
89 #=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
90
91 use integer; # vroom vroom... applies to the whole rest of the module
92
93 sub language_tag {
94   my $it = ref($_[0]) || $_[0];
95   return undef unless $it =~ m/([^':]+)(?:::)?$/s;
96   $it = lc($1);
97   $it =~ tr<_><->;
98   return $it;
99 }
100
101 sub encoding {
102   my $it = $_[0];
103   return(
104    (ref($it) && $it->{'encoding'})
105    || "iso-8859-1"   # Latin-1
106   );
107
108
109 #--------------------------------------------------------------------------
110
111 sub fallback_languages { return('i-default', 'en', 'en-US') }
112
113 sub fallback_language_classes { return () }
114
115 #--------------------------------------------------------------------------
116
117 sub fail_with { # an actual attribute method!
118   my($handle, @params) = @_;
119   return unless ref($handle);
120   $handle->{'fail'} = $params[0] if @params;
121   return $handle->{'fail'};
122 }
123
124 #--------------------------------------------------------------------------
125
126 sub failure_handler_auto {
127   # Meant to be used like:
128   #  $handle->fail_with('failure_handler_auto')
129
130   my($handle, $phrase, @params) = @_;
131   $handle->{'failure_lex'} ||= {};
132   my $lex = $handle->{'failure_lex'};
133
134   my $value;
135   $lex->{$phrase} ||= ($value = $handle->_compile($phrase));
136
137   # Dumbly copied from sub maketext:
138   {
139     local $SIG{'__DIE__'};
140     eval { $value = &$value($handle, @_) };
141   }
142   # If we make it here, there was an exception thrown in the
143   #  call to $value, and so scream:
144   if($@) {
145     my $err = $@;
146     # pretty up the error message
147     $err =~ s<\s+at\s+\(eval\s+\d+\)\s+line\s+(\d+)\.?\n?>
148              <\n in bracket code [compiled line $1],>s;
149     #$err =~ s/\n?$/\n/s;
150     Carp::croak "Error in maketexting \"$phrase\":\n$err as used";
151     # Rather unexpected, but suppose that the sub tried calling
152     # a method that didn't exist.
153   } else {
154     return $value;
155   }
156 }
157
158 #==========================================================================
159
160 sub new {
161   # Nothing fancy!
162   my $class = ref($_[0]) || $_[0];
163   my $handle = bless {}, $class;
164   $handle->init;
165   return $handle;
166 }
167
168 sub init { return } # no-op
169
170 ###########################################################################
171
172 sub maketext {
173   # Remember, this can fail.  Failure is controllable many ways.
174   Carp::croak "maketext requires at least one parameter" unless @_ > 1;
175
176   my($handle, $phrase) = splice(@_,0,2);
177
178   # Look up the value:
179
180   my $value;
181   foreach my $h_r (
182     @{  $isa_scan{ref($handle) || $handle} || $handle->_lex_refs  }
183   ) {
184     print "* Looking up \"$phrase\" in $h_r\n" if DEBUG;
185     if(exists $h_r->{$phrase}) {
186       print "  Found \"$phrase\" in $h_r\n" if DEBUG;
187       unless(ref($value = $h_r->{$phrase})) {
188         # Nonref means it's not yet compiled.  Compile and replace.
189         $value = $h_r->{$phrase} = $handle->_compile($value);
190       }
191       last;
192     } elsif($phrase !~ m/^_/s and $h_r->{'_AUTO'}) {
193       # it's an auto lex, and this is an autoable key!
194       print "  Automaking \"$phrase\" into $h_r\n" if DEBUG;
195       
196       $value = $h_r->{$phrase} = $handle->_compile($phrase);
197       last;
198     }
199     print "  Not found in $h_r, nor automakable\n" if DEBUG > 1;
200     # else keep looking
201   }
202
203   unless(defined($value)) {
204     print "! Lookup of \"$phrase\" in/under ", ref($handle) || $handle,
205       " fails.\n" if DEBUG;
206     if(ref($handle) and $handle->{'fail'}) {
207       print "WARNING0: maketext fails looking for <$phrase>\n" if DEBUG;
208       my $fail;
209       if(ref($fail = $handle->{'fail'}) eq 'CODE') { # it's a sub reference
210         return &{$fail}($handle, $phrase, @_);
211          # If it ever returns, it should return a good value.
212       } else { # It's a method name
213         return $handle->$fail($phrase, @_);
214          # If it ever returns, it should return a good value.
215       }
216     } else {
217       # All we know how to do is this;
218       Carp::croak("maketext doesn't know how to say:\n$phrase\nas needed");
219     }
220   }
221
222   return $$value if ref($value) eq 'SCALAR';
223   return $value unless ref($value) eq 'CODE';
224   
225   {
226     local $SIG{'__DIE__'};
227     eval { $value = &$value($handle, @_) };
228   }
229   # If we make it here, there was an exception thrown in the
230   #  call to $value, and so scream:
231   if($@) {
232     my $err = $@;
233     # pretty up the error message
234     $err =~ s<\s+at\s+\(eval\s+\d+\)\s+line\s+(\d+)\.?\n?>
235              <\n in bracket code [compiled line $1],>s;
236     #$err =~ s/\n?$/\n/s;
237     Carp::croak "Error in maketexting \"$phrase\":\n$err as used";
238     # Rather unexpected, but suppose that the sub tried calling
239     # a method that didn't exist.
240   } else {
241     return $value;
242   }
243 }
244
245 ###########################################################################
246
247 sub get_handle {  # This is a constructor and, yes, it CAN FAIL.
248   # Its class argument has to be the base class for the current
249   # application's l10n files.
250
251   my($base_class, @languages) = @_;
252   $base_class = ref($base_class) || $base_class;
253    # Complain if they use __PACKAGE__ as a project base class?
254   
255   if( @languages ) {
256     DEBUG and print "Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
257     if($USING_LANGUAGE_TAGS) {   # An explicit language-list was given!
258       @languages =
259        map {; $_, I18N::LangTags::alternate_language_tags($_) }
260         # Catch alternation
261        map I18N::LangTags::locale2language_tag($_),
262         # If it's a lg tag, fine, pass thru (untainted)
263         # If it's a locale ID, try converting to a lg tag (untainted),
264         # otherwise nix it.
265        @languages;
266       DEBUG and print "Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
267     }
268   } else {
269     @languages = $base_class->_ambient_langprefs;
270   }
271  
272   @languages = $base_class->_langtag_munging(@languages);
273
274   my %seen;
275   foreach my $module_name ( map { $base_class . "::" . $_ }  @languages ) {
276     next unless length $module_name; # sanity
277     next if $seen{$module_name}++        # Already been here, and it was no-go
278             || !&_try_use($module_name); # Try to use() it, but can't it.
279     return($module_name->new); # Make it!
280   }
281
282   return undef; # Fail!
283 }
284
285 ###########################################################################
286
287 sub _langtag_munging {
288   my($base_class, @languages) = @_;
289
290   # We have all these DEBUG statements because otherwise it's hard as hell
291   # to diagnose ifwhen something goes wrong.
292
293   DEBUG and print "Lgs1: ", map("<$_>", @languages), "\n";
294
295   if($USING_LANGUAGE_TAGS) {
296     DEBUG and print "Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
297     @languages     = $base_class->_add_supers( @languages );
298
299     push @languages, I18N::LangTags::panic_languages(@languages);
300     DEBUG and print "After adding panic languages:\n", 
301       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
302
303     push @languages, $base_class->fallback_languages;
304      # You are free to override fallback_languages to return empty-list!
305     DEBUG and print "Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
306
307     @languages =  # final bit of processing to turn them into classname things
308       map {
309         my $it = $_;  # copy
310         $it =~ tr<-A-Z><_a-z>; # lc, and turn - to _
311         $it =~ tr<_a-z0-9><>cd;  # remove all but a-z0-9_
312         $it;
313       } @languages
314     ;
315     DEBUG and print "Nearing end of munging:\n", 
316       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
317   } else {
318     DEBUG and print "Bypassing language-tags.\n", 
319       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
320   }
321
322   DEBUG and print "Before adding fallback classes:\n", 
323     " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
324
325   push @languages, $base_class->fallback_language_classes;
326    # You are free to override that to return whatever.
327
328   DEBUG and print "Finally:\n", 
329     " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
330
331   return @languages;
332 }
333
334 ###########################################################################
335
336 sub _ambient_langprefs {
337   require I18N::LangTags::Detect;
338   return  I18N::LangTags::Detect::detect();
339 }
340
341 ###########################################################################
342
343 sub _add_supers {
344   my($base_class, @languages) = @_;
345
346   if(!$MATCH_SUPERS) {
347     # Nothing
348     DEBUG and print "Bypassing any super-matching.\n", 
349       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
350
351   } elsif( $MATCH_SUPERS_TIGHTLY ) {
352     DEBUG and print "Before adding new supers tightly:\n", 
353       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
354     @languages = I18N::LangTags::implicate_supers( @languages );
355     DEBUG and print "After adding new supers tightly:\n", 
356       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
357
358   } else {
359     DEBUG and print "Before adding supers to end:\n", 
360       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
361     @languages = I18N::LangTags::implicate_supers_strictly( @languages );
362     DEBUG and print "After adding supers to end:\n", 
363       " Lgs\@", __LINE__, ": ", map("<$_>", @languages), "\n";
364   }
365   
366   return @languages;
367 }
368
369 ###########################################################################
370 #
371 # This is where most people should stop reading.
372 #
373 ###########################################################################
374
375 use Locale::Maketext::GutsLoader;
376
377 ###########################################################################
378
379 my %tried = ();
380   # memoization of whether we've used this module, or found it unusable.
381
382 sub _try_use {   # Basically a wrapper around "require Modulename"
383   # "Many men have tried..."  "They tried and failed?"  "They tried and died."
384   return $tried{$_[0]} if exists $tried{$_[0]};  # memoization
385
386   my $module = $_[0];   # ASSUME sane module name!
387   { no strict 'refs';
388     return($tried{$module} = 1)
389      if defined(%{$module . "::Lexicon"}) or defined(@{$module . "::ISA"});
390     # weird case: we never use'd it, but there it is!
391   }
392
393   print " About to use $module ...\n" if DEBUG;
394   {
395     local $SIG{'__DIE__'};
396     eval "require $module"; # used to be "use $module", but no point in that.
397   }
398   if($@) {
399     print "Error using $module \: $@\n" if DEBUG > 1;
400     return $tried{$module} = 0;
401   } else {
402     print " OK, $module is used\n" if DEBUG;
403     return $tried{$module} = 1;
404   }
405 }
406
407 #--------------------------------------------------------------------------
408
409 sub _lex_refs {  # report the lexicon references for this handle's class
410   # returns an arrayREF!
411   no strict 'refs';
412   my $class = ref($_[0]) || $_[0];
413   print "Lex refs lookup on $class\n" if DEBUG > 1;
414   return $isa_scan{$class} if exists $isa_scan{$class};  # memoization!
415
416   my @lex_refs;
417   my $seen_r = ref($_[1]) ? $_[1] : {};
418
419   if( defined( *{$class . '::Lexicon'}{'HASH'} )) {
420     push @lex_refs, *{$class . '::Lexicon'}{'HASH'};
421     print "%" . $class . "::Lexicon contains ",
422          scalar(keys %{$class . '::Lexicon'}), " entries\n" if DEBUG;
423   }
424
425   # Implements depth(height?)-first recursive searching of superclasses.
426   # In hindsight, I suppose I could have just used Class::ISA!
427   foreach my $superclass (@{$class . "::ISA"}) {
428     print " Super-class search into $superclass\n" if DEBUG;
429     next if $seen_r->{$superclass}++;
430     push @lex_refs, @{&_lex_refs($superclass, $seen_r)};  # call myself
431   }
432
433   $isa_scan{$class} = \@lex_refs; # save for next time
434   return \@lex_refs;
435 }
436
437 sub clear_isa_scan { %isa_scan = (); return; } # end on a note of simplicity!
438
439 ###########################################################################
440 1;
441
442 __END__
443
444 HEY YOU!  You need some FOOD!
445
446
447   ~~ Tangy Moroccan Carrot Salad ~~
448
449 * 6 to 8 medium carrots, peeled and then sliced in 1/4-inch rounds
450 * 1/4 teaspoon chile powder (cayenne, chipotle, ancho, or the like)
451 * 1 tablespoon ground cumin
452 * 1 tablespoon honey
453 * The juice of about a half a big lemon, or of a whole smaller one
454 * 1/3 cup olive oil
455 * 1 tablespoon of fresh dill, washed and chopped fine
456 * Pinch of salt, maybe a pinch of pepper
457
458 Cook the carrots in a pot of boiling water until just tender -- roughly
459 six minutes.  (Just don't let them get mushy!)  Drain the carrots.
460
461 In a largish bowl, combine the lemon juice, the cumin, the chile
462 powder, and the honey.  Mix well.
463 Add the olive oil and whisk it together well.  Add the dill and stir.
464
465 Add the warm carrots to the bowl and toss it all to coat the carrots
466 well.  Season with salt and pepper, to taste.
467
468 Serve warm or at room temperature.
469
470 The measurements here are very approximate, and you should feel free to
471 improvise and experiment.  It's a very forgiving recipe.  For example,
472 you could easily halve or double the amount of cumin, or use chopped mint
473 leaves instead of dill, or lime juice instead of lemon, et cetera.
474
475 [end]
476