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   139028 use MKDoc::XML::TreeBuilder;
  77         8425  
  77         2126  
6 77     77   31124 use MKDoc::XML::TreePrinter;
  77         42056  
  77         2186  
7 77     77   1131 use Petal::Hash::String;
  77         150  
  77         1399  
8 77     77   344 use warnings;
  77         117  
  77         1789  
9 77     77   338 use strict;
  77         137  
  77         93983  
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 30 my $class = shift;
19 7         11 my $data = shift;
20              
21 7         12 local $Namespace = $Namespace;
22 7         10 local $Prefix = $Prefix;
23 7         12 local $Domain = $Domain;
24              
25 7         35 my @nodes = MKDoc::XML::TreeBuilder->process_data ($data);
26 7         19483 for (@nodes) { $class->_process ($_) }
  15         38  
27 7         51 return MKDoc::XML::TreePrinter->process (@nodes);
28             }
29              
30              
31             sub _process
32             {
33 78     78   101 my $class = shift;
34 78         91 my $tree = shift;
35 78 100       211 return unless (ref $tree);
36              
37 31         39 local $Prefix = $Prefix;
38 31         39 local $Domain = $Domain;
39              
40             # process the I18N namespace
41 31         31 foreach my $key (keys %{$tree})
  31         87  
42             {
43 150         186 my $value = $tree->{$key};
44 150 100       271 if ($value eq $Namespace)
45             {
46 6 50       22 next unless ($key =~ /^xmlns\:/);
47 6         11 delete $tree->{$key};
48 6         9 $Prefix = $key;
49 6         18 $Prefix =~ s/^xmlns\://;
50             }
51             }
52              
53             # set the current i18n:domain
54 31   66     121 $Domain = delete $tree->{"$Prefix:domain"} || $Domain;
55              
56 31         47 my $tag = $tree->{_tag};
57 31 100       34 my $attr = { map { /^_/ ? () : ( $_ => $tree->{$_} ) } keys %{$tree} };
  138         337  
  31         64  
58 31 100 66     152 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       82 $tree->{"$Prefix:attributes"} && do {
63 3         22 my $attributes = $tree->{"$Prefix:attributes"};
64 3         12 $attributes =~ s/\s*;\s*$//;
65 3         13 $attributes =~ s/^\s*//;
66 3         10 my @attributes = split /\s*\;\s*/, $attributes;
67 3         7 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         6 my ($attribute_name, $translate_id);
73 3 50       12 if ($attribute =~ /\s/)
74             {
75 3         14 ($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     5 my $value = eval { $Petal::TranslationService->maketext ($translate_id) } || $default_value;
94              
95             # if maketext() failed, let's know why.
96 3 100       70 $@ && warn $@;
97              
98             # set the (hopefully) translated value
99 3         15 $tree->{$attribute_name} = $value;
100             }
101             };
102              
103              
104             # replace content with its translation
105 27 100       73 exists $tree->{"$Prefix:translate"} && do {
106 9         10 my ($translate_id);
107              
108             # if we have $Domain:translate="something",
109             # then the translate_id is 'something'
110 9 100 66     59 if (defined $tree->{"$Prefix:translate"} and $tree->{"$Prefix:translate"} ne '')
111             {
112 8         15 $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         5 $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         28 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     13 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         503 my %named_nodes = _extract_named_nodes ($tree);
136 9         11 my @tokens = @{Petal::Hash::String->_tokenize (\$value)};
  9         59  
137             my @res = map {
138 9         22 ($_ =~ /$Petal::Hash::String::TOKEN_RE/gsm) ?
139             do {
140 5         23 s/^\$//;
141 5         16 s/^\{//;
142 5         19 s/\}$//;
143 5         25 $named_nodes{$_};
144             } :
145 16 100       146 do {
146 11         40 s/\\(.)/$1/gsm;
147 11         64 $_;
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         58 delete $tree->{"$Prefix:attributes"};
157 27         53 delete $tree->{"$Prefix:translate"};
158 27         40 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       57 defined $tree->{_content} and do {
164 27         30 for (@{$tree->{_content}}) { $class->_process ($_) }
  27         48  
  63         166  
165             };
166             }
167              
168              
169             sub _canonicalize
170             {
171 10     10   14 my $string = shift;
172 10 50       28 return '' unless (defined $string);
173              
174 10         59 $string =~ s/\s+/ /gsm;
175 10         26 $string =~ s/^ //;
176 10         22 $string =~ s/ $//;
177 10         36 return $string;
178             }
179              
180              
181             sub _extract_named_nodes
182             {
183 9     9   14 my $tree = shift;
184 9         14 my @nodes = ();
185 9         12 foreach my $node (@{$tree->{_content}})
  9         20  
186             {
187 19 100       40 ref $node || next;
188 5         15 push @nodes, $node;
189             }
190            
191 9         15 my %nodes = ();
192 9         11 my $count = 0;
193 9         15 foreach my $node (@nodes)
194             {
195 5         6 $count++;
196 5   66     21 my $name = $node->{"$Prefix:name"} || $count;
197 5         17 $nodes{$name} = $node;
198             }
199            
200 9         29 return %nodes;
201             }
202              
203              
204             sub _extract_content_string
205             {
206 10     10   16 my $tree = shift;
207 10         22 my @res = ();
208              
209 10         14 my $count = 0;
210 10         11 foreach my $node (@{$tree->{_content}})
  10         23  
211             {
212 24 100       41 ref $node or do {
213 17         34 push @res, $node;
214 17         26 next;
215             };
216            
217 7         11 $count++;
218 7   66     25 my $name = $node->{"$Prefix:name"} || $count;
219 7         19 push @res, '${' . $name . '}';
220             }
221            
222 10         41 return join '', @res;
223             }
224              
225              
226             1;
227              
228              
229             __END__