source: trunk/third/gnome-applets/intltool-update.in @ 18647

Revision 18647, 15.9 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18646, which included commits to RCS files with non-trunk default branches.
Line 
1#!@INTLTOOL_PERL@ -w
2
3#
4#  The Intltool Message Updater
5#
6#  Copyright (C) 2000-2002 Free Software Foundation.
7#
8#  Intltool is free software; you can redistribute it and/or
9#  modify it under the terms of the GNU General Public License
10#  version 2 published by the Free Software Foundation.
11#
12#  Intltool is distributed in the hope that it will be useful,
13#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15#  General Public License for more details.
16#
17#  You should have received a copy of the GNU General Public License
18#  along with this program; if not, write to the Free Software
19#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20#
21#  As a special exception to the GNU General Public License, if you
22#  distribute this file as part of a program that contains a
23#  configuration script generated by Autoconf, you may include it under
24#  the same distribution terms that you use for the rest of that program.
25#
26#  Authors: Kenneth Christiansen <kenneth@gnu.org>
27#           Maciej Stachowiak
28#           Darin Adler <darin@bentspoon.com>
29
30## Release information
31my $PROGRAM = "intltool-update";
32my $VERSION = "0.23";
33my $PACKAGE = "intltool";
34
35## Loaded modules
36use strict;
37use Getopt::Long;
38use Cwd;
39use File::Copy;
40use File::Find;
41
42## Scalars used by the option stuff
43my $HELP_ARG       = 0;
44my $VERSION_ARG    = 0;
45my $DIST_ARG       = 0;
46my $POT_ARG        = 0;
47my $HEADERS_ARG    = 0;
48my $MAINTAIN_ARG   = 0;
49my $REPORT_ARG     = 0;
50my $VERBOSE        = 0;
51my $GETTEXT_PACKAGE = "";
52
53my @languages;
54my %po_files_by_lang = ();
55
56# Regular expressions to categorize file types.
57# FIXME: Please check if the following is correct
58
59my $xml_extension =
60"xml(\.in)*|".          # .in is not required
61"ui|".
62"glade2?(\.in)*|".      # .in is not required
63"scm(\.in)*|".          # .in is not required
64"oaf(\.in)+|".
65"etspec|".
66"sheet(\.in)+|".
67"schemas(\.in)+|".
68"pong(\.in)+";
69
70my $ini_extension =
71"desktop(\.in)+|".
72"caves(\.in)+|".
73"directory(\.in)+|".
74"soundlist(\.in)+|".
75"keys(\.in)+|".
76"theme(\.in)+|".
77"server(\.in)+";
78
79## Always print as the first thing
80$| = 1;
81
82## Handle options
83GetOptions
84(
85 "help"                => \$HELP_ARG,
86 "version"             => \$VERSION_ARG,
87 "dist|d"              => \$DIST_ARG,
88 "pot|p"               => \$POT_ARG,
89 "headers|s"           => \$HEADERS_ARG,
90 "maintain|m"          => \$MAINTAIN_ARG,
91 "report|r"            => \$REPORT_ARG,
92 "verbose|x"           => \$VERBOSE,
93 "gettext-package|g=s" => \$GETTEXT_PACKAGE,
94 ) or &print_error_invalid_option;
95
96&print_help if $HELP_ARG;
97&print_version if $VERSION_ARG;
98
99my $arg_count = ($DIST_ARG > 0)
100    + ($POT_ARG > 0)
101    + ($HEADERS_ARG > 0)
102    + ($MAINTAIN_ARG > 0)
103    + ($REPORT_ARG > 0);
104&print_help if $arg_count > 1;
105
106# --version and --help don't require a module name
107my $MODULE = $GETTEXT_PACKAGE || &find_package_name;
108
109if ($DIST_ARG) {
110    if ($ARGV[0] =~ /^[a-z]/){
111        &update_po_file ($ARGV[0]);
112        &print_status ($ARGV[0]);
113    } else {
114        &print_help;
115    }
116} elsif ($POT_ARG) {
117    &generate_headers;
118    &generate_po_template;
119} elsif ($HEADERS_ARG) {
120    &generate_headers;
121} elsif ($MAINTAIN_ARG) {
122    &find_leftout_files;
123} elsif ($REPORT_ARG) {
124    &print_report;
125} else {
126    if ($ARGV[0] =~ /^[a-z]/) {
127        &main ($ARGV[0]);
128    } else {
129        &print_help;
130    }
131}
132
133exit;
134
135#########
136
137sub print_version
138{
139    ## Print version information
140    print "${PROGRAM} (${PACKAGE}) $VERSION\n";
141    print "Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler.\n\n";
142    print "Copyright (C) 2000-2002 Free Software Foundation, Inc.\n";
143    print "This is free software; see the source for copying conditions.  There is NO\n";
144    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
145    exit;
146}
147
148sub print_help
149{
150    ## Print usage information
151    print "Usage: ${PROGRAM} [OPTIONS] ...LANGCODE\n";
152    print "Updates PO template files and merge them with the translations.\n\n";
153    print "  -p, --pot              generate the PO template only\n";
154    print "  -s, --headers          generate the header files in POTFILES.in\n";
155    print "  -m, --maintain         search for left out files from POTFILES.in\n";
156    print "  -r, --report           display a status report for the module.\n";
157    print "  -x, --verbose          display lots of feedback\n";
158    print "      --help             display this help and exit\n";
159    print "      --version          output version information and exit\n";
160    print "\nExamples of use:\n";
161    print "${PROGRAM} --pot    just creates a new PO template from the source\n";
162    print "${PROGRAM} da       created new PO template and updated the da.po file\n\n";
163    print "Report bugs to bugzilla.gnome.org, module 'intltool'.\n";
164    exit;
165}
166
167sub main
168{
169    my ($lang) = @_;
170
171    ## Report error if the language file supplied
172    ## to the command line is non-existent
173    &print_error_not_existing("$lang.po") if ! -s "$lang.po";
174
175    print "Working, please wait..." unless $VERBOSE;
176    &generate_headers;
177    &generate_po_template;
178    &update_po_file ($lang);
179    &print_status ($lang);
180}
181
182sub determine_type ($)
183{
184   my $type = $_;
185   my $gettext_type;
186
187   # FIXME: Use $xml_extentions, and maybe do all this even nicer
188   my $xml_regex =
189       "(?:xml(\.in)*|ui|oaf(?:\.in)+|server(?:\.in)+|sheet(?:\.in)+|".
190       "pong(?:\.in)+|etspec|schemas(?:\.in)+)";
191   my $ini_regex =
192       "(?:desktop(?:\.in)+|theme(?:\.in)+|caves(?:\.in)+|directory(?:\.in)+|".
193       "soundlist(?:\.in)+)";
194
195   if ($type =~ /\[type: gettext\/([^\]].*)]/) {
196        $gettext_type=$1;
197   }
198   elsif ($type =~ /schemas(\.in)+$/) {
199        $gettext_type="schemas";
200   }
201   elsif ($type =~ /$xml_regex$/) {
202        $gettext_type="xml";
203   }
204   elsif ($type =~ /glade2?(\.in)*$/) {
205        $gettext_type="glade";
206   }
207   elsif ($type =~ /$ini_regex$/) {
208        $gettext_type="ini";
209   }
210   elsif ($type =~ /scm(\.in)*$/) {
211        $gettext_type="scheme";
212   }
213   elsif ($type =~ /keys(\.in)+$/) {
214        $gettext_type="keys";
215   }
216   else { $gettext_type=""; }
217
218   return "gettext\/$gettext_type";
219}
220
221sub find_leftout_files
222{
223    my (@buf_i18n_plain,
224        @buf_i18n_xml,
225        @buf_i18n_xml_unmarked,
226        @buf_i18n_ini,
227        @buf_potfiles,
228        @buf_potfiles_ignore,
229        @buf_allfiles,
230        @buf_allfiles_sorted,
231        @buf_potfiles_sorted
232    );
233
234    ## Search and find all translatable files
235    find sub {
236        push @buf_i18n_plain, "$File::Find::name" if /\.(c|y|cc|cpp|c\+\+|h|gob)$/
237        }, "..";
238    find sub {
239        push @buf_i18n_xml, "$File::Find::name" if /\.($xml_extension)$/
240        }, "..";
241    find sub {
242        push @buf_i18n_ini, "$File::Find::name" if /\.($ini_extension)$/
243        }, "..";
244    find sub {
245        push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/
246        }, "..";
247
248
249    open POTFILES, "POTFILES.in" or die "$PROGRAM:  there's no POTFILES.in!\n";
250
251    @buf_potfiles = grep /^[^#]/, <POTFILES>;
252                           
253    print "Searching for missing translatable files...\n" if $VERBOSE;
254
255    ## Check if we should ignore some found files, when
256    ## comparing with POTFILES.in
257    foreach my $ignore ("POTFILES.skip", "POTFILES.ignore") {
258        if (-s $ignore) {
259            open FILE, $ignore;
260            while (<FILE>) {
261                if (/^[^#]/){
262                    push @buf_potfiles_ignore, $_;
263                }
264            }
265            print "Found $ignore: Ignoring files...\n" if $VERBOSE;
266            @buf_potfiles = (@buf_potfiles_ignore, @buf_potfiles);
267        }
268    }
269
270    foreach my $file (@buf_i18n_plain)
271      {
272        my $in_comment = 0;
273        my $in_macro = 0;
274
275        open FILE, "<$file";
276        while (<FILE>)
277          {
278            # Handle continued multi-line comment.
279            if ($in_comment)
280              {
281                next unless s-.*\*/--;
282                $in_comment = 0;
283              }
284
285            # Handle continued macro.
286            if ($in_macro)
287              {
288                $in_macro = 0 unless /\\$/;
289                next;
290              }
291
292            # Handle start of macro (or any preprocessor directive).
293            if (/^\s*\#/)
294              {
295                $in_macro = 1 if /^([^\\]|\\.)*\\$/;
296                next;
297              }
298
299            # Handle comments and quoted text.
300            while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy
301              {
302                my $match = $1;
303                if ($match eq "/*")
304                  {
305                    if (!s-/\*.*?\*/--)
306                      {
307                        s-/\*.*--;
308                        $in_comment = 1;
309                      }
310                  }
311                elsif ($match eq "//")
312                  {
313                    s-//.*--;
314                  }
315                else # ' or "
316                  {
317                    if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-)
318                      {
319                        warn "mismatched quotes at line $. in $file\n";
320                        s-$match.*--;
321                      }
322                  }
323              }
324       
325
326            if (/_\(QUOTEDTEXT/)
327              {
328                ## Remove the first 3 chars and add newline
329                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
330                last;
331              }
332          }
333        close FILE;
334      }
335
336    foreach my $file (@buf_i18n_xml) {
337        open FILE, "<$file";
338        while (<FILE>) {
339            if (/\s_(.*)=\"/ || /translatable=\"yes\"/){
340                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
341                last;
342            }
343        }
344    }
345
346    foreach my $file (@buf_i18n_ini){
347        open FILE, "<$file";
348        while (<FILE>) {
349            if (/_(.*)=/){
350                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
351                last;
352            }
353        }
354    }
355
356    foreach my $file (@buf_i18n_xml_unmarked){
357        push @buf_allfiles, unpack("x3 A*", $file) . "\n";
358    }
359
360
361    @buf_allfiles_sorted = sort (@buf_allfiles);
362    @buf_potfiles_sorted = sort (@buf_potfiles);
363
364    my %in2;
365    foreach (@buf_potfiles_sorted) {
366        $in2{$_} = 1;
367    }
368
369    my @result;
370
371    foreach (@buf_allfiles_sorted){
372        if (!exists($in2{$_})){
373            push @result, $_
374        }
375    }
376
377    ## Save file with information about the files missing
378    ## if any, and give information about this procedure.
379    if (@result) {
380        print "\n" if $VERBOSE;
381        open OUT, ">missing";
382        print OUT @result;
383        print "The following files contain translations and are currently not in use. Please\n";
384        print "consider adding these to the POTFILES.in file, located in the po/ directory.\n\n";
385        print @result, "\n";
386        print "If some of these files are left out on purpose then please add them to\n";
387        print "POTFILES.skip instead of POTFILES.in. A file 'missing' containing this list\n";
388        print "of left out files has been written in the current directory.\n";
389    }
390
391    ## If there is nothing to complain about, notify the user
392    else {
393        print "\nAll files containing translations are present in POTFILES.in.\n";
394    }
395}
396
397sub print_error_invalid_option
398{
399    ## Handle invalid arguments
400    print "Try `${PROGRAM} --help' for more information.\n";
401    exit 1;
402}
403
404sub generate_headers
405{
406    my $EXTRACT = `which intltool-extract 2>/dev/null`;
407    chomp $EXTRACT;
408
409    $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} if $ENV{"INTLTOOL_EXTRACT"};
410
411    ## Generate the .h header files, so we can allow glade and
412    ## xml translation support
413    if (! -s $EXTRACT)
414    {
415        print "\n *** The intltool-extract script wasn't found!"
416             ."\n *** Without it, intltool-update can not generate files.\n";
417        exit;
418    }
419    else
420    {
421        open FILE, "<POTFILES.in";
422        while (<FILE>) {
423           chomp;
424
425           ## Find xml files in POTFILES.in and generate the
426           ## files with help from the extract script
427
428           my $gettext_type= &determine_type ($1);
429
430           if (/\.($xml_extension|$ini_extension)$/ || /^\[/){
431               $_ =~ s/^\[[^\[].*]\s*//;
432               my $filename = "../$_";
433
434               if ($VERBOSE){
435                   system($EXTRACT, "--update", "--type=$gettext_type", $filename);
436               } else {
437                   system($EXTRACT, "--update", "--type=$gettext_type", "--quiet", $filename);
438               }
439           }
440       }
441       close FILE;
442   }
443}
444
445sub generate_po_template
446{
447    ## Generate the potfiles from the POTFILES.in file
448
449    print "Building the $MODULE.pot...\n" if $VERBOSE;
450
451    move ("POTFILES.in", "POTFILES.in.old");
452
453    open INFILE, "<POTFILES.in.old";
454    open OUTFILE, ">POTFILES.in";
455    while (<INFILE>) {
456        s/\.($xml_extension|$ini_extension)$/$&.h/;
457        s/^\[.*]\s*(.*)/$1.h/;
458        print OUTFILE $_;
459    }
460    close OUTFILE;
461    close INFILE;
462
463    system ("xgettext", "--default-domain\=$MODULE",
464                        "--directory\=\.\.",
465                        "--add-comments",
466                        "--keyword\=\_",
467                        "--keyword\=N\_",
468                        "--keyword\=U\_",
469                        "--files-from\=\.\/POTFILES\.in");
470
471    move ("POTFILES.in.old", "POTFILES.in");
472
473    print "Removing generated header (.h) files..." if $VERBOSE;
474
475    open FILE, "<POTFILES.in";
476
477    while (<FILE>)
478    {
479        chomp;
480        unlink "../$_.h" if /\.($xml_extension|$ini_extension)$/;
481    }
482
483    close FILE;
484    print "done\n" if $VERBOSE;
485
486    if (!-e "$MODULE.po") {
487        print "WARNING: It seems that none of the files in POTFILES.in ".
488              "contain marked strings\n";
489        exit (1);
490    }
491
492    system ("rm", "-f", "$MODULE.pot");
493    move ("$MODULE.po", "$MODULE.pot") or die "$PROGRAM: couldn't move $MODULE.po to $MODULE.pot.\n";
494
495    print "Wrote $MODULE.pot\n" if $VERBOSE;
496}
497
498sub update_po_file
499{
500    my ($lang) = @_;
501
502    print "Merging $lang.po with $MODULE.pot..." if $VERBOSE;
503
504    copy ("$lang.po", "$lang.po.old") || die "copy failed: $!";
505
506    # Perform merge, remove backup file and the "messages" trash file
507    # generated by gettext
508    system ("msgmerge", "$lang.po.old", "$MODULE.pot", "-o", "$lang.po");
509    unlink "$lang.po.old";
510    unlink "messages";
511}
512
513sub print_error_not_existing
514{
515    my ($file) = @_;
516
517    ## Report error if supplied language file is non-existing
518    print "$PROGRAM: $file does not exist!\n";
519    print "Try '$PROGRAM --help' for more information.\n";
520    exit;
521}
522
523sub gather_po_files
524{
525    my @po_files = glob ("./*.po");
526
527    @languages = map (&po_file2lang, @po_files);
528
529    foreach my $lang (@languages) {
530        $po_files_by_lang{$lang} = shift (@po_files);
531    }
532}
533
534sub po_file2lang ($)
535{
536    my $tmp = $_;
537    $tmp =~ s/^.*\/(.*)\.po$/$1/;
538    return $tmp;
539}
540
541sub print_status
542{
543    my ($lang) = @_;
544
545    system ("msgfmt", "--statistics", "$lang.po");
546    print "\n";
547}
548
549sub print_report
550{
551    &generate_headers;
552    &generate_po_template;
553    &gather_po_files;
554
555    foreach my $lang (@languages) {
556        print "$lang: ";
557        &update_po_file ($lang);
558    }
559
560    print "\n\n * Current translation support in $MODULE \n\n";
561
562    foreach my $lang (@languages){
563        print "$lang: ";
564        system ("msgfmt", "--statistics", "$lang.po");
565    }
566}
567
568sub find_package_name
569{
570    my $base_dirname = getcwd();
571    $base_dirname =~ s@.*/@@;
572
573    my ($conf_in, $src_dir);
574
575    if ($base_dirname =~ /^po(-.+)?$/) {
576        if (-f "../configure.in") {
577            $conf_in = "../configure.in";
578        } elsif (-f "../configure.ac") {
579            $conf_in = "../configure.ac";
580        } else {
581            my $makefile_source;
582            local (*IN);
583            open IN, "<Makefile" || die "can't open Makefile: $!";
584
585            while (<IN>) {
586                if (/^top_srcdir[ \t]*=/) {
587                    $src_dir = $_;
588                    # print "${src_dir}\n";
589
590                    $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
591                    # print "${src_dir}\n";
592                    chomp $src_dir;
593                    $conf_in = "$src_dir" . "/configure.in" . "\n";
594                    last;
595                }
596            }
597            $conf_in || die "Cannot find top_srcdir in Makefile."
598        }
599
600        my %varhash = ();
601        my $conf_source; {
602           local (*IN);
603           open (IN, "<$conf_in") || die "can't open $conf_in: $!";
604           while (<IN>) {
605              if (/^(\w+)=(\S+)/) { $varhash{$1} = $2 };
606           }
607           seek (IN, 0, 0);
608           local $/; # slurp mode
609           $conf_source = <IN>;
610        }
611
612        my $name = "";
613        $name = $1 if $conf_source =~ /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m;
614        if ($conf_source =~ /^AC_INIT\([\s\[]*([^,\)\s\]]+)\]?\s*,/m) {
615            $name = $1;
616            $varhash{"AC_PACKAGE_NAME"} = $1;
617        }
618        $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\s\]]+)/m;
619
620        $name = "\$AC_PACKAGE_NAME" if "$name" eq "AC_PACKAGE_NAME";
621
622        my $oldname = "";
623        while (($name =~ /[\$](\S+)/) && ("$oldname" ne "$name")) {
624            $oldname = $name;
625            if (exists $varhash{$1}) {
626                $name =~ s/[\$](\S+)/$varhash{$1}/;
627            }
628        }
629        return $name if $name;
630    }
631
632    print "$PROGRAM: Unable to determine package name.\n" .
633          "Make sure to run this script inside the po directory.\n";
634    exit;
635}
Note: See TracBrowser for help on using the repository browser.