diff options
Diffstat (limited to 'po/intltool-update')
-rwxr-xr-x | po/intltool-update | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/po/intltool-update b/po/intltool-update new file mode 100755 index 00000000..71a556e5 --- /dev/null +++ b/po/intltool-update @@ -0,0 +1,634 @@ +#!/usr/bin/perl -w + +# +# The Intltool Message Updater +# +# Copyright (C) 2000-2002 Free Software Foundation. +# +# Intltool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# version 2 published by the Free Software Foundation. +# +# Intltool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. +# +# Authors: Kenneth Christiansen <kenneth@gnu.org> +# Maciej Stachowiak +# Darin Adler <darin@bentspoon.com> + +## Release information +my $PROGRAM = "intltool-update"; +my $VERSION = "0.22"; +my $PACKAGE = "intltool"; + +## Loaded modules +use strict; +use Getopt::Long; +use Cwd; +use File::Copy; +use File::Find; + +## Scalars used by the option stuff +my $HELP_ARG = 0; +my $VERSION_ARG = 0; +my $DIST_ARG = 0; +my $POT_ARG = 0; +my $HEADERS_ARG = 0; +my $MAINTAIN_ARG = 0; +my $REPORT_ARG = 0; +my $VERBOSE = 0; +my $GETTEXT_PACKAGE = ""; + +my @languages; +my %po_files_by_lang = (); + +# Regular expressions to categorize file types. +# FIXME: Please check if the following is correct + +my $xml_extension = +"xml(\.in)*|". # .in is not required +"ui|". +"glade2?(\.in)*|". # .in is not required +"scm(\.in)*|". # .in is not required +"oaf(\.in)+|". +"etspec|". +"sheet(\.in)+|". +"schemas(\.in)+|". +"pong(\.in)+"; + +my $ini_extension = +"desktop(\.in)+|". +"caves(\.in)+|". +"directory(\.in)+|". +"soundlist(\.in)+|". +"keys(\.in)+|". +"server(\.in)+"; + +## Always print as the first thing +$| = 1; + +## Handle options +GetOptions +( + "help" => \$HELP_ARG, + "version" => \$VERSION_ARG, + "dist|d" => \$DIST_ARG, + "pot|p" => \$POT_ARG, + "headers|s" => \$HEADERS_ARG, + "maintain|m" => \$MAINTAIN_ARG, + "report|r" => \$REPORT_ARG, + "verbose|x" => \$VERBOSE, + "gettext-package|g=s" => \$GETTEXT_PACKAGE, + ) or &print_error_invalid_option; + +&print_help if $HELP_ARG; +&print_version if $VERSION_ARG; + +my $arg_count = ($DIST_ARG > 0) + + ($POT_ARG > 0) + + ($HEADERS_ARG > 0) + + ($MAINTAIN_ARG > 0) + + ($REPORT_ARG > 0); +&print_help if $arg_count > 1; + +# --version and --help don't require a module name +my $MODULE = $GETTEXT_PACKAGE || &find_package_name; + +if ($DIST_ARG) { + if ($ARGV[0] =~ /^[a-z]/){ + &update_po_file ($ARGV[0]); + &print_status ($ARGV[0]); + } else { + &print_help; + } +} elsif ($POT_ARG) { + &generate_headers; + &generate_po_template; +} elsif ($HEADERS_ARG) { + &generate_headers; +} elsif ($MAINTAIN_ARG) { + &find_leftout_files; +} elsif ($REPORT_ARG) { + &print_report; +} else { + if ($ARGV[0] =~ /^[a-z]/) { + &main ($ARGV[0]); + } else { + &print_help; + } +} + +exit; + +######### + +sub print_version +{ + ## Print version information + print "${PROGRAM} (${PACKAGE}) $VERSION\n"; + print "Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler.\n\n"; + print "Copyright (C) 2000-2002 Free Software Foundation, Inc.\n"; + print "This is free software; see the source for copying conditions. There is NO\n"; + print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; + exit; +} + +sub print_help +{ + ## Print usage information + print "Usage: ${PROGRAM} [OPTIONS] ...LANGCODE\n"; + print "Updates PO template files and merge them with the translations.\n\n"; + print " -p, --pot generate the PO template only\n"; + print " -s, --headers generate the header files in POTFILES.in\n"; + print " -m, --maintain search for left out files from POTFILES.in\n"; + print " -r, --report display a status report for the module.\n"; + print " -x, --verbose display lots of feedback\n"; + print " --help display this help and exit\n"; + print " --version output version information and exit\n"; + print "\nExamples of use:\n"; + print "${PROGRAM} --pot just creates a new PO template from the source\n"; + print "${PROGRAM} da created new PO template and updated the da.po file\n\n"; + print "Report bugs to bugzilla.gnome.org, module 'intltool'.\n"; + exit; +} + +sub main +{ + my ($lang) = @_; + + ## Report error if the language file supplied + ## to the command line is non-existent + &print_error_not_existing("$lang.po") if ! -s "$lang.po"; + + print "Working, please wait..." unless $VERBOSE; + &generate_headers; + &generate_po_template; + &update_po_file ($lang); + &print_status ($lang); +} + +sub determine_type ($) +{ + my $type = $_; + my $gettext_type; + + # FIXME: Use $xml_extentions, and maybe do all this even nicer + my $xml_regex = + "(?:xml(\.in)*|ui|oaf(?:\.in)+|server(?:\.in)+|sheet(?:\.in)+|". + "pong(?:\.in)+|etspec|schemas(?:\.in)+)"; + my $ini_regex = + "(?:desktop(?:\.in)+|caves(?:\.in)+|directory(?:\.in)+|". + "soundlist(?:\.in)+)"; + + if ($type =~ /\[type: gettext\/([^\]].*)]/) { + $gettext_type=$1; + } + elsif ($type =~ /schemas(\.in)+$/) { + $gettext_type="schemas"; + } + elsif ($type =~ /$xml_regex$/) { + $gettext_type="xml"; + } + elsif ($type =~ /glade2?(\.in)*$/) { + $gettext_type="glade"; + } + elsif ($type =~ /$ini_regex$/) { + $gettext_type="ini"; + } + elsif ($type =~ /scm(\.in)*$/) { + $gettext_type="scheme"; + } + elsif ($type =~ /keys(\.in)+$/) { + $gettext_type="keys"; + } + else { $gettext_type=""; } + + return "gettext\/$gettext_type"; +} + +sub find_leftout_files +{ + my (@buf_i18n_plain, + @buf_i18n_xml, + @buf_i18n_xml_unmarked, + @buf_i18n_ini, + @buf_potfiles, + @buf_potfiles_ignore, + @buf_allfiles, + @buf_allfiles_sorted, + @buf_potfiles_sorted + ); + + ## Search and find all translatable files + find sub { + push @buf_i18n_plain, "$File::Find::name" if /\.(c|y|cc|cpp|c\+\+|h|gob)$/ + }, ".."; + find sub { + push @buf_i18n_xml, "$File::Find::name" if /\.($xml_extension)$/ + }, ".."; + find sub { + push @buf_i18n_ini, "$File::Find::name" if /\.($ini_extension)$/ + }, ".."; + find sub { + push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/ + }, ".."; + + + open POTFILES, "POTFILES.in" or die "$PROGRAM: there's no POTFILES.in!\n"; + + @buf_potfiles = grep /^[^#]/, <POTFILES>; + + print "Searching for missing translatable files...\n" if $VERBOSE; + + ## Check if we should ignore some found files, when + ## comparing with POTFILES.in + foreach my $ignore ("POTFILES.skip", "POTFILES.ignore") { + if (-s $ignore) { + open FILE, $ignore; + while (<FILE>) { + if (/^[^#]/){ + push @buf_potfiles_ignore, $_; + } + } + print "Found $ignore: Ignoring files...\n" if $VERBOSE; + @buf_potfiles = (@buf_potfiles_ignore, @buf_potfiles); + } + } + + foreach my $file (@buf_i18n_plain) + { + my $in_comment = 0; + my $in_macro = 0; + + open FILE, "<$file"; + while (<FILE>) + { + # Handle continued multi-line comment. + if ($in_comment) + { + next unless s-.*\*/--; + $in_comment = 0; + } + + # Handle continued macro. + if ($in_macro) + { + $in_macro = 0 unless /\\$/; + next; + } + + # Handle start of macro (or any preprocessor directive). + if (/^\s*\#/) + { + $in_macro = 1 if /^([^\\]|\\.)*\\$/; + next; + } + + # Handle comments and quoted text. + while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy + { + my $match = $1; + if ($match eq "/*") + { + if (!s-/\*.*?\*/--) + { + s-/\*.*--; + $in_comment = 1; + } + } + elsif ($match eq "//") + { + s-//.*--; + } + else # ' or " + { + if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-) + { + warn "mismatched quotes at line $. in $file\n"; + s-$match.*--; + } + } + } + + + if (/_\(QUOTEDTEXT/) + { + ## Remove the first 3 chars and add newline + push @buf_allfiles, unpack("x3 A*", $file) . "\n"; + last; + } + } + close FILE; + } + + foreach my $file (@buf_i18n_xml) { + open FILE, "<$file"; + while (<FILE>) { + if (/\s_(.*)=\"/ || /translatable=\"yes\"/){ + push @buf_allfiles, unpack("x3 A*", $file) . "\n"; + last; + } + } + } + + foreach my $file (@buf_i18n_ini){ + open FILE, "<$file"; + while (<FILE>) { + if (/_(.*)=/){ + push @buf_allfiles, unpack("x3 A*", $file) . "\n"; + last; + } + } + } + + foreach my $file (@buf_i18n_xml_unmarked){ + push @buf_allfiles, unpack("x3 A*", $file) . "\n"; + } + + + @buf_allfiles_sorted = sort (@buf_allfiles); + @buf_potfiles_sorted = sort (@buf_potfiles); + + my %in2; + foreach (@buf_potfiles_sorted) { + $in2{$_} = 1; + } + + my @result; + + foreach (@buf_allfiles_sorted){ + if (!exists($in2{$_})){ + push @result, $_ + } + } + + ## Save file with information about the files missing + ## if any, and give information about this procedure. + if (@result) { + print "\n" if $VERBOSE; + open OUT, ">missing"; + print OUT @result; + print "The following files contain translations and are currently not in use. Please\n"; + print "consider adding these to the POTFILES.in file, located in the po/ directory.\n\n"; + print @result, "\n"; + print "If some of these files are left out on purpose then please add them to\n"; + print "POTFILES.skip instead of POTFILES.in. A file 'missing' containing this list\n"; + print "of left out files has been written in the current directory.\n"; + } + + ## If there is nothing to complain about, notify the user + else { + print "\nAll files containing translations are present in POTFILES.in.\n"; + } +} + +sub print_error_invalid_option +{ + ## Handle invalid arguments + print "Try `${PROGRAM} --help' for more information.\n"; + exit 1; +} + +sub generate_headers +{ + my $EXTRACT = `which intltool-extract 2>/dev/null`; + chomp $EXTRACT; + + $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} if $ENV{"INTLTOOL_EXTRACT"}; + + ## Generate the .h header files, so we can allow glade and + ## xml translation support + if (! -s $EXTRACT) + { + print "\n *** The intltool-extract script wasn't found!" + ."\n *** Without it, intltool-update can not generate files.\n"; + exit; + } + else + { + open FILE, "<POTFILES.in"; + while (<FILE>) { + chomp; + + ## Find xml files in POTFILES.in and generate the + ## files with help from the extract script + + my $gettext_type= &determine_type ($1); + + if (/\.($xml_extension|$ini_extension)$/ || /^\[/){ + $_ =~ s/^\[[^\[].*]\s*//; + my $filename = "../$_"; + + if ($VERBOSE){ + system($EXTRACT, "--update", "--type=$gettext_type", $filename); + } else { + system($EXTRACT, "--update", "--type=$gettext_type", "--quiet", $filename); + } + } + } + close FILE; + } +} + +sub generate_po_template +{ + ## Generate the potfiles from the POTFILES.in file + + print "Building the $MODULE.pot...\n" if $VERBOSE; + + move ("POTFILES.in", "POTFILES.in.old"); + + open INFILE, "<POTFILES.in.old"; + open OUTFILE, ">POTFILES.in"; + while (<INFILE>) { + s/\.($xml_extension|$ini_extension)$/$&.h/; + s/^\[.*]\s*(.*)/$1.h/; + print OUTFILE $_; + } + close OUTFILE; + close INFILE; + + system ("xgettext", "--default-domain\=$MODULE", + "--directory\=\.\.", + "--add-comments", + "--keyword\=\_", + "--keyword\=N\_", + "--keyword\=U\_", + "--files-from\=\.\/POTFILES\.in"); + + move ("POTFILES.in.old", "POTFILES.in"); + + print "Removing generated header (.h) files..." if $VERBOSE; + + open FILE, "<POTFILES.in"; + + while (<FILE>) + { + chomp; + unlink "../$_.h" if /\.($xml_extension|$ini_extension)$/; + } + + close FILE; + print "done\n" if $VERBOSE; + + if (!-e "$MODULE.po") { + print "WARNING: It seems that none of the files in POTFILES.in ". + "contain marked strings\n"; + exit (1); + } + + system ("rm", "-f", "$MODULE.pot"); + move ("$MODULE.po", "$MODULE.pot") or die "$PROGRAM: couldn't move $MODULE.po to $MODULE.pot.\n"; + + print "Wrote $MODULE.pot\n" if $VERBOSE; +} + +sub update_po_file +{ + my ($lang) = @_; + + print "Merging $lang.po with $MODULE.pot..." if $VERBOSE; + + copy ("$lang.po", "$lang.po.old") || die "copy failed: $!"; + + # Perform merge, remove backup file and the "messages" trash file + # generated by gettext + system ("msgmerge", "$lang.po.old", "$MODULE.pot", "-o", "$lang.po"); + unlink "$lang.po.old"; + unlink "messages"; +} + +sub print_error_not_existing +{ + my ($file) = @_; + + ## Report error if supplied language file is non-existing + print "$PROGRAM: $file does not exist!\n"; + print "Try '$PROGRAM --help' for more information.\n"; + exit; +} + +sub gather_po_files +{ + my @po_files = glob ("./*.po"); + + @languages = map (&po_file2lang, @po_files); + + foreach my $lang (@languages) { + $po_files_by_lang{$lang} = shift (@po_files); + } +} + +sub po_file2lang ($) +{ + my $tmp = $_; + $tmp =~ s/^.*\/(.*)\.po$/$1/; + return $tmp; +} + +sub print_status +{ + my ($lang) = @_; + + system ("msgfmt", "--statistics", "$lang.po"); + print "\n"; +} + +sub print_report +{ + &generate_headers; + &generate_po_template; + &gather_po_files; + + foreach my $lang (@languages) { + print "$lang: "; + &update_po_file ($lang); + } + + print "\n\n * Current translation support in $MODULE \n\n"; + + foreach my $lang (@languages){ + print "$lang: "; + system ("msgfmt", "--statistics", "$lang.po"); + } +} + +sub find_package_name +{ + my $base_dirname = getcwd(); + $base_dirname =~ s@.*/@@; + + my ($conf_in, $src_dir); + + if ($base_dirname =~ /^po(-.+)?$/) { + if (-f "../configure.in") { + $conf_in = "../configure.in"; + } elsif (-f "../configure.ac") { + $conf_in = "../configure.ac"; + } else { + my $makefile_source; + local (*IN); + open IN, "<Makefile" || die "can't open Makefile: $!"; + + while (<IN>) { + if (/^top_srcdir[ \t]*=/) { + $src_dir = $_; + # print "${src_dir}\n"; + + $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/; + # print "${src_dir}\n"; + chomp $src_dir; + $conf_in = "$src_dir" . "/configure.in" . "\n"; + last; + } + } + $conf_in || die "Cannot find top_srcdir in Makefile." + } + + my %varhash = (); + my $conf_source; { + local (*IN); + open (IN, "<$conf_in") || die "can't open $conf_in: $!"; + while (<IN>) { + if (/^(\w+)=(\S+)/) { $varhash{$1} = $2 }; + } + seek (IN, 0, 0); + local $/; # slurp mode + $conf_source = <IN>; + } + + my $name = ""; + $name = $1 if $conf_source =~ /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m; + if ($conf_source =~ /^AC_INIT\([\s\[]*([^,\)\s\]]+)\]?\s*,/m) { + $name = $1; + $varhash{"AC_PACKAGE_NAME"} = $1; + } + $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\s\]]+)/m; + + $name = "\$AC_PACKAGE_NAME" if "$name" eq "AC_PACKAGE_NAME"; + + my $oldname = ""; + while (($name =~ /[\$](\S+)/) && ("$oldname" ne "$name")) { + $oldname = $name; + if (exists $varhash{$1}) { + $name =~ s/[\$](\S+)/$varhash{$1}/; + } + } + return $name if $name; + } + + print "$PROGRAM: Unable to determine package name.\n" . + "Make sure to run this script inside the po directory.\n"; + exit; +} |