1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
#! /usr/bin/perl -w
# file pocheck.pl
#
# This file is part of LyX, the document processor.
# Licence details can be found in the file COPYING.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# The GNU General Public License version 2 is included below for your reference.
# author: Michael Gerz, michael.gerz@teststep.org
# update by Sylvestre Ledru <sylvestre.ledru@scilab.org> 2011 - Integration
# of Scilab string + automatic correction of the missing quotes.
use strict;
use warnings;
use Getopt::Std;
use File::Copy;
my $usage = <<EOT;
pocheck.pl [-acmpqstzx] po_file [po_file] ...
This script performs some consistency checks on po files.
We check for everything listed here, unless one or more of these
options is given, in which case we checks only for those requested.
-a: Check arguments, like %1\$s
-c: Check for colons at end
-m: Check for menu shortcuts
-p: Check for period at end
-q: Check Qt shortcuts
-s: Check for space at end
-t: Check for uniform translation
-n: Check for Scilab macros error
-x: Automagically fix Scilab macros quote errors
These options can be given with or without other options.
-f: Ignore fuzzy translations
-w: Only report summary total of errors
-i: Silent mode, report only errors
EOT
my %options;
getopts(":hacfmpqstnwix", \%options);
if (defined($options{h})) {
print $usage;
exit 0;
}
my $only_total = defined($options{w});
delete $options{w} if $only_total;
my $ignore_fuzzy = defined($options{f});
delete $options{f} if $ignore_fuzzy;
my $silent_mode = defined($options{i});
delete $options{i} if $silent_mode;
my $check_args = (!%options or defined($options{a}));
my $check_colons = (!%options or defined($options{c}));
my $check_spaces = (!%options or defined($options{m}));
my $check_periods = (!%options or defined($options{p}));
my $check_qt = (!%options or defined($options{q}));
my $check_menu = (!%options or defined($options{s}));
my $check_trans = (!%options or defined($options{t}));
# Fix Scilab code implies the detection
my $check_scilab_macros = (!%options or defined($options{n}) or defined($options{x}));
my $fix_scilab_quote = (!%options or defined($options{x}));
my %trans;
my $global_warn = 0;
sub replaceInFile {
my $pofilename=$_[0];
my $kindofreplace=$_[1];
open( INPUT, "<$pofilename" )
|| die "Cannot read po file '$pofilename'";
open(OUTPUTFILE, ">$pofilename.tmp" );
while (<INPUT>) {
if ($kindofreplace eq "single") {
$_ =~ s/^msgstr "(.*)([^'])'([^'])(.*)"/msgstr "$1$2''$3$4"/g;
$_ =~ s/^"(.*)([^'])'([^'])(.*)"/"$1$2''$3$4"/g;
} else {
if ($kindofreplace eq "double") {
$_ =~ s/^msgstr "(.*)([^"])"([^"])(.*)"/msgstr "$1$2""$3$4"/g;
$_ =~ s/^"(.*)([^"])"([^"])(.*)"/"$1$2""$3$4"/g;
#$_ =~ s/^msgstr "([^"])"([^"])"/msgstr "$1""$2"/g;
# $_ =~ s/^"([^"])"([^"])"/"$1""$2"/g;
} else {
$_ =~ s/^msgstr "(.*)\\"(.*)"/msgstr "$1"$2"/g;
$_ =~ s/^"(.*)\\"(.*)"/"$1"$2"/g;
}
}
print OUTPUTFILE $_;
}
close( INPUT );
close( OUTPUTFILE );
copy("$pofilename.tmp",$pofilename);
unlink("$pofilename.tmp");
}
foreach my $pofilename ( @ARGV ) {
my %bad;
if (!$silent_mode) {
print "Processing po file '$pofilename'...\n";
}
open( INPUT, "<$pofilename" )
|| die "Cannot read po file '$pofilename'";
my @pofile = <INPUT>;
close( INPUT );
undef( %trans );
keys( %trans ) = 10000;
my $noOfLines = $#pofile;
my $warn = 0;
my $i = 0;
my ($msgid, $msgstr, $more);
while ($i <= $noOfLines) {
my $linenum = $i;
( $msgid ) = ( $pofile[$i] =~ m/^msgid "(.*)"/ );
$i++;
next unless $msgid;
if ($ignore_fuzzy) {
my $previous = $pofile[$i - 2];
next if $previous =~ m/#,.*fuzzy/;
}
# some msgid's are more than one line long, so add those.
while ( ( $more ) = $pofile[$i] =~ m/^"(.*)"/ ) {
$msgid = $msgid . $more;
$i++;
}
# now look for the associated msgstr.
until ( ( $msgstr ) = ( $pofile[$i] =~ m/^msgstr "(.*)"/ ) ) { $i++; };
$i++;
# again collect any extra lines.
while ( ( $i <= $noOfLines ) &&
( ( $more ) = $pofile[$i] =~ m/^"(.*)"/ ) ) {
$msgstr = $msgstr . $more;
$i++;
}
# nothing to do if one of them is empty.
# (surely that is always $msgstr?)
next if ($msgid eq "" or $msgstr eq "");
# Check for matching %1$s, etc.
if ($check_args) {
my @argstrs = ( $msgid =~ m/%(\d)\$s/g );
if (@argstrs) {
my $n = 0;
foreach my $arg (@argstrs) { $n = $arg if $arg > $n; }
if ($n <= 0) {
print "$pofilename, line $linenum: Problem finding arguments in:\n $msgid!\n"
unless $only_total;
++$bad{"Missing arguments"};
$warn++;
} else {
foreach my $i (1..$n) {
my $arg = "%$i\\\$s";
if ( $msgstr !~ m/$arg/ ) {
print "$pofilename, line $linenum: Missing argument `$arg'\n '$msgid' ==> '$msgstr'\n"
unless $only_total;
++$bad{"Missing arguments"};
$warn++;
}
}
}
}
}
if ($check_colons) {
# Check colon at the end of a message
if ( ( $msgid =~ m/: *(\|.*)?$/ ) != ( $msgstr =~ m/: *(\|.*)?$/ ) ) {
print "Line $linenum: Missing or unexpected colon:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Bad colons"};
$warn++;
}
}
if ($check_periods) {
# Check period at the end of a message; uncomment code if you are paranoid
if ( ( $msgid =~ m/\. *(\|.*)?$/ ) != ( $msgstr =~ m/\. *(\|.*)?$/ ) ) {
print "Line $linenum: Missing or unexpected period:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Bad periods"};
$warn++;
}
}
if ($check_spaces) {
# Check space at the end of a message
if ( ( $msgid =~ m/ *?(\|.*)?$/ ) != ( $msgstr =~ m/ *?(\|.*)?$/ ) ) {
print "Line $linenum: Missing or unexpected space:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Bad spaces"};
$warn++;
}
}
if ($check_qt) {
# Check for "&" shortcuts
if ( ( $msgid =~ m/&[^ ]/ ) != ( $msgstr =~ m/&[^ ]/ ) ) {
print "Line $linenum: Missing or unexpected Qt shortcut:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Bad Qt shortcuts"};
$warn++;
}
}
if ($check_scilab_macros) {
if ( ( $msgstr =~ /\\"/ ) ) {
print "Line $linenum: Useless \\\":\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Scilab error in \\\" (should be a double quote instead)"};
$warn++;
if ($fix_scilab_quote) {
replaceInFile($pofilename,"backslash");
}
}
# Check for single single/double quote in the string
if ( ( $msgstr =~ m/[^']'[^']/ ) ) {
print "Line $linenum: Missing single quote:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Scilab error in single quote (should be doubled)"};
$warn++;
if ($fix_scilab_quote) {
replaceInFile($pofilename,"single");
}
}
if ( ( $msgstr =~ m/[^"]"[^"]/ ) ) {
print "Line $linenum: Missing double quote:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Scilab error in double quote (should be doubled)"};
$warn++;
if ($fix_scilab_quote) {
replaceInFile($pofilename,"double");
}
}
}
if ($check_menu) {
# Check for "|..." shortcuts
if ( ( $msgid =~ m/\|[^ ]/ ) != ( $msgstr =~ m/\|[^ ]/ ) ) {
print "Line $linenum: Missing or unexpected menu shortcut:\n '$msgid' => '$msgstr'\n"
unless $only_total;
++$bad{"Bad menu shortcuts"};
$warn++;
}
}
next unless $check_trans;
# we now collect these translations in a hash.
# this will allow us to check below if we have translated
# anything more than one way.
my $msgid_clean = lc($msgid);
my $msgstr_clean = lc($msgstr);
$msgid_clean =~ s/(.*)\|.*?$/$1/; # strip menu shortcuts
$msgstr_clean =~ s/(.*)\|.*?$/$1/;
$msgid_clean =~ s/&([^ ])/$1/; # strip Qt shortcuts
$msgstr_clean =~ s/&([^ ])/$1/;
# this is a hash of hashes. the keys of the outer hash are
# cleaned versions of ORIGINAL strings. the keys of the inner hash
# are the cleaned versions of their TRANSLATIONS. The value for the
# inner hash is an array of the orignal string and translation.
$trans{$msgid_clean}{$msgstr_clean} = [ $msgid, $msgstr, $linenum ];
}
if ($check_trans) {
foreach $msgid ( keys %trans ) {
# so $ref is a reference to the inner hash.
my $ref = $trans{$msgid};
# @msgstrkeys is an array of the keys of that inner hash.
my @msgstrkeys = keys %$ref;
# do we have more than one such key?
if ( $#msgstrkeys > 0 ) {
if (!$only_total) {
print "Different translations for '$msgid':\n";
foreach $msgstr ( @msgstrkeys ) {
print "Line $ref->{$msgstr}[2]: '" .
$ref->{$msgstr}[0] . "' => '" .
$ref->{$msgstr}[1] . "'\n";
}
}
++$bad{"Inconsistent translations"};
$warn++;
}
}
}
if (!$silent_mode) {
if ($warn) {
$global_warn += $warn;
while (my ($k, $v) = each %bad) { print "$k: $v\n"; }
if (scalar(keys %bad) > 1) {
print "Total warnings: $warn\n";
}
} else {
print "No warnings!\n";
}
print "\n";
}
}
print "Warnings on all files: $global_warn\n";
|