File Coverage

blib/lib/Locale/MakePhrase.pm
Criterion Covered Total %
statement 275 342 80.4
branch 109 218 50.0
condition 12 42 28.5
subroutine 42 59 71.1
pod 23 26 88.4
total 461 687 67.1


line stmt bran cond sub pod time code
1             package Locale::MakePhrase;
2             our $VERSION = 0.4;
3             our $DEBUG = 0;
4              
5             =head1 NAME
6              
7             Locale::MakePhrase - Language translation facility
8              
9             =head1 SYNOPSIS
10              
11             These group of modules are used to translate application text strings,
12             which may or may not include values which also need to be translated,
13             into the prefered language of the end-user.
14              
15             Example:
16              
17             use Locale::MakePhrase::BackingStore::Directory;
18             use Locale::MakePhrase;
19             my $bs = new Locale::MakePhrase::BackingStore::Directory(
20             directory => '/some/path/to/language/files',
21             );
22             my $mp = new Locale::MakePhrase(
23             language => 'en_AU',
24             backing_store => $bs,
25             );
26             ...
27             my $color_count = 1;
28             print $mp->translate("Please select [_1] colors.",$color_count);
29              
30             Output:
31              
32             Please select a colour.
33              
34             Notice that a) the word 'color' has been localised to Australian
35             English, and b) that the argument has influenced the resultant output
36             text to take into account the display of the singular version.
37              
38             =head1 DESCRIPTION
39              
40             This aim of these modules are to implement run-time evaluation of an
41             input phrase, including program arguments, and have it generate a
42             suitable output phrase, in the language and encoding specified by the
43             user of the application.
44              
45             Since this problem has been around for some time, there are a number
46             of sources of useful information available on the web, which describes
47             why this problem is hard to solve. The problem with most existing
48             solutions is that each design suffers some form of limitation, often
49             due to the designer thinking that there are enough commonalities
50             between all/some langugaes that these commonalities can be factored
51             into a various rules which can be implemented in programming code.
52              
53             However, each language has it own history and evolution. Thus it is
54             pointless to compare two different languages unless they have a common
55             history and a common character set.
56              
57             I continuing to read this document, you really should read the
58             following info on the L Perl module:
59              
60             http://search.cpan.org/~sburke/Locale-Maketext-1.08/lib/Locale/Maketext.pod
61              
62             and at the slides presented here:
63              
64             http://www.autrijus.org/webl10n/
65              
66             The L modules are based on a design similar to the
67             L module, except that this new implementation has
68             taken a different approach, that being...
69              
70             Since it is possible (and quite likely) that the application will need
71             to be able to understand the language rules of any specific language,
72             we want to use a run-time evaluation of the rules that a linguist
73             would use to convert one language to another. Thus we have coined the
74             term I as a means to describe this technique. These
75             rules are used to decide which piece of text is displayed, for a given
76             input text and arguments.
77              
78             =head1 REQUIREMENTS
79              
80             The L module was initially designed to meet the
81             requirements of a web application (as opposed to a desktop
82             application), which may display many languages in the HTML form at any
83             given instance.
84              
85             Its design is modelled on a similar design of using language lexicons,
86             which is in use in the existing L Perl module. The
87             reason for building a new module is because:
88              
89             =over 2
90              
91             =item *
92              
93             We wanted to completely abstract the language rule capability, to be
94             programming language agnostic so that we could re-implement this
95             module in other programming languages.
96              
97             =item *
98              
99             We needed run-time evaluation of the rules, since the translations
100             may be updated at any time; new rules may be added whenever there is
101             some ambigutiy in the existing phrase. Also, we didn't want to
102             re-start the application whenever we updated a rule.
103              
104             =item *
105              
106             We would like to support various types of storage mechanisms for the
107             translations. The origonal design constraint prefered the use of a
108             PostgreSQL database to hold the translations - most existing language
109             translation systems use flat files.
110              
111             =item *
112              
113             We want to store/manipulate the current text phrase, only encoded in
114             UTF-8 (ie: we dont want to store the text in a locale-specific
115             encoding). This allows us to output text to any other character set.
116              
117             =back
118              
119             As an example of application usage, it is possible for a Hebrew
120             speaking user to be logged into a web-form which contains Japanese
121             data. As such they will see:
122              
123             =over 2
124              
125             =item *
126              
127             Menus and tooltips will be translated into the users' language (ie: Hebrew).
128              
129             =item *
130              
131             Titles will be in the language of the dataset (ie: Japanese).
132              
133             =item *
134              
135             Some of the data was in Latin character set (ie: English).
136              
137             =item *
138              
139             If the user prefered to see the page as RTL rather than LTR, the page
140             was altered to reflect this preference.
141              
142             =back
143              
144             =head1 BACKGROUND
145              
146             When implementing any new software, it is necessary to understand
147             the problem domain. In the case of language translation, there
148             are a number of requirements that we can define:
149              
150             =over 4
151              
152             =item 1.
153              
154             Quite a few people speak multiple languages; we would like the
155             language translation system to use the users preferred language
156             localisation, or if we don't know which language that is, try to make
157             an approximate guess, based on application capabilites.
158              
159             =over 4
160              
161             =item Eg:
162              
163             In a web-browser, the user normally sets their prefered language/dialect.
164             The browser normally sends this information to a web-server during the
165             request for a page. The server may choose to show the page contents in
166             the language the user prefers.
167              
168             =back
169              
170             =item 2.
171              
172             Since some people speak multiple languages, the application may not
173             have been localised to their prefered localisation. We should try to
174             fallback to using a language which is similar.
175              
176             =over 4
177              
178             =item Eg:
179              
180             If there are no Spanish translations available, we should fallback
181             to Mexican, since Mexican and Spanish have many words in common.
182              
183             =back
184              
185             =item 3.
186              
187             Some languages support the notion of a dialect for that language. A
188             good example is that the English language is used in many
189             countries, but countries such as the United States, Australia and
190             Great Britain each have their own localised version ie. the dialect is
191             specified as the country or region. The language translation
192             mechanism needs to be able to use the users' preferred dialect when
193             looking up the text to display. If no translation is found, then it
194             should fall back to the parent language.
195              
196             =over 4
197              
198             =item Eg:
199              
200             The language/dialect of Australia is defined as 'en_AU' - when we
201             lookup a text translation, if we fail we should try to lookup the 'en'
202             translation.
203              
204             =back
205              
206             =item 4.
207              
208             Some languages are written using a script which displays its
209             output as right-to-left text (as used by Arabic, Hebrew, etc), rather
210             than left-to-right text (as used by English, Latin, Greek, etc). The
211             language translation mechanism should allow the text display mechanism
212             to change the text direction if that is a requirement (which is
213             another reason for mandating the use of UTF-8).
214              
215             =item 5.
216              
217             The string to be translated should support the ability to re-order
218             the wording of the text.
219              
220             =over 4
221              
222             =item Eg:
223              
224             In English we would normally say something like "Please enter your
225             name"; in Japanese the equivalent translation would be something like
226             "Enter your name, please" (although it would be in Japanese, not
227             English).
228              
229             =back
230              
231             =item 6.
232              
233             The text translation mechanism should support the ability to show
234             arguments supplied to the string (by the application), within the
235             correct context of the meaning of the string.
236              
237             =over 4
238              
239             =item Eg:
240              
241             We could say something like "You selected 4 balls" (where the number 4
242             is program dependant); in another language you may want to say the
243             equivalent of "4 balls selected".
244              
245             =back
246              
247             Notice that the numeric position has moved from being the third
248             mnemonic, to being the first mnemonic. The requirement is that we
249             would like to be able to rearrange the order/placement of any
250             mnemonic (including any program arguments).
251              
252             =item 7.
253              
254             We would like to be able to support an arbitrary number of argument
255             replacements. We shouldn't be limited in the number of replacements
256             that need to occur, for any given number program arguments.
257              
258             =over 4
259              
260             =item Eg:
261              
262             We want to have an unlimited number of placeholders as exemplified
263             by the string "Select __ balls, __ bats, __ wickets, plus choose __
264             people, ___ ..." and so on.
265              
266             =back
267              
268             =item 8.
269              
270             Most program arguments that are given to strings are in numeric
271             format (i.e. they are a number). We would also like to support
272             arguments which are text strings, which themselves should be open to
273             language translation (but only after rule evaluation). The purpose
274             being that the output phrase should make sense within the current
275             context of the application.
276              
277             =item 9.
278              
279             In a lot of languages there is the concept of singular and plural.
280             While in other languages there is no such concept, while in others
281             still there is the concept of duality. There is also the concept that
282             a phrase can be descriptive when discussing the zero of something.
283             Thus we want to display a specific phrase, depending on the value of
284             an argument.
285              
286             =over 4
287              
288             =item Eg:
289              
290             In English, the following text "Selected __ files" has multiple
291             possible outputs, depending on the program value; we can have:
292              
293             0 case: "No files selected" - no numeric value
294             1 case: "One file selected" - 'files' is singular
295             2 case: "Selected two files" - the '__' is a text value, not a number
296             more than 2 case: "Lots of selections" - no direct comparison to the original text
297              
298             =back
299              
300             ...as we can see, this is just for translating a single text
301             string, from English to English.
302              
303             To counter this problem, the translation system needs to be able
304             to apply linguistic rules to the original text, so that it can
305             evaluate which piece of text should be displayed, given the current
306             context and program argument.
307              
308             =item 10.
309              
310             When updating a specific phrase for language translation, the next
311             screen re-draw should show the new translation text. Thus translations
312             need to be dynamically changeable, and run-time configurable.
313              
314             =back
315              
316             =head1 INTERNAL TEXT ENCODING
317              
318             This module uses UTF-8 text encoding internally, thus it requires a
319             minimum of Perl 5.8. So, for any given application string and user
320             language combination, we require the backing store look-up the
321             combination, then return a list of L
322             objects, which must be created with the key and translated strings
323             being stored in the UTF-8 encoding.
324              
325             Thus, to simplify the string-load functionality, we recommend to load
326             / store the translated strings as UTF-8 encoded strings. See
327             L for more information.
328              
329             =over 4
330              
331             =item ie.
332              
333             The PostgreSQL backing store assumes that the database instance
334             stores strings in the UNICODE encoding (rather than, say, ASCII); this
335             avoids the need to translate every string when we load it.
336              
337             =back
338              
339             =head1 OUTPUT TEXT ENCODING
340              
341             L uses UTF-8 encoding internally, as described
342             above. This is also the default output encoding. You can choose to
343             have a different output encoding, such as ISO-8859-1.
344              
345             Normlly, if the output display mechanism can display UNICODE (encoded as
346             UTF-8), then text will be rendered in the correct language and correct
347             text direction (ie. left-to-right or right-to-left).
348              
349             By supplying the encoding as a constructor argument, L
350             will transpose the translated text from UTF-8, into your output-specific
351             encoding (using the L module). This is useful in cases where
352             font support within an application, hasn't yet evolved to the same
353             level as a language-specific font.
354              
355             See the L module for a list of available output encodings.
356              
357             Default output character set encoding: B
358              
359             =head1 WHAT ARE LINGUISTIC RULES?
360              
361             Since the concept of a linguistic rule is at the heart of this
362             translation module, its documentation is located in L.
363             It explains the syntax of the rule expressions, how rules are sorted and
364             selected, as well as the operators and functions that are available
365             within the expressions. You should read that information, before
366             continuing.
367              
368             =over 2
369              
370             =item Available operators:
371              
372             B<==>, B, B>, B>, B=>, B=>, B, B
373              
374             =item Available functions:
375              
376             B, B, B, B, B, B,
377             B, B, B, B
378              
379             =back
380              
381             =head1 Object API
382              
383             The following methods are part of the L object API:
384              
385             =cut
386              
387 15     15   63193 { no warnings; require v5.8.0; }
  9         21  
  9         530  
388 15     15   2503 use strict;
  9         20  
  9         303  
389 15     15   2329 use warnings;
  9         18  
  9         208  
390 15     15   10220 use utf8;
  9         75  
  9         50  
391 15     15   19142 use integer;
  9         93  
  9         47  
392 15     15   5297 use base qw(Exporter);
  15         39  
  15         5298  
393 9     9   8003 use Data::Dumper;
  9         98204  
  9         905  
394 9     9   8690 use I18N::LangTags 0.21 ();
  9         26156  
  9         288  
395 9     9   17606 use Encode;
  9         142411  
  9         897  
396 9     9   159 use Encode::Alias;
  9         17  
  9         447  
397 9     9   4563 use Locale::MakePhrase::BackingStore;
  9         26  
  9         260  
398 9     9   6103 use Locale::MakePhrase::RuleManager;
  9         24  
  9         340  
399 9     9   71 use Locale::MakePhrase::LanguageRule;
  9         16  
  9         204  
400 9     9   5958 use Locale::MakePhrase::Numeric;
  9         26  
  9         654  
401 9     9   59 use Locale::MakePhrase::Utils qw(is_number die_from_caller);
  9         17  
  9         49198  
402             our $default_language = "en";
403             our $default_backing_store = "Locale::MakePhrase::BackingStore";
404             our $default_rule_manager = "Locale::MakePhrase::RuleManager";
405             our $default_malformed_mode = Encode::FB_PERLQQ;
406             our $default_numeric_format = Locale::MakePhrase::Numeric->DOT;
407             our $internal_encoding = "utf-8";
408             our $this = undef;
409             local $Data::Dumper::Indent = 1 if $DEBUG;
410              
411             # Exported symbols
412             our @EXPORT_OK = qw(mp __ print);
413              
414             # constants
415 7     7 0 73 sub MALFORMED_MODE_ESCAPE { return Encode::FB_PERLQQ; }
416 0     0 0 0 sub MALFORMED_MODE_HTML { return Encode::FB_HTMLCREF; }
417 0     0 0 0 sub MALFORMED_MODE_XML { return Encode::FB_XMLCREF; }
418              
419             # We add the 'utf-8' alias for the 'utf8' encoding,
420             # as we support both syntactical forms.
421             Encode::Alias::define_alias('utf-8' => 'utf8');
422              
423             #--------------------------------------------------------------------------
424              
425             =head2 new()
426              
427             Construct new instance of Locale::MakePhrase object. Takes the
428             following named parameters (ie: via a hash or hashref):
429              
430             =over 2
431              
432             =item C
433              
434             =item C
435              
436             Specify one or more languages which are used for locating the
437             correct language string (all forms are supported; first found is used).
438              
439             They take either a string (eg 'en'), a comma-seperated list (eg
440             'en_AU, en_GB') or an array of strings (eg ['en_AU','en_GB']).
441              
442             The order specified, is the order that phrases are looked up. These
443             strings go through a manipulation process (using the Perl module
444             L) of:
445              
446             =over 3
447              
448             =item 1.
449              
450             The strings are converted to RFC3066 language tags; these become
451             the primary tags.
452              
453             =item 2.
454              
455             Superordinate tags are retrieved for each primary tag.
456              
457             =item 3.
458              
459             Alternates of the primary tags are then retrieved.
460              
461             =item 4.
462              
463             Panic language tags are retrieved for each primary tag (if enabled).
464              
465             =item 5.
466              
467             The fallback language is retrieved (see 'fallback language').
468              
469             =item 6.
470              
471             Duplicate language tags are removed.
472              
473             =item 7.
474              
475             All tags are converted to lowercase, and '-' are changed to '_'.
476              
477             =back
478              
479             This leaves us with a list of at least the fallback language.
480              
481             =item C
482              
483             =item C
484              
485             This option (both forms are supported; first found is used) allows you
486             to change the output character set encoding, to something other than
487             UTF-8, such as ISO-8859-1.
488              
489             See L for more information.
490              
491             =item C
492              
493             Takes either a reference to a backing store instance, or to a string
494             which can be used to dynamically construct the instance.
495              
496             The final backing store instance must have a type of L.
497              
498             Default: use a L
499              
500             =item C
501              
502             Takes either a reference to a rule manager instance, or to a string
503             which can be used to dynamically construct the instance.
504              
505             The final manager instance must have a type of L.
506              
507             Default: use a L
508              
509             =item C
510              
511             Perl normally outputs \x{HH} for malformed characters (or \x{HHHH},
512             \x{HHHHHH}, etc. for wide characters). Setting this value, changes
513             the behaviour to output alternative character entity formats.
514              
515             Note that if you are using L to generate strings
516             used within web pages / HTML, you should set this parameter to
517             CMALFORMED_MODE_HTML>.
518              
519             =item C
520              
521             This option allows the user to control how numbers are output. You
522             can set the output to be one of a number of forms of stringification
523             defined in L, eg:
524              
525             =over 2
526              
527             =item '.', ',', '(', ')'
528              
529             Place comma seperators before every third digit; use brackets for
530             negative, as in: (10,000,000.1)
531              
532             =back
533              
534             This takes either a string format or an array reference containing
535             the format.
536              
537             Default: dont format; show decimal as full-stop
538              
539             =item C
540              
541             Set this option to true to make L die if the
542             translated string is incorrectly formatted (eg: too many argument
543             place holders are specified) or the expression is not valid. The
544             alternative is to output the phrase BINVALID TRANSLATIONE>
545             or BINVALID EXPRESSIONE>.
546              
547             Die'ing here means that translations have the ability to
548             abort your code. If you dont have control over the quality of the
549             phrases added to your dictionary, you should probably use the default
550             behaviour.
551              
552             Note that an invalid expression or translation generates a warning to
553             STDERR.
554              
555             Default: dont die; output the appropriate error phrase
556              
557             =item C
558              
559             Set this option to false to make L not translate
560             the applied arguments, before applying them to the output of the
561             engine. This saves you from having to call translate() for each
562             argument, within your own code.
563              
564             Default: do translate arguments
565              
566             =item C
567              
568             Set this option to true to make L automatically
569             add newline characters to the end of every translated string. The
570             reason for having this is to allow your translation-key to not require
571             the OS-dependent newline character(s), and to not require newline
572             character(s) on the target-translation.
573              
574             Note that the API provides alternate method calls so as to allow
575             you to add newline character(s) as necessary.
576              
577             Default: dont add any newline characters
578              
579             =item C
580              
581             Set this option to true to make L load 'panic'
582             languages as defined by L. Basically
583             it provides a mechanism to allow the engine to return a language
584             string from languages which has a similar heritage to the primary
585             language(s), if a translation from the primary language hasn't been
586             found.
587              
588             eg: Spanish has a similar heritage as Italian, thus if no translations
589             are found in Italian, then Spanish translations will be used.
590              
591             Default: dont lookup panic-languages
592              
593             =item Notes:
594              
595             If the arguments aren't a hash or hashref, then we assume that the
596             arguments are languages tags.
597              
598             If you dont supply any language, the fallback language will be used.
599              
600             Default language: B
601              
602             =back
603              
604             =cut
605              
606             sub new {
607 7     7 1 2533 my $proto = shift;
608 7   33     55 my $class = ref($proto) || $proto;
609 7         29 my $self = bless {}, $class;
610              
611             # We allow different forms of argument passing.
612             # The only argument we really need is the language, but we should use other arguments as well.
613 7         16 my %options;
614 7 50 33     187 if (@_ == 1 and ref($_[0]) eq "HASH") {
    50 33        
    0          
615 0         0 %options = %{$_[0]};
  0         0  
616             } elsif (@_ > 1 and not(@_ % 2)) {
617 7         41 %options = @_;
618             } elsif (@_ > 0) {
619 0         0 my @languages = split(',',$_[0]);
620 0         0 $options{languages} = \@languages;
621             };
622 7 50       41 print STDERR "Arguments to ". $class .": ". Dumper(\%options) if $DEBUG > 5;
623 7         74 $self->{options} = \%options;
624              
625             # allow sub-class to control construction
626 7         38 $self = $self->init();
627 7 50       38 return undef unless $self;
628              
629             # process options, and initialise module
630 7         48 $self->{encoding} = $self->_get_encoding();
631 7         35 $self->{malformed_character_mode} = $self->_get_malformed_mode();
632 7         35 $self->{numeric_format} = $self->_get_numeric_format();
633 7 0       57 $self->{die_on_bad_translation} = (exists $options{die_on_bad_translation}) ? ($options{die_on_bad_translation} ? 1 : 0) : $self->{die_on_bad_translation} ? 1 : 0;
    50          
    50          
634 7 0       52 $self->{translate_arguments} = (exists $options{translate_arguments}) ? ($options{translate_arguments} ? 1 : 0) : (exists $self->{translate_arguments}) ? ($self->{translate_arguments} ? 1 : 0) : 1;
    0          
    50          
    50          
635 7 0       59 $self->{add_newline} = (exists $options{add_newline}) ? ($options{add_newline} ? 1 : 0) : $self->{add_newline} ? 1 : 0;
    50          
    50          
636 7 0       61 $self->{panic_language_lookup} = (exists $options{panic_language_lookup}) ? ($options{panic_language_lookup} ? 1 : 0) : $self->{panic_language_lookup} ? 1 : 0;
    50          
    50          
637 7         41 $self->{languages} = $self->_get_languages();
638 7         40 $self->{rule_manager} = $self->_get_rule_manager();
639 7         39 $self->{language_modules} = $self->_load_language_modules();
640 7         43 $self->{backing_store} = $self->_attach_backing_store();
641 7         18 $this = $self;
642              
643 7 50       49 print STDERR "Resultant $class object: ". Dumper($self) if $DEBUG > 7;
644 7         34 return $self;
645             }
646              
647             #--------------------------------------------------------------------------
648              
649             =head2 $self init([...])
650              
651             Allow sub-class a chance to control construction of the object. You
652             must return a reference to $self, to 'allow' the construction to
653             complete.
654              
655             At this point of construction you can call C<$self-Eoptions()>
656             which returns a reference to the current constructor options. This
657             allows you to add/modify any existing options; for example you may
658             want to inject something specific...
659              
660             =cut
661              
662 7     7 1 21 sub init { shift }
663              
664             #--------------------------------------------------------------------------
665              
666             =head2 $string context_translate($context, $string [, ...])
667              
668             [ C<$context> is either a text string or an object reference (which
669             then gets stringified into its class name). ]
670              
671             This is a primary entry point; call this with your application
672             context, your string and any program arguments which need to be
673             translated. Note however that in most cases you will most likely want
674             to call the L function
675             instead; see below.
676              
677             In some cases you will find that you will use the same text phrase in
678             one part of your application, in a seperate part of your application,
679             but the meaning of the phrase is different (due to the different
680             application context); supplying a context will allow your backing
681             store to use the extra context information, to return the correct
682             language rules.
683              
684             The steps involved in a string translation are:
685              
686             =over 3
687              
688             =item 1.
689              
690             Fetch all possible translation rules for all language tags (including
691             alternates and the fallbacks), from the backing store. The store will
692             return a list reference of LanguageRule objects.
693              
694             =item 2.
695              
696             Sort the list based on the implementation defined in the
697             L module.
698              
699             =item 3.
700              
701             The the rule instance for which the rule-expression evaluates to B
702             for the supplied program arguments (if there is no expression, the rule
703             is always true).
704              
705             =item 4.
706              
707             If no rules have been selected, then make a rule from the input string.
708              
709             =item 5.
710              
711             Apply the program arguments to the rules' translated text. If the
712             argument is a text phrase, it (optionally) undergoes the language
713             translation procedure. If the argument is numeric, it is formatted by
714             one of your language sub-classes, or the L
715             module.
716              
717             =item 6.
718              
719             We apply the output character set encoding to convert the text from
720             UTF-8 into the prefered character set. If the output encoding is UTF-8
721             (thus matching the internal encoding), this item does nothing.
722              
723             =back
724              
725             =cut
726              
727             sub context_translate {
728 26 50   26 1 66 die_from_caller("context_translate() requires at least two parameters") unless @_ > 2;
729 26         56 my ($self,$context,$key) = (shift,shift,shift);
730 26 50 33     153 die_from_caller("context_translate() requires a valid key") unless (defined $key and length $key);
731 26 50 33     82 $context = ref($context) if (defined $context and ref($context) ne 'SCALAR');
732 26 50       63 print STDERR "Translation key: $key\n" if $DEBUG;
733              
734 26         43 my $backing_store = $self->{backing_store};
735 26         42 my $languages = $self->{languages};
736              
737             # Get all possible translations/rules from backing store
738 26         110 my $rule_objs = $backing_store->get_rules($context,$key,$languages);
739              
740             # Sort the rules according to the linguistic rule algorithms
741 26         74 $rule_objs = $self->_sort_rules($rule_objs,$languages);
742              
743             # Select the specific rule, based on the linguistic rules for each rule
744 26         85 my $rule_obj = $self->_select_rule($rule_objs, @_);
745              
746             # at this point we can clean up some resources
747 26         38 $backing_store = undef;
748 26         30 $languages = undef;
749 26         35 $rule_objs = undef;
750              
751             # If no rule found, use input key
752 26 100       84 $rule_obj = new Locale::MakePhrase::LanguageRule(
753             language => $self->fallback_language,
754             translation => $key,
755             ) unless (defined $rule_obj);
756              
757             # Apply arguments to translated text
758 26         79 my $translated_text = $self->_apply_arguments($rule_obj, @_);
759              
760             # apply encoding
761 26         92 $translated_text = $self->_apply_encoding($translated_text);
762              
763 26 50       67 print STDERR "Translated text: $translated_text\n" if $DEBUG;
764 26 50       65 return $translated_text.$\ if $self->{add_newline};
765 26         145 return $translated_text;
766             }
767              
768             #--------------------------------------------------------------------------
769              
770             =head2 $string translate($string [, ...])
771              
772             This is a primary entry point; call this with your string and any
773             program arguments which need to be translated.
774              
775             This function is a wrapper around the C function,
776             where the context is set to undef (which is usually what you want).
777              
778             =cut
779              
780             sub translate {
781 22 50   22 1 10150 die_from_caller("translate() requires at least one parameter") unless @_ > 1;
782 22         74 return shift->context_translate(undef,@_);
783             }
784              
785             #--------------------------------------------------------------------------
786              
787             =head2 $string context_translate_ln($context, $string [, ...])
788              
789             This is a primary entry point; call this with your context, string and
790             any program arguments which need to be translated.
791              
792             This function is a wrapper around the C function,
793             but this adds newline character(s) to the output.
794              
795             =cut
796              
797             sub context_translate_ln {
798 0 0   0 1 0 die_from_caller("translate() requires at least two parameters") unless @_ > 2;
799 0         0 my $s = shift->context_translate(@_);
800 0         0 return $s.$\;
801             }
802              
803             #--------------------------------------------------------------------------
804              
805             =head2 $string translate_ln($string [, ...])
806              
807             This is a primary entry point; call this with your string and any
808             program arguments which need to be translated.
809              
810             As above, this function is a wrapper around the C
811             function, where the context is set to undef, but this adds newline
812             character(s) to the output.
813              
814             =cut
815              
816             sub translate_ln {
817 0 0   0 1 0 die_from_caller("translate() requires at least one parameter") unless @_ > 1;
818 0         0 my $s = shift->context_translate(undef,@_);
819 0         0 return $s.$\;
820             }
821              
822             #--------------------------------------------------------------------------
823              
824             =head2 $string format_number($number,$options)
825              
826             This method implements the numbers-specific formatting, by calling into
827             L's C method.
828              
829             To provide custom handling of number formatting, you can do one of:
830              
831             =over 2
832              
833             =item *
834              
835             Define a L number formatting option.
836              
837             =item *
838              
839             Implement 'per-language' number formatting, by sub-classing the
840             L module, then implementing a
841             C method.
842              
843             =back
844              
845             =cut
846              
847             sub format_number {
848 8     8 1 13 my ($self, $number, $options) = @_;
849 8 50       25 $options = {} unless $options;
850              
851 8 50       26 if (ref($self)) {
852 8 50       23 unless (exists $options->{numeric_format}) {
853 8 50       20 if ($self->{numeric_format}) {
854 8         25 $options->{numeric_format} = $self->{numeric_format};
855             } else {
856 0         0 $options->{numeric_format} = Locale::MakePhrase::Numeric->DOT;
857             }
858             }
859              
860             # Allow the custom language-handling module a chance at formatting the number
861 8         15 my $modules = $self->{language_modules};
862 8         17 foreach my $module (@$modules) {
863 8         54 my $can = $module->can('format_number');
864 8 50       44 next unless $can;
865 0 0       0 print STDERR "Found language specific number formatter on module: ". ref($module) ."\n" if $DEBUG > 3;
866 0         0 return &$can($self,$number,$options);
867             }
868             }
869              
870 8         46 return Locale::MakePhrase::Numeric->stringify($number,$options);
871             }
872              
873             #--------------------------------------------------------------------------
874              
875             =head2 $backing_store fallback_backing_store()
876              
877             Backing store to use, if not specified on construction. You can
878             overload this in a sub-class.
879              
880             =cut
881              
882 1     1 1 3 sub fallback_backing_store { $default_backing_store }
883              
884             #--------------------------------------------------------------------------
885              
886             =head2 $string fallback_language()
887              
888             Language to fallback to, if all others fail (this defaults to 'B').
889             You can override this method in a sub-class.
890              
891             Usually this will be the language that you are writing your application
892             code (eg: you may be coding using German rather than English).
893              
894             Note that this must return a RFC-3066 compliant language tag.
895              
896             =cut
897              
898 18     18 1 88 sub fallback_language { $default_language }
899              
900             #--------------------------------------------------------------------------
901              
902             =head2 $string_array language_classes()
903              
904             This method returns a list of possible class names (which must be
905             sub-classes of L) which can get
906             prepended to the language tags for this instance. L
907             will then try to dynamically load these modules during construction.
908              
909             The idea being that you simply need to put your language-specific
910             module in the same directory as your sub-class, thus we will find the
911             custom modules.
912              
913             Alternatively, you can sub-class this method, to return the correct
914             class heirachy name.
915              
916             =cut
917              
918             sub language_classes {
919 7     7 1 16 my ($self) = @_;
920 7         18 my $class = ref($self);
921 7         238 my $superclass = $class;
922 7         50 $superclass =~ s/(.*)::.+$/$1/;
923 7         46 my @classes = (
924             $class,
925             $class."::Language",
926             $class."::Languages",
927             $superclass,
928             $superclass."::Language",
929             $superclass."::Languages"
930             );
931 7         22 return \@classes;
932             }
933              
934             #--------------------------------------------------------------------------
935              
936             =head2 $format numeric_format($format)
937              
938             This method allows you to set and/or get the format that is being used
939             for numeric formatting. You can supply an array, an array ref, or a string.
940              
941             =cut
942              
943             sub numeric_format {
944 4     4 1 220 my $self = shift;
945 4 50       12 if (@_) {
946 4         5 my $mode;
947 4 100       10 if (@_ > 1) { @$mode = @_; } else { ($mode) = @_; }
  1         5  
  3         5  
948 4         10 my $options = { numeric_format => $mode };
949 4         11 $self->{numeric_format} = $self->_get_numeric_format($options);
950             }
951 4 50       17 return $self->{numeric_format} ? $self->{numeric_format} : Locale::MakePhrase::Numeric->DOT;
952             }
953              
954             #--------------------------------------------------------------------------
955              
956             =head2 Accessor methods
957              
958             =over 2
959              
960             =item $hash B
961              
962             Returns the options that were supplied to the constructor.
963              
964             =item $string_array B
965              
966             Returns a list of the language tags that are in use.
967              
968             =item $object_list B
969              
970             Returns a list of the loaded language modules.
971              
972             =item $object B
973              
974             Returns the loaded backing store instance.
975              
976             =item $object B
977              
978             Returns the loaded rule manager instance.
979              
980             =item $string B
981              
982             Returns the output character set encoding.
983              
984             =item $int B
985              
986             Returns the current UTF-8 malformed character output mode.
987              
988             =item $bool B
989              
990             Returns the current state of 'L'.
991              
992             =item $bool B
993              
994             Returns the current state of 'L'.
995              
996             =item $bool B
997              
998             Returns the current state of 'L'.
999              
1000             =item $bool B
1001              
1002             Returns the current state of 'L'.
1003              
1004             =back
1005              
1006             =cut
1007              
1008 0     0 1 0 sub options { shift->{options} }
1009 0     0 1 0 sub languages { shift->{languages} }
1010 0     0 1 0 sub language_modules { shift->{language_modules} }
1011 0     0 1 0 sub backing_store { shift->{backing_store} }
1012 0     0 1 0 sub rule_manager { shift->{rule_manager} }
1013 0     0 1 0 sub encoding { shift->{encoding} }
1014 0     0 1 0 sub malformed_character_mode { shift->{malformed_character_mode} }
1015 0     0 1 0 sub die_on_bad_translation { shift->{die_on_bad_translation} }
1016 0     0 1 0 sub translate_arguments { shift->{translate_arguments} }
1017 0     0 1 0 sub add_newline { shift->{add_newline} }
1018 0     0 1 0 sub panic_language_lookup { shift->{panic_language_lookup} }
1019              
1020             #--------------------------------------------------------------------------
1021              
1022             =head1 Function API
1023              
1024             The following items are helper functions, which can be used to
1025             simplify the usage of L objects.
1026              
1027             =cut
1028              
1029             #--------------------------------------------------------------------------
1030              
1031             =head2 $string mp($string [, ...])
1032              
1033             This is a helper function to the translate() function call. It will
1034             use the last-constructed instance of L to invoke
1035             the translate function on. eg:
1036              
1037             print mp("This is test no: [_1]",$test_no);
1038              
1039             could produce:
1040              
1041             This is the first test.
1042              
1043             =cut
1044              
1045             sub mp {
1046 4     4 1 1314 local $Locale::MakePhrase::Utils::DIE_FROM_CALLER = 1;
1047 4 100       18 die_from_caller("You must construct at least one Locale::MakePhrase object, before using this function.") unless $this;
1048 3 100       15 die_from_caller("mp() requires at least one parameter") unless @_ > 0;
1049 2         7 return $this->context_translate(undef,@_);
1050             }
1051              
1052             #--------------------------------------------------------------------------
1053              
1054             =head2 $string __ $string [, ...]
1055              
1056             This function is the same as the previous helper function, except that
1057             it makes you code easier to read and easier to write. eg:
1058              
1059             print __"This is test no: [_1]",$test_no;
1060              
1061             could produce:
1062              
1063             This is test no: 4
1064              
1065             Note that we use double-underscore as this makes search-n-replace tasks
1066             easier than if we used a single-underscore.
1067              
1068             =cut
1069              
1070             sub __ {
1071 4     4   1081 local $Locale::MakePhrase::Utils::DIE_FROM_CALLER = 1;
1072 4 100       19 die_from_caller("You must construct at least one Locale::MakePhrase object, before using this function.") unless $this;
1073 3 100       12 die_from_caller("__() requires at least one parameter") unless @_ > 0;
1074 2         8 return $this->context_translate(undef,@_);
1075             }
1076              
1077             =cut
1078              
1079             #--------------------------------------------------------------------------
1080              
1081             =head2 NOTE
1082              
1083             The previous functions use a reference to an internal variable. If
1084             you are using this module from within Apache (say under mod_perl),
1085             make sure that you construct a new instance of a L
1086             object, in the child Apache processes.
1087              
1088             =cut
1089              
1090             #--------------------------------------------------------------------------
1091             # The following methods are not part of the API - they are private.
1092             #
1093             # This means that everything above this code-break is allowed/designed
1094             # to be overloaded.
1095             #--------------------------------------------------------------------------
1096              
1097             #--------------------------------------------------------------------------
1098             #
1099             # Load/construct the backing store.
1100             #
1101             # We can pass in a string name of a backing store to use,
1102             # or an object reference to a previously constructed backing store.
1103             #
1104             sub _attach_backing_store {
1105 7     7   15 my ($self) = @_;
1106 7         101 my $options = $self->{options};
1107 7         15 my $backing_store;
1108             my $store;
1109              
1110             # use default backing store if none defined
1111 7 100       43 if (exists $options->{backing_store}) {
    50          
1112 6         16 $backing_store = $options->{backing_store};
1113             } elsif (exists $options->{backing_store}) {
1114 0         0 $backing_store = $self->{backing_store};
1115             } else {
1116 1         4 $backing_store = $self->fallback_backing_store;
1117 1 50       3 die_from_caller("Failed to locate a default backing store") unless $backing_store;
1118 1 50       4 print STDERR "Using fallback backing store ($backing_store)\n" if $DEBUG > 1;
1119             }
1120              
1121             # if not a reference, try to construct one
1122 7 100       33 unless (ref($backing_store)) {
1123              
1124             ## see if perl module can be loaded
1125 1     1   56 eval "use $backing_store";
  1         7  
  1         1  
  1         16  
1126 1 50       4 die_from_caller("Failed to load backing store: $backing_store") if ($@);
1127              
1128             ## try constructing it
1129 1         59 eval '$store = '. "$backing_store" .'->new()';
1130 1 50       5 die_from_caller("Failed to construct backing store: $backing_store") if ($@);
1131 1 50       6 die_from_caller("Backing store connection failure: $backing_store") unless ($store);
1132              
1133             # use a passed in reference to a backing store
1134             } else {
1135 6         13 $store = $backing_store;
1136             }
1137 7 100       43 $options->{backing_store} = ref($store) if (exists $options->{backing_store});
1138              
1139             ## make sure backing store ISA Locale::MakePhrase::BackingStore object
1140 7 50       80 die_from_caller("Backing store is not of type Local::MakePhrase::BackingStore")
1141             unless ($store->isa('Locale::MakePhrase::BackingStore'));
1142              
1143 7         32 return $store;
1144             }
1145              
1146             #--------------------------------------------------------------------------
1147             #
1148             # Return an rule_manager object that is to be used in subsequent rule evaluations
1149             #
1150             # We can pass in a string name of a rule manager to use,
1151             # or an object reference to a previously constructed rule manager.
1152             #
1153             sub _get_rule_manager {
1154 7     7   17 my ($self) = @_;
1155 7         17 my $options = $self->{options};
1156 7         22 my $rule_manager;
1157             my $manager;
1158              
1159             # use default manager if none defined
1160 7 50       54 if (exists $options->{rule_manager}) {
    50          
1161 0         0 $rule_manager = $options->{rule_manager};
1162             } elsif (exists $self->{rule_manager}) {
1163 0         0 $rule_manager = $self->{rule_manager};
1164             } else {
1165 7 50       27 print STDERR "Using default rule_manager ($default_rule_manager)\n" if $DEBUG > 1;
1166 7         95 $rule_manager = $default_rule_manager;
1167             }
1168              
1169             # if its not a reference, try constructing it
1170 7 50       28 unless (ref($rule_manager)) {
1171              
1172             # see if perl modle can be loaded
1173 7     7   716 eval "use $rule_manager";
  7         74  
  7         14  
  7         124  
1174 7 50       36 die_from_caller("Failed to load rule manager: $rule_manager") if ($@);
1175              
1176             # try constructing it
1177 7         530 eval '$manager = '. "$rule_manager" .'->new()';
1178 7 50 33     90 die_from_caller("Failed to construct rule manager: $rule_manager") if ($@ or not $manager);
1179              
1180             # use passed in rule_manager
1181             } else {
1182 0         0 $manager = $rule_manager;
1183             }
1184 7 50       37 $options->{rule_manager} = ref($manager) if (exists $options->{rule_manager});
1185              
1186             # make sure rule_manager ISA Locale::MakePhrase::RuleManager object
1187 7 50       66 die_from_caller("Rule manager is not of type Locale::MakePhrase::RuleManager")
1188             unless ($manager->isa('Locale::MakePhrase::RuleManager'));
1189              
1190 7         56 return $manager;
1191             }
1192              
1193             #--------------------------------------------------------------------------
1194             #
1195             # Return list of languages that we want to handle (highest to lowest priority).
1196             #
1197             # This implementation does the following:
1198             #
1199             # a) grab the required language(s) by looking for optins (in order) of:
1200             # - language -> string,
1201             # - languages -> string array,
1202             # - languages -> string containing a comma seperated list,
1203             #
1204             # b) then convert those language/dialect(s) into 'languages tags'
1205             #
1206             # c) generate 'super ordinate' language tags for results from b)
1207             #
1208             # d) generate 'alternate' languages tags for result from c)
1209             #
1210             # e) add the 'panic' language tags from the results of d) (if enabled)
1211             #
1212             # f) add the fallback language (after converting it to a language tag)
1213             #
1214             # g) strip off any duplicate tags
1215             #
1216             # h) make sure all tags only contain [a-z0-9_], by
1217             # - stripping unknown characters
1218             # - converting uppercase to lowercase
1219             # - converting '-' to '_'
1220             #
1221             sub _get_languages {
1222 7     7   19 my ($self) = @_;
1223 7         16 my $options = $self->{options};
1224 7         13 my @languages;
1225              
1226             ## get prefered language(s)
1227 7 50 0     35 if (exists $options->{language}) {
    0 0        
    0          
    0          
    0          
    0          
1228 7         37 push @languages, $options->{language};
1229             } elsif (exists $options->{languages} && ref($options->{languages}) eq "ARRAY") {
1230 0         0 @languages = @{$options->{languages}};
  0         0  
1231             } elsif (exists $options->{languages}) {
1232 0         0 @languages = split(',',$options->{languages});
1233             } elsif (exists $self->{language}) {
1234 0         0 push @languages, $self->{language};
1235             } elsif (exists $self->{languages} && ref($self->{languages}) eq "ARRAY") {
1236 0         0 @languages = @{$self->{languages}};
  0         0  
1237             } elsif (exists $self->{languages}) {
1238 0         0 @languages = split(',',$self->{languages});
1239             }
1240              
1241             # Lookup real language/dialect definitions, from supplied language
1242 7         53 @languages = map I18N::LangTags::locale2language_tag($_), @languages;
1243 7         255 push @languages, map I18N::LangTags::super_languages($_), @languages;
1244              
1245             # catch alternations
1246 7         238 @languages = map { $_, I18N::LangTags::alternate_language_tags($_) } @languages;
  13         236  
1247              
1248             # get at least an approximate language
1249 7 50       264 if ($self->{panic_language_lookup}) {
1250 0         0 push @languages, I18N::LangTags::panic_languages(@languages);
1251             }
1252              
1253             # add a fallback language, just in case specified languages dont work
1254 7         49 my $fallback = $self->fallback_language;
1255 7 50       29 die_from_caller("Must implement something valid for 'fallback_language' method") unless $fallback;
1256 7         32 $fallback = I18N::LangTags::locale2language_tag($fallback);
1257 7         112 push @languages, $fallback;
1258              
1259             # strip off duplicate languages
1260             {
1261 7         26 my @langs;
  7         15  
1262 7         18 LOOP: foreach my $lang (@languages) {
1263 20 100       45 foreach my $l (@langs) { next LOOP if I18N::LangTags::same_language_tag($l,$lang); }
  19         280  
1264 13         312 push @langs, $lang;
1265             }
1266 7         292 @languages = @langs;
1267             }
1268              
1269             # final bit of processing:
1270             {
1271 7         14 my @langs;
  7         21  
1272 7         18 foreach my $lang (@languages) {
1273 13         27 $lang =~ tr<-A-Z><_a-z>; # lc, and turn - to _
1274 13         26 $lang =~ tr<_a-z0-9><>cd; # remove all but a-z0-9_
1275 13 50       54 next unless $lang;
1276 13         28 push @langs, $lang;
1277             }
1278 7         27 @languages = @langs;
1279             }
1280              
1281 7 50       27 print STDERR "Available languages: ", join(',',@languages), "\n" if $DEBUG > 1;
1282 7         39 return \@languages;
1283             }
1284              
1285             #--------------------------------------------------------------------------
1286             #
1287             # If the user specified a charset (and its not UTF-8), we want to be
1288             # able to encode the output translation into that charset/encoding,
1289             # before returning. Here we simply capture that info.
1290             #
1291             sub _get_encoding {
1292 7     7   14 my ($self) = @_;
1293 7         28 my $options = $self->{options};
1294 7         17 my $encoding = $internal_encoding;
1295 7 50       83 if (exists $options->{charset}) {
    50          
    50          
    50          
1296 0         0 $encoding = $options->{charset};
1297             } elsif (exists $options->{encoding}) {
1298 0         0 $encoding = $options->{encoding};
1299             } elsif (exists $self->{charset}) {
1300 0         0 $encoding = $self->{charset};
1301             } elsif (exists $self->{encoding}) {
1302 0         0 $encoding = $self->{encoding};
1303             }
1304 7 50       26 die_from_caller("Invalid encoding specified") unless $encoding;
1305 7         19 $encoding =~ tr<_A-Z><-a-z>; # lc, and turn _ to -
1306 7         29 return $encoding;
1307             }
1308              
1309             #--------------------------------------------------------------------------
1310             #
1311             # Figure out what to do when there is a malformed character in the string,
1312             # when transcoding from UTF-8 to another charset/encoding.
1313             #
1314             sub _get_malformed_mode {
1315 7     7   18 my ($self) = @_;
1316 7         16 my $options = $self->{options};
1317 7         16 my $mode = $default_malformed_mode;
1318 7 50       58 if (exists $options->{malformed_character_mode}) {
    50          
1319 0         0 $mode = $options->{malformed_character_mode};
1320             } elsif (exists $self->{malformed_character_mode}) {
1321 0         0 $mode = $self->{malformed_character_mode};
1322             }
1323 7 0 33     53 if (!defined $mode or ($mode != MALFORMED_MODE_ESCAPE and $mode != MALFORMED_MODE_HTML and $mode != MALFORMED_MODE_XML)) {
      33        
      33        
1324 0         0 die_from_caller("Unknown malformed-character mode:",$mode);
1325             }
1326 7         37 return $mode;
1327             }
1328              
1329             #--------------------------------------------------------------------------
1330             #
1331             # Figure out what numeric formatting we want
1332             #
1333             sub _get_numeric_format {
1334 11     11   29 my ($self,$options) = @_;
1335 11 100       40 $options = $self->{options} unless $options;
1336 11         22 my $mode = $default_numeric_format;
1337 11 100 33     97 if (exists $options->{numeric_format}) {
    50          
1338 4         6 $mode = $options->{numeric_format};
1339             } elsif (exists $self->{numeric_format} and defined $self->{numeric_format}) {
1340 0         0 $mode = $self->{numeric_format};
1341             }
1342 11 50       40 $mode = [] unless (defined $mode);
1343 11 50       52 @$mode = split('',$mode) if (ref($mode) eq '');
1344 11 50       37 die_from_caller("Unknown numeric formatting mode") unless (ref($mode) eq 'ARRAY');
1345 11 50       54 $mode->[0] = '.' if (@$mode == 0);
1346 11 50       40 $mode->[1] = '' if (@$mode == 1);
1347 11 50       37 $mode->[2] = '-' if (@$mode == 2);
1348 11 50       40 $mode->[3] = '' if (@$mode == 3);
1349 11 50       38 die_from_caller("Unknown numeric-formatting mode:",join(',',@$mode)) unless (@$mode == 4);
1350 11         137 for (0..scalar(@$mode)-1) {
1351 44 50       130 die_from_caller("Undefined numeric format in placeholder: $_") unless (defined $mode->[$_]);
1352             }
1353 11 50       105 $mode = undef if (join(',',$mode) eq join(',',Locale::MakePhrase::Numeric->DOT));
1354 11         54 return $mode;
1355             }
1356              
1357             #--------------------------------------------------------------------------
1358             #
1359             # We try to load all the languages that user is able to use.
1360             # This allows the application to install their own method calls into the
1361             # language (even at run-time), if they really deem it to be necessary.
1362             #
1363             sub _load_language_modules {
1364 7     7   17 my ($self) = @_;
1365 7         18 my $languages = $self->{languages};
1366 7         42 my $classes = $self->language_classes();
1367              
1368 7         13 my @language_modules;
1369 7         18 foreach my $language (@$languages) {
1370              
1371             # try loading the language specific module
1372             # (we try various module-names to see which one resolves)
1373 13         20 my $module;
1374 13         26 foreach my $class (@$classes) {
1375 50         103 $module = $class ."::". $language;
1376 50 50       116 print STDERR "Trying to load language module: $module\n" if $DEBUG > 2;
1377 50     7   3116 eval "use ". $module;
  7     7   3505  
  0         0  
  0         0  
  7         3210  
  1         3  
  1         18  
1378 50 100       189 last unless ($@);
1379 43         88 $module = undef;
1380             }
1381 13 100       51 next unless $module;
1382 7 50       40 print STDERR "Loaded language module: $module\n" if $DEBUG > 2;
1383              
1384             # try constructing the language specific module
1385 7         13 my $object;
1386 7         450 eval '$object = '. "$module" .'->new()';
1387 7 50       46 next if ($@);
1388              
1389             # coool - special code for this language
1390 7 50       29 print STDERR "Found custom language handling object for language: $language\n" if $DEBUG > 1;
1391 7         27 push @language_modules, $object;
1392             }
1393              
1394 7         297 return \@language_modules;
1395             }
1396              
1397             #--------------------------------------------------------------------------
1398             #
1399             # Take the list of all rules, then sort them by language, then by priority,
1400             # then by non-specified rule/language
1401             #
1402             sub _sort_rules {
1403 26     26   43 my ($self,$rule_objs,$languages) = @_;
1404 26 100 66     82 return undef if (not defined $rule_objs or @{$rule_objs} < 1);
  26         107  
1405 17 100       22 return $rule_objs if (@{$rule_objs} == 1);
  17         64  
1406              
1407             # sort the rules by language
1408 11         20 my $manager = $self->{rule_manager};
1409 11         55 return $manager->sort($rule_objs,$languages);
1410             }
1411              
1412             #--------------------------------------------------------------------------
1413             #
1414             # Select one of the rules, by applying the arguements
1415             #
1416             sub _select_rule {
1417 26     26   54 my ($self,$rule_objs,@args) = @_;
1418 26 100       71 return undef unless $rule_objs;
1419 17         29 my $manager = $self->{rule_manager};
1420              
1421             # run manager on the translation rules
1422 17         31 foreach my $r_obj (@$rule_objs) {
1423 23         68 my $expression = $r_obj->expression;
1424 23 100       75 return $r_obj unless (length $expression);
1425 17         23 my $result = eval { $manager->evaluate($expression,@args); };
  17         88  
1426 17 50       42 if ($@) {
1427 0 0       0 die $@ if $self->{die_on_bad_translation};
1428 0         0 print STDERR $@;
1429 0         0 next;
1430             }
1431 17 100       64 return $r_obj if $result;
1432             }
1433              
1434             # no rule matched
1435 2         5 return undef;
1436             }
1437              
1438             #--------------------------------------------------------------------------
1439             #
1440             # Apply the arguments, to the new translated text
1441             #
1442             sub _apply_arguments {
1443 26     26   44 my ($self,$rule_obj) = (shift,shift);
1444 26         86 my $translation = $rule_obj->translation();
1445 26         49 my $manager = $self->{rule_manager};
1446 26         110 return $manager->apply_arguments($self,$translation,@_);
1447             }
1448              
1449             #--------------------------------------------------------------------------
1450             #
1451             # Apply the current encoding to the translated text
1452             #
1453             sub _apply_encoding {
1454 26     26   42 my ($self,$text) = @_;
1455 26 50       110 return $text if ($self->{encoding} eq $internal_encoding);
1456 0         0 $text = encode("UTF8", $text, $self->{malformed_character_mode});
1457 0         0 from_to($text, "UTF8", $self->{encoding});
1458 0         0 return $text;
1459             }
1460              
1461             #--------------------------------------------------------------------------
1462             #
1463             # We want to see if the function called, appears in the language specific sub-class.
1464             # If so, then we execute that function, in _that class_'s scope, then return the results.
1465             #
1466             # We want to do this so as to allow functionality such as:
1467             # $mp->y_or_n( get_user_input() );
1468             #
1469             sub AUTOLOAD {
1470 0     0   0 my $func = our $AUTOLOAD;
1471 0         0 $func =~ s/^.*:://;
1472 0         0 my $self = $this;
1473 0 0 0     0 $self = shift unless ($func eq 'mp' or $func eq '_');
1474 0         0 my $language_modules = $self->{language_modules};
1475              
1476             # See if the language-specific module contains this function name, and if so, run it
1477 0         0 foreach my $module (@$language_modules) {
1478 0 0       0 print STDERR "Trying to find function \"$func\" on module: ". ref($module) ."\n" if $DEBUG > 1;
1479 0         0 my $can = $module->can($func);
1480 0 0       0 next unless $can;
1481 0         0 return &$can($module,@_);
1482             }
1483              
1484             # generate error from caller perspective, if we couldn't execute the function
1485 0         0 my $languages = $self->{languages};
1486 0         0 die_from_caller("No function \"$func\" found for languages:", join(',',@$languages));
1487             }
1488              
1489             #--------------------------------------------------------------------------
1490             #
1491             # Whenever use AUTOLOAD, we need to implement DESTROY
1492             #
1493 0     0   0 sub DESTROY {}
1494              
1495             1;
1496             __END__