File Coverage

lib/Petal/I18N.pm
Criterion Covered Total %
statement 113 115 98.2
branch 26 30 86.6
condition 15 24 62.5
subroutine 10 10 100.0
pod 0 1 0.0
total 164 180 91.1


line stmt bran cond sub pod time code
1             # ------------------------------------------------------------------
2             # Petal::I18N - Independant I18N processing
3             # ------------------------------------------------------------------
4             package Petal::I18N;
5 77     77   143719 use MKDoc::XML::TreeBuilder;
  77         8535  
  77         2268  
6 77     77   33405 use MKDoc::XML::TreePrinter;
  77         44495  
  77         2303  
7 77     77   1169 use Petal::Hash::String;
  77         140  
  77         1466  
8 77     77   345 use warnings;
  77         134  
  77         1768  
9 77     77   365 use strict;
  77         123  
  77         98953  
10              
11             our $Namespace = "http://xml.zope.org/namespaces/i18n";
12             our $Prefix = 'i18n';
13             our $Domain = 'default';
14              
15              
16             sub process
17             {
18 7     7 0 31 my $class = shift;
19 7         10 my $data = shift;
20              
21 7         14 local $Namespace = $Namespace;
22 7         151 local $Prefix = $Prefix;
23 7         14 local $Domain = $Domain;
24              
25 7         36 my @nodes = MKDoc::XML::TreeBuilder->process_data ($data);
26 7         19044 for (@nodes) { $class->_process ($_) }
  15         36  
27 7         51 return MKDoc::XML::TreePrinter->process (@nodes);
28             }
29              
30              
31             sub _process
32             {
33 78     78   95 my $class = shift;
34 78         100 my $tree = shift;
35 78 100       195 return unless (ref $tree);
36              
37 31         47 local $Prefix = $Prefix;
38 31         37 local $Domain = $Domain;
39              
40             # process the I18N namespace
41 31         31 foreach my $key (keys %{$tree})
  31         93  
42             {
43 150         172 my $value = $tree->{$key};
44 150 100       253 if ($value eq $Namespace)
45             {
46 6 50       24 next unless ($key =~ /^xmlns\:/);
47 6         14 delete $tree->{$key};
48 6         7 $Prefix = $key;
49 6         21 $Prefix =~ s/^xmlns\://;
50             }
51             }
52              
53             # set the current i18n:domain
54 31   66     148 $Domain = delete $tree->{"$Prefix:domain"} || $Domain;
55              
56 31         48 my $tag = $tree->{_tag};
57 31 100       38 my $attr = { map { /^_/ ? () : ( $_ => $tree->{$_} ) } keys %{$tree} };
  138         334  
  31         73  
58 31 100 66     160 return if ($tag eq '~comment' or $tag eq '~pi' or $tag eq '~declaration');
      66        
59              
60              
61             # replace attributes with their respective translations
62 27 100       70 $tree->{"$Prefix:attributes"} && do {
63 3         6 my $attributes = $tree->{"$Prefix:attributes"};
64 3         11 $attributes =~ s/\s*;\s*$//;
65 3         9 $attributes =~ s/^\s*//;
66 3         8 my @attributes = split /\s*\;\s*/, $attributes;
67 3         8 foreach my $attribute (@attributes)
68             {
69             # if we have i18n:attributes="alt alt_text", then the
70             # attribute name is 'alt' and the
71             # translate_id is 'alt_text'
72 3         15 my ($attribute_name, $translate_id);
73 3 50       31 if ($attribute =~ /\s/)
74             {
75 3         25 ($attribute_name, $translate_id) = split /\s+/, $attribute, 2;
76             }
77              
78             # otherwise, if we have i18n:attributes="alt", then the
79             # attribute name is 'alt' and the
80             # translate_id is $tree->{'alt'}
81             else
82             {
83 0         0 $attribute_name = $attribute;
84 0         0 $translate_id = _canonicalize ( $tree->{$attribute_name} );
85             }
86              
87             # the default value if maketext() fails should be the current
88             # value of the attribute
89 3         9 my $default_value = $tree->{$attribute_name};
90              
91             # the value to replace the attribute with should be either the
92             # translation, or the default value if maketext() failed.
93 3   33     6 my $value = eval { $Petal::TranslationService->maketext ($translate_id) } || $default_value;
94              
95             # if maketext() failed, let's know why.
96 3 100       84 $@ && warn $@;
97              
98             # set the (hopefully) translated value
99 3         14 $tree->{$attribute_name} = $value;
100             }
101             };
102              
103              
104             # replace content with its translation
105 27 100       74 exists $tree->{"$Prefix:translate"} && do {
106 9         13 my ($translate_id);
107              
108             # if we have $Domain:translate="something",
109             # then the translate_id is 'something'
110 9 100 66     64 if (defined $tree->{"$Prefix:translate"} and $tree->{"$Prefix:translate"} ne '')
111             {
112 8         17 $translate_id = $tree->{"$Prefix:translate"};
113             }
114              
115             # otherwise, the translate_id has to be computed
116             # from the contents of this node, so that
117             #
Hello, David, how are you?
118             # becomes 'Hello, ${user}, how are you?'
119             else
120             {
121 1         4 $translate_id = _canonicalize ( _extract_content_string ($tree) );
122             }
123              
124             # the default value if maketext() fails should be the current
125             # value of the attribute
126 9         23 my $default_value = _canonicalize ( _extract_content_string ($tree) );
127              
128             # the value to replace the content with should be either the
129             # translation, or the default value if maketext() failed.
130 9   66     15 my $value = eval { $Petal::TranslationService->maketext ($translate_id) } || $default_value;
131              
132             # now, $value is supposed to have the translated string, which looks like
133             # 'Bonjour, ${user}, comment allez-vous?'. We need to turn this back into
134             # a tree structure.
135 9         470 my %named_nodes = _extract_named_nodes ($tree);
136 9         14 my @tokens = @{Petal::Hash::String->_tokenize (\$value)};
  9         48  
137             my @res = map {
138 9         25 ($_ =~ /$Petal::Hash::String::TOKEN_RE/gsm) ?
139             do {
140 5         25 s/^\$//;
141 5         50 s/^\{//;
142 5         20 s/\}$//;
143 5         20 $named_nodes{$_};
144             } :
145 16 100       155 do {
146 11         44 s/\\(.)/$1/gsm;
147 11         32 $_;
148             };
149             } @tokens;
150              
151 9         35 $tree->{_content} = \@res;
152             };
153              
154             # I know, I know, the I18N namespace processing is a bit broken...
155             # It should suffice for now.
156 27         59 delete $tree->{"$Prefix:attributes"};
157 27         45 delete $tree->{"$Prefix:translate"};
158 27         38 delete $tree->{"$Prefix:name"};
159              
160             # Do the same i18n thing with child nodes, recursively.
161             # for some reason it always makes me think of roller coasters.
162             # Yeeeeeeee!
163 27 50       56 defined $tree->{_content} and do {
164 27         28 for (@{$tree->{_content}}) { $class->_process ($_) }
  27         83  
  63         163  
165             };
166             }
167              
168              
169             sub _canonicalize
170             {
171 10     10   15 my $string = shift;
172 10 50       23 return '' unless (defined $string);
173              
174 10         93 $string =~ s/\s+/ /gsm;
175 10         24 $string =~ s/^ //;
176 10         21 $string =~ s/ $//;
177 10         24 return $string;
178             }
179              
180              
181             sub _extract_named_nodes
182             {
183 9     9   15 my $tree = shift;
184 9         15 my @nodes = ();
185 9         11 foreach my $node (@{$tree->{_content}})
  9         20  
186             {
187 19 100       39 ref $node || next;
188 5         9 push @nodes, $node;
189             }
190              
191 9         16 my %nodes = ();
192 9         13 my $count = 0;
193 9         16 foreach my $node (@nodes)
194             {
195 5         6 $count++;
196 5   66     40 my $name = $node->{"$Prefix:name"} || $count;
197 5         16 $nodes{$name} = $node;
198             }
199              
200 9         32 return %nodes;
201             }
202              
203              
204             sub _extract_content_string
205             {
206 10     10   12 my $tree = shift;
207 10         18 my @res = ();
208              
209 10         20 my $count = 0;
210 10         14 foreach my $node (@{$tree->{_content}})
  10         18  
211             {
212 24 100       54 ref $node or do {
213 17         26 push @res, $node;
214 17         26 next;
215             };
216              
217 7         11 $count++;
218 7   66     28 my $name = $node->{"$Prefix:name"} || $count;
219 7         36 push @res, '${' . $name . '}';
220             }
221              
222 10         50 return join '', @res;
223             }
224              
225              
226             1;
227              
228              
229             __END__