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