58
59
# Regular expressions to categorize file types.
59
60
# FIXME: Please check if the following is correct
62
"xml(\.in)*|". # .in is not required
65
"glade2?(\.in)*|". # .in is not required
66
"scm(\.in)*|". # .in is not required
63
"xml(?:\\.in)*|". # http://www.w3.org/XML/ (Note: .in is not required)
64
"ui|". # Bonobo specific - User Interface desc. files
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
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.
77
"icon(?:\\.in)+|". # http://www.freedesktop.org/Standards/icon-theme-spec
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
"service(?:\\.in)+"; # DBus specific
86
my $buildin_gettext_support =
87
"c|y|cs|cc|cpp|c\\+\\+|h|hh|gob|py";
82
89
## Always flush buffer when printing
92
## Sometimes the source tree will be rooted somewhere else.
93
my $SRCDIR = $ENV{"srcdir"} || ".";
96
$POTFILES_in = "<$SRCDIR/POTFILES.in";
98
my $devnull = ($^O eq 'MSWin32' ? 'NUL:' : '/dev/null');
95
110
"verbose|x" => \$VERBOSE,
96
111
"gettext-package|g=s" => \$GETTEXT_PACKAGE,
97
112
"output-file|o=s" => \$OUTPUT_FILE,
98
) or &print_error_invalid_option;
113
) or &Console_WriteError_InvalidOption;
100
&print_help if $HELP_ARG;
101
&print_version if $VERSION_ARG;
115
&Console_Write_IntltoolHelp if $HELP_ARG;
116
&Console_Write_IntltoolVersion if $VERSION_ARG;
103
118
my $arg_count = ($DIST_ARG > 0)
106
121
+ ($MAINTAIN_ARG > 0)
107
122
+ ($REPORT_ARG > 0);
109
&print_help if $arg_count > 1;
124
&Console_Write_IntltoolHelp if $arg_count > 1;
126
my $PKGNAME = FindPackageName ();
111
128
# --version and --help don't require a module name
112
my $MODULE = $GETTEXT_PACKAGE || &find_package_name;
129
my $MODULE = $GETTEXT_PACKAGE || $PKGNAME || "unknown";
117
&generate_po_template;
119
136
elsif ($HEADERS_ARG)
123
140
elsif ($MAINTAIN_ARG)
127
144
elsif ($REPORT_ARG)
130
&generate_po_template;
148
&Console_Write_CoverageReport;
133
150
elsif ((defined $ARGV[0]) && $ARGV[0] =~ /^[a-z]/)
137
154
## Report error if the language file supplied
138
155
## to the command line is non-existent
139
&print_error_not_existing("$lang.po") if ! -s "$lang.po";
156
&Console_WriteError_NotExisting("$SRCDIR/$lang.po")
157
if ! -s "$SRCDIR/$lang.po";
143
161
print "Working, please wait..." if $VERBOSE;
145
&generate_po_template;
147
&update_po_file ($lang, $OUTPUT_FILE);
148
&print_status ($lang, $OUTPUT_FILE);
165
&POFile_Update ($lang, $OUTPUT_FILE);
166
&Console_Write_TranslationStatus ($lang, $OUTPUT_FILE);
170
&Console_Write_IntltoolHelp;
177
sub Console_Write_IntltoolVersion
162
180
${PROGRAM} (${PACKAGE}) $VERSION
202
sub determine_type ($)
223
my $ret = `echo "$str"`;
225
$ret =~ s/\n$//; # do we need the "s" flag?
230
sub POFile_DetermineType ($)
205
233
my $gettext_type;
207
# FIXME: Use $xml_extension, and maybe do all this even nicer
209
"(?:xml(\.in)*|ui|lang|oaf(?:\.in)+|server(?:\.in)+|sheet(?:\.in)+|".
210
"pong(?:\.in)+|etspec|schemas(?:\.in)+)";
212
"(?:desktop(?:\.in)+|theme(?:\.in)+|caves(?:\.in)+|directory(?:\.in)+|".
213
"soundlist(?:\.in)+)";
235
my $xml_regex = "(?:" . $xml_support . ")";
236
my $ini_regex = "(?:" . $ini_support . ")";
237
my $buildin_regex = "(?:" . $buildin_gettext_support . ")";
215
239
if ($type =~ /\[type: gettext\/([^\]].*)]/)
241
257
$gettext_type="keys";
262
elsif ($type =~ /$xml_regex$/)
266
elsif ($type =~ /$ini_regex$/)
270
elsif ($type =~ /$buildin_regex$/)
272
$gettext_type="buildin";
276
$gettext_type="unknown";
248
279
return "gettext\/$gettext_type";
251
sub determine_code ($)
282
sub TextFile_DetermineEncoding ($)
253
284
my $gettext_code="ASCII"; # All files are ASCII by default
254
285
my $filetype=`file $_ | cut -d ' ' -f 2`;
279
317
@buf_potfiles_ignore,
281
319
@buf_allfiles_sorted,
320
@buf_potfiles_sorted,
321
@buf_potfiles_ignore_sorted
285
324
## Search and find all translatable files
287
push @buf_i18n_plain, "$File::Find::name" if /\.(c|y|cc|cpp|c\+\+|h|gob)$/;
288
push @buf_i18n_xml, "$File::Find::name" if /\.($xml_extension)$/;
289
push @buf_i18n_ini, "$File::Find::name" if /\.($ini_extension)$/;
326
push @buf_i18n_plain, "$File::Find::name" if /\.($buildin_gettext_support)$/;
327
push @buf_i18n_xml, "$File::Find::name" if /\.($xml_support)$/;
328
push @buf_i18n_ini, "$File::Find::name" if /\.($ini_support)$/;
290
329
push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/;
294
open POTFILES, "<POTFILES.in" or die "$PROGRAM: there's no POTFILES.in!\n";
333
open POTFILES, $POTFILES_in or die "$PROGRAM: there's no POTFILES.in!\n";
295
334
@buf_potfiles = grep !/^(#|\s*$)/, <POTFILES>;
385
## Remove the first 3 chars and add newline
386
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
422
if (/\w\.GetString *\(QUOTEDTEXT/)
424
if (defined isNotValidMissing (unpack("x3 A*", $file))) {
425
## Remove the first 3 chars and add newline
426
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
431
## N_ Q_ and _ are the three macros defined in gi8n.h
432
if (/[NQ]?_ *\(QUOTEDTEXT/)
434
if (defined isNotValidMissing (unpack("x3 A*", $file))) {
435
## Remove the first 3 chars and add newline
436
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
399
450
# FIXME: share the pattern matching code with intltool-extract
400
if (/\s_(.*)=\"/ || /<_[^>]+>/ || /translatable=\"yes\"/)
451
if (/\s_[-A-Za-z0-9._:]+\s*=\s*\"([^"]+)\"/ || /<_[^>]+>/ || /translatable=\"yes\"/)
402
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
453
if (defined isNotValidMissing (unpack("x3 A*", $file))) {
454
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
423
478
foreach my $file (@buf_i18n_xml_unmarked)
425
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
480
if (defined isNotValidMissing (unpack("x3 A*", $file))) {
481
push @buf_allfiles, unpack("x3 A*", $file) . "\n";
483
545
warn "\n" if ($VERBOSE or @result);
484
546
warn "\e[1mThe following files do not exist anymore:\e[0m\n\n";
485
547
warn @buf_potfiles_notexist, "\n";
486
warn "Please remove them from POTFILES.in or POTFILES.skip. A file \e[1m'notexist'\e[0m\n".
548
warn "Please remove them from POTFILES.in. A file \e[1m'notexist'\e[0m\n".
487
549
"containing this list of absent files has been written in the current directory.\n";
497
sub print_error_invalid_option
559
sub Console_WriteError_InvalidOption
499
561
## Handle invalid arguments
500
562
print STDERR "Try `${PROGRAM} --help' for more information.\n";
566
sub isIntltoolExtractInPath
506
my $EXTRACT = `which intltool-extract 2>/dev/null`;
569
# If either a file exists, or when run it returns 0 exit status
570
return 1 if ((-x $file) or (system("$file >/dev/null") == 0));
509
$EXTRACT = $ENV{"INTLTOOL_EXTRACT"} if $ENV{"INTLTOOL_EXTRACT"};
576
my $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} || "intltool-extract";
511
578
## Generate the .h header files, so we can allow glade and
512
579
## xml translation support
580
if (! isIntltoolExtractInPath("$EXTRACT"))
515
582
print STDERR "\n *** The intltool-extract script wasn't found!"
516
583
."\n *** Without it, intltool-update can not generate files.\n";
528
595
## Find xml files in POTFILES.in and generate the
529
596
## files with help from the extract script
531
my $gettext_type= &determine_type ($1);
598
my $gettext_type= &POFile_DetermineType ($1);
533
if (/\.($xml_extension|$ini_extension)$/ || /^\[/)
600
if (/\.($xml_support|$ini_support)$/ || /^\[/)
535
602
s/^\[[^\[].*]\s*//;
541
system ($EXTRACT, "--update",
608
system ($EXTRACT, "--update", "--srcdir=$SRCDIR",
542
609
"--type=$gettext_type", $filename);
546
613
system ($EXTRACT, "--update", "--type=$gettext_type",
547
"--quiet", $filename);
614
"--srcdir=$SRCDIR", "--quiet", $filename);
556
623
# Generate .pot file from POTFILES.in
558
sub generate_po_template
625
sub GeneratePOTemplate
560
my $XGETTEXT = `which xgettext 2>/dev/null`;
627
my $XGETTEXT = $ENV{"XGETTEXT"} || "@INTLTOOL_XGETTEXT@";
628
my $XGETTEXT_ARGS = $ENV{"XGETTEXT_ARGS"} || '';
563
$XGETTEXT = $ENV{"XGETTEXT"} if $ENV{"XGETTEXT"};
565
631
if (! -x $XGETTEXT)
567
633
print STDERR " *** xgettext is not found on this system!\n".
572
638
print "Building $MODULE.pot...\n" if $VERBOSE;
574
open INFILE, "<POTFILES.in";
640
open INFILE, $POTFILES_in;
575
641
unlink "POTFILES.in.temp";
576
open OUTFILE, ">POTFILES.in.temp";
642
open OUTFILE, ">POTFILES.in.temp" or die("Cannot open POTFILES.in.temp for writing");
578
644
my $gettext_support_nonascii = 0;
580
646
# checks for GNU gettext >= 0.12
581
# don't use argument list, since shell interpretation is desired here
582
if (system("$XGETTEXT --version --from-code=UTF-8 >&/dev/null") == 0)
647
my $dummy = `$XGETTEXT --version --from-code=UTF-8 >$devnull 2>$devnull`;
584
650
$gettext_support_nonascii = 1;
589
655
print STDERR "WARNING: This version of gettext does not support extracting non-ASCII\n".
590
656
" strings. That means you should install a version of gettext\n".
591
657
" that supports non-ASCII strings (such as GNU gettext >= 0.12),\n".
592
" or have to let non-ASCII strings untranslated.\n";
658
" or have to let non-ASCII strings untranslated. (If there is any)\n";
595
661
my $encoding = "ASCII";
610
676
$forced_gettext_code=$1;
612
elsif (/\.($xml_extension|$ini_extension)$/ || /^\[/)
678
elsif (/\.($xml_support|$ini_support)$/ || /^\[/)
615
print OUTFILE "$_.h\n";
681
print OUTFILE "../$_.h\n";
616
682
push @temp_headers, "../$_.h";
617
$gettext_code = &determine_code ("../$_.h") if ($gettext_support_nonascii and not defined $forced_gettext_code);
683
$gettext_code = &TextFile_DetermineEncoding ("../$_.h") if ($gettext_support_nonascii and not defined $forced_gettext_code);
621
print OUTFILE "$_\n";
622
$gettext_code = &determine_code ("../$_") if ($gettext_support_nonascii and not defined $forced_gettext_code);
687
print OUTFILE "$SRCDIR/../$_\n";
688
$gettext_code = &TextFile_DetermineEncoding ("$SRCDIR/../$_") if ($gettext_support_nonascii and not defined $forced_gettext_code);
625
691
next if (! $gettext_support_nonascii);
660
726
unlink "$MODULE.pot";
661
727
my @xgettext_argument=("$XGETTEXT",
662
728
"--add-comments",
667
730
"--output\=$MODULE\.pot",
668
731
"--files-from\=\.\/POTFILES\.in\.temp");
732
my $XGETTEXT_KEYWORDS = &FindPOTKeywords;
733
push @xgettext_argument, $XGETTEXT_KEYWORDS;
734
my $MSGID_BUGS_ADDRESS = &FindMakevarsBugAddress;
735
push @xgettext_argument, "--msgid-bugs-address\=$MSGID_BUGS_ADDRESS" if $MSGID_BUGS_ADDRESS;
669
736
push @xgettext_argument, "--from-code\=$encoding" if ($gettext_support_nonascii);
737
push @xgettext_argument, $XGETTEXT_ARGS if $XGETTEXT_ARGS;
670
738
my $xgettext_command = join ' ', @xgettext_argument;
672
740
# intercept xgettext error message
741
print "Running $xgettext_command\n" if $VERBOSE;
673
742
my $xgettext_error_msg = `$xgettext_command 2>\&1`;
674
743
my $command_failed = $?;
725
794
-f "$MODULE.pot" or die "$PROGRAM: $MODULE.pot does not exist.\n";
796
my $MSGMERGE = $ENV{"MSGMERGE"} || "@INTLTOOL_MSGMERGE@";
727
797
my ($lang, $outfile) = @_;
729
print "Merging $lang.po with $MODULE.pot..." if $VERBOSE;
799
print "Merging $SRCDIR/$lang.po with $MODULE.pot..." if $VERBOSE;
731
my $infile = "$lang.po";
732
$outfile = "$lang.po" if ($outfile eq "");
801
my $infile = "$SRCDIR/$lang.po";
802
$outfile = "$SRCDIR/$lang.po" if ($outfile eq "");
734
804
# I think msgmerge won't overwrite old file if merge is not successful
735
system ("msgmerge", "-o", $outfile, $infile, "$MODULE.pot");
805
system ("$MSGMERGE", "-o", $outfile, $infile, "$MODULE.pot");
738
sub print_error_not_existing
808
sub Console_WriteError_NotExisting
830
sub POFile_GetLanguage ($)
762
832
s/^(.*\/)?(.+)\.po$/$2/;
836
sub Console_Write_TranslationStatus
768
838
my ($lang, $output_file) = @_;
770
$output_file = "$lang.po" if ($output_file eq "");
772
system ("msgfmt", "-o", "/dev/null", "--statistics", $output_file);
839
my $MSGFMT = $ENV{"MSGFMT"} || "@INTLTOOL_MSGFMT@";
841
$output_file = "$SRCDIR/$lang.po" if ($output_file eq "");
843
system ("$MSGFMT", "-o", "$devnull", "--verbose", $output_file);
846
sub Console_Write_CoverageReport
848
my $MSGFMT = $ENV{"MSGFMT"} || "@INTLTOOL_MSGFMT@";
779
852
foreach my $lang (@languages)
782
&update_po_file ($lang, "");
855
&POFile_Update ($lang, "");
785
858
print "\n\n * Current translation support in $MODULE \n\n";
787
860
foreach my $lang (@languages)
790
system ("msgfmt", "-o", "/dev/null", "--statistics", "$lang.po");
863
system ("$MSGFMT", "-o", "$devnull", "--verbose", "$SRCDIR/$lang.po");
867
sub SubstituteVariable
814
887
my $untouched = $1;
815
my $sub = $varhash{$2};
817
return substitute_var ("$untouched$sub$rest");
889
# Ignore recursive definitions of variables
890
$sub = $varhash{$2} if defined $varhash{$2} and $varhash{$2} !~ /\${?$2}?/;
892
return SubstituteVariable ("$untouched$sub$rest");
895
# We're using Perl backticks ` and "echo -n" here in order to
896
# expand any shell escapes (such as backticks themselves) in every variable
897
return echo_n ($str);
824
902
my $base_dirname = getcwd();
825
903
$base_dirname =~ s@.*/@@;
829
907
if ($base_dirname =~ /^po(-.+)?$/)
831
if (-f "../configure.ac")
914
open (IN, "<Makevars") || die "can't open Makevars: $!";
918
if (/^top_builddir[ \t]*=/)
921
$src_dir =~ s/^top_builddir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
924
if (-f "$src_dir" . "/configure.ac") {
925
$conf_in = "$src_dir" . "/configure.ac" . "\n";
927
$conf_in = "$src_dir" . "/configure.in" . "\n";
934
$conf_in || die "Cannot find top_builddir in Makevars.";
936
elsif (-f "../configure.ac")
833
938
$conf_in = "../configure.ac";
874
sub find_package_name
982
my $domain = &FindMakevarsDomain;
983
my $name = $domain || "untitled";
878
987
my $conf_source; {
880
open (IN, "<&CONF") || die "can't open configure.in/configure.ac: $!";
989
open (IN, "<&CONF") || return $name;
882
991
local $/; # slurp mode
883
992
$conf_source = <IN>;
887
my $name = "untitled";
890
996
# priority for getting package name:
891
997
# 1. GETTEXT_PACKAGE
892
998
# 2. first argument of AC_INIT (with >= 2 arguments)
897
1003
if ($conf_source =~ /^AM_INIT_AUTOMAKE\(([^,\)]+),([^,\)]+)/m)
899
1005
($name, $version) = ($1, $2);
900
$name =~ s/[\[\]\s]//g;
901
($varhash{"AC_PACKAGE_VERSION"} = $version) =~ s/[\[\]\s]//g;
1006
$name =~ s/[\[\]\s]//g;
1007
$version =~ s/[\[\]\s]//g;
1008
$varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/);
1009
$varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/);
1010
$varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/);
1011
$varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/);
904
1014
if ($conf_source =~ /^AC_INIT\(([^,\)]+),([^,\)]+)/m)
906
1016
($name, $version) = ($1, $2);
907
$name=~ s/[\[\]\s]//g;
908
$varhash{"AC_PACKAGE_NAME"} = $name;
909
($varhash{"AC_PACKAGE_VERSION"} = $version) =~ s/[\[\]\s]//g;
1017
$name =~ s/[\[\]\s]//g;
1018
$version =~ s/[\[\]\s]//g;
1019
$varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/);
1020
$varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/);
1021
$varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/);
1022
$varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/);
912
1025
# \s makes this not work, why?
913
1026
$name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\n\]]+)/m;
915
# prepend '$' to auto* internal variables, usually they are
916
# used in configure.in/ac without the '$'
917
$name =~ s/AC_/\$AC_/g;
918
$name =~ s/\$\$/\$/g;
920
$name = substitute_var ($name);
1028
# m4 macros AC_PACKAGE_NAME, AC_PACKAGE_VERSION etc. have same value
1029
# as corresponding $PACKAGE_NAME, $PACKAGE_VERSION etc. shell variables.
1030
$name =~ s/\bAC_PACKAGE_/\$PACKAGE_/g;
1032
$name = $domain if $domain;
1034
$name = SubstituteVariable ($name);
1035
$name =~ s/^["'](.*)["']$/$1/;
922
1037
return $name if $name;
1044
my $keywords = "--keyword\=\_ --keyword\=N\_ --keyword\=U\_ --keyword\=Q\_";
1045
my $varname = "XGETTEXT_OPTIONS";
1048
open (IN, "<Makevars") || (open(IN, "<Makefile.in.in") && ($varname = "XGETTEXT_KEYWORDS")) || return $keywords;
1050
local $/; # slurp mode
1051
$make_source = <IN>;
1055
$keywords = $1 if $make_source =~ /^$varname[ ]*=\[?([^\n\]]+)/m;
1060
sub FindMakevarsDomain
1064
my $makevars_source; {
1066
open (IN, "<Makevars") || return $domain;
1068
local $/; # slurp mode
1069
$makevars_source = <IN>;
1073
$domain = $1 if $makevars_source =~ /^DOMAIN[ ]*=\[?([^\n\]\$]+)/m;
1074
$domain =~ s/^\s+//;
1075
$domain =~ s/\s+$//;
1080
sub FindMakevarsBugAddress
1084
my $makevars_source; {
1086
open (IN, "<Makevars") || return undef;
1088
local $/; # slurp mode
1089
$makevars_source = <IN>;
1093
$address = $1 if $makevars_source =~ /^MSGID_BUGS_ADDRESS[ ]*=\[?([^\n\]\$]+)/m;
1094
$address =~ s/^\s+//;
1095
$address =~ s/\s+$//;