diff options
Diffstat (limited to 'po')
-rw-r--r-- | po/.cvsignore | 2 | ||||
-rw-r--r-- | po/Makefile | 3 | ||||
-rw-r--r-- | po/POTFILES.in | 4 | ||||
-rwxr-xr-x | po/intltool-merge | 657 | ||||
-rwxr-xr-x | po/intltool-update | 634 |
5 files changed, 1299 insertions, 1 deletions
diff --git a/po/.cvsignore b/po/.cvsignore new file mode 100644 index 00000000..7ab1701f --- /dev/null +++ b/po/.cvsignore @@ -0,0 +1,2 @@ +*.mo +.intltool-merge-cache diff --git a/po/Makefile b/po/Makefile index 0876dfd7..9627b115 100644 --- a/po/Makefile +++ b/po/Makefile @@ -34,7 +34,8 @@ merge: $(PGOAL).pot done $(PGOAL).pot: $(PL_CFILES) $(CFILES) - xgettext -F -n --add-comments='-PO' \ + ./intltool-update --gettext-package rpmdrake --pot + xgettext -j -F -n --add-comments='-PO' \ --keyword=_ --keyword=__ --keyword=N_ \ --language=C -o $@ $(PL_CFILES) $(CFILES) @rm -rf $(PL_CFILES) diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 00000000..5a441c20 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,4 @@ +data/SoftwareManagement.directory.in +data/rpmdrake.desktop.in +data/rpmdrake-remove.desktop.in +data/rpmdrake-sources.desktop.in
\ No newline at end of file diff --git a/po/intltool-merge b/po/intltool-merge new file mode 100755 index 00000000..06637146 --- /dev/null +++ b/po/intltool-merge @@ -0,0 +1,657 @@ +#!/usr/bin/perl -w + +# +# The Intltool Message Merger +# +# Copyright (C) 2000, 2002 Free Software Foundation. +# Copyright (C) 2000, 2001 Eazel, Inc +# +# 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: Maciej Stachowiak <mjs@noisehavoc.org> +# Kenneth Christiansen <kenneth@gnu.org> +# Darin Adler <darin@bentspoon.com> +# +# Proper XML UTF-8'ification written by Cyrille Chepelov <chepelov@calixo.net> +# + +## Release information +my $PROGRAM = "intltool-merge"; +my $PACKAGE = "intltool"; +my $VERSION = "0.22"; + +## Loaded modules +use strict; +use Getopt::Long; + +## Scalars used by the option stuff +my $HELP_ARG = 0; +my $VERSION_ARG = 0; +my $BA_STYLE_ARG = 0; +my $XML_STYLE_ARG = 0; +my $KEYS_STYLE_ARG = 0; +my $DESKTOP_STYLE_ARG = 0; +my $SCHEMAS_STYLE_ARG = 0; +my $QUIET_ARG = 0; +my $PASS_THROUGH_ARG = 0; +my $UTF8_ARG = 0; +my $cache_file; + +## Handle options +GetOptions +( + "help" => \$HELP_ARG, + "version" => \$VERSION_ARG, + "quiet|q" => \$QUIET_ARG, + "oaf-style|o" => \$BA_STYLE_ARG, ## for compatibility + "ba-style|b" => \$BA_STYLE_ARG, + "xml-style|x" => \$XML_STYLE_ARG, + "keys-style|k" => \$KEYS_STYLE_ARG, + "desktop-style|d" => \$DESKTOP_STYLE_ARG, + "schemas-style|s" => \$SCHEMAS_STYLE_ARG, + "pass-through|p" => \$PASS_THROUGH_ARG, + "utf8|u" => \$UTF8_ARG, + "cache|c=s" => \$cache_file + ) or &error; + +my $PO_DIR; +my $FILE; +my $OUTFILE; + +my %po_files_by_lang = (); +my %translations = (); + +# Use this instead of \w for XML files to handle more possible characters. +my $w = "[-A-Za-z0-9._:]"; + +# XML quoted string contents +my $q = "[^\\\"]*"; + +## Check for options. + +if ($VERSION_ARG) { + &print_version; +} elsif ($HELP_ARG) { + &print_help; +} elsif ($BA_STYLE_ARG && @ARGV > 2) { + &preparation; + &print_message; + &ba_merge_translations; + &finalize; +} elsif ($XML_STYLE_ARG && @ARGV > 2) { + &utf8_sanity_check; + &preparation; + &print_message; + &xml_merge_translations; + &finalize; +} elsif ($KEYS_STYLE_ARG && @ARGV > 2) { + &utf8_sanity_check; + &preparation; + &print_message; + &keys_merge_translations; + &finalize; +} elsif ($DESKTOP_STYLE_ARG && @ARGV > 2) { + &preparation; + &print_message; + &desktop_merge_translations; + &finalize; +} elsif ($SCHEMAS_STYLE_ARG && @ARGV > 2) { + &preparation; + &print_message; + &schemas_merge_translations; + &finalize; +} else { + &print_help; +} + +exit; + +## Sub for printing release information +sub print_version +{ + print "${PROGRAM} (${PACKAGE}) ${VERSION}\n"; + print "Written by Maciej Stachowiak, Darin Adler and Kenneth Christiansen.\n\n"; + print "Copyright (C) 2000-2002 Free Software Foundation, Inc.\n"; + print "Copyright (C) 2000-2001 Eazel, 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 for printing usage information +sub print_help +{ + print "Usage: ${PROGRAM} [OPTIONS] PO_DIRECTORY FILENAME OUTPUT_FILE\n"; + print "Generates an output file that includes translated versions of some attributes,\n"; + print "from an untranslated source and a po directory that includes translations.\n\n"; + print " -b, --ba-style includes translations in the bonobo-activation style\n"; + print " -d, --desktop-style includes translations in the desktop style\n"; + print " -k, --keys-style includes translations in the keys style\n"; + print " -s, --schemas-style includes translations in the schemas style\n"; + print " -x, --xml-style includes translations in the standard xml style\n"; + print " -u, --utf8 convert all strings to UTF-8 before merging\n"; + print " -p, --pass-through use strings as found in .po files, without\n"; + print " conversion (STRONGLY unrecommended with -x)\n"; + print " -q, --quiet suppress most messages\n"; + print " --help display this help and exit\n"; + print " --version output version information and exit\n"; + print "\nReport bugs to bugzilla.gnome.org, module intltool, or contact us through \n"; + print "<xml-i18n-tools-list\@gnome.org>.\n"; + exit; +} + + +## Sub for printing error messages +sub print_error +{ + print "Try `${PROGRAM} --help' for more information.\n"; + exit; +} + + +sub print_message +{ + print "Merging translations into $OUTFILE.\n" unless $QUIET_ARG; +} + + +sub preparation +{ + $PO_DIR = $ARGV[0]; + $FILE = $ARGV[1]; + $OUTFILE = $ARGV[2]; + + &gather_po_files; + &get_translation_database; +} + +# General-purpose code for looking up translations in .po files + +sub po_file2lang +{ + my ($tmp) = @_; + $tmp =~ s/^.*\/(.*)\.po$/$1/; + return $tmp; +} + +sub gather_po_files +{ + for my $po_file (glob "$PO_DIR/*.po") { + $po_files_by_lang{po_file2lang($po_file)} = $po_file; + } +} + +sub get_po_encoding +{ + my ($in_po_file) = @_; + my $encoding = ""; + + open IN_PO_FILE, $in_po_file or die; + while (<IN_PO_FILE>) { + ## example: "Content-Type: text/plain; charset=ISO-8859-1\n" + if (/Content-Type\:.*charset=([-a-zA-Z0-9]+)\\n/) { + $encoding = $1; + last; + } + } + close IN_PO_FILE; + + if (!$encoding) { + print "Warning: no encoding found in $in_po_file. Assuming ISO-8859-1\n"; + $encoding = "ISO-8859-1"; + } + return $encoding +} + +sub utf8_sanity_check +{ + if (!$UTF8_ARG) { + if (!$PASS_THROUGH_ARG) { + $PASS_THROUGH_ARG="1"; + } + } +} + +sub get_translation_database +{ + if ($cache_file) { + &get_cached_translation_database; + } else { + &create_translation_database; + } +} + +sub get_newest_po_age +{ + my $newest_age; + + foreach my $file (values %po_files_by_lang) { + my $file_age = -M $file; + $newest_age = $file_age if !$newest_age || $file_age < $newest_age; + } + + return $newest_age; +} + +sub create_cache +{ + print "Generating and caching the translation database\n" unless $QUIET_ARG; + + &create_translation_database; + + open CACHE, ">$cache_file" || die; + print CACHE join "\x01", %translations; + close CACHE; +} + +sub load_cache +{ + print "Found cached translation database\n" unless $QUIET_ARG; + + my $contents; + open CACHE, "<$cache_file" || die; + { + local $/; + $contents = <CACHE>; + } + close CACHE; + %translations = split "\x01", $contents; +} + +sub get_cached_translation_database +{ + my $cache_file_age = -M $cache_file; + if (defined $cache_file_age) { + if ($cache_file_age <= &get_newest_po_age) { + &load_cache; + return; + } + print "Found too-old cached translation database\n" unless $QUIET_ARG; + } + + &create_cache; +} + +sub create_translation_database +{ + for my $lang (keys %po_files_by_lang) { + my $po_file = $po_files_by_lang{$lang}; + + if ($UTF8_ARG) { + my $encoding = get_po_encoding ($po_file); + if (lc $encoding eq "utf-8") { + open PO_FILE, "<$po_file"; + } else { + my $iconv = $ENV{"INTLTOOL_ICONV"} || "iconv"; + open PO_FILE, "$iconv -f $encoding -t UTF-8 $po_file|"; + } + } else { + open PO_FILE, "<$po_file"; + } + + my $nextfuzzy = 0; + my $inmsgid = 0; + my $inmsgstr = 0; + my $msgid = ""; + my $msgstr = ""; + while (<PO_FILE>) { + $nextfuzzy = 1 if /^#, fuzzy/; + if (/^msgid "((\\.|[^\\])*)"/ ) { + $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr; + $msgid = ""; + $msgstr = ""; + + if ($nextfuzzy) { + $inmsgid = 0; + } else { + $msgid = unescape_po_string($1); + $inmsgid = 1; + } + $inmsgstr = 0; + $nextfuzzy = 0; + } + if (/^msgstr "((\\.|[^\\])*)"/) { + $msgstr = unescape_po_string($1); + $inmsgstr = 1; + $inmsgid = 0; + } + if (/^"((\\.|[^\\])*)"/) { + $msgid .= unescape_po_string($1) if $inmsgid; + $msgstr .= unescape_po_string($1) if $inmsgstr; + } + } + $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr; + } +} + +sub finalize +{ +} + +sub unescape_one_sequence +{ + my ($sequence) = @_; + + return "\\" if $sequence eq "\\\\"; + return "\"" if $sequence eq "\\\""; + + # gettext also handles \n, \t, \b, \r, \f, \v, \a, \xxx (octal), + # \xXX (hex) and has a comment saying they want to handle \u and \U. + + return $sequence; +} + +sub unescape_po_string +{ + my ($string) = @_; + + $string =~ s/(\\.)/unescape_one_sequence($1)/eg; + + return $string; +} + +sub entity_decode +{ + local ($_) = @_; + + s/'/'/g; # ' + s/"/"/g; # " + s/&/&/g; + + return $_; +} + +sub entity_encode +{ + my ($pre_encoded) = @_; + + my @list_of_chars = unpack ('C*', $pre_encoded); + + if ($PASS_THROUGH_ARG) { + return join ('', map (&entity_encode_int_even_high_bit, @list_of_chars)); + } else { + return join ('', map (&entity_encode_int_minimalist, @list_of_chars)); + } +} + +sub entity_encode_int_minimalist +{ + return """ if $_ == 34; + return "&" if $_ == 38; + return "'" if $_ == 39; + return chr $_; +} + +sub entity_encode_int_even_high_bit +{ + if ($_ > 127 || $_ == 34 || $_ == 38 || $_ == 39) { + # the ($_ > 127) should probably be removed + return "&#" . $_ . ";"; + } else { + return chr $_; + } +} + +sub entity_encoded_translation +{ + my ($lang, $string) = @_; + + my $translation = $translations{$lang, $string}; + return $string if !$translation; + return entity_encode ($translation); +} + +## XML (bonobo-activation specific) merge code + +sub ba_merge_translations +{ + my $source; + + { + local $/; # slurp mode + open INPUT, "<$FILE" or die "can't open $FILE: $!"; + $source = <INPUT>; + close INPUT; + } + + open OUTPUT, ">$OUTFILE" or die "can't open $OUTFILE: $!"; + + while ($source =~ s|^(.*?)([ \t]*<\s*$w+\s+($w+\s*=\s*"$q"\s*)+/?>)([ \t]*\n)?||s) { + print OUTPUT $1; + + my $node = $2 . "\n"; + + my @strings = (); + $_ = $node; + while (s/(\s)_($w+\s*=\s*"($q)")/$1$2/s) { + push @strings, entity_decode($3); + } + print OUTPUT; + + my %langs; + for my $string (@strings) { + for my $lang (keys %po_files_by_lang) { + $langs{$lang} = 1 if $translations{$lang, $string}; + } + } + + for my $lang (sort keys %langs) { + $_ = $node; + s/(\sname\s*=\s*)"($q)"/$1"$2-$lang"/s; + s/(\s)_($w+\s*=\s*")($q)"/$1 . $2 . entity_encoded_translation($lang, $3) . '"'/seg; + print OUTPUT; + } + } + + print OUTPUT $source; + + close OUTPUT; +} + + +## XML (non-bonobo-activation) merge code + +sub xml_merge_translations +{ + my $source; + + { + local $/; # slurp mode + open INPUT, "<$FILE" or die "can't open $FILE: $!"; + $source = <INPUT>; + close INPUT; + } + + open OUTPUT, ">$OUTFILE" or die; + + # FIXME: support attribute translations + + # Empty nodes never need translation, so unmark all of them. + # For example, <_foo/> is just replaced by <foo/>. + $source =~ s|<\s*_($w+)\s*/>|<$1/>|g; + + # Support for <_foo>blah</_foo> style translations. + while ($source =~ s|^(.*?)([ \t]*)<\s*_($w+)\s*>(.*?)<\s*/_\3\s*>([ \t]*\n)?||s) { + print OUTPUT $1; + + my $spaces = $2; + my $tag = $3; + my $string = $4; + + print OUTPUT "$spaces<$tag>$string</$tag>\n"; + + $string =~ s/\s+/ /g; + $string =~ s/^ //; + $string =~ s/ $//; + $string = entity_decode($string); + + for my $lang (sort keys %po_files_by_lang) { + my $translation = $translations{$lang, $string}; + next if !$translation; + $translation = entity_encode($translation); + print OUTPUT "$spaces<$tag xml:lang=\"$lang\">$translation</$tag>\n"; + } + } + + print OUTPUT $source; + + close OUTPUT; +} + +sub keys_merge_translations +{ + open INPUT, "<${FILE}" or die; + open OUTPUT, ">${OUTFILE}" or die; + + while (<INPUT>) { + if (s/^(\s*)_(\w+=(.*))/$1$2/) { + my $string = $3; + + print OUTPUT; + + my $non_translated_line = $_; + + for my $lang (sort keys %po_files_by_lang) { + my $translation = $translations{$lang, $string}; + next if !$translation; + + $_ = $non_translated_line; + s/(\w+)=.*/[$lang]$1=$translation/; + print OUTPUT; + } + } else { + print OUTPUT; + } + } + + close OUTPUT; + close INPUT; +} + +sub desktop_merge_translations +{ + open INPUT, "<${FILE}" or die; + open OUTPUT, ">${OUTFILE}" or die; + + while (<INPUT>) { + if (s/^(\s*)_(\w+=(.*))/$1$2/) { + my $string = $3; + + print OUTPUT; + + my $non_translated_line = $_; + + for my $lang (sort keys %po_files_by_lang) { + my $translation = $translations{$lang, $string}; + next if !$translation; + + $_ = $non_translated_line; + s/(\w+)=.*/${1}[$lang]=$translation/; + print OUTPUT; + } + } else { + print OUTPUT; + } + } + + close OUTPUT; + close INPUT; +} + +sub schemas_merge_translations +{ + my $source; + + { + local $/; # slurp mode + open INPUT, "<$FILE" or die "can't open $FILE: $!"; + $source = <INPUT>; + close INPUT; + } + + open OUTPUT, ">$OUTFILE" or die; + + # FIXME: support attribute translations + + # Empty nodes never need translation, so unmark all of them. + # For example, <_foo/> is just replaced by <foo/>. + $source =~ s|<\s*_($w+)\s*/>|<$1/>|g; + + # Support for <_foo>blah</_foo> style translations. + + my $regex_start = "^(.*?)([ \t]*)<locale name=\"C\">"; + my $regex_short = "([ \t\n]*)<short>(.*?)</short>"; + my $regex_long = "([ \t\n]*)<long>(.*?)</long>"; + my $regex_end = "([ \t\n]*)</locale>"; + + while ($source =~ s|$regex_start$regex_short$regex_long$regex_end||s) { + print OUTPUT $1; + + my $locale_start_spaces = $2; + my $locale_end_spaces = $7; + my $short_spaces = $3; + my $short_string = $4; + my $long_spaces = $5; + my $long_string = $6; + + # English first + + print OUTPUT "$locale_start_spaces<locale name=\"C\">"; + print OUTPUT "$short_spaces<short>$short_string</short>"; + print OUTPUT "$long_spaces<long>$long_string</long>"; + print OUTPUT "$locale_end_spaces</locale>"; + + $short_string =~ s/\s+/ /g; + $short_string =~ s/^ //; + $short_string =~ s/ $//; + $short_string = entity_decode($short_string); + + $long_string =~ s/\s+/ /g; + $long_string =~ s/^ //; + $long_string =~ s/ $//; + $long_string = entity_decode($long_string); + + for my $lang (sort keys %po_files_by_lang) { + my $short_translation = $translations{$lang, $short_string}; + my $long_translation = $translations{$lang, $long_string}; + + next if (!$short_translation && !$long_translation); + + print OUTPUT "\n$locale_start_spaces<locale name=\"$lang\">"; + + if ($short_translation) + { + $short_translation = entity_encode($short_translation); + print OUTPUT "$short_spaces<short>$short_translation</short>"; + } + + if ($long_translation) + { + $long_translation = entity_encode($long_translation); + print OUTPUT "$long_spaces<long>$long_translation</long>"; + } + + print OUTPUT "$locale_end_spaces</locale>"; + } + } + + print OUTPUT $source; + + close OUTPUT; +} 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; +} |