line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Template::Multilingual::Parser; |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
29
|
use strict; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
164
|
|
4
|
3
|
|
|
3
|
|
17
|
use base qw(Template::Parser); |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
3887
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '1.00'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
sub new |
9
|
|
|
|
|
|
|
{ |
10
|
4
|
|
|
4
|
1
|
10
|
my ($class, $options) = @_; |
11
|
4
|
|
|
|
|
42
|
my $self = $class->SUPER::new($options); |
12
|
4
|
|
|
|
|
784
|
$self->{_sections} = []; |
13
|
4
|
|
50
|
|
|
73
|
$self->{_langvar} = $options->{LANGUAGE_VAR} || 'language'; |
14
|
|
|
|
|
|
|
|
15
|
4
|
|
|
|
|
12
|
my $style = $self->{ STYLE }->[-1]; |
16
|
4
|
|
|
|
|
20
|
@$self{ qw(_start _end) } = @$style{ qw( START_TAG END_TAG ) }; |
17
|
4
|
|
|
|
|
13
|
for (qw( _start _end )) { |
18
|
8
|
|
|
|
|
59
|
$self->{$_} =~ s/\\([^\\])/$1/g; |
19
|
|
|
|
|
|
|
} |
20
|
|
|
|
|
|
|
|
21
|
4
|
|
|
|
|
20
|
return $self; |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub parse |
25
|
|
|
|
|
|
|
{ |
26
|
30
|
|
|
30
|
1
|
6444
|
my ($self, $text) = @_; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# isolate multilingual sections |
29
|
30
|
|
|
|
|
234
|
$self->_tokenize($text); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# replace multilingual sections with TT directives |
32
|
30
|
|
|
|
|
415
|
my ($S, $E, $LANGVAR) = map $self->{$_}, qw(_start _end _langvar); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# if language is a variant (en_US), create a template variable holding the fallback value (en) |
35
|
30
|
|
|
|
|
245
|
$text = "$S IF (tm_matches = $LANGVAR.match('^(\\w+)[-_].*\$')); tm_fb = tm_matches.0; END $E"; |
36
|
|
|
|
|
|
|
|
37
|
30
|
|
|
|
|
68
|
for my $section (@{$self->{_sections}}) { |
|
30
|
|
|
|
|
89
|
|
38
|
34
|
100
|
|
|
|
177
|
if ($section->{nolang}) { |
|
|
50
|
|
|
|
|
|
39
|
8
|
|
|
|
|
21
|
$text .= $section->{nolang}; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
elsif (my $t = $section->{lang}) { |
42
|
26
|
|
|
|
|
98
|
my @languages = keys %$t; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# first loop through languages: look for exact match |
45
|
26
|
|
|
|
|
86
|
$text .= "$S tm_f = 0; SWITCH $LANGVAR $E"; |
46
|
26
|
|
|
|
|
111
|
for my $lang (@languages) { |
47
|
44
|
|
|
|
|
175
|
$text .= "$S CASE '$lang' $E" . $t->{$lang}; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
# add a default case to trigger fallback |
50
|
26
|
|
|
|
|
69
|
$text .= "$S CASE; tm_f=1; END; $E"; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# second loop: fallback to primary language (en_US matches en) |
53
|
26
|
|
|
|
|
65
|
$text .= "$S IF tm_fb AND tm_f; tm_f=0; SWITCH tm_fb; $E"; |
54
|
26
|
|
|
|
|
134
|
for my $lang (@languages) { |
55
|
44
|
|
|
|
|
458
|
$text .= "$S CASE '$lang' $E" . $t->{$lang}; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
# add a default case to trigger last resort fallback |
58
|
|
|
|
|
|
|
# LANG is fr_XX or fr but template has neither |
59
|
|
|
|
|
|
|
# we try to fallback to fr_YY is present |
60
|
26
|
|
|
|
|
39
|
my %seen; |
61
|
26
|
100
|
66
|
|
|
184
|
my @fallbacks = map { /^(\w+)[-_].*$/ && !$seen{$_}++ ? [ $1 => $_] : () } sort @languages; |
|
44
|
|
|
|
|
446
|
|
62
|
26
|
100
|
|
|
|
80
|
if (@fallbacks) { |
63
|
|
|
|
|
|
|
# third loop: fallback to first available variant |
64
|
12
|
|
|
|
|
39
|
$text .= "$S CASE; tm_f=1; END; END; IF tm_f; SWITCH tm_fb || $LANGVAR; $E"; |
65
|
12
|
|
|
|
|
25
|
for my $ref (@fallbacks) { |
66
|
20
|
|
|
|
|
39
|
my ($lang, $variant) = @$ref; |
67
|
20
|
|
|
|
|
154
|
$text .= "$S CASE '$lang' $E" . $t->{$variant}; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
} |
70
|
26
|
|
|
|
|
129
|
$text .= "$S END; END $E"; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
30
|
|
|
|
|
210
|
return $self->SUPER::parse ($text); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub _tokenize |
77
|
|
|
|
|
|
|
{ |
78
|
30
|
|
|
30
|
|
69
|
my ($self, $text) = @_; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# extract all sections from the text |
81
|
30
|
|
|
|
|
91
|
$self->{_sections} = []; |
82
|
30
|
|
|
|
|
379
|
my @tokens = split m!(.*?)!s, $text; |
83
|
30
|
|
|
|
|
127
|
my $i = 0; |
84
|
30
|
|
|
|
|
81
|
for my $t (@tokens) { |
85
|
56
|
100
|
|
|
|
144
|
if ($i) { # ... multilingual section |
86
|
26
|
|
|
|
|
139
|
my %section; |
87
|
26
|
|
|
|
|
228
|
while ($t =~ m!<([^<>]+)>(.*?)!gs) { |
88
|
44
|
|
|
|
|
740
|
$section{$1} = $2; |
89
|
|
|
|
|
|
|
} |
90
|
26
|
50
|
|
|
|
277
|
push @{$self->{_sections}}, { lang => \%section } |
|
26
|
|
|
|
|
138
|
|
91
|
|
|
|
|
|
|
if %section; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
else { # bare text |
94
|
30
|
100
|
|
|
|
101
|
push @{$self->{_sections}}, { nolang => $t } if $t; |
|
8
|
|
|
|
|
57
|
|
95
|
|
|
|
|
|
|
} |
96
|
56
|
|
|
|
|
297
|
$i = 1 - $i; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
30
|
|
|
30
|
1
|
541863
|
sub sections { $_[0]->{_sections} } |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head1 NAME |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Template::Multilingual::Parser - Multilingual template parser |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head1 SYNOPSIS |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
use Template; |
108
|
|
|
|
|
|
|
use Template::Multilingual::Parser; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
my $parser = Template::Multilingual::Parser->new(); |
111
|
|
|
|
|
|
|
my $template = Template->new(PARSER => $parser); |
112
|
|
|
|
|
|
|
$template->process('example.ttml', { language => 'en'}); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head1 DESCRIPTION |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
This subclass of Template Toolkit's C parses multilingual |
117
|
|
|
|
|
|
|
templates: templates that contain text in several languages. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Hello! |
121
|
|
|
|
|
|
|
Bonjour ! |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Use this module directly if you have subclassed C, otherwise you
125
|
|
|
|
|
|
|
may find it easier to use C. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Language codes can be any string that matches C<\w+>, but we suggest |
128
|
|
|
|
|
|
|
sticking to ISO-639 which provides 2-letter codes for common languages |
129
|
|
|
|
|
|
|
and 3-letter codes for many others. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=head1 METHODS |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=head2 new(\%params) |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
The new() constructor creates and returns a reference to a new |
136
|
|
|
|
|
|
|
parser object. A reference to a hash may be supplied as a |
137
|
|
|
|
|
|
|
parameter to provide configuration values. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Parser objects are typically provided as the C option |
140
|
|
|
|
|
|
|
to the C constructor.
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
Configuration values are all valid C superclass |
143
|
|
|
|
|
|
|
options, and one specific to this class: |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=over |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item LANGUAGE_VAR |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
The LANGUAGE_VAR option can be used to set the name of the template |
150
|
|
|
|
|
|
|
variable which contains the current language. Defaults to |
151
|
|
|
|
|
|
|
I. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
my $parser = Template::Multilingual::Parser->new({ |
154
|
|
|
|
|
|
|
LANGUAGE_VAR => 'global.language', |
155
|
|
|
|
|
|
|
}); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
You will need to set this variable with the current language value |
158
|
|
|
|
|
|
|
at request time, usually in your C subclass' C
159
method.
160
161
=back
162
163
=head2 parse($text)
164
165
parse() is called by the Template Toolkit. It parses multilingual
166
sections from the input text and translates them to Template Toolkit
167
directives. The result is then passed to the C superclass.
168
169
=head2 sections
170
171
Returns a reference to an array of tokenized sections. Each section is a
172
reference to hash with either a C key or a C key.
173
174
A C key denotes text outside of any multilingual sections. The value
175
is the text itself.
176
177
A C key denotes text inside a multilingual section. The value is a
178
reference to a hash, whose keys are language codes and values the corresponding
179
text. For example, the following multilingual template:
180
181
foo bonjourHello bar
182
183
will parse to the following sections:
184
185
[ { nolang => 'foo ' },
186
{ lang => { fr => 'bonjour', en => 'hello' } },
187
{ nolang => ' bar' },
188
]
189
190
=head1 LANGUAGE SUBTAG HANDLING
191
192
This module supports language subtags to express variants, e.g. "en_US" or "en-US".
193
Here are the rules used for language matching:
194
195
=over
196
197
=item *
198
199
Exact match: the current language is found in the template
200
201
language template output
202
fr foobar foo
203
fr_CA foobar bar
204
205
=item *
206
207
Fallback to the primary language
208
209
language template output
210
fr_CA foobar foo
211
212
=item *
213
214
Fallback to first (in alphabetical order) other variant of the primary language
215
216
language template output
217
fr foobar bar
218
fr_CA foobar bar
219
220
=back
221
222
=head1 AUTHOR
223
224
Eric Cholet, C<< >>
225
226
=head1 BUGS
227
228
Multilingual text sections cannot be used inside TT directives.
229
The following is illegal and will trigger a TT syntax error:
230
231
[% title = "BonjourHello" %]
232
233
Use this instead:
234
235
[% title = BLOCK %]BonjourHello[% END %]
236
237
238
The TAG_STYLE, START_TAG and END_TAG directives are supported, but the
239
TAGS directive is not.
240
241
Please report any bugs or feature requests to
242
C, or through the web interface at
243
L.
244
I will be notified, and then you'll automatically be notified of progress on
245
your bug as I make changes.
246
247
=head1 SEE ALSO
248
249
L
250
251
ISO 639-2 Codes for the Representation of Names of Languages:
252
http://www.loc.gov/standards/iso639-2/langcodes.html
253
254
=head1 COPYRIGHT & LICENSE
255
256
Copyright 2009 Eric Cholet, All Rights Reserved.
257
258
This program is free software; you can redistribute it and/or modify it
259
under the same terms as Perl itself.
260
261
=cut
262
263
1; # End of Template::Multilingual::Parser
| | |