]> git.lyx.org Git - lyx.git/blob - po/pocheck.pl
listerrors.lyx : Update a link.
[lyx.git] / po / pocheck.pl
1 #! /usr/bin/perl -w
2
3 # file pocheck.pl
4 #
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7 #
8 # author: Michael Gerz, michael.gerz@teststep.org
9 #
10
11 use strict;
12 use warnings;
13 use Getopt::Std;
14
15 my $usage = <<EOT;
16 pocheck.pl [-acmpqst] po_file [po_file] ...
17
18 This script performs some consistency checks on po files. 
19
20 We check for everything listed here, unless one or more of these 
21 options is given, in which case we checks only for those requested.
22 -a: Check arguments, like %1\$s
23 -c: Check for colons at end
24 -m: Check for menu shortcuts
25 -p: Check for period at end
26 -q: Check Qt shortcuts
27 -s: Check for space at end
28 -t: Check for uniform translation
29 These options can be given with or without other options.
30 -f: Ignore fuzzy translations
31 -w: Only report summary total of errors
32 -i: Silent mode, report only errors
33 EOT
34
35 my %options;
36 getopts(":hacfmpqstwi", \%options);
37
38 if (defined($options{h})) { 
39   print $usage; 
40   exit 0; 
41 }
42
43 my $only_total = defined($options{w});
44 delete $options{w} if $only_total;
45 my $ignore_fuzzy = defined($options{f});
46 delete $options{f} if $ignore_fuzzy;
47 my $silent_mode = defined($options{i});
48 delete $options{i} if $silent_mode;
49
50 my $check_args = (!%options or defined($options{a}));
51 my $check_colons = (!%options or defined($options{c}));
52 my $check_spaces = (!%options or defined($options{m}));
53 my $check_periods = (!%options or defined($options{p}));
54 my $check_qt = (!%options or defined($options{q}));
55 my $check_menu = (!%options or defined($options{s}));
56 my $check_trans = (!%options or defined($options{t}));
57
58 my %trans;
59
60 foreach my $pofilename ( @ARGV ) {
61   my %bad;
62   if (!$silent_mode) {
63     print "Processing po file '$pofilename'...\n";
64   }
65
66   open( INPUT, "<$pofilename" )
67     || die "Cannot read po file '$pofilename'";
68   my @pofile = <INPUT>;
69   close( INPUT );
70
71   undef( %trans );
72   keys( %trans ) = 10000;
73
74   my $noOfLines = $#pofile;
75
76   my $warn = 0;
77
78   my $i = 0;
79   my ($msgid, $msgstr, $more);
80
81   while ($i <= $noOfLines) {
82     my $linenum = $i;
83     ( $msgid ) = ( $pofile[$i] =~ m/^msgid "(.*)"/ );
84     $i++;
85     next unless $msgid;
86     if ($ignore_fuzzy) {
87       my $previous = $pofile[$i - 2];
88       next if $previous =~ m/#,.*fuzzy/;
89     }
90     
91     # some msgid's are more than one line long, so add those.
92     while ( ( $more ) = $pofile[$i] =~ m/^"(.*)"/ ) {
93       $msgid = $msgid . $more;
94       $i++;
95     }
96     
97     # now look for the associated msgstr.
98     until ( ( $msgstr ) = ( $pofile[$i] =~ m/^msgstr "(.*)"/ ) ) { $i++; };
99     $i++;
100     # again collect any extra lines.
101     while ( ( $i <= $noOfLines ) &&
102             ( ( $more ) = $pofile[$i] =~ m/^"(.*)"/ ) ) {
103       $msgstr = $msgstr . $more;
104       $i++;
105     }
106
107     # nothing to do if one of them is empty. 
108     # (surely that is always $msgstr?)
109     next if ($msgid eq "" or $msgstr eq "");
110
111     # Check for matching %1$s, etc.
112       if ($check_args) {
113       my @argstrs = ( $msgid =~ m/%(\d)\$s/g );
114       if (@argstrs) {
115         my $n = 0;
116         foreach my $arg (@argstrs) { $n = $arg if $arg > $n; }
117         if ($n <= 0) { 
118           print "$pofilename, line $linenum: Problem finding arguments in:\n    $msgid!\n"
119             unless $only_total;
120           ++$bad{"Missing arguments"};
121           $warn++;
122         } else {
123           foreach my $i (1..$n) {
124             my $arg = "%$i\\\$s"; 
125             if ( $msgstr !~ m/$arg/ ) {
126               print "$pofilename, line $linenum: Missing argument `$arg'\n  '$msgid' ==> '$msgstr'\n"
127                 unless $only_total;
128               ++$bad{"Missing arguments"};
129               $warn++;
130             }
131           }
132         }
133       }
134     }
135
136     if ($check_colons) {
137       # Check colon at the end of a message
138       if ( ( $msgid =~ m/: *(\|.*)?$/ ) != ( $msgstr =~ m/: *(\|.*)?$/ ) ) {
139         print "Line $linenum: Missing or unexpected colon:\n  '$msgid' => '$msgstr'\n"
140           unless $only_total;
141         ++$bad{"Bad colons"};
142         $warn++;
143       }
144     }
145
146     if ($check_periods) {
147       # Check period at the end of a message; uncomment code if you are paranoid
148       if ( ( $msgid =~ m/\. *(\|.*)?$/ ) != ( $msgstr =~ m/\. *(\|.*)?$/ ) ) {
149        print "Line $linenum: Missing or unexpected period:\n  '$msgid' => '$msgstr'\n"
150         unless $only_total;
151       ++$bad{"Bad periods"};
152        $warn++;
153       }
154     }
155
156     if ($check_spaces) {
157       # Check space at the end of a message
158       if ( ( $msgid =~ m/  *?(\|.*)?$/ ) != ( $msgstr =~ m/  *?(\|.*)?$/ ) ) {
159         print "Line $linenum: Missing or unexpected space:\n  '$msgid' => '$msgstr'\n"
160           unless $only_total;
161         ++$bad{"Bad spaces"};
162         $warn++;
163       }
164     }
165
166     if ($check_qt) {
167       # Check for "&" shortcuts
168       if ( ( $msgid =~ m/&[^ ]/ ) != ( $msgstr =~ m/&[^ ]/ ) ) {
169         print "Line $linenum: Missing or unexpected Qt shortcut:\n  '$msgid' => '$msgstr'\n"
170           unless $only_total;
171         ++$bad{"Bad Qt shortcuts"};
172         $warn++;
173       }
174     }
175
176     if ($check_menu) {
177       # Check for "|..." shortcuts
178       if ( ( $msgid =~ m/\|[^ ]/ ) != ( $msgstr =~ m/\|[^ ]/ ) ) {
179         print "Line $linenum: Missing or unexpected menu shortcut:\n  '$msgid' => '$msgstr'\n"
180           unless $only_total;
181         ++$bad{"Bad menu shortcuts"};
182         $warn++;
183       }
184     }
185     
186     next unless $check_trans;
187     
188     # we now collect these translations in a hash.
189     # this will allow us to check below if we have translated
190     # anything more than one way.
191     my $msgid_clean  = lc($msgid);
192     my $msgstr_clean = lc($msgstr);
193
194     $msgid_clean  =~ s/(.*)\|.*?$/$1/;  # strip menu shortcuts
195     $msgstr_clean =~ s/(.*)\|.*?$/$1/;
196     $msgid_clean  =~ s/&([^ ])/$1/;     # strip Qt shortcuts
197     $msgstr_clean =~ s/&([^ ])/$1/;
198
199     # this is a hash of hashes. the keys of the outer hash are
200     # cleaned versions of ORIGINAL strings. the keys of the inner hash 
201     # are the cleaned versions of their TRANSLATIONS. The value for the 
202     # inner hash is an array of the orignal string and translation.
203     $trans{$msgid_clean}{$msgstr_clean} = [ $msgid, $msgstr, $linenum ];
204   }
205
206   if ($check_trans) {
207     foreach $msgid ( keys %trans ) {
208       # so $ref is a reference to the inner hash.
209       my $ref = $trans{$msgid};
210       # @msgstrkeys is an array of the keys of that inner hash.
211       my @msgstrkeys = keys %$ref;
212
213       # do we have more than one such key?
214       if ( $#msgstrkeys > 0 ) {
215         if (!$only_total) {
216           print "Different translations for '$msgid':\n";
217           foreach $msgstr ( @msgstrkeys ) {
218             print "Line $ref->{$msgstr}[2]: '" . 
219               $ref->{$msgstr}[0] . "' => '" . 
220               $ref->{$msgstr}[1] . "'\n";
221           }
222         }
223         ++$bad{"Inconsistent translations"};
224         $warn++;
225       }
226     }
227   }
228   if (!$silent_mode) {
229     if ($warn) {
230       while (my ($k, $v) = each %bad) { print "$k: $v\n"; }
231       if (scalar(keys %bad) > 1) {
232         print "Total warnings: $warn\n";
233       }
234     } else {
235       print "No warnings!\n";
236     }
237     print "\n";
238   }
239 }