home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / bin / debconf-gettextize < prev    next >
Encoding:
Text File  |  2006-12-12  |  16.0 KB  |  406 lines

  1. #!/usr/bin/perl -w
  2.  
  3. =head1 NAME
  4.  
  5. debconf-gettextize - convert master and localized templates files to PO files
  6.  
  7. =cut
  8.  
  9. use strict;
  10. use Debconf::Template::Transient;
  11. use Getopt::Long;
  12. use Text::Wrap;
  13. use Text::Tabs;
  14. use File::Spec;
  15. use File::Path;
  16. use File::Temp;
  17.  
  18. sub usage {
  19.         print STDERR <<EOF;
  20. Usage: debconf-gettextize [OPTIONS] master [master ...]
  21. Options:
  22.   -h,  --help          display this help message
  23.   -v,  --verbose       enable verbose mode
  24.        --podir=DIR     specify PO output directory
  25.                        (Default: <master directory>/po)
  26.        --choices       prepend two underscores before Choices fields
  27.        --merge         merge extracted strings with existing PO files
  28. EOF
  29.         exit $_[0];
  30. }
  31.  
  32. # Ignore the users locale settings.
  33. Debconf::Template::Transient->i18n(0);
  34.  
  35. # Prevent a wrapped line from beginning with a space
  36. $Text::Wrap::break = qr/\n|\s(?=\S)/;
  37.  
  38. my $help = 0;
  39. my $verbose = 0;
  40. my $podir = "";
  41. my $choices = 0;
  42. my $merge = 0;
  43. GetOptions(
  44.         'podir=s' => \$podir,
  45.         'choices' => \$choices,
  46.         'merge' => \$merge,
  47.         'verbose|v' => \$verbose,
  48.         'help|h' => \$help,
  49. ) or usage(1);
  50. $help && usage(0);
  51. $#ARGV >= 0 || usage(1);
  52.  
  53. $ENV{PODEBCONF_ENCODINGS} ||= "/usr/share/po-debconf/encodings";
  54. $ENV{PODEBCONF_HEADER} ||= "/usr/share/po-debconf/pot-header";
  55.  
  56. if ($podir eq '') {
  57.         $podir = $ARGV[0];
  58.         if ($podir =~ m#/#) {
  59.                 $podir =~ s{/[^/]*$}{/po};
  60.         } else {
  61.                 $podir = 'po';
  62.         }
  63. }
  64.  
  65. my $tmpdir = '';
  66. my $oldpodir = '';
  67. if ($merge) {
  68.         die "Error: $podir subdirectory does not exist ...exiting\n"
  69.                 unless -d $podir;
  70.         $oldpodir = $podir;
  71.         $tmpdir = File::Temp::tempdir() or die "tmpdir() failed: $!\n";
  72.         print STDERR "Running po2debconf --podir=$podir -e po -o $tmpdir/templates $ARGV[0]\n" if $verbose;
  73.         system("po2debconf", "--podir=$podir", "-e", "po", "-o", $tmpdir."/templates", $ARGV[0]) == 0
  74.                 or die "po2debconf failed: $? $!\n";
  75.         shift @ARGV;
  76.         for (@ARGV) {
  77.                 system("po2debconf", "--podir=$podir", "-e", "po", "-o", $tmpdir."/templates.tmp", $_) == 0
  78.                         or die "po2debconf failed: $!\n";
  79.                 local $/ = undef;
  80.                 open (IN, "<", $tmpdir."/templates.tmp");
  81.                 my $add = <IN>;
  82.                 close (IN);
  83.                 open (OUT, ">>", $tmpdir."/templates");
  84.                 print OUT "\n".$add;
  85.                 close (OUT);
  86.                 unlink $tmpdir."/templates.tmp";
  87.         }
  88.         @ARGV = ($tmpdir."/templates");
  89.         $podir = $tmpdir."/po";
  90. } else {
  91.         die "Error: $podir subdirectory already exist ...exiting\n"
  92.                 if -d $podir;
  93. }
  94.  
  95. #   Hacked version of the stringify routine from Debconf/Template.pm
  96. #   It prepends an underscore to translatable entries
  97. sub new_stringify {
  98.         my $template = shift;
  99.         my @needfields = @_;
  100.  
  101.         my @templatestrings;
  102.         my $transfields = ','.join(',', @needfields).',';
  103.         foreach (($template)) {
  104.                 my $data='';
  105.                 # Order the fields with Template and Type the top and the
  106.                 # rest sorted.
  107.                 foreach my $key ('template', 'type',
  108.                         (grep { $_ ne 'template' && $_ ne 'type'} sort $_->fields)) {
  109.                         next if $key=~/^extended_/;
  110.                         # Do not output localized fields.
  111.                         next if $key =~ m/-/;
  112.  
  113.                         if ($transfields =~ m/,$key,/) {
  114.                                 $data.='_';
  115.                                 $data.='_' if ($key eq 'choices' && $choices);
  116.                         }
  117.                         $data.=ucfirst($key).": ".$_->$key."\n";
  118.                         my $e="extended_$key";
  119.                         my $ext=$_->$e;
  120.                         if (defined $ext) {
  121.                                 # Add extended field.
  122.                                 my $extended=expand(wrap(' ', ' ', $ext));
  123.                                 # The word wrapper sometimes outputs multiple
  124.                                 # " \n" lines, so collapse those into one.
  125.                                 $extended=~s/(\n )+\n/\n .\n/g;
  126.                                 $data.=$extended."\n" if length $extended;
  127.                         }
  128.                 }
  129.                 push @templatestrings, $data;
  130.         }
  131.         return join("\n", @templatestrings);
  132. }
  133.  
  134. sub escape_po {
  135.         my $txt = shift;
  136.  
  137.         $txt =~ s/\\/\\\\/g;
  138.         $txt =~ s/\n/\\n/g;
  139.         $txt =~ s/"/\\"/g;
  140.  
  141.         return $txt;
  142. }
  143.  
  144. my %out = ();
  145. my %enc = ();
  146. my %txt = ();
  147. my @obsolete = ();
  148. my @files = @ARGV;
  149.  
  150. #     Initialization
  151. foreach my $master (@ARGV) {
  152.         die "Invalid master file: $master ...exiting\n".
  153.   "templates.* file names are reserved for localized templates files, you\n".
  154.   "should rename master files before running debconf-gettextize.\n".
  155.   "When finished, you can rename new master files into templates.in and\n".
  156.   "update POTFILES.in accordingly.\n\n"
  157.                 if $master =~ m#templates\.([^./]+)$#;
  158.         die "File $master.old already exist ...exiting\n"
  159.                 if -e "$master.old";
  160.         push (@obsolete, "$master.old");
  161.         push (@files, glob ("$master.?? $master.??_??"));
  162.         push (@obsolete, glob ("$master.?? $master.??_??"));
  163. }
  164.  
  165. -d $podir || mkdir ($podir, 0755) || die "Can't create directory $podir: $!\n";
  166.  
  167. #     Fix relative links
  168. foreach (@ARGV) {
  169.         $_ = File::Spec->abs2rel($_, $podir);
  170.         s/^\.\.\///;
  171. }
  172.  
  173. #     Load master files
  174. my %templates = ();
  175. foreach my $fn (@files) {
  176.         next if $fn =~ m#templates\.([^./]+)$#;
  177.         $txt{$fn} = '';
  178.         my %master = map { $_->template => $_ } Debconf::Template::Transient->load($fn);
  179.         foreach (keys %master) {
  180.                 $templates{$_} = $master{$_};
  181.         }
  182. }
  183.  
  184. #     Parse master and localized templates files
  185. foreach my $fn (@files) {
  186.         print STDERR "Reading $fn\n" if $verbose;
  187.  
  188.         my $filelang = ($fn=~m#templates\.([^./]+)$# ? lc($1) : undef);
  189.         foreach my $in (Debconf::Template::Transient->load($fn)) {
  190.                 next unless exists $templates{$in->template};
  191.                 my $master=$templates{$in->template};
  192.  
  193.                 # Work out what fields need to be translated.
  194.                 my @needfields=qw{description extended_description};
  195.                 if ($master->type !~ /^((multi)?select|note|boolean)$/) {
  196.                         push @needfields, 'default' if defined $master->default;
  197.                 }
  198.                 if ($master->type =~ /(multi)?select/) {
  199.                         push @needfields, 'choices';
  200.                 }
  201.  
  202.                 # Parse the translated material
  203.                 foreach my $field ($in->fields) {
  204.                         next if $field eq 'template';
  205.                         if ($field =~ m/^(.*?)-(.+)$/) {
  206.                                 my $origin_field=$1;
  207.                                 my $lang=$2;
  208.                                 if ($lang =~ s/\.(.*)//) {
  209.                                         die "Multiple encoding found for language: $lang ... exiting\n"
  210.                                                 if (defined ($enc{$lang}) && $enc{$lang} ne uc ($1));
  211.                                         $enc{$lang} = uc ($1);
  212.                                 }
  213.                                 # Skip fields that are for some other language.
  214.                                 if (!defined($filelang) || $lang eq $filelang) {
  215.                                         $out{$lang} = []
  216.                                                 unless (defined($out{$lang}));
  217.  
  218.                                         my $label = $origin_field;
  219.                                         $label =~ s/extended_//;
  220.                                         $label = ucfirst($label);
  221.                                         my $origmaster = $master->$origin_field || '';
  222.                                         my @orig = split(/\n\n+/, $origmaster);
  223.                                         my $orignew = $in->$origin_field || '';
  224.                                         my @trans = split(/\n\n+/, $in->$field());
  225.                                         if ($label eq 'Choices' && $choices) {
  226.                                                 @orig = ();
  227.                                                 for my $value (split(/(?<!\\), */, $origmaster, 0))
  228.                                                 {
  229.                                                         $value =~ s/\\,/,/g;
  230.                                                         push @orig, $value;
  231.                                                 }
  232.                                                 @trans = ();
  233.                                                 for my $value (split(/(?<!\\), */, $in->$field(), 0))
  234.                                                 {
  235.                                                         $value =~ s/\\,/,/g;
  236.                                                         push @trans, $value;
  237.                                                 }
  238.                                         }
  239.                                         $origmaster =~ s/[\n\s]+//g;
  240.                                         $orignew =~ s/[\n\s]+//g;
  241.                                         my $fuzzy = ($origmaster ne $orignew) || (scalar @orig != scalar @trans);
  242.                                         if (scalar @orig <= scalar @trans) {
  243.                                                 foreach (@orig) {
  244.                                                         push @{$out{$lang}}, $label, $fuzzy, $_, shift(@trans);
  245.                                                 }
  246.                                         } else {
  247.                                                 foreach (@trans) {
  248.                                                         push @{$out{$lang}}, $label, 1, shift(@orig), $_;
  249.                                                 }
  250.                                                 foreach (@orig) {
  251.                                                         push @{$out{$lang}}, $label, 1, $_, '';
  252.                                                 }
  253.                                         }
  254.                                 }
  255.                         }
  256.                 }
  257.  
  258.                 $txt{$fn} .= new_stringify($in, @needfields)."\n"
  259.                         unless defined $filelang;
  260.         }
  261.         $txt{$fn} =~ s/\s*$/\n/s unless defined $filelang;
  262. }
  263.  
  264. if (-f $ENV{PODEBCONF_ENCODINGS}) {
  265.         open(ENC, "<", $ENV{PODEBCONF_ENCODINGS})
  266.                 || die "Can't open $ENV{PODEBCONF_ENCODINGS} for reading: $!\n";
  267.         while (<ENC>) {
  268.                 chomp;
  269.                 next unless s/^([a-z][a-z](_[A-Z][A-Z])?)\s+//;
  270.                 $enc{lc $1} ||= $_;
  271.         }
  272.         close(ENC);
  273. }
  274. foreach my $lang (sort keys %out) {
  275.         $enc{$lang} ||= 'CHARSET';
  276. }
  277.  
  278. my $unknown_encoding = 0;
  279. foreach my $lang (sort keys %out) {
  280.         my $polang = $lang;
  281.         $polang =~ s/_(..)$/_\U$1\E/;
  282.         $polang .= '.po';
  283.         print STDERR "Creating $podir/$polang\n" if $verbose;
  284.  
  285.         my $header = '';
  286.         unless (-e "$podir/$polang") {
  287.                 if (open (TOP, "<", $ENV{PODEBCONF_HEADER})) {
  288.                         while (<TOP>) {
  289.                                 $header .= $_;
  290.                         }
  291.                         close (TOP);
  292.                 }
  293.                 $header .= "#\n"
  294.                  ."#, fuzzy\n"
  295.                  ."msgid \"\"\n"
  296.                  ."msgstr \"\"\n"
  297.                  ."\"Project-Id-Version: PACKAGE VERSION\\n\"\n"
  298.                  ."\"Report-Msgid-Bugs-To: \\n\"\n"
  299.                  ."\"POT-Creation-Date: ".localtime()."\\n\"\n"
  300.                  ."\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n"
  301.                  ."\"Last-Translator: FULL NAME <EMAIL\@ADDRESS>\\n\"\n"
  302.                  ."\"Language-Team: LANGUAGE <LL\@li.org>\\n\"\n"
  303.                  ."\"MIME-Version: 1.0\\n\"\n"
  304.                  ."\"Content-Type: text/plain; charset=".$enc{$lang}."\\n\"\n"
  305.                  ."\"Content-Transfer-Encoding: 8bit\\n\"\n\n";
  306.         }
  307.  
  308.         open (OUT, ">>", "$podir/$polang")
  309.                 || die "Can't open $podir/$polang for writing: $!\n";
  310.         binmode(OUT);
  311.         print OUT $header;
  312.         while (@{$out{$lang}}) {
  313.                 print OUT "#: ".(shift @{$out{$lang}})."\n";
  314.                 print OUT "#, fuzzy\n" if (shift @{$out{$lang}});
  315.                 print OUT "msgid \"".
  316.                           escape_po(shift @{$out{$lang}}).
  317.                           "\"\n";
  318.                 print OUT "msgstr \"".
  319.                           escape_po(shift @{$out{$lang}}).
  320.                           "\"\n\n";
  321.         }
  322.         close (OUT) || die "Can't write $podir/$polang: $!\n";
  323.         if ($enc{$lang} eq 'CHARSET') {
  324.                 $unknown_encoding = 1;
  325.                 warn "Warning: Encoding for language $lang is unknown.\n".
  326.                      "Warning: This must be fixed before running po2debconf, so\n".
  327.                      "Warning:   $podir/$polang is renamed into $podir/$polang.unknown\n".
  328.                      "Warning: in order not to break other languages.\n".
  329.                      "Warning: Do not forget to rename it after having fixed its ".
  330.                      "encoding declaration!\n";
  331.                 rename "$podir/$polang", "$podir/$polang.unknown";
  332.         } else {
  333.                 system("msguniq", "--use-first", "-o", "$podir/$polang.tmp", "$podir/$polang") == 0 or die "msguniq failed: $!\n";
  334.                 rename "$podir/$polang.tmp", "$podir/$polang";
  335.         }
  336. }
  337.  
  338. foreach my $master (keys %txt) {
  339.         rename $master, "$master.old"
  340.                 or die "Unable to rename $master into $master.old\n";
  341.         print STDERR "Creating $master\n" if $verbose;
  342.         open (NEW, ">", $master) ||
  343.                 die "Can't open $master for writing: $!\n";
  344.         print NEW $txt{$master};
  345.         close (NEW);
  346. }
  347.  
  348. my $current = '';
  349. if (-e "$podir/POTFILES.in") {
  350.         local $/ = undef;
  351.         open (LIST, "<",  "$podir/POTFILES.in") ||
  352.                 die "Can't read $podir/POTFILES.in: $!\n";
  353.         $current = <LIST>;
  354.         close (LIST);
  355. }
  356. print STDERR "Create $podir/POTFILES.in\n" if $verbose;
  357. open (LIST, ">", "$podir/POTFILES.in") ||
  358.         die "Can't open $podir/POTFILES.in for writing: $!\n";
  359. print LIST $current;
  360. foreach (@ARGV) {
  361.         print LIST "[type: gettext/rfc822deb] $_\n"
  362.                 unless $current =~ m/ $_\b/m;
  363. }
  364. close (LIST) || die "Can't open write $podir/POTFILES.in$!\n";
  365.  
  366. if ($merge) {
  367.         foreach my $lang (keys %out) {
  368.                 my $polang = $lang;
  369.                 $polang =~ s/_(..)$/_\U$1\E/;
  370.                 if (-f "$oldpodir/$polang.po") {
  371.                         unlink "$podir/temp";
  372.                         system("msgcat", "--use-first", "-o", "$podir/temp", "$oldpodir/$polang.po", "$podir/$polang.po") == 0 && system("cp", "$podir/temp", "$oldpodir/$polang.po");
  373.                 } else {
  374.                         system("cp", "$podir/$polang.po", "$oldpodir/$polang.po");
  375.                 }
  376.         }
  377.         File::Path::rmtree($tmpdir, 0, 1);
  378.         print "\n".
  379.                 "  To complete conversion, you must:\n".
  380.                 "    a. Adjust _Choices or __Choices fields in master templates file.\n".
  381.                 "    b. Run debconf-updatepo\n";
  382. } else {
  383.         print STDERR "Running debconf-updatepo --podir=$podir\n" if $verbose;
  384.         system("debconf-updatepo", "--podir=$podir");
  385.  
  386.         print "\n".
  387.                 "  To complete conversion, you must:\n".
  388.                 "    a. Check that new templates files contain all the localized fields\n".
  389.                 "    b. Add po-debconf to Build-Depends or Build-Depends-Indep in debian/control\n".
  390.                 "    c. Remove obsolete files:\n".
  391.                 wrap('         ', '         ', join(' ', @obsolete))."\n".
  392.                 "    d. Edit debian/rules to generate the localized templates file, either\n".
  393.                 "       with dh_installdebconf or directly with po2debconf\n";
  394.         print   "    e. Check encoding in $podir/*.po.unknown files\n"
  395.                 if ($unknown_encoding);
  396. }
  397.  
  398. exit 0;
  399.  
  400. =head1 AUTHORS
  401.  
  402. Martin Quinson <martin.quinson@ens-lyon.fr>
  403. Denis Barbier <barbier@linuxfr.org>
  404.  
  405. =cut
  406.