/[webwml]/webwml/check_trans.pl
ViewVC logotype

Contents of /webwml/check_trans.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.80 - (show annotations) (download)
Sat Oct 4 17:02:45 2008 UTC (4 years, 7 months ago) by bas
Branch: MAIN
Changes since 1.79: +1 -1 lines
File MIME type: text/plain
typo
1 #!/usr/bin/perl
2 #
3 # This is a little utility designed to keep track of translations
4 # in the Debian web site Subversion repository.
5 #
6 ## For information about translation revisions please see
7 ## http://www.debian.org/devel/website/uptodate
8 #
9 # Copyright (C) 2008 Bas Zoetekouw <bas@debian.org>
10 # Based on on code from:
11 # Copyright (C) 1998 Paolo Molaro <lupus@debian.org>
12 # Copyright (C) 1999-2003 Peter Karlsson <peterk@debian.org>
13 # Copyright (C) 2000,2001 Martin Quinson <mquinson@ens-lyon.fr>
14 #
15 # This program is free software; you can redistribute it and/or modify
16 # it under the terms of version 2 of the GNU General Public License as
17 # published by the Free Software Foundation.
18 #
19 ## This program is distributed in the hope that it will be useful, but
20 ## WITHOUT ANY WARRANTY; without even the implied warranty of
21 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 ## General Public License for more details.
23 #
24 #
25 # Invocation:
26 # check_trans.pl [-dlvqQ] [-C dir] [-p pattern] [-s subtree]
27 # [-m email -n N] [-c charset] [-g] [-t outputtype]
28 # [language]
29 #
30 # It needs to be run from the top level webwml directory.
31 # If you don't specify a language on the command line, the language name
32 # will be loaded from a file called language.conf, if such a file exists.
33 #
34 # For example:
35 # $ check_trans.pl -v italian
36 # You may also check only some subtrees as in:
37 # $ check_trans.pl -s devel italian
38 #
39 # Options:
40 # -Q be really quiet (only show errors/warnings on stderr)
41 # -q just don't whine about missing files
42 # -v show the status of all files (verbose)
43 # -V output what we're doing (very verbose)
44 # -d output diffs
45 # -l output log messages
46 # -T output translated diffs
47 # -p <pattern> include only files matching <pattern>,
48 # default is *.src|*.wml
49 # -s <subtree> check only that subtree
50 # -a output age of translation (if older than 2 months)
51 #
52 # Options useful when sending mails:
53 # -m <email> sends mails to translation maintainers
54 # PLEASE CAREFULLY READ THE BELOW TEXT ABOUT MAKING MAILS!
55 # <email> is the default recipient
56 # (it should be the list used for organisation,
57 # e.g. debian-l10n-french@lists.debian.org)
58 # -g debuG mail send process
59 # -n <1|2|3> send mails of priority upper or equal to
60 # 1 (monthly), 2 (weekly) or 3 (daily)
61 #
62 # GENERATING EMAILS
63 # If you want to, this script send mails to the maintainer of the mails.
64 # BEWARE, SOME PEOPLE DO NOT LIKE TO RECEIVE AUTOMATIC MAILS!
65 #
66 # PREREQUISITES:
67 # You will need one database:
68 # - one in which to get info about translators and the frequency at
69 # which they want to get mails. It must be named
70 # webwml/$langto/international/$langto/translator.db.pl
71 # Please refer to the French one for more info.
72 #
73 # USAGE:
74 # If you give the "-g" option, all mails are sent to the default addressee
75 # (i.e. the one given as value to the -m option), without respect to their
76 # normal addressee. It is useful if you want to run it for yourself,
77 # and for debugging.
78 # Without that option, it sends real mails to real addresses.
79 # MAKE SURE THE ADDRESSEES REALLY WANT TO GET THESE MAILS
80
81 use Getopt::Std;
82 use File::Basename;
83 use File::Spec::Functions;
84 use Term::ANSIColor;
85 use Encode;
86 #use Data::Dumper;
87 use FindBin;
88 FindBin::again();
89
90 # These modules reside under webwml/Perl
91 use lib "$FindBin::Bin/Perl";
92 use Local::VCS ':all';
93 use Local::Util qw/ uniq read_file /;
94 use Local::WmlDiffTrans;
95 use Webwml::TransCheck;
96 use Webwml::TransIgnore;
97
98 use strict;
99 use warnings;
100
101
102
103 # misc hardcoded things
104 my $MY_EMAIL = q{Debian WWW translation watch <debian-www@lists.debian.org>};
105 my $DEFAULT_PATTERN = '(?:\.wml|\.src)$';
106
107 # global variable to record verbosity
108 my $VERBOSE = 0;
109
110 # status codes
111 use constant {
112 ST_MISSING => 1,
113 ST_NEEDSUPDATE => 3,
114 ST_UPTODATE => 4,
115 ST_NOTATRANSL => 5,
116 ST_BROKEN => 6,
117 ST_OBSOLETE => 7,
118 ST_UNDEFINED => 8,
119 };
120
121 # how to colour each different status
122 my %COLOURS = (
123 main::ST_MISSING => 'magenta',
124 main::ST_NEEDSUPDATE => 'blue',
125 main::ST_UPTODATE => 'green',
126 main::ST_NOTATRANSL => 'yellow',
127 main::ST_BROKEN => 'red',
128 main::ST_OBSOLETE => 'red',
129 main::ST_UNDEFINED => 'red',
130 'warn' => 'bold red',
131 );
132
133 # these is called in "main" so need to be declared here
134 sub switch_var(\$\$);
135 sub verbose;
136
137 #=================================================
138 #== "main"
139 #==
140 {
141 my ($language,$file_pattern,%OPT) = parse_cmdargs();
142 my %translators = read_translators( $language, $OPT{m} );
143 my %emails_to_send;
144
145 # -s allows the user to restrict processing to a subtree
146 my $english_path = 'english';
147 my $language_path = $language;
148
149 my $subdir = $OPT{'s'} || undef;
150
151 # Global .transignore
152 my $transignore = Webwml::TransIgnore->new( vcs_get_topdir );
153
154 # first get a list with revision information from all files in english...
155 my %english_revs = vcs_path_info( $english_path,
156 'recursive' => 1,
157 'match_pat' => $file_pattern,
158 );
159 # ... and the translation
160 my %translation_revs = vcs_path_info( $language_path,
161 'recursive' => 1,
162 'match_pat' => $file_pattern,
163 );
164
165 # construct a list with all files that either occur in english or
166 # in the translation
167 my @files = uniq ( keys %english_revs, keys %translation_revs );
168
169
170 # now check each of the files
171 foreach my $file (sort @files)
172 {
173 # ignore this file?
174 next if $transignore->is_global( $file );
175 next if $subdir and not $file =~ m{^$subdir};
176
177 # note: $language is the name of the current language we're
178 # processing, whereas $transl is the name of the language which the
179 # current file is translated into (which might be english!)
180 my $orig = 'english';
181 my $transl = $language;
182
183 my $file_orig = catfile( $orig, $file );
184 my $file_transl = catfile( $transl, $file );
185
186 my $revinfo_orig = $english_revs{$file};
187 my $revinfo_transl = $translation_revs{$file};
188
189 # TODO: put this in a separate function
190 # first we check if the translated file has an origin other than
191 # english
192 if ( -e $file_transl )
193 {
194 my $transcheck = Webwml::TransCheck->new( $file_transl );
195 my $original_lang = $transcheck->original();
196
197 if ( $original_lang and $original_lang ne 'english' )
198 {
199 die( "Unknown original language `$original_lang' "
200 ."for `$file_transl'\n" ) unless -d $original_lang;
201
202 verbose "`$file_transl' is translated from $original_lang";
203
204 # now, we use the correct (non-english) original file
205 $file_orig = catfile( $original_lang, $file );
206
207 # and find the correct revision info for this file
208 $revinfo_orig = { vcs_file_info( $file_orig ) };
209 }
210 }
211
212 # TODO: put this in a separate function
213 # secondly, we check if perhaps the original file is a translation
214 # (such as in the case of english/international/Swedish/index.wml)
215 if ( -e $file_transl and -e $file_orig )
216 {
217 my $transcheck = Webwml::TransCheck->new( $file_orig );
218 my $original_lang = $transcheck->original();
219 my $rev = $transcheck->revision();
220
221 if ( $rev )
222 {
223 ## This check is too strict: some translators like to translate
224 ##from other translations rather than from the original english
225 ##(see e.g., danish/international/Norwegian.wml)
226 #if ( not $original_lang )
227 #{
228 # # TODO: ideally, this would also be mailed out to the
229 # # translation team
230 # warn "`$file_orig' has a revision header but no origin language\n";
231 # next;
232 #}
233
234 if ( $original_lang and $original_lang eq $language )
235 {
236 verbose "`$file_orig' is a translation from $language";
237
238 # switch $orig and $transl
239 switch_var( $orig, $transl );
240 switch_var( $file_orig, $file_transl );
241 switch_var( $revinfo_orig, $revinfo_transl );
242 }
243 }
244
245 }
246
247 # determine the status of the file
248 my ($status,$str,$rev_transl,$maintainer,$maxdelta) = check_file(
249 $file,
250 $orig, $transl,
251 $revinfo_orig, $revinfo_transl,
252 );
253
254
255 ######################################################################
256 ## Everything below is just output logic
257 ######################################################################
258
259 # print info
260 if ( ( $OPT{v} )
261 or ( $status == ST_MISSING and not $OPT{q} )
262 or ( $status != ST_MISSING and $status != ST_UPTODATE
263 and $status != ST_NOTATRANSL )
264 )
265 {
266 print colored( "$str\n", $COLOURS{$status} );
267 }
268
269 # check age of the translation
270 if ( $OPT{a} and $status == ST_NEEDSUPDATE )
271 {
272 my $age = int get_revision_age( $revinfo_transl );
273
274 # only warn if the translation is older than 2 weeks
275 if ( $age > 14 )
276 {
277 print colored( "$file is outdated by $age days\n",
278 $COLOURS{warn} );
279 }
280 }
281
282 # print log if requested and an update is needed
283 if ( $OPT{'l'} and $status == ST_NEEDSUPDATE )
284 {
285 my $log = get_log(
286 $file_orig,
287 $rev_transl,
288 $revinfo_orig->{'cmt_rev'},
289 );
290 print $log;
291 }
292
293 # print diff if requested and an update is needed
294 if ( $OPT{'d'} and $status == ST_NEEDSUPDATE )
295 {
296 my $diff = get_diff(
297 $file_orig,
298 $rev_transl,
299 $revinfo_orig->{'cmt_rev'},
300 );
301 print $diff;
302 }
303
304 # print text diff, if requested and an update is needed
305 if ( $OPT{'T'} and $status == ST_NEEDSUPDATE )
306 {
307 my $diff = get_diff_txt(
308 $file_orig,
309 $rev_transl,
310 $revinfo_orig->{'cmt_rev'},
311 $file_transl
312 );
313 print $diff;
314 }
315
316
317 # prepare a mail to be sent
318 if ( $OPT{'m'} and $status != ST_UPTODATE )
319 {
320 # handle special case maintainer fields
321 $maintainer = 'unmaintained'
322 unless $maintainer and exists $translators{$maintainer};
323 $maintainer = 'untranslated'
324 if $status == ST_MISSING;
325
326 # mail to send to the maintainer
327 push @{ $emails_to_send{$maintainer} }, {
328 'file' => $file,
329 'status' => $status,
330 'info' => $str,
331 'last_trans_rev' => $rev_transl,
332 }
333 if ( exists $translators{$maintainer} );
334
335 # mail for maxdelta
336 if ( $status != ST_MISSING )
337 {
338 $maxdelta ||= $translators{maxdelta}{maxdelta} || 5;
339
340 my $delta = undef;
341 if ( -e $file_orig )
342 {
343 $delta = vcs_count_changes( $file_orig, $rev_transl, 'HEAD' );
344
345 push @{ $emails_to_send{'maxdelta'} }, {
346 'file' => $file,
347 'status' => $status,
348 'info' => $str,
349 'delta' => $delta,
350 'last_trans_rev' => $rev_transl,
351 }
352 if ( $delta >= $maxdelta );
353 }
354 }
355
356 }
357
358 }
359
360 send_email( \%emails_to_send, \%translators, $language, $OPT{'n'}, !$OPT{'g'} );
361
362 exit 0;
363 }
364 die("Never reached");
365
366
367 #=================================================
368 #== swich two variables around
369 #==
370 sub switch_var(\$\$)
371 {
372 my $a = shift;
373 my $b = shift;
374
375 my $c = $$a;
376 $$a = $$b;
377 $$b = $c;
378 }
379
380
381 #=================================================
382 #== output verbose messages
383 #==
384 sub verbose
385 {
386 return unless $VERBOSE;
387 print @_, "\n";
388 }
389
390
391
392 #=================================================
393 #== send out the emails
394 #==
395 sub send_email
396 {
397 my $emails = shift or die("No emails specified");
398 my $translators = shift or die("No translators specified");
399 my $lang = shift or die("No language specified");
400 my $priority = shift or die("No priority specified");
401 my $really_send = shift || 0;
402
403
404 foreach my $name (sort keys %$emails)
405 {
406 verbose("Preparing email for $name");
407
408 # skip unconfigured users
409 if ( not exists $translators->{$name}
410 or not $translators->{$name}{'email'} )
411 {
412 verbose( "Woops! Can't find info for user `$name'\n" );
413 next;
414 }
415
416 # check the user's email addres
417 if ( not Email::Address->parse( $translators->{$name}{'email'} ) )
418 {
419 printf STDERR "Can't parse email address `%s' for %s!\n",
420 $translators->{$name}{'email'}, $name;
421 next;
422 }
423
424 # skip if the user doesn't want a summary at all
425 if ( $translators->{$name}{'summary'} < $priority )
426 {
427 verbose( "Not sending message to $name (prio "
428 . $translators->{$name}{'summary'} . " < $priority)" );
429 next;
430 }
431
432 my %transl = %{ $translators->{$name} };
433
434 #print Dumper($emails->{$name});
435
436 my $msg = MIME::Lite->new(
437 'From' => $MY_EMAIL,
438 'To' => $translators->{$name}{'email'},
439 'Subject' => $translators->{$name}{'mailsubject'},
440 'Type' => 'multipart/mixed',
441 );
442
443 # read body and interpret perl that's embedded there
444 my $body = read_file_enc( $transl{'mailbody'} )
445 or die("Can't read $transl{'mailbody'}");
446 {
447 # a bit hackish, but I want to keep the curent format of
448 # the mail body files intact, for now
449 # so we need to use the same old variable names as the original
450 # script used
451 my %translators = %{$translators};
452 $body =~ s{#(.*?)#}{eval $1}mge;
453 }
454
455 # and attach the body to the mail
456 my $part = MIME::Lite->new(
457 'Type' => 'text/plain',
458 'Data' => $body,
459 );
460 $part->attr( 'content-type.charset' => 'utf-8' );
461 $msg->attach( $part );
462
463 # attach part about NeedToUpdate files
464 my $text = '';
465 foreach my $file ( @{ $emails->{$name} } )
466 {
467 next unless $file->{'status'} == ST_NEEDSUPDATE;
468 $text .= $file->{'info'};
469
470 if ( exists $file->{'delta'} )
471 {
472 $text .= sprintf( " [out of date by %d revisions]",
473 $file->{'delta'} );
474 }
475
476 $text .= "\n";
477 }
478 $msg->attach(
479 'Type' => 'TEXT',
480 'Filename' => 'NeedToUpdate summary',
481 'Data' => $text,
482 )
483 if $text;
484
485 # attach part about Missing files
486 $text = '';
487 foreach my $file ( @{ $emails->{$name} } )
488 {
489 next unless $file->{'status'} == ST_MISSING;
490 $text .= sprintf( "%s\n", $file->{'info'} );
491 }
492 $msg->attach(
493 'Type' => 'TEXT',
494 'Filename' => 'Missing summary',
495 'Data' => $text,
496 'Encoding' => 'quoted-printable',
497 )
498 if $text;
499
500 # add diffs, if requested
501 if ( $priority <= $translators->{$name}{'diff'} )
502 {
503 foreach my $file ( @{ $emails->{$name} } )
504 {
505 # diffs really only make sense if there is an existing
506 # translation
507 next unless $file->{'status'} == ST_NEEDSUPDATE;
508
509 my $filename = catfile( 'english', $file->{'file'} );
510 my $rev = $file->{'last_trans_rev'};
511 my $diff = get_diff( $filename, $rev, 'HEAD' );
512 $msg->attach(
513 'Type' => 'TEXT',
514 'Filename' => "$filename.diff",
515 'Data' => $diff,
516 'Encoding' => 'quoted-printable',
517 );
518 }
519 }
520 else
521 {
522 verbose( "Not attaching diffs (prio "
523 . $translators->{$name}{'diff'} . " < $priority)" );
524 }
525
526 # add tdiffs, if requested
527 if ( $priority <= $translators->{$name}{'tdiff'} )
528 {
529 foreach my $file ( @{ $emails->{$name} } )
530 {
531 # diffs really only make sense if there is an existing
532 # translation
533 next unless $file->{'status'} == ST_NEEDSUPDATE;
534
535 my $filename = catfile( 'english', $file->{'file'} );
536 my $filename2 = catfile( $lang, $file->{'file'} );
537 my $rev = $file->{'last_trans_rev'};
538 my $tdiff = get_diff_txt( $filename, $rev, 'HEAD',
539 $filename2 );
540 $msg->attach(
541 'Type' => 'TEXT',
542 'Filename' => "$filename.tdiff",
543 'Data' => $tdiff,
544 'Encoding' => 'quoted-printable',
545 );
546 }
547 }
548 else
549 {
550 verbose( "Not attaching tdiffs (prio "
551 . $translators->{$name}{'tdiff'} . " < $priority)" );
552 }
553
554 # add logs, if requested
555 if ( $priority <= $translators->{$name}{'logs'} )
556 {
557 foreach my $file ( @{ $emails->{$name} } )
558 {
559 # logs really only make sense if there is an existing
560 # translation
561 next unless $file->{'status'} == ST_NEEDSUPDATE;
562
563 my $filename = catfile( 'english', $file->{'file'} );
564 my $rev = $file->{'last_trans_rev'};
565 my $log = get_log( $filename, $rev, 'HEAD' );
566 my $part = MIME::Lite->new(
567 'Type' => 'TEXT',
568 'Filename' => "$filename.log",
569 'Data' => $log,
570 'Encoding' => 'quoted-printable',
571 );
572 $part->attr( 'content-type.charset' => 'utf-8' );
573 $msg->attach( $part );
574 }
575 }
576 else
577 {
578 verbose( "Not attaching logs (prio "
579 . $translators->{$name}{'logs'} . " < $priority)" );
580 }
581
582 # add file, if requested
583 if ( $priority <= $translators->{$name}{'file'} )
584 {
585 foreach my $file ( @{ $emails->{$name} } )
586 {
587 my $filename = catfile( $lang, $file->{'file'} );
588 my $part = MIME::Lite->new(
589 'Type' => 'text/wml',
590 'Filename' => $filename,
591 'Path' => $filename,
592 'Encoding' => 'quoted-printable',
593 );
594 $part->attr( 'content-type.charset' => get_file_charset($filename) );
595 $msg->attach( $part );
596
597 }
598 }
599 else
600 {
601 verbose( "Not attaching files (prio "
602 . $translators->{$name}{'file'} . " < $priority)" );
603 }
604
605
606
607 # check if we really want to send the mail
608 if ( $really_send )
609 {
610 verbose 'Sending email to ' . $translators->{$name}{'email'};
611 $msg->send or warn("Couldn't send message to $name");
612 }
613 else
614 {
615 print $msg->as_string;
616 }
617 }
618 }
619
620
621 #=================================================
622 #== return the age of the revision (in days)
623 #==
624 sub get_revision_age
625 {
626 my $rev_info = shift;
627
628 die("No revision info specified") unless ref $rev_info eq 'HASH';
629
630 my $rev_timestamp = $rev_info->{'cmt_date'};
631 my $age = time - $rev_timestamp;
632
633 warn( "Timestamp is in the future!" ) if $age < 0;
634
635 # return age in days
636 return $age / ( 60*60*24 );
637 }
638
639
640
641 #=================================================
642 #== get a log
643 #==
644 sub get_log
645 {
646 my $file = shift or die("No file specified for diff");
647 my $rev1 = shift;
648 my $rev2 = shift;
649
650 die("NO such file `$file'") unless -e $file;
651
652 my @log = vcs_get_log( $file, $rev1, $rev2 );
653
654 # remove the first item of the log, because we only want
655 # to see when changed in the (left-open) range (rev1,rev2]
656 shift @log;
657
658 # format it nicely
659 my $str = '-' x 78 . "\n";
660 foreach my $l (@log)
661 {
662 chomp $l->{'message'};
663
664 $str .= sprintf( "%s | %s | %s\n",
665 $l->{'rev'}, $l->{'author'}, scalar localtime $l->{'date'} );
666 $str .= "\n";
667 $str .= $l->{'message'} . "\n";
668 $str .= "\n";
669
670 $str .= '-' x 78 . "\n";
671
672 }
673
674
675 return $str;
676 }
677
678 #=================================================
679 #== get a diff
680 #==
681 sub get_diff
682 {
683 my $file = shift or die("No file specified for diff");
684 my $rev1 = shift;
685 my $rev2 = shift;
686
687 die("NO such file `$file'") unless -e $file;
688
689 my %diffs = vcs_get_diff( $file, $rev1, $rev2 );
690
691 # just glue all diffs together and return it as a big string
692 my $difftxt = join( '', values %diffs );
693
694 return $difftxt;
695 }
696
697 #=================================================
698 #== get a diff while trying to match html tags
699 #==
700 sub get_diff_txt
701 {
702 my $english_file = shift or die("No file specified");
703 my $rev1 = shift or die("No revision specified");
704 my $rev2 = shift or die("No revision specified");
705 my $transl_file = shift or die("No transl file specified");
706
707 die("No such file $english_file") unless -e $english_file;
708 die("No such file $transl_file") unless -e $transl_file;
709
710 # Get old revision file
711 my @english_txt = split( "\n", vcs_get_file( $english_file, $rev1 ) );
712
713 # Get translation file
714 my $transl_txt = read_file( $transl_file )
715 or die("Couln't read `$transl_file': $!");
716 my @transl_txt = split( "\n", $transl_txt );
717
718 # Get diff lines
719 my @diff_txt = split( "\n", get_diff( $english_file, $rev1, $rev2 ) );
720
721 # do the matching
722 my $txt = Local::WmlDiffTrans::find_trans_parts(
723 \@english_txt,
724 \@transl_txt,
725 \@diff_txt
726 );
727
728 return $txt;
729 }
730
731
732 #=================================================
733 #== show help from the top of this file
734 #==
735 sub show_help
736 {
737 # read the help from the comments above and display it
738 open( my $me, '<', $0 ) or die "Unable to display help: $!\n";
739
740 while ( my $line = <$me> )
741 {
742 last if $line =~ m{^use};
743 print "\n" if $line =~ m{^#$};
744 next unless $line =~ m{^# };
745
746 $line =~ s{^# ?}{};
747
748 print $line;
749 }
750
751 close( $me );
752 }
753
754
755 #=================================================
756 #== parse command line options and read defaults
757 #==
758 sub parse_cmdargs
759 {
760 my %OPT;
761 $OPT{n} = 5; # an invalid default
762 $OPT{s} = '';
763
764 # parse options
765 if ( not getopts( 'adghlm:n:p:Qqs:TvV', \%OPT ) )
766 {
767 show_help();
768 exit -1;
769 }
770
771 # show help
772 if ( $OPT{h} )
773 {
774 show_help();
775 exit 0;
776 }
777
778 # handle verbosity setting
779 if ( ( $OPT{'v'} or $OPT{'V'} ) and ( $OPT{'q'} or $OPT{'Q'} ) )
780 {
781 die "you can't have both verbose and quiet, doh!\n";
782 }
783 $VERBOSE = 1 if $OPT{'V'};
784 $OPT{'v'} = 1 if $OPT{'V'};
785
786 # handle really quiet setting
787 if ( $OPT{'Q'} )
788 {
789 # redirect stdout to /dev/null
790 close( STDOUT );
791 open( STDOUT, '>', '/dev/null' )
792 or die( "Can't redirect STDOUT to /dev/null: $!" );
793 }
794
795 # handle -s (subtree check) setting
796 if ( $OPT{s})
797 {
798 verbose "Checking subtree $OPT{s} only\n";
799 }
800
801 if ( $OPT{'m'} and $OPT{'n'} !~ m{^[123]$} )
802 {
803 die "Invalid priority `$OPT{n}'. Please set -n value to 1, 2 or 3.\n"
804 ."(assuming you know what you're doing)\n";
805 }
806
807 # load additional module we need for mail
808 if ( $OPT{'m'} )
809 {
810 eval {
811 require MIME::Lite;
812 import MIME::Lite;
813 };
814 die "The module MIME::Lite could not be loaded.\n"
815 ."Please install libmime-lite-perl\n" if $@;
816
817 eval {
818 require Email::Address;
819 import Email::Address;
820 };
821 die "The module Email::Address could not be loaded.\n"
822 ."Please install libemail-address-perl\n" if $@;
823 }
824
825 if ( $OPT{'g'} and not $OPT{'m'} )
826 {
827 die "Option -g (debuG mail) without -m (use mail) "
828 ."really doesn't make much sense\n";
829 }
830
831 # include only files matching $filename
832 my $file_pattern = $OPT{'p'} || $DEFAULT_PATTERN;
833
834 my $translation = shift @ARGV || '';
835
836 # language configuration
837 if ( not $translation )
838 {
839 if ( exists $ENV{DWWW_LANG} )
840 {
841 $translation = $ENV{DWWW_LANG};
842 }
843 elsif ( -e "language.conf" )
844 {
845 open( my $conf, '<', 'language.conf' )
846 or die("Can't read language.conf: $!\n");
847 while (<$conf>)
848 {
849 next if /^#/;
850 chomp;
851 $translation = $_;
852 last;
853 }
854 close $conf;
855 }
856 }
857
858 # Remove slash from the end
859 $translation =~ s{/$}{};
860
861 if ( $translation eq '' )
862 {
863 die "Language not defined in DWWW_LANG, language.conf, "
864 ."or on command line\n";
865 }
866
867 return ($translation,$file_pattern,%OPT);
868 }
869
870 #=================================================
871 #== read the translators from translator.db
872 #==
873 sub read_translators
874 {
875 my $lang = shift or die("Internal error: no language specified");
876 my $need_translators = shift;
877
878 my %translators;
879
880 my $db_file = catfile( $lang, 'international', $lang, 'translator.db.pl' );
881
882 verbose "Reading translation database $db_file";
883
884 if ( -e $db_file)
885 {
886 require $db_file;
887
888 verbose "READ TRANSLATOR DB: $db_file\n";
889
890 %translators = %{ init_translators() };
891
892 if ( exists $translators{default} )
893 {
894 my @field_list = keys %{ $translators{default} };
895 foreach my $user (keys %translators)
896 {
897 next if $user eq 'default';
898 foreach my $f (@field_list)
899 {
900 $translators{$user}{$f} = $translators{default}{$f}
901 unless exists $translators{$user}{$f};
902 }
903 }
904 }
905 }
906
907 if ( $need_translators and not %translators )
908 {
909 die "I need my DBs to send mails !\n"
910 ."Please read the comments in the script and try again\n";
911 }
912
913 return %translators;
914 }
915
916 #=================================================
917 #== check if a single file is up to date
918 #== returns ($status,$message)
919 #== where status is one of the ST_* constants (see top of file)
920 #==
921 sub check_file
922 {
923 my $file = shift;
924 my $orig = shift;
925 my $lang = shift;
926 my $english_rev = shift; # might be undef
927 my $translation_rev = shift; # might be undef
928
929 die("Internal error: insufficient arguments")
930 unless $file and $orig and $lang;
931
932 # filename of english and translated files
933 my $file_orig = catfile( $orig, $file );
934 my $file_translation = catfile( $lang, $file );
935
936 # revision of the latest change in the english file
937 my $orig_last_change = $english_rev ? $english_rev->{cmt_rev} : 'n/a';
938
939 # revision of the english file that was translated
940 my $transcheck = Webwml::TransCheck->new( $file_translation );
941 my $translation_last_change = $transcheck->revision() || 'n/a';
942 my $translation_translator = $transcheck->maintainer() || undef;
943 my $translation_maxdelta = $transcheck->maxdelta() || undef;
944
945 verbose "Checking $file_translation, $orig revision $orig_last_change";
946
947 # status information
948 my $status = undef;
949 my $str = undef;
950
951 # at this point, there are several possibilities:
952 # 1) file exists both in english and translation
953 # 2) file exists only in english
954 # 3) file exists only in translation
955 # 4) file exists in neither original or translation: can't happen
956 # we handle those cases one by one
957
958 # 1) both files exist
959 if ( -e $file_orig and -e $file_translation )
960 {
961 # now check if both files have correct revisions
962 # again, three cases
963 # 1a) original file doesn't have a revision (can't happen)
964 # 1b) translated file doesn't have a revision (error in wml file)
965 # 1c) revision of both files is known
966
967 # 1a) no revision for english file
968 if ( $orig_last_change eq 'n/a' )
969 {
970 # this can't happen: something must be wrong with this script
971 die( "internal error: no revision for english file" );
972 }
973
974 # 1b) no revision on translated file: error
975 elsif ( $translation_last_change eq 'n/a' )
976 {
977 $status = ST_UNDEFINED;
978 $str = "Unknown status of $file_translation "
979 ."(revision should be $orig_last_change)";
980 }
981
982 # 1c) both files have revisions
983 else
984 {
985 # check the revisions to see if they're up to date
986 my $cmp = vcs_cmp_rev( $translation_last_change,
987 $orig_last_change );
988
989 if ( $cmp == 0 ) # revisions equal
990 {
991 # up to date
992 $str = "UpToDate $file_translation";
993 $status = ST_UPTODATE;
994 }
995 elsif ( $cmp == -1 ) # $translation_last_change < $orig_last_change
996 {
997 # out of date
998 $status = ST_NEEDSUPDATE;
999 $str = "NeedToUpdate $file_translation from revision "
1000 ."$translation_last_change to revision $orig_last_change";
1001 }
1002 else # $translation_last_change > $orig_last_change
1003 {
1004 # weirdness: translation is newer than original
1005 $status = ST_BROKEN;
1006 $str = "Broken revision number r$translation_last_change "
1007 ."for $file_translation, it should be $orig_last_change";
1008 }
1009 }
1010 }
1011
1012 # 2) original file exists, but translation is missing
1013 elsif ( -e $file_orig and not -e $file_translation )
1014 {
1015 $status = ST_MISSING;
1016 $str = "Missing $file_translation version $orig_last_change";
1017 }
1018
1019 # 3) translation exists, but original is missing
1020 elsif ( not -e $file_orig and -e $file_translation )
1021 {
1022 # the translated file doesn't have a translation header,
1023 # so it probably is an original
1024 if ( $translation_last_change eq 'n/a' )
1025 {
1026 $status = ST_NOTATRANSL;
1027 $str = "NotATranslation $file_translation";
1028 }
1029 # otherwise, it has a translation header,
1030 # so the english file was removed
1031 else
1032 {
1033 $status = ST_OBSOLETE;
1034 $str = "Obsolete $file_translation";
1035 }
1036 }
1037
1038 # neither original nor translation exists
1039 else
1040 {
1041 # this should never occur, because it means the function was
1042 # called with an invalid argument
1043 die( "Internal error: file not present in english nor $lang" );
1044 }
1045
1046 # add name of translator
1047 $str .= " (maintainer $translation_translator)" if $translation_translator;
1048
1049 return ($status,$str,$translation_last_change,
1050 $translation_translator,$translation_maxdelta);
1051 }
1052
1053
1054 # get the encoding of a certain file, by looking for wmlrc
1055 sub get_file_charset
1056 {
1057 my $file = shift or croak("No file specified");
1058
1059 # default charset
1060 my $charset = 'utf-8';
1061
1062 # read the wmlrc file
1063 my $wmlrc_dir = dirname($file);
1064 while ( not -e catfile( $wmlrc_dir, '.wmlrc' ) )
1065 {
1066 $wmlrc_dir = dirname $wmlrc_dir;
1067 last if length( $wmlrc_dir ) < 3
1068 }
1069
1070 # now read the wmlrc file to find the charset
1071 my $wmlrc = catfile( $wmlrc_dir,'.wmlrc' );
1072 if ( open( my $fd, '<', $wmlrc ) )
1073 {
1074 while ( my $line = <$fd> )
1075 {
1076 next unless $line =~ m{CHARSET=(.*?)\s*$};
1077 $charset = $1;
1078 last;
1079 }
1080 close($fd);
1081 }
1082 else
1083 {
1084 verbose "wmlrc for `$file' not found; assuming $charset charset";
1085 }
1086
1087 return $charset;
1088 }
1089
1090 # Slurp a file from a particular language in the right encoding
1091 sub read_file_enc
1092 {
1093 my $file = shift or croak("No file specified");
1094
1095 my $charset = get_file_charset( $file );
1096
1097 # now read the file
1098 open( my $fd, '<:bytes', $file ) or return undef;
1099 my $text;
1100 {
1101 local $/ = undef;
1102 $text = <$fd>;
1103 }
1104 close( $fd );
1105
1106 # decode the text
1107 $text = decode( $charset, $text );
1108
1109 return $text;
1110 }
1111
1112 __END__

  ViewVC Help
Powered by ViewVC 1.1.5