source: for-distributions/trunk/bin/windows/perl/lib/Locale/Maketext.pm@ 14489

Last change on this file since 14489 was 14489, checked in by oranfry, 17 years ago

upgrading to perl 5.8

File size: 15.0 KB
Line 
1
2# Time-stamp: "2004-03-30 16:33:31 AST"
3
4require 5;
5package Locale::Maketext;
6use strict;
7use vars qw( @ISA $VERSION $MATCH_SUPERS $USING_LANGUAGE_TAGS
8 $USE_LITERALS $MATCH_SUPERS_TIGHTLY);
9use Carp ();
10use I18N::LangTags 0.30 ();
11
12#--------------------------------------------------------------------------
13
14BEGIN { 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
31my %isa_scan = ();
32
33###########################################################################
34
35sub 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
48sub 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
63sub 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
82sub 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
91use integer; # vroom vroom... applies to the whole rest of the module
92
93sub 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
101sub encoding {
102 my $it = $_[0];
103 return(
104 (ref($it) && $it->{'encoding'})
105 || "iso-8859-1" # Latin-1
106 );
107}
108
109#--------------------------------------------------------------------------
110
111sub fallback_languages { return('i-default', 'en', 'en-US') }
112
113sub fallback_language_classes { return () }
114
115#--------------------------------------------------------------------------
116
117sub 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
126sub 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
160sub new {
161 # Nothing fancy!
162 my $class = ref($_[0]) || $_[0];
163 my $handle = bless {}, $class;
164 $handle->init;
165 return $handle;
166}
167
168sub init { return } # no-op
169
170###########################################################################
171
172sub 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
247sub 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
287sub _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
336sub _ambient_langprefs {
337 require I18N::LangTags::Detect;
338 return I18N::LangTags::Detect::detect();
339}
340
341###########################################################################
342
343sub _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
375use Locale::Maketext::GutsLoader;
376
377###########################################################################
378
379my %tried = ();
380 # memoization of whether we've used this module, or found it unusable.
381
382sub _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
409sub _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
437sub clear_isa_scan { %isa_scan = (); return; } # end on a note of simplicity!
438
439###########################################################################
4401;
441
442__END__
443
444HEY 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
458Cook the carrots in a pot of boiling water until just tender -- roughly
459six minutes. (Just don't let them get mushy!) Drain the carrots.
460
461In a largish bowl, combine the lemon juice, the cumin, the chile
462powder, and the honey. Mix well.
463Add the olive oil and whisk it together well. Add the dill and stir.
464
465Add the warm carrots to the bowl and toss it all to coat the carrots
466well. Season with salt and pepper, to taste.
467
468Serve warm or at room temperature.
469
470The measurements here are very approximate, and you should feel free to
471improvise and experiment. It's a very forgiving recipe. For example,
472you could easily halve or double the amount of cumin, or use chopped mint
473leaves instead of dill, or lime juice instead of lemon, et cetera.
474
475[end]
476
Note: See TracBrowser for help on using the repository browser.