[Po4a-devel] po4a's --split option

Nicolas François nicolas.francois at centraliens.net
Sun Feb 26 23:36:19 UTC 2006


Hello,

po4a currently has a --split option, but the support for this option is
not complete (the option is not documented, so this is not a problem).

It currently uses Po.pm:filter to select the strings used in one document,
but, for all the documents, it writes the POT in the same file.
The main issue of --split is that the user doesn't specify where the POT
and POs should be written.

I propose to use a $master variable in po4a_paths for that. For example:

[po4a_paths] po4a/po/$master/$master.pot $lang:po4a/po/$master/$lang.po

(Then the --split option is not needed, it can be autodetected)


The second issue with --split is that all the POs for a language should be
merged (i.e. if a string is translated in a PO, the translation should be
shared by the other POs of the same language).
I propose to use msgcat for that.
Then, if 2 POs have different translations, msgcat will mark this string
fuzzy, and will propose both translations in the POs:

# type: type
#: ref1 ref2
#, fuzzy, no-wrap
msgid "my string"
msgstr ""
"#-#-#-#-# ref1 #-#-#-#-#\n"
"first translation\n"
"#-#-#-#-# ref2 #-#-#-#-#\n"
"second translation"

Later, if this string is unfuzzied in one of these POs, po4a will remove the
fuzzy mark on this string from all the POs.


The attached patch should perform what is described above. It does not use
Po.pm:filter, but msggrep.
I will commit it this week unless you think there is something wrong in
the above description.

Kind Regards,
-- 
Nekral
-------------- next part --------------
Index: po4a
===================================================================
RCS file: /cvsroot/po4a/po4a/po4a,v
retrieving revision 1.52
diff -u -r1.52 po4a
--- po4a	26 Feb 2006 20:19:44 -0000	1.52
+++ po4a	26 Feb 2006 22:09:32 -0000
@@ -289,6 +289,7 @@
 use Pod::Usage qw(pod2usage);
 
 use File::Temp;
+use File::Basename;
 use File::Copy;
 
 Locale::Po4a::Common::textdomain('po4a');
@@ -439,6 +440,36 @@
 # Argument check
 $po4a_opts{"help"} && pod2usage (-verbose => 1, -exitval => 0);
 
+sub run_cmd {
+    my $cmd = shift;
+    print $cmd."\n" if $po4a_opts{"debug"};
+
+    my $out = qx/$cmd 2>&1/;
+    print $out if ($po4a_opts{"verbose"});
+    unless ($? == 0) {
+        my $err = "";
+        if ($? == -1) {
+            $err = sprintf(gettext("failed to execute '%s': %s."),
+                           $cmd, $!);
+        } elsif ($? & 127) {
+            if ($? & 128) {
+                $err = sprintf(gettext("'%s' died with signal %d, ".
+                                       "with coredump."),
+                               $cmd, $? & 127);
+            } else {
+                $err = sprintf(gettext("'%s' died with signal %d, ".
+                                       "without coredump."),
+                               $cmd, $? & 127);
+            }
+        } else {
+            $err = sprintf(gettext("'%s' exited with value %d."),
+                           $cmd, $? >> 8);
+        }
+
+        die wrap_msg(gettext("Error: %s"), $err);
+    }
+}
+
 my $config_file= shift(@ARGV) || pod2usage();
 # Check file existence
 -e $config_file || die wrap_msg(gettext("File %s does not exist."), $config_file);
@@ -570,8 +601,24 @@
 die wrap_msg(gettext("po4a_paths not declared. Dunno where to find the pot and po files"))
   unless (length $pot_filename);
 
+my %splitted_po; # po_files: '$lang','$master' => '$path'
+my %splitted_pot; # pot_files: '$master' => '$path'
+
 # make a big pot
 my $update_pot_file = 0;
+if ($pot_filename =~ m/\$master/) {
+    print "Splitted mode, creating a temporary POT.\n"
+        if $po4a_opts{"verbose"};
+    foreach my $master (keys %document) {
+        my $m = basename $master;
+        my $master_pot = $pot_filename;
+        $master_pot =~ s/\$master/$m/g;
+        $splitted_pot{$master} = $master_pot;
+    }
+    # The POT needs to be generated anyway.
+    $update_pot_file = 1;
+    $po4a_opts{"split"} = 1;
+} else {
 if (-e $pot_filename) {
     my $modtime = (stat $pot_filename)[9];
     # The POT needs to be re-generated if a master document is more recent
@@ -600,6 +647,7 @@
 	if $po4a_opts{"verbose"};
     $update_pot_file = 1;
 }
+}
 
 my $potfile=Locale::Po4a::Po->new();
 if ($update_pot_file) {
@@ -625,11 +673,22 @@
                       'file_in_charset'  => $file_opts{"mastchar"});
         $potfile = $doc->getpoout();
     }
+    if ($po4a_opts{"split"}) {
+        (undef,$pot_filename)=File::Temp->tempfile("po4aXXXX",
+                                                   DIR    => "/tmp",
+                                                   SUFFIX => ".pot",
+                                                   OPEN   => 0,
+                                                   UNLINK => 0)
+            or die wrap_msg(gettext("Can't create a temporary pot file: %s"),
+                            $!);
+        $potfile->write($pot_filename);
+    } else {
     if ($po4a_opts{"force"}) {
         $potfile->write($pot_filename);
     } else {
         $potfile->write_if_needed($pot_filename);
     }
+    }
 
     print wrap_msg(gettext(" (%d entries)"), $potfile->count_entries())
         unless ($po4a_opts{"quiet"});
@@ -637,107 +696,72 @@
     $potfile->read($pot_filename);
 }
 
+if ($po4a_opts{"split"}) {
+    # Generate a .pot for each document
+    foreach my $master (keys %document) {
+        my $tmp_file;
+        # Create a temporary POT, and check if the old one needs to be
+        # updated (unless --force was specified).
+        unless ($po4a_opts{"force"}) {
+            (undef,$tmp_file)=File::Temp->tempfile("po4aXXXX",
+                                                   DIR    => "/tmp",
+                                                   SUFFIX => ".pot",
+                                                   OPEN   => 0,
+                                                   UNLINK => 0)
+                or die wrap_msg(gettext("Can't create a temporary pot file: %s"),
+                                $!);
+        }
+
+        my $cmd = "msggrep -N '$master' -o ".
+                  ($po4a_opts{"force"}?$splitted_pot{$master}:$tmp_file).
+                  " $pot_filename";
+        run_cmd($cmd);
+
+        unless ($po4a_opts{"force"}) {
+            Locale::Po4a::Po::move_po_if_needed($tmp_file,
+                                                $splitted_pot{$master});
+        }
+    }
+    # Generate a complete .po
+    foreach my $lang (sort keys %po_filename) {
+        my $tmp_bigpo;
+        (undef,$tmp_bigpo)=File::Temp->tempfile("po4aXXXX",
+                                                DIR    => "/tmp",
+                                                SUFFIX => "-$lang.po",
+                                                OPEN   => 0,
+                                                UNLINK => 0)
+            or die wrap_msg(gettext("Can't create a temporary po file: %s"),
+                            $!);
+        my $cmd_cat = "";
+        foreach my $master (keys %document) {
+            my $m = basename $master;
+            my $master_po = $po_filename{$lang};
+            $master_po =~ s/\$master/$m/g;
+            if (-e "$master_po") {
+                $cmd_cat .= " $master_po";
+            }
+            $splitted_po{$lang}{$master} = $master_po;
+        }
+        if (length $cmd_cat) {
+            $cmd_cat = "msgcat -o $tmp_bigpo $cmd_cat";
+            run_cmd($cmd_cat);
+        }
+        # We do not need to keep the original name with $master
+        $po_filename{$lang} = $tmp_bigpo;
+    }
+}
+
 # update all po files
 my $lang;
 foreach $lang (sort keys %po_filename) {
     if (-e $po_filename{$lang}) {
 	print wrap_msg(gettext("Updating %s:")." ", $po_filename{$lang})
 	    if ($po4a_opts{"verbose"});
-	if ($po4a_opts{"split"}) {
-	    my ($pot_filename,$po_filename,$bigpo_filename);
-	    (undef,$pot_filename)=File::Temp->tempfile("po4aXXXX",
-						       DIR    => "/tmp",
-						       SUFFIX => ".pot",
-						       OPEN   => 0,
-						       UNLINK => 0)
-		or die wrap_msg(gettext("Can't create a temporary pot file: %s"), $!);
-	    (undef,$po_filename)=File::Temp->tempfile("po4aXXXX",
-						      DIR    => "/tmp",
-						      SUFFIX => ".po",
-						      OPEN   => 0,
-						      UNLINK => 0)
-		or die wrap_msg(gettext("Can't create a temporary po file: %s"), $!);
-	    my ($poorig,$pores)=(Locale::Po4a::Po->new(),Locale::Po4a::Po->new());
-	    $poorig->read($po_filename{$lang});
-
-	    foreach my $master (sort keys %document) {
-		my $pot=Locale::Po4a::Po->new();
-		my $po=Locale::Po4a::Po->new();
-		print "  $master:" unless ($po4a_opts{"quiet"});
-		print "(pot)" unless ($po4a_opts{"quiet"});
-		$pot=$potfile->filter("(reference='\Q$master:\E')");
-		print "(po) " unless ($po4a_opts{"quiet"});
-		$po=$poorig->filter("(reference='\Q$master:\E')");
-		unlink($pot_filename) if -e $pot_filename;
-		unlink($po_filename) if -e $po_filename;
-
-		$pot->write($pot_filename);
-		$po->write($po_filename);
-
-		my $cmd;
-		$cmd = "msgmerge -U $po_filename $pot_filename --backup=none";
-		my $out = qx/$cmd 2>&1/;
-		print $out if ($po4a_opts{"verbose"});
-		unless ($? == 0) {
-		    my $err = "";
-		    if ($? == -1) {
-			$err = sprintf(gettext("failed to execute '%s': %s."),
-			               $cmd, $!);
-		    } elsif ($? & 127) {
-			if ($? & 128) {
-			    $err = sprintf(gettext("'%s' died with signal %d, ".
-			                           "with coredump."),
-			                   $cmd, $? & 127);
-			} else {
-			    $err = sprintf(gettext("'%s' died with signal %d, ".
-			                           "without coredump."),
-			                   $cmd, $? & 127);
-			}
-		    } else {
-			$err = sprintf(gettext("'%s' exited with value %d."),
-			               $cmd, $? >> 8);
-		    }
-
-		    die wrap_msg(gettext("Error while running msgmerge: %s"),
-		                 $err);
-		}
-		$pores->read($po_filename);
-	    }
-	    $pores->write($po_filename{$lang}.".new");
-	    unlink($pot_filename) if -e $pot_filename;
-	    unlink($po_filename) if -e $po_filename;
-
-	} else {
-	    my $cmd = "msgmerge -U ".$po_filename{$lang}." $pot_filename".
-	              ($po4a_opts{"no-backups"}?" --backup=none":"");
-	    my $out = qx/$cmd 2>&1/;
-	    print $out if ($po4a_opts{"verbose"});
-	    unless ($? == 0) {
-		my $err = "";
-		if ($? == -1) {
-		    $err = sprintf(gettext("failed to execute '%s': %s."),
-		                   $cmd, $!);
-		} elsif ($? & 127) {
-		    if ($? & 128) {
-			$err = sprintf(gettext("'%s' died with signal %d, ".
-		                               "with coredump."),
-			               $cmd, $? & 127);
-		    } else {
-			$err = sprintf(gettext("'%s' died with signal %d, ".
-			                       "without coredump."),
-			               $cmd, $? & 127);
-		    }
-		} else {
-		    $err = sprintf(gettext("'%s' exited with value %d."),
-		                   $cmd, $? >> 8);
-		}
-
-		die wrap_msg(gettext("Error while running msgmerge: %s"),
-		             $err);
-	    }
-	    system "msgfmt --statistics -v -o /dev/null ".$po_filename{$lang}
-		if $po4a_opts{"verbose"};
-	}
+	my $cmd = "msgmerge -U ".$po_filename{$lang}." $pot_filename".
+	          ($po4a_opts{"no-backups"}?" --backup=none":"");
+	run_cmd($cmd);
+	system "msgfmt --statistics -v -o /dev/null ".$po_filename{$lang}
+	    if $po4a_opts{"verbose"};
     } else {
 	print wrap_msg(gettext("Creating %s:"), $po_filename{$lang})
 	    if $po4a_opts{"verbose"};
@@ -746,14 +770,45 @@
     }
 }
 
+if ($po4a_opts{"split"}) {
+    # We don't need the tmp big POT anymore
+    unlink($pot_filename);
+
+    # Split the complete PO in multiple POs
+    foreach $lang (sort keys %po_filename) {
+        foreach my $master (keys %document) {
+            my $tmp_file;
+            # Create a temporary PO, and check if the old one needs to be
+            # updated (unless --force was specified).
+            unless ($po4a_opts{"force"}) {
+                (undef,$tmp_file)=File::Temp->tempfile("po4aXXXX",
+                                                       DIR    => "/tmp",
+                                                       SUFFIX => ".po",
+                                                       OPEN   => 0,
+                                                       UNLINK => 0)
+                    or die wrap_msg(
+                        gettext("Can't create a temporary pot file: %s"), $!);
+            }
+
+            my $cmd = "msggrep -N '$master' -o ".
+                      ($po4a_opts{"force"}?$splitted_po{$lang}{$master}:
+                                           $tmp_file).
+                      " $po_filename{$lang}";
+            run_cmd($cmd);
+
+            Locale::Po4a::Po::move_po_if_needed($tmp_file,
+                                                $splitted_po{$lang}{$master})
+                unless ($po4a_opts{"force"});
+        }
+    }
+}
+
 if ($po4a_opts{"rm-backups"}) {
     # Delete the .po~ backup files generated by msgmerge
 
-    unless ($po4a_opts{"split"}) {
 	foreach $lang (sort keys %po_filename) {
 	    unlink $po_filename{$lang}."~";
 	}
-    }
 }
 
 if (not $po4a_opts{"no-translations"}) {
@@ -825,6 +880,13 @@
     }
 }
 
+if ($po4a_opts{"split"}) {
+    # We don't need the tmp big POs anymore
+    foreach $lang (sort keys %po_filename) {
+        unlink $po_filename{$lang};
+    }
+}
+
 if ($po4a_opts{"rm-translations"}) {
     # Delete the translated documents
     foreach $lang (sort keys %po_filename) {
Index: lib/Locale/Po4a/Po.pm
===================================================================
RCS file: /cvsroot/po4a/po4a/lib/Locale/Po4a/Po.pm,v
retrieving revision 1.57
diff -u -r1.57 Po.pm
--- lib/Locale/Po4a/Po.pm	25 Feb 2006 15:20:05 -0000	1.57
+++ lib/Locale/Po4a/Po.pm	26 Feb 2006 22:09:33 -0000
@@ -313,31 +313,44 @@
 
 =cut
 
+sub move_po_if_needed {
+    my ($new_po, $old_po) = (shift, shift);
+    my $diff;
+
+    if (-e $old_po) {
+        $diff = qx(diff -q -I'^#:' -I'^"POT-Creation-Date:' -I'^"PO-Revision-Date:' $old_po $new_po);
+        if ( $diff eq "" ) {
+            unlink $new_po
+                or die wrap_msg(dgettext("po4a","Can't unlink %s."),
+                                $new_po);
+            # touch the old PO
+            my ($atime, $mtime) = (time,time);
+            utime $atime, $mtime, $old_po;
+        } else {
+            move $new_po, $old_po
+                or die wrap_msg(dgettext("po4a","Can't move %s to %s."),
+                                $new_po, $old_po);
+        }
+    } else {
+        move $new_po, $old_po
+            or die wrap_msg(dgettext("po4a","Can't move %s to %s."),
+                            $new_po, $old_po);
+    }
+}
+
 sub write_if_needed {
     my $self=shift;
     my $filename=shift 
 	or croak (dgettext("po4a","Can't write to a file without filename")."\n");
 
     if (-e $filename) {
-        my ($tmp_filename, $diff);
+        my ($tmp_filename);
         (undef,$tmp_filename)=File::Temp->tempfile($filename."XXXX",
                                                    DIR    => "/tmp",
                                                    OPEN   => 0,
                                                    UNLINK => 0);
         $self->write($tmp_filename);
-        $diff = qx(diff -q -I'^#:' -I'^"POT-Creation-Date:' -I'^"PO-Revision-Date:' $filename $tmp_filename);
-        if ( $diff eq "" ) {
-            unlink $tmp_filename or
-                die wrap_msg(dgettext("po4a","Can't unlink %s."),
-                             $tmp_filename);
-            # touch it
-            my ($atime, $mtime) = (time,time);
-            utime $atime, $mtime, $filename;
-        } else {
-            move $tmp_filename, $filename or
-                die wrap_msg(dgettext("po4a","Can't rename %s to %s."),
-                             $tmp_filename, $filename);
-        }
+        move_po_if_needed($tmp_filename, $filename);
     } else {
         $self->write($filename);
     }


More information about the Po4a-devel mailing list