File Coverage

lib/I18NFool/Extractor.pm
Criterion Covered Total %
statement 92 97 94.8
branch 22 30 73.3
condition 15 31 48.3
subroutine 8 8 100.0
pod 0 1 0.0
total 137 167 82.0


line stmt bran cond sub pod time code
1             package I18NFool::Extractor;
2 5     5   26516 use MKDoc::XML::TreeBuilder;
  5         46167  
  5         166  
3 5     5   8152 use Locale::PO;
  5         37902  
  5         211  
4 5     5   65 use warnings;
  5         20  
  5         184  
5 5     5   28 use strict;
  5         9  
  5         9833  
6              
7             our $Namespace = "http://xml.zope.org/namespaces/i18n";
8             our $Prefix = 'i18n';
9             our $Domain = 'default';
10             our $Results = {};
11              
12              
13             sub process
14             {
15 6     6 0 76 my $class = shift;
16 6         15 my $data = shift;
17            
18 6         14 local $Namespace = $Namespace;
19 6         16 local $Prefix = $Prefix;
20 6         12 local $Domain = $Domain;
21 6         18 local $Results = {};
22            
23 6         168 my @nodes = MKDoc::XML::TreeBuilder->process_data ($data);
24 6         39755 for (@nodes) { $class->_process ($_) }
  6         41  
25 6         67 return $Results;
26             }
27              
28              
29             sub _process
30             {
31 65     65   89 my $class = shift;
32 65         83 my $tree = shift;
33 65 100       250 return unless (ref $tree);
34              
35 24         49 local $Prefix = $Prefix;
36 24         34 local $Domain = $Domain;
37              
38             # process the I18N namespace
39 24         28 foreach my $key (keys %{$tree})
  24         103  
40             {
41 133         187 my $value = $tree->{$key};
42 133 100       331 if ($value eq $Namespace)
43             {
44 9 50       53 next unless ($key =~ /^xmlns\:/);
45 9         24 delete $tree->{$key};
46 9         18 $Prefix = $key;
47 9         46 $Prefix =~ s/^xmlns\://;
48             }
49             }
50              
51             # set the current i18n:domain
52 24   66     154 $Domain = delete $tree->{"$Prefix:domain"} || $Domain;
53              
54 24         94 my $tag = $tree->{_tag};
55 24 100       29 my $attr = { map { /^_/ ? () : ( $_ => $tree->{$_} ) } keys %{$tree} };
  118         578  
  24         67  
56 24 50 33     276 return if ($tag eq '~comment' or $tag eq '~pi' or $tag eq '~declaration');
      33        
57            
58             # lookup for attributes...
59 24 100       229 $tree->{"$Prefix:attributes"} && do {
60 1         3 my $attributes = $tree->{"$Prefix:attributes"};
61 1         26 $attributes =~ s/\s*;\s*$//;
62 1         5 $attributes =~ s/^\s*//;
63 1         13 my @attributes = split /\s*\;\s*/, $attributes;
64 1         3 foreach my $attribute (@attributes)
65             {
66             # if we have i18n:attributes="alt alt_text", then the
67             # attribute name is 'alt' and the
68             # translate_id is 'alt_text'
69 4         6 my ($attribute_name, $translate_id);
70 4 50       46 if ($attribute =~ /\s/)
71             {
72 4         12 ($attribute_name, $translate_id) = split /\s+/, $attribute, 2;
73             }
74              
75             # otherwise, if we have i18n:attributes="alt", then the
76             # attribute name is 'alt' and the
77             # translate_id is $tree->{'alt'}
78             else
79             {
80 0         0 $attribute_name = $attribute;
81 0         0 $translate_id = _canonicalize ( $tree->{$attribute_name} );
82             }
83              
84 4 50       11 $translate_id || next;
85 4   100     15 $Results->{$Domain} ||= {};
86              
87 4         5 my $existing_po = $Results->{$Domain}->{$translate_id};
88 4   50     16 my $new_po = Locale::PO->new (
89             -msgid => $translate_id,
90             -msgstr => _canonicalize ( $tree->{$attribute_name} ) || '',
91             );
92              
93 4 50 33     216 if ($existing_po && ($existing_po->{msgstr} ne $new_po->{msgstr}))
94             {
95 0         0 print STDERR "String for '$translate_id' doesn't match:\n".
96             " old: $existing_po->{msgstr}\n".
97             " new: $new_po->{msgstr}\n"
98             }
99              
100 4         16 $Results->{$Domain}->{$translate_id} = $new_po;
101             }
102             };
103              
104             # lookup for content...
105 24 100       237 exists $tree->{"$Prefix:translate"} && do {
106 12         19 my ($translate_id);
107              
108             # if we have $Domain:translate="something",
109             # then the translate_id is 'something'
110 12 50 33     86 if (defined $tree->{"$Prefix:translate"} and $tree->{"$Prefix:translate"} ne '')
111             {
112 0         0 $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 12         34 $translate_id = _canonicalize ( _extract_content_string ($tree) );
122             }
123              
124 12 50       53 $translate_id || next;
125 12   100     86 $Results->{$Domain} ||= {};
126              
127 12         22 my $existing_po = $Results->{$Domain}->{$translate_id};
128 12   50     42 my $new_po = Locale::PO->new (
129             -msgid => $translate_id,
130             -msgstr => _canonicalize ( _extract_content_string ($tree) ) || '',
131             );
132              
133 12 50 33     725 if ($existing_po && ($existing_po->{msgstr} ne $new_po->{msgstr}))
134             {
135 0         0 print STDERR "String for '$translate_id' doesn't match:\n".
136             " old: $existing_po->{msgstr}\n".
137             " new: $new_po->{msgstr}\n"
138             }
139              
140 12         46 $Results->{$Domain}->{$translate_id} = $new_po;
141             };
142              
143             # I know, I know, the I18N namespace processing is a bit broken...
144             # It should suffice for now
145 24         55 delete $tree->{"$Prefix:attributes"};
146 24         51 delete $tree->{"$Prefix:translate"};
147 24         37 delete $tree->{"$Prefix:name"};
148              
149             # Do the same i18n thing with child nodes, recursively.
150             # for some reason it always makes me think of roller coasters.
151             # Yeeeeeeee!
152 24 100       71 defined $tree->{_content} and do {
153 23         91 for (@{$tree->{_content}}) { $class->_process ($_) }
  23         59  
  59         715  
154             };
155             }
156              
157              
158             sub _canonicalize
159             {
160 28   50 28   74 my $string = shift || '';
161 28         52 $string =~ s/\r/ /g;
162 28         39 $string =~ s/\n/ /g;
163 28         503 $string =~ s/\s+/ /gsm;
164 28         88 $string =~ s/^ //;
165 28         48 $string =~ s/ $//;
166 28         150 return $string;
167             }
168              
169              
170             sub _extract_content_string
171             {
172 24     24   31 my $tree = shift;
173 24         82 my @res = ();
174              
175 24         28 my $count = 0;
176 24         27 foreach my $node (@{$tree->{_content}})
  24         55  
177             {
178 28 100       68 ref $node or do {
179 26         39 push @res, $node;
180 26         56 next;
181             };
182            
183 2         2 $count++;
184 2   33     8 my $name = $node->{"$Prefix:name"} || $count;
185 2         5 push @res, '${' . $name . '}';
186             }
187            
188 24         116 return join '', @res;
189             }
190              
191              
192             1;