| OLD | NEW |
| (Empty) |
| 1 # This Source Code Form is subject to the terms of the Mozilla Public | |
| 2 # License, v. 2.0. If a copy of the MPL was not distributed with this | |
| 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| 4 | |
| 5 package LocaleTester; | |
| 6 | |
| 7 use strict; | |
| 8 use warnings; | |
| 9 | |
| 10 my %keepAccessKeys = map {$_ => $_} ( | |
| 11 'ja-JP', | |
| 12 'ja', | |
| 13 'ko-KR', | |
| 14 'ko', | |
| 15 'zh-CN', | |
| 16 'zh-TW', | |
| 17 ); | |
| 18 | |
| 19 my @placeholders = ( | |
| 20 '?1?', | |
| 21 '?2?', | |
| 22 '?3?', | |
| 23 '?4?', | |
| 24 '?5?', | |
| 25 '?6?', | |
| 26 '?7?', | |
| 27 '?8?', | |
| 28 '?9?', | |
| 29 '--', | |
| 30 '%S', | |
| 31 '[link]', | |
| 32 '[/link]', | |
| 33 ); | |
| 34 | |
| 35 sub testLocales | |
| 36 { | |
| 37 my %params = @_; | |
| 38 die "Need at least one locale path to work on" unless exists($params{paths}) &
& %{$params{paths}}; | |
| 39 $params{mustDiffer} = [] unless exists($params{mustDiffer}); | |
| 40 $params{mustEqual} = [] unless exists($params{mustEqual}); | |
| 41 $params{ignoreUntranslated} = [] unless exists($params{ignoreUntranslated}); | |
| 42 $params{lengthRestrictions} = {} unless exists($params{lengthRestrictions}); | |
| 43 | |
| 44 my @locales = sort {$a cmp $b} (exists($params{locales}) && @{$params{locales}
} ? @{$params{locales}} : makeLocaleList($params{paths})); | |
| 45 | |
| 46 my $referenceLocale = readLocaleFiles($params{paths}, "en-US"); | |
| 47 | |
| 48 foreach my $locale (@locales) | |
| 49 { | |
| 50 my $currentLocale = $locale eq "en-US" ? $referenceLocale : readLocaleFiles(
$params{paths}, $locale); | |
| 51 | |
| 52 compareLocales($locale, $currentLocale, $referenceLocale) unless $currentLoc
ale == $referenceLocale; | |
| 53 | |
| 54 foreach my $entry (@{$params{mustDiffer}}) | |
| 55 { | |
| 56 my %values = (); | |
| 57 foreach my $key (@$entry) | |
| 58 { | |
| 59 my ($dir, $file, $name) = split(/:/, $key); | |
| 60 next unless exists($currentLocale->{"$dir:$file"}) && exists($currentLoc
ale->{"$dir:$file"}{$name}) && $currentLocale->{"$dir:$file"}{$name} =~ /\S/; | |
| 61 my $value = lc($currentLocale->{"$dir:$file"}{$name}); | |
| 62 | |
| 63 print "$locale: Values for '$values{$value}' and '$key' are identical, m
ust differ\n" if exists $values{$value}; | |
| 64 $values{$value} = $key; | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 foreach my $entry (@{$params{mustEqual}}) | |
| 69 { | |
| 70 my $stdValue; | |
| 71 my $stdName; | |
| 72 foreach my $key (@$entry) | |
| 73 { | |
| 74 my ($dir, $file, $name) = split(/:/, $key); | |
| 75 next unless exists($currentLocale->{"$dir:$file"}) && exists($currentLoc
ale->{"$dir:$file"}{$name}); | |
| 76 my $value = lc($currentLocale->{"$dir:$file"}{$name}); | |
| 77 | |
| 78 $stdValue = $value unless defined $stdValue; | |
| 79 $stdName = $key unless defined $stdName; | |
| 80 print "$locale: Values for '$stdName' and '$key' differ, must be equal\n
" if $value ne $stdValue; | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 foreach my $key (keys %{$params{lengthRestrictions}}) | |
| 85 { | |
| 86 my $maxLength = $params{lengthRestrictions}{$key}; | |
| 87 my ($dir, $file, $name) = split(/:/, $key); | |
| 88 print "$locale: Value of '$key' is too long, must not be longer than $maxL
ength characters\n" if exists($currentLocale->{"$dir:$file"}) && exists($current
Locale->{"$dir:$file"}{$name}) && length($currentLocale->{"$dir:$file"}{$name})
> $maxLength; | |
| 89 } | |
| 90 | |
| 91 foreach my $file (keys %$currentLocale) | |
| 92 { | |
| 93 my $fileData = $currentLocale->{$file}; | |
| 94 foreach my $key (keys %$fileData) | |
| 95 { | |
| 96 if (($key =~ /\.accesskey$/ || $key =~ /\.key$/) && length($fileData->{$
key}) != 1) | |
| 97 { | |
| 98 print "$locale: Length of accesskey '$file:$key' isn't 1 character\n"; | |
| 99 } | |
| 100 | |
| 101 if ($key =~ /\.accesskey$/) | |
| 102 { | |
| 103 if (exists($keepAccessKeys{$locale})) | |
| 104 { | |
| 105 if (exists($referenceLocale->{$file}{$key}) && lc($fileData->{$key})
ne lc($referenceLocale->{$file}{$key})) | |
| 106 { | |
| 107 print "$locale: Accesskey '$file:$key' should be the same as in th
e reference locale\n"; | |
| 108 } | |
| 109 } | |
| 110 else | |
| 111 { | |
| 112 my $labelKey = $key; | |
| 113 $labelKey =~ s/\.accesskey$/.label/; | |
| 114 if (exists($fileData->{$labelKey}) && $fileData->{$labelKey} !~ /\Q$
fileData->{$key}/i) | |
| 115 { | |
| 116 print "$locale: Accesskey '$file:$key' not found in the correspond
ing label '$file:$labelKey'\n"; | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 if ($currentLocale != $referenceLocale && $locale ne "en-GB" && exists($
referenceLocale->{$file}{$key}) && length($fileData->{$key}) > 1 && $fileData->{
$key} eq $referenceLocale->{$file}{$key}) | |
| 122 { | |
| 123 my $ignore = 0; | |
| 124 foreach my $re (@{$params{ignoreUntranslated}}) | |
| 125 { | |
| 126 $ignore = 1 if "$file:$key" =~ $re; | |
| 127 } | |
| 128 print "$locale: Value of '$file:$key' is the same as in the reference
locale, probably an untranslated string\n" unless $ignore; | |
| 129 } | |
| 130 | |
| 131 if ($currentLocale != $referenceLocale && exists($referenceLocale->{$fil
e}{$key})) | |
| 132 { | |
| 133 foreach my $placeholder (@placeholders) | |
| 134 { | |
| 135 print "$locale: Placeholder '$placeholder' missing in '$file:$key'\n
" if index($referenceLocale->{$file}{$key}, $placeholder) >= 0 && index($current
Locale->{$file}{$key}, $placeholder) < 0; | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 } | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 sub makeLocaleList | |
| 144 { | |
| 145 my $paths = shift; | |
| 146 | |
| 147 my %locales = (); | |
| 148 foreach my $dir (keys %$paths) | |
| 149 { | |
| 150 opendir(local* DIR, $paths->{$dir}) or next; | |
| 151 my @locales = grep {!/[^\w\-]/ && !-e("$paths->{$dir}/$_/.incomplete")} read
dir(DIR); | |
| 152 $locales{$_} = 1 foreach @locales; | |
| 153 closedir(DIR); | |
| 154 } | |
| 155 return keys %locales; | |
| 156 } | |
| 157 | |
| 158 sub readFile | |
| 159 { | |
| 160 my $file = shift; | |
| 161 | |
| 162 open(local *FILE, "<", $file) || die "Could not read file '$file'"; | |
| 163 binmode(FILE); | |
| 164 local $/; | |
| 165 my $result = <FILE>; | |
| 166 close(FILE); | |
| 167 | |
| 168 print "Byte Order Mark found in file '$file'\n" if $result =~ /\xEF\xBB\xBF/; | |
| 169 print "File '$file' is not valid UTF-8\n" unless (utf8::decode($result)); | |
| 170 | |
| 171 return $result; | |
| 172 } | |
| 173 | |
| 174 sub parseDTDFile | |
| 175 { | |
| 176 my $file = shift; | |
| 177 | |
| 178 my %result = (); | |
| 179 | |
| 180 my $data = readFile($file); | |
| 181 | |
| 182 my $S = qr/[\x20\x09\x0D\x0A]/; | |
| 183 my $Name = qr/[A-Za-z_:][\w.\-:]*/; | |
| 184 my $Reference = qr/&$Name;|&#\d+;|&#x[\da-fA-F]+;/; | |
| 185 my $PEReference = qr/%$Name;/; | |
| 186 my $EntityValue = qr/\"((?:[^%&\"]|$PEReference|$Reference)*)\"|'((?:[^%&']|$P
EReference|$Reference)*)'/; | |
| 187 | |
| 188 sub processEntityValue | |
| 189 { | |
| 190 my $text = shift; | |
| 191 $text =~ s/&#(\d+);/chr($1)/ge; | |
| 192 $text =~ s/&#x([\da-fA-F]+);/chr(hex($1))/ge; | |
| 193 $text =~ s/'/'/g; | |
| 194 return $text; | |
| 195 } | |
| 196 | |
| 197 # Remove comments | |
| 198 $data =~ s/<!--([^\-]|-[^\-])*-->//gs; | |
| 199 | |
| 200 # Process entities | |
| 201 while ($data =~ /<!ENTITY$S+($Name)$S+$EntityValue$S*>/gs) | |
| 202 { | |
| 203 my ($name, $value) = ($1, $2 || $3); | |
| 204 $result{$name} = processEntityValue($value); | |
| 205 } | |
| 206 | |
| 207 # Remove entities | |
| 208 $data =~ s/<!ENTITY$S+$Name$S+$EntityValue$S*>//gs; | |
| 209 | |
| 210 # Remove spaces | |
| 211 $data =~ s/^\s+//gs; | |
| 212 $data =~ s/\s+$//gs; | |
| 213 $data =~ s/\s+/ /gs; | |
| 214 | |
| 215 print "Unrecognized data in file '$file': $data\n" if $data ne ''; | |
| 216 | |
| 217 return \%result; | |
| 218 } | |
| 219 | |
| 220 sub parsePropertiesFile | |
| 221 { | |
| 222 my $file = shift; | |
| 223 | |
| 224 my %result = (); | |
| 225 | |
| 226 my $data = readFile($file); | |
| 227 while ($data =~ /^(.*)$/mg) | |
| 228 { | |
| 229 my $line = $1; | |
| 230 | |
| 231 # ignore comments | |
| 232 next if $line =~ /^\s*[#!]/; | |
| 233 | |
| 234 if ($line =~ /=/) | |
| 235 { | |
| 236 my ($key, $value) = split(/=/, $line, 2); | |
| 237 $result{$key} = $value; | |
| 238 } | |
| 239 elsif ($line =~ /\S/) | |
| 240 { | |
| 241 print "Unrecognized data in file '$file': $line\n"; | |
| 242 } | |
| 243 } | |
| 244 close(FILE); | |
| 245 | |
| 246 return \%result; | |
| 247 } | |
| 248 | |
| 249 sub readLocaleFiles | |
| 250 { | |
| 251 my $paths = shift; | |
| 252 my $locale = shift; | |
| 253 | |
| 254 my %result = (); | |
| 255 foreach my $dir (keys %$paths) | |
| 256 { | |
| 257 opendir(local *DIR, "$paths->{$dir}/$locale") or next; | |
| 258 foreach my $file (readdir(DIR)) | |
| 259 { | |
| 260 if ($file =~ /(.*)\.dtd$/) | |
| 261 { | |
| 262 $result{"$dir:$1"} = parseDTDFile("$paths->{$dir}/$locale/$file"); | |
| 263 } | |
| 264 elsif ($file =~ /(.*)\.properties$/) | |
| 265 { | |
| 266 $result{"$dir:$1"} = parsePropertiesFile("$paths->{$dir}/$locale/$file")
; | |
| 267 } | |
| 268 } | |
| 269 closedir(DIR); | |
| 270 } | |
| 271 | |
| 272 return \%result; | |
| 273 } | |
| 274 | |
| 275 sub compareLocales | |
| 276 { | |
| 277 my ($locale, $current, $reference) = @_; | |
| 278 | |
| 279 my %hasFile = (); | |
| 280 foreach my $file (keys %$current) | |
| 281 { | |
| 282 unless (exists($reference->{$file})) | |
| 283 { | |
| 284 print "$locale: Extra file '$file'\n"; | |
| 285 next; | |
| 286 } | |
| 287 $hasFile{$file} = 1; | |
| 288 | |
| 289 my %hasValue = (); | |
| 290 foreach my $key (keys %{$current->{$file}}) | |
| 291 { | |
| 292 unless (exists($reference->{$file}{$key})) | |
| 293 { | |
| 294 print "$locale: Extra value '$file:$key'\n"; | |
| 295 next; | |
| 296 } | |
| 297 $hasValue{$key} = 1; | |
| 298 } | |
| 299 | |
| 300 foreach my $key (keys %{$reference->{$file}}) | |
| 301 { | |
| 302 unless (exists($current->{$file}{$key})) | |
| 303 { | |
| 304 print "$locale: Missing value '$file:$key'\n"; | |
| 305 next; | |
| 306 } | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 foreach my $file (keys %$reference) | |
| 311 { | |
| 312 unless (exists($current->{$file})) | |
| 313 { | |
| 314 print "$locale: Missing file '$file'\n"; | |
| 315 next; | |
| 316 } | |
| 317 } | |
| 318 } | |
| 319 | |
| 320 1; | |
| OLD | NEW |