source: trunk/third/gnopernicus/intltool-update.in @ 21427

Revision 21427, 25.4 KB checked in by ghudson, 19 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21426, which included commits to RCS files with non-trunk default branches.
  • Property svn:executable set to *
Line 
1#!@INTLTOOL_PERL@ -w
2# -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4  -*-
3
4#
5#  The Intltool Message Updater
6#
7#  Copyright (C) 2000-2003 Free Software Foundation.
8#
9#  Intltool is free software; you can redistribute it and/or
10#  modify it under the terms of the GNU General Public License
11#  version 2 published by the Free Software Foundation.
12#
13#  Intltool is distributed in the hope that it will be useful,
14#  but WITHOUT ANY WARRANTY; without even the implied warranty of
15#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16#  General Public License for more details.
17#
18#  You should have received a copy of the GNU General Public License
19#  along with this program; if not, write to the Free Software
20#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21#
22#  As a special exception to the GNU General Public License, if you
23#  distribute this file as part of a program that contains a
24#  configuration script generated by Autoconf, you may include it under
25#  the same distribution terms that you use for the rest of that program.
26#
27#  Authors: Kenneth Christiansen <kenneth@gnu.org>
28#           Maciej Stachowiak
29#           Darin Adler <darin@bentspoon.com>
30
31## Release information
32my $PROGRAM = "intltool-update";
33my $VERSION = "0.31.3";
34my $PACKAGE = "intltool";
35
36## Loaded modules
37use strict;
38use Getopt::Long;
39use Cwd;
40use File::Copy;
41use File::Find;
42
43## Scalars used by the option stuff
44my $HELP_ARG       = 0;
45my $VERSION_ARG    = 0;
46my $DIST_ARG       = 0;
47my $POT_ARG        = 0;
48my $HEADERS_ARG    = 0;
49my $MAINTAIN_ARG   = 0;
50my $REPORT_ARG     = 0;
51my $VERBOSE        = 0;
52my $GETTEXT_PACKAGE = "";
53my $OUTPUT_FILE    = "";
54
55my @languages;
56my %varhash = ();
57my %po_files_by_lang = ();
58
59# Regular expressions to categorize file types.
60# FIXME: Please check if the following is correct
61
62my $xml_support =
63"xml(?:\\.in)*|".       # http://www.w3.org/XML/ (Note: .in is not required)
64"ui|".                  # Bonobo specific - User Interface desc. files
65"lang|".                # ?
66"glade2?(?:\\.in)*|".   # Glade specific - User Interface desc. files (Note: .in is not required)
67"scm(?:\\.in)*|".       # ? (Note: .in is not required)
68"oaf(?:\\.in)+|".       # DEPRECATED: Replaces by Bonobo .server files
69"etspec|".              # ?
70"server(?:\\.in)+|".    # Bonobo specific
71"sheet(?:\\.in)+|".     # ?
72"schemas(?:\\.in)+|".   # GConf specific
73"pong(?:\\.in)+|".      # DEPRECATED: PONG is not used [by GNOME] any longer.
74"kbd(?:\\.in)+";        # GOK specific.
75
76my $ini_support =
77"icon(?:\\.in)+|".      # GNOME Icon specific
78"desktop(?:\\.in)+|".   # http://www.freedesktop.org/Standards/menu-spec
79"caves(?:\\.in)+|".     # GNOME Games specific
80"directory(?:\\.in)+|". # http://www.freedesktop.org/Standards/menu-spec
81"soundlist(?:\\.in)+|". # GNOME specific
82"keys(?:\\.in)+|".      # GNOME Mime database specific
83"theme(?:\\.in)+";      # http://www.freedesktop.org/Standards/icon-theme-spec
84
85my $buildin_gettext_support =
86"c|y|cs|cc|cpp|c\\+\\+|h|hh|gob|py";
87
88## Always flush buffer when printing
89$| = 1;
90
91## Sometimes the source tree will be rooted somewhere else.
92my $SRCDIR = ".";
93my $POTFILES_in;
94
95$SRCDIR = $ENV{"srcdir"} if $ENV{"srcdir"};
96$POTFILES_in = "<$SRCDIR/POTFILES.in";
97
98## Handle options
99GetOptions
100(
101 "help"                => \$HELP_ARG,
102 "version"             => \$VERSION_ARG,
103 "dist|d"              => \$DIST_ARG,
104 "pot|p"               => \$POT_ARG,
105 "headers|s"           => \$HEADERS_ARG,
106 "maintain|m"          => \$MAINTAIN_ARG,
107 "report|r"            => \$REPORT_ARG,
108 "verbose|x"           => \$VERBOSE,
109 "gettext-package|g=s" => \$GETTEXT_PACKAGE,
110 "output-file|o=s"     => \$OUTPUT_FILE,
111 ) or &Console_WriteError_InvalidOption;
112
113&Console_Write_IntltoolHelp if $HELP_ARG;
114&Console_Write_IntltoolVersion if $VERSION_ARG;
115
116my $arg_count = ($DIST_ARG > 0)
117    + ($POT_ARG > 0)
118    + ($HEADERS_ARG > 0)
119    + ($MAINTAIN_ARG > 0)
120    + ($REPORT_ARG > 0);
121
122&Console_Write_IntltoolHelp if $arg_count > 1;
123
124# --version and --help don't require a module name
125my $MODULE = $GETTEXT_PACKAGE || &FindPackageName;
126
127if ($POT_ARG)
128{
129    &GenerateHeaders;
130    &GeneratePOTemplate;
131}
132elsif ($HEADERS_ARG)
133{
134    &GenerateHeaders;
135}
136elsif ($MAINTAIN_ARG)
137{
138    &FindLeftoutFiles;
139}
140elsif ($REPORT_ARG)
141{
142    &GenerateHeaders;
143    &GeneratePOTemplate;
144    &Console_Write_CoverageReport;
145}
146elsif ((defined $ARGV[0]) && $ARGV[0] =~ /^[a-z]/)
147{
148    my $lang = $ARGV[0];
149
150    ## Report error if the language file supplied
151    ## to the command line is non-existent
152    &Console_WriteError_NotExisting("$lang.po") if ! -s "$lang.po";
153
154    if (!$DIST_ARG)
155    {
156        print "Working, please wait..." if $VERBOSE;
157        &GenerateHeaders;
158        &GeneratePOTemplate;
159    }
160    &POFile_Update ($lang, $OUTPUT_FILE);
161    &Console_Write_TranslationStatus ($lang, $OUTPUT_FILE);
162}
163else
164{
165    &Console_Write_IntltoolHelp;
166}
167
168exit;
169
170#########
171
172sub Console_Write_IntltoolVersion
173{
174    print <<_EOF_;
175${PROGRAM} (${PACKAGE}) $VERSION
176Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler.
177
178Copyright (C) 2000-2003 Free Software Foundation, Inc.
179This is free software; see the source for copying conditions.  There is NO
180warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
181_EOF_
182    exit;
183}
184
185sub Console_Write_IntltoolHelp
186{
187    print <<_EOF_;
188Usage: ${PROGRAM} [OPTION]... LANGCODE
189Updates PO template files and merge them with the translations.
190
191Mode of operation (only one is allowed):
192  -p, --pot                   generate the PO template only
193  -s, --headers               generate the header files in POTFILES.in
194  -m, --maintain              search for left out files from POTFILES.in
195  -r, --report                display a status report for the module
196  -d, --dist                  merge LANGCODE.po with existing PO template
197
198Extra options:
199  -g, --gettext-package=NAME  override PO template name, useful with --pot
200  -o, --output-file=FILE      write merged translation to FILE
201  -x, --verbose               display lots of feedback
202      --help                  display this help and exit
203      --version               output version information and exit
204
205Examples of use:
206${PROGRAM} --pot    just create a new PO template
207${PROGRAM} xy       create new PO template and merge xy.po with it
208
209Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE")
210or send email to <xml-i18n-tools\@gnome.org>.
211_EOF_
212    exit;
213}
214
215sub POFile_DetermineType ($)
216{
217   my $type = $_;
218   my $gettext_type;
219
220   my $xml_regex     = "(?:" . $xml_support . ")";
221   my $ini_regex     = "(?:" . $ini_support . ")";
222   my $buildin_regex = "(?:" . $buildin_gettext_support . ")";
223
224   if ($type =~ /\[type: gettext\/([^\]].*)]/)
225   {
226        $gettext_type=$1;
227   }
228   elsif ($type =~ /schemas(\.in)+$/)
229   {
230        $gettext_type="schemas";
231   }
232   elsif ($type =~ /glade2?(\.in)*$/)
233   {
234       $gettext_type="glade";
235   }
236   elsif ($type =~ /scm(\.in)*$/)
237   {
238       $gettext_type="scheme";
239   }
240   elsif ($type =~ /keys(\.in)+$/)
241   {
242       $gettext_type="keys";
243   }
244
245   # bucket types
246
247   elsif ($type =~ /$xml_regex$/)
248   {
249       $gettext_type="xml";
250   }
251   elsif ($type =~ /$ini_regex$/)
252   {
253       $gettext_type="ini";
254   }
255   elsif ($type =~ /$buildin_regex$/)
256   {
257       $gettext_type="buildin";
258   }
259   else
260   {
261       $gettext_type="unknown";
262   }
263
264   return "gettext\/$gettext_type";
265}
266
267sub TextFile_DetermineEncoding ($)
268{
269    my $gettext_code="ASCII"; # All files are ASCII by default
270    my $filetype=`file $_ | cut -d ' ' -f 2`;
271
272    if ($? eq "0")
273    {
274        if ($filetype =~ /^(ISO|UTF)/)
275        {
276            chomp ($gettext_code = $filetype);
277        }
278        elsif ($filetype =~ /^XML/)
279        {
280            $gettext_code="UTF-8"; # We asume that .glade and other .xml files are UTF-8
281        }
282    }
283
284    return $gettext_code;
285}
286
287
288sub FindLeftoutFiles
289{
290    my (@buf_i18n_plain,
291        @buf_i18n_xml,
292        @buf_i18n_xml_unmarked,
293        @buf_i18n_ini,
294        @buf_potfiles,
295        @buf_potfiles_ignore,
296        @buf_allfiles,
297        @buf_allfiles_sorted,
298        @buf_potfiles_sorted
299    );
300
301    ## Search and find all translatable files
302    find sub {
303        push @buf_i18n_plain,        "$File::Find::name" if /\.($buildin_gettext_support)$/;
304        push @buf_i18n_xml,          "$File::Find::name" if /\.($xml_support)$/;
305        push @buf_i18n_ini,          "$File::Find::name" if /\.($ini_support)$/;
306        push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/;
307        }, "..";
308
309
310    open POTFILES, $POTFILES_in or die "$PROGRAM:  there's no POTFILES.in!\n";
311    @buf_potfiles = grep !/^(#|\s*$)/, <POTFILES>;
312    close POTFILES;
313
314    foreach (@buf_potfiles) {
315        s/^\[.*]\s*//;
316    }
317
318    print "Searching for missing translatable files...\n" if $VERBOSE;
319
320    ## Check if we should ignore some found files, when
321    ## comparing with POTFILES.in
322    foreach my $ignore ("POTFILES.skip", "POTFILES.ignore")
323    {
324        (-s $ignore) or next;
325
326        if ("$ignore" eq "POTFILES.ignore")
327        {
328            print "The usage of POTFILES.ignore is deprecated. Please consider moving the\n".
329                  "content of this file to POTFILES.skip.\n";
330        }
331
332        print "Found $ignore: Ignoring files...\n" if $VERBOSE;
333        open FILE, "<$ignore" or die "ERROR: Failed to open $ignore!\n";
334           
335        while (<FILE>)
336        {
337            push @buf_potfiles_ignore, $_ unless /^(#|\s*$)/;
338        }
339        close FILE;
340
341        @buf_potfiles = (@buf_potfiles_ignore, @buf_potfiles);
342    }
343
344    foreach my $file (@buf_i18n_plain)
345    {
346        my $in_comment = 0;
347        my $in_macro = 0;
348
349        open FILE, "<$file";
350        while (<FILE>)
351        {
352            # Handle continued multi-line comment.
353            if ($in_comment)
354            {
355                next unless s-.*\*/--;
356                $in_comment = 0;
357            }
358
359            # Handle continued macro.
360            if ($in_macro)
361            {
362                $in_macro = 0 unless /\\$/;
363                next;
364            }
365
366            # Handle start of macro (or any preprocessor directive).
367            if (/^\s*\#/)
368            {
369                $in_macro = 1 if /^([^\\]|\\.)*\\$/;
370                next;
371            }
372
373            # Handle comments and quoted text.
374            while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy
375            {
376                my $match = $1;
377                if ($match eq "/*")
378                {
379                    if (!s-/\*.*?\*/--)
380                    {
381                        s-/\*.*--;
382                        $in_comment = 1;
383                    }
384                }
385                elsif ($match eq "//")
386                {
387                    s-//.*--;
388                }
389                else # ' or "
390                {
391                    if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-)
392                    {
393                        warn "mismatched quotes at line $. in $file\n";
394                        s-$match.*--;
395                    }
396                }
397            }       
398
399            if (/\.GetString ?\(QUOTEDTEXT/)
400            {
401                ## Remove the first 3 chars and add newline
402                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
403                last;
404            }
405
406            if (/_\(QUOTEDTEXT/)
407            {
408                ## Remove the first 3 chars and add newline
409                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
410                last;
411            }
412        }
413        close FILE;
414    }
415
416    foreach my $file (@buf_i18n_xml)
417    {
418        open FILE, "<$file";
419       
420        while (<FILE>)
421        {
422            # FIXME: share the pattern matching code with intltool-extract
423            if (/\s_(.*)=\"/ || /<_[^>]+>/ || /translatable=\"yes\"/)
424            {
425                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
426                last;
427            }
428        }
429        close FILE;
430    }
431
432    foreach my $file (@buf_i18n_ini)
433    {
434        open FILE, "<$file";
435        while (<FILE>)
436        {
437            if (/_(.*)=/)
438            {
439                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
440                last;
441            }
442        }
443        close FILE;
444    }
445
446    foreach my $file (@buf_i18n_xml_unmarked)
447    {
448        push @buf_allfiles, unpack("x3 A*", $file) . "\n";
449    }
450
451
452    @buf_allfiles_sorted = sort (@buf_allfiles);
453    @buf_potfiles_sorted = sort (@buf_potfiles);
454
455    my %in2;
456    foreach (@buf_potfiles_sorted)
457    {
458        $in2{$_} = 1;
459    }
460
461    my @result;
462
463    foreach (@buf_allfiles_sorted)
464    {
465        if (!exists($in2{$_}))
466        {
467            push @result, $_
468        }
469    }
470
471    my @buf_potfiles_notexist;
472
473    foreach (@buf_potfiles_sorted)
474    {
475        chomp (my $dummy = $_);
476        if ("$dummy" ne "" and ! -f "../$dummy")
477        {
478            push @buf_potfiles_notexist, $_;
479        }
480    }
481
482    ## Save file with information about the files missing
483    ## if any, and give information about this procedure.
484    if (@result + @buf_potfiles_notexist > 0)
485    {
486        if (@result)
487        {
488            print "\n" if $VERBOSE;
489            unlink "missing";
490            open OUT, ">missing";
491            print OUT @result;
492            close OUT;
493            warn "\e[1mThe following files contain translations and are currently not in use. Please\e[0m\n".
494                 "\e[1mconsider adding these to the POTFILES.in file, located in the po/ directory.\e[0m\n\n";
495            print STDERR @result, "\n";
496            warn "If some of these files are left out on purpose then please add them to\n".
497                 "POTFILES.skip instead of POTFILES.in. A file \e[1m'missing'\e[0m containing this list\n".
498                 "of left out files has been written in the current directory.\n";
499        }
500        if (@buf_potfiles_notexist)
501        {
502            unlink "notexist";
503            open OUT, ">notexist";
504            print OUT @buf_potfiles_notexist;
505            close OUT;
506            warn "\n" if ($VERBOSE or @result);
507            warn "\e[1mThe following files do not exist anymore:\e[0m\n\n";
508            warn @buf_potfiles_notexist, "\n";
509            warn "Please remove them from POTFILES.in or POTFILES.skip. A file \e[1m'notexist'\e[0m\n".
510                 "containing this list of absent files has been written in the current directory.\n";
511        }
512    }
513
514    ## If there is nothing to complain about, notify the user
515    else {
516        print "\nAll files containing translations are present in POTFILES.in.\n" if $VERBOSE;
517    }
518}
519
520sub Console_WriteError_InvalidOption
521{
522    ## Handle invalid arguments
523    print STDERR "Try `${PROGRAM} --help' for more information.\n";
524    exit 1;
525}
526
527sub GenerateHeaders
528{
529    my $EXTRACT = "@INTLTOOL_EXTRACT@";
530    chomp $EXTRACT;
531
532    $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} if $ENV{"INTLTOOL_EXTRACT"};
533
534    ## Generate the .h header files, so we can allow glade and
535    ## xml translation support
536    if (! -x "$EXTRACT")
537    {
538        print STDERR "\n *** The intltool-extract script wasn't found!"
539             ."\n *** Without it, intltool-update can not generate files.\n";
540        exit;
541    }
542    else
543    {
544        open (FILE, $POTFILES_in) or die "$PROGRAM: POTFILES.in not found.\n";
545       
546        while (<FILE>)
547        {
548           chomp;
549           next if /^\[\s*encoding/;
550
551           ## Find xml files in POTFILES.in and generate the
552           ## files with help from the extract script
553
554           my $gettext_type= &POFile_DetermineType ($1);
555
556           if (/\.($xml_support|$ini_support)$/ || /^\[/)
557           {
558               s/^\[[^\[].*]\s*//;
559
560               my $filename = "../$_";
561
562               if ($VERBOSE)
563               {
564                   system ($EXTRACT, "--update", "--srcdir=$SRCDIR",
565                           "--type=$gettext_type", $filename);
566               }
567               else
568               {
569                   system ($EXTRACT, "--update", "--type=$gettext_type",
570                           "--srcdir=$SRCDIR", "--quiet", $filename);
571               }
572           }
573       }
574       close FILE;
575   }
576}
577
578#
579# Generate .pot file from POTFILES.in
580#
581sub GeneratePOTemplate
582{
583    my $XGETTEXT = $ENV{"XGETTEXT"} || "@INTLTOOL_XGETTEXT@";
584    my $XGETTEXT_ARGS = $ENV{"XGETTEXT_ARGS"} || '';
585    chomp $XGETTEXT;
586
587    if (! -x $XGETTEXT)
588    {
589        print STDERR " *** xgettext is not found on this system!\n".
590                     " *** Without it, intltool-update can not extract strings.\n";
591        exit;
592    }
593
594    print "Building $MODULE.pot...\n" if $VERBOSE;
595
596    open INFILE, $POTFILES_in;
597    unlink "POTFILES.in.temp";
598    open OUTFILE, ">POTFILES.in.temp" or die("Cannot open POTFILES.in.temp for writing");
599
600    my $gettext_support_nonascii = 0;
601
602    # checks for GNU gettext >= 0.12
603    my $dummy = `$XGETTEXT --version --from-code=UTF-8 >/dev/null 2>/dev/null`;
604    if ($? == 0)
605    {
606        $gettext_support_nonascii = 1;
607    }
608    else
609    {
610        # urge everybody to upgrade gettext
611        print STDERR "WARNING: This version of gettext does not support extracting non-ASCII\n".
612                     "         strings. That means you should install a version of gettext\n".
613                     "         that supports non-ASCII strings (such as GNU gettext >= 0.12),\n".
614                     "         or have to let non-ASCII strings untranslated. (If there is any)\n";
615    }
616
617    my $encoding = "ASCII";
618    my $forced_gettext_code;
619    my @temp_headers;
620    my $encoding_problem_is_reported = 0;
621
622    while (<INFILE>)
623    {
624        next if (/^#/ or /^\s*$/);
625
626        chomp;
627
628        my $gettext_code;
629
630        if (/^\[\s*encoding:\s*(.*)\s*\]/)
631        {
632            $forced_gettext_code=$1;
633        }
634        elsif (/\.($xml_support|$ini_support)$/ || /^\[/)
635        {
636            s/^\[.*]\s*//;
637            print OUTFILE "$_.h\n";
638            push @temp_headers, "../$_.h";
639            $gettext_code = &TextFile_DetermineEncoding ("../$_.h") if ($gettext_support_nonascii and not defined $forced_gettext_code);
640        }
641        else
642        {
643            if ($SRCDIR eq ".") {
644                print OUTFILE "$_\n";
645            } else {
646                print OUTFILE "$SRCDIR/../$_\n";
647            }
648            $gettext_code = &TextFile_DetermineEncoding ("../$_") if ($gettext_support_nonascii and not defined $forced_gettext_code);
649        }
650
651        next if (! $gettext_support_nonascii);
652
653        if (defined $forced_gettext_code)
654        {
655            $encoding=$forced_gettext_code;
656        }
657        elsif (defined $gettext_code and "$encoding" ne "$gettext_code")
658        {
659            if ($encoding eq "ASCII")
660            {
661                $encoding=$gettext_code;
662            }
663            elsif ($gettext_code ne "ASCII")
664            {
665                # Only report once because the message is quite long
666                if (! $encoding_problem_is_reported)
667                {
668                    print STDERR "WARNING: You should use the same file encoding for all your project files,\n".
669                                 "         but $PROGRAM thinks that most of the source files are in\n".
670                                 "         $encoding encoding, while \"$_\" is (likely) in\n".
671                                 "         $gettext_code encoding. If you are sure that all translatable strings\n".
672                                 "         are in same encoding (say UTF-8), please \e[1m*prepend*\e[0m the following\n".
673                                 "         line to POTFILES.in:\n\n".
674                                 "                 [encoding: UTF-8]\n\n".
675                                 "         and make sure that configure.in/ac checks for $PACKAGE >= 0.27 .\n".
676                                 "(such warning message will only be reported once.)\n";
677                    $encoding_problem_is_reported = 1;
678                }
679            }
680        }
681    }
682
683    close OUTFILE;
684    close INFILE;
685
686    unlink "$MODULE.pot";
687    my @xgettext_argument=("$XGETTEXT",
688                           "--add-comments",
689                           "--directory\=\.\.",
690                           "--output\=$MODULE\.pot",
691                           "--files-from\=\.\/POTFILES\.in\.temp");
692    my $XGETTEXT_KEYWORDS = &FindPOTKeywords;
693    push @xgettext_argument, $XGETTEXT_KEYWORDS;
694    push @xgettext_argument, "--from-code\=$encoding" if ($gettext_support_nonascii);
695    push @xgettext_argument, $XGETTEXT_ARGS if $XGETTEXT_ARGS;
696    my $xgettext_command = join ' ', @xgettext_argument;
697
698    # intercept xgettext error message
699    print "Running $xgettext_command\n" if $VERBOSE;
700    my $xgettext_error_msg = `$xgettext_command 2>\&1`;
701    my $command_failed = $?;
702
703    unlink "POTFILES.in.temp";
704
705    print "Removing generated header (.h) files..." if $VERBOSE;
706    unlink foreach (@temp_headers);
707    print "done.\n" if $VERBOSE;
708
709    if (! $command_failed)
710    {
711        if (! -e "$MODULE.pot")
712        {
713            print "None of the files in POTFILES.in contain strings marked for translation.\n" if $VERBOSE;
714        }
715        else
716        {
717            print "Wrote $MODULE.pot\n" if $VERBOSE;
718        }
719    }
720    else
721    {
722        if ($xgettext_error_msg =~ /--from-code/)
723        {
724            # replace non-ASCII error message with a more useful one.
725            print STDERR "ERROR: xgettext failed to generate PO template file because there is non-ASCII\n".
726                         "       string marked for translation. Please make sure that all strings marked\n".
727                         "       for translation are in uniform encoding (say UTF-8), then \e[1m*prepend*\e[0m the\n".
728                         "       following line to POTFILES.in and rerun $PROGRAM:\n\n".
729                         "           [encoding: UTF-8]\n\n";
730        }
731        else
732        {
733            print STDERR "$xgettext_error_msg";
734            if (-e "$MODULE.pot")
735            {
736                # is this possible?
737                print STDERR "ERROR: xgettext failed but still managed to generate PO template file.\n".
738                             "       Please consult error message above if there is any.\n";
739            }
740            else
741            {
742                print STDERR "ERROR: xgettext failed to generate PO template file. Please consult\n".
743                             "       error message above if there is any.\n";
744            }
745        }
746        exit (1);
747    }
748}
749
750sub POFile_Update
751{
752    -f "$MODULE.pot" or die "$PROGRAM: $MODULE.pot does not exist.\n";
753
754    my $MSGMERGE = $ENV{"MSGMERGE"} || "@INTLTOOL_MSGMERGE@";
755    my ($lang, $outfile) = @_;
756
757    print "Merging $lang.po with $MODULE.pot..." if $VERBOSE;
758
759    my $infile = "$lang.po";
760    $outfile = "$lang.po" if ($outfile eq "");
761
762    # I think msgmerge won't overwrite old file if merge is not successful
763    system ("$MSGMERGE", "-o", $outfile, $infile, "$MODULE.pot");
764}
765
766sub Console_WriteError_NotExisting
767{
768    my ($file) = @_;
769
770    ## Report error if supplied language file is non-existing
771    print STDERR "$PROGRAM: $file does not exist!\n";
772    print STDERR "Try '$PROGRAM --help' for more information.\n";
773    exit;
774}
775
776sub GatherPOFiles
777{
778    my @po_files = glob ("./*.po");
779
780    @languages = map (&POFile_GetLanguage, @po_files);
781
782    foreach my $lang (@languages)
783    {
784        $po_files_by_lang{$lang} = shift (@po_files);
785    }
786}
787
788sub POFile_GetLanguage ($)
789{
790    s/^(.*\/)?(.+)\.po$/$2/;
791    return $_;
792}
793
794sub Console_Write_TranslationStatus
795{
796    my ($lang, $output_file) = @_;
797    my $MSGFMT = $ENV{"MSGFMT"} || "@INTLTOOL_MSGFMT@";
798
799    $output_file = "$lang.po" if ($output_file eq "");
800
801    system ("$MSGFMT", "-o", "/dev/null", "--statistics", $output_file);
802}
803
804sub Console_Write_CoverageReport
805{
806    my $MSGFMT = $ENV{"MSGFMT"} || "@INTLTOOL_MSGFMT@";
807
808    &GatherPOFiles;
809
810    foreach my $lang (@languages)
811    {
812        print "$lang: ";
813        &POFile_Update ($lang, "");
814    }
815
816    print "\n\n * Current translation support in $MODULE \n\n";
817
818    foreach my $lang (@languages)
819    {
820        print "$lang: ";
821        system ("$MSGFMT", "-o", "/dev/null", "--statistics", "$lang.po");
822    }
823}
824
825sub SubstituteVariable
826{
827    my ($str) = @_;
828   
829    # always need to rewind file whenever it has been accessed
830    seek (CONF, 0, 0);
831
832    # cache each variable. varhash is global to we can add
833    # variables elsewhere.
834    while (<CONF>)
835    {
836        if (/^(\w+)=(.*)$/)
837        {
838            ($varhash{$1} = $2) =~  s/^["'](.*)["']$/$1/;
839        }
840    }
841   
842    if ($str =~ /^(.*)\${?([A-Z_]+)}?(.*)$/)
843    {
844        my $rest = $3;
845        my $untouched = $1;
846        my $sub = $varhash{$2};
847       
848        return SubstituteVariable ("$untouched$sub$rest");
849    }
850   
851    # We're using Perl backticks ` and "echo -n" here in order to
852    # expand any shell escapes (such as backticks themselves) in every variable
853    return `echo -n "$str"`;
854}
855
856sub CONF_Handle_Open
857{
858    my $base_dirname = getcwd();
859    $base_dirname =~ s@.*/@@;
860
861    my ($conf_in, $src_dir);
862
863    if ($base_dirname =~ /^po(-.+)?$/)
864    {
865        if (-f "Makevars")
866        {
867            my $makefile_source;
868
869            local (*IN);
870            open (IN, "<Makevars") || die "can't open Makevars: $!";
871
872            while (<IN>)
873            {
874                if (/^top_builddir[ \t]*=/)
875                {
876                    $src_dir = $_;
877                    $src_dir =~ s/^top_builddir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
878
879                    chomp $src_dir;
880                    if (-f "$src_dir" . "/configure.ac") {
881                        $conf_in = "$src_dir" . "/configure.ac" . "\n";
882                    } else {
883                        $conf_in = "$src_dir" . "/configure.in" . "\n";
884                    }
885                    last;
886                }
887            }
888            close IN;
889
890            $conf_in || die "Cannot find top_builddir in Makevars.";
891        }
892        elsif (-f "../configure.ac")
893        {
894            $conf_in = "../configure.ac";
895        }
896        elsif (-f "../configure.in")
897        {
898            $conf_in = "../configure.in";
899        }
900        else
901        {
902            my $makefile_source;
903
904            local (*IN);
905            open (IN, "<Makefile") || return;
906
907            while (<IN>)
908            {
909                if (/^top_srcdir[ \t]*=/)
910                {
911                    $src_dir = $_;                 
912                    $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
913
914                    chomp $src_dir;
915                    $conf_in = "$src_dir" . "/configure.in" . "\n";
916
917                    last;
918                }
919            }
920            close IN;
921
922            $conf_in || die "Cannot find top_srcdir in Makefile.";
923        }
924
925        open (CONF, "<$conf_in");
926    }
927    else
928    {
929        print STDERR "$PROGRAM: Unable to proceed.\n" .
930                     "Make sure to run this script inside the po directory.\n";
931        exit;
932    }
933}
934
935sub FindPackageName
936{
937    my $version;
938    my $domain = &FindMakevarsDomain;
939    my $name = $domain || "untitled";
940
941    &CONF_Handle_Open;
942
943    my $conf_source; {
944        local (*IN);
945        open (IN, "<&CONF") || return $name;
946        seek (IN, 0, 0);
947        local $/; # slurp mode
948        $conf_source = <IN>;
949        close IN;
950    }
951
952    # priority for getting package name:
953    # 1. GETTEXT_PACKAGE
954    # 2. first argument of AC_INIT (with >= 2 arguments)
955    # 3. first argument of AM_INIT_AUTOMAKE (with >= 2 argument)
956
957    # /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m
958    # the \s makes this not work, why?
959    if ($conf_source =~ /^AM_INIT_AUTOMAKE\(([^,\)]+),([^,\)]+)/m)
960    {
961        ($name, $version) = ($1, $2);
962        $name    =~ s/[\[\]\s]//g;
963        $version =~ s/[\[\]\s]//g;
964        $varhash{"AC_PACKAGE_NAME"} = $name;
965        $varhash{"PACKAGE"} = $name;
966        $varhash{"AC_PACKAGE_VERSION"} = $version;
967        $varhash{"VERSION"} = $version;
968    }
969   
970    if ($conf_source =~ /^AC_INIT\(([^,\)]+),([^,\)]+)/m)
971    {
972        ($name, $version) = ($1, $2);
973        $name    =~ s/[\[\]\s]//g;
974        $version =~ s/[\[\]\s]//g;
975        $varhash{"AC_PACKAGE_NAME"} = $name;
976        $varhash{"PACKAGE"} = $name;
977        $varhash{"AC_PACKAGE_VERSION"} = $version;
978        $varhash{"VERSION"} = $version;
979    }
980
981    # \s makes this not work, why?
982    $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\n\]]+)/m;
983   
984    # prepend '$' to auto* internal variables, usually they are
985    # used in configure.in/ac without the '$'
986    $name =~ s/AC_/\$AC_/g;
987    $name =~ s/\$\$/\$/g;
988
989    $name = $domain if $domain;
990
991    $name = SubstituteVariable ($name);
992    $name =~ s/^["'](.*)["']$/$1/;
993
994    return $name if $name;
995}
996
997
998sub FindPOTKeywords
999{
1000
1001    my $keywords = "--keyword\=\_ --keyword\=N\_ --keyword\=U\_";
1002    my $varname = "XGETTEXT_OPTIONS";
1003    my $make_source; {
1004        local (*IN);
1005        open (IN, "<Makevars") || (open(IN, "<Makefile.in.in") && ($varname = "XGETTEXT_KEYWORDS")) || return $keywords;
1006        seek (IN, 0, 0);
1007        local $/; # slurp mode
1008        $make_source = <IN>;
1009        close IN;
1010    }
1011
1012    $keywords = $1 if $make_source =~ /^$varname[ ]*=\[?([^\n\]]+)/m;
1013   
1014    return $keywords;
1015}
1016
1017sub FindMakevarsDomain
1018{
1019
1020    my $domain = "";
1021    my $makevars_source; {
1022        local (*IN);
1023        open (IN, "<Makevars") || return $domain;
1024        seek (IN, 0, 0);
1025        local $/; # slurp mode
1026        $makevars_source = <IN>;
1027        close IN;
1028    }
1029
1030    $domain = $1 if $makevars_source =~ /^DOMAIN[ ]*=\[?([^\n\]\$]+)/m;
1031    $domain =~ s/^\s+//;
1032    $domain =~ s/\s+$//;
1033   
1034    return $domain;
1035}
Note: See TracBrowser for help on using the repository browser.