4 # script to compare changes between translation files before merging them
7 # ./diff_po.pl cs.po.old cs.po
8 # svn diff -r38367 --diff-cmd ./diff_po.pl cs.po
9 # git difftool --extcmd=./diff_po.pl sk.po
10 # ./diff_po.pl -r HEAD~100 cs.po #fetch git revision and compare
12 # This file is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public
14 # License as published by the Free Software Foundation; either
15 # version 2 of the License, or (at your option) any later version.
17 # This software is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 # General Public License for more details.
22 # You should have received a copy of the GNU General Public
23 # License along with this software; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 # author: Kornel Benko, kornel@lyx.org
28 # TODO: Search for good correlations of deleted and inserted string
29 # using Text::Levenshtein or Algorithm::Diff
32 use Term::ANSIColor qw(:constants);
34 my ($status, $foundline, $msgid, $msgstr, $fuzzy);
36 my %Messages = (); # Used for original po-file
37 my %newMessages = (); # new po-file
38 my %Untranslated = (); # inside new po-file
39 my %Fuzzy = (); # inside new po-file
40 my $result = 0; # exit value
43 my $tmpfile = "/tmp/blax";
45 # Check first, if called as standalone program for git
46 if ($ARGV[0] =~ /^-r(.*)/) {
52 for my $argf (@ARGV) {
54 ($baseargf = $argf) =~ s/^.*\///;
56 push(@args, "-L", $argf . " (" . $rev . ")");
57 push(@args, "-L", $argf . " (local copy)");
58 open(FI, "git show $rev:po/$baseargf|");
59 open(FO, '>', $tmpfile);
65 push(@args, $tmpfile, $argf);
74 #########################################################
84 print "========================================================\n";
85 while(defined($args[0])) {
86 last if ($args[0] !~ /^\-/);
87 my $param = shift(@args);
89 my $name = shift(@args);
93 if (! defined($names[0])) {
94 push(@names, "original");
96 if (! defined($names[1])) {
101 die("names = \"", join('" "', @names) . "\"... args = \"" . join('" "', @args) . "\" Expected exactly 2 parameters");
104 &check($names[0], $args[0]);
105 &check($names[1], $args[1]);
107 &parse_po_file($args[0], \%Messages);
108 &parse_po_file($args[1], \%newMessages);
110 my @MsgKeys = &getLineSortedKeys(\%newMessages);
112 print RED "<<< \"$names[0]\"\n", RESET;
113 print GREEN ">>> \"$names[1]\"\n", RESET;
114 for my $k (@MsgKeys) {
115 if ($newMessages{$k}->{msgstr} eq "") {
116 # this is still untranslated string
117 $Untranslated{$newMessages{$k}->{line}} = $k;
119 elsif ($newMessages{$k}->{fuzzy}) {
121 $Fuzzy{$newMessages{$k}->{line}} = $k;
123 if (exists($Messages{$k})) {
124 &printIfDiff($k, $Messages{$k}, $newMessages{$k});
125 delete($Messages{$k});
126 delete($newMessages{$k});
130 @MsgKeys = &getLineSortedKeys(\%Messages);
131 for my $k (@MsgKeys) {
133 print "deleted message\n";
134 print "< line = " . $Messages{$k}->{line} . "\n" if ($printlines);
135 print RED "< fuzzy = " . $Messages{$k}->{fuzzy} . "\n", RESET;
136 print RED "< msgid = \"$k\"\n", RESET;
137 print RED "< msgstr = \"" . $Messages{$k}->{msgstr} . "\"\n", RESET;
140 @MsgKeys = &getLineSortedKeys(\%newMessages);
141 for my $k (@MsgKeys) {
143 print "new message\n";
144 print "> line = " . $newMessages{$k}->{line} . "\n" if ($printlines);
145 print GREEN "> fuzzy = " . $newMessages{$k}->{fuzzy} . "\n", RESET;
146 print GREEN "> msgid = \"$k\"\n", RESET;
147 print GREEN "> msgstr = \"" . $newMessages{$k}->{msgstr} . "\"\n", RESET;
150 &printExtraMessages("fuzzy", \%Fuzzy);
151 &printExtraMessages("untranslated", \%Untranslated);
156 my ($spec, $filename) = @_;
158 if (! -e $filename ) {
159 die("$spec po file does not exist");
161 if ( ! -f $filename ) {
162 die("$spec po file is not regular");
164 if ( ! -r $filename ) {
165 die("$spec po file is not readable");
171 my ($k, $nk, $rM, $rnM) = @_;
172 print "diffline = " . $rM->{line} . "," . $rnM->{line} . "\n" if ($printlines);
173 print " msgid = \"$k\"\n";
174 if ($rM->{fuzzy} eq $rnM->{fuzzy}) {
175 print " fuzzy = " . $rM->{fuzzy} . "\n" if ($printlines);
178 print RED "< fuzzy = " . $rM->{fuzzy} . "\n", RESET;
180 print RED "< msgstr = " . $rM->{msgstr} . "\n", RESET;
182 print GREEN "> msgid = \"$nk\"\n", RESET;
184 if ($rM->{fuzzy} ne $rnM->{fuzzy}) {
185 print GREEN "> fuzzy = " . $rnM->{fuzzy} . "\n", RESET;
187 print GREEN "> msgstr = " . $rnM->{msgstr} . "\n", RESET;
193 my ($k, $rM, $rnM) = @_;
195 $doprint = 1 if ($rM->{fuzzy} != $rnM->{fuzzy});
196 $doprint = 1 if ($rM->{msgstr} ne $rnM->{msgstr});
199 &printDiff($k, $k, $rM, $rnM);
203 sub parse_po_file($$)
205 my ($file, $rMessages) = @_;
206 if (open(FI, '<', $file)) {
210 while (my $line = <FI>) {
212 &parse_po_line($line, $lineno, $rMessages);
214 &parse_po_line("", $lineno + 1, $rMessages);
219 sub parse_po_line($$$)
221 my ($line, $lineno, $rMessages) = @_;
224 if ($status eq "normal") {
225 if ($line =~ /^#, fuzzy/) {
228 elsif ($line =~ s/^msgid\s+//) {
229 $foundline = $lineno;
232 &parse_po_line($line);
235 elsif ($status eq "msgid") {
236 if ($line =~ /^\s*"(.*)"\s*/) {
239 elsif ($line =~ s/^msgstr\s+//) {
242 &parse_po_line($line);
245 elsif ($status eq "msgstr") {
246 if ($line =~ /^\s*"(.*)"\s*/) {
251 $rMessages->{$msgid}->{line} = $foundline;
252 $rMessages->{$msgid}->{fuzzy} = $fuzzy;
253 $rMessages->{$msgid}->{msgstr} = $msgstr;
260 die("invalid status");
264 sub getLineSortedKeys($)
266 my ($rMessages) = @_;
268 return sort {$rMessages->{$a}->{line} <=> $rMessages->{$b}->{line};} keys %{$rMessages};
271 sub printExtraMessages($$)
273 my ($type, $rExtra) = @_;
274 my @UntranslatedKeys = sort { $a <=> $b;} keys %{$rExtra};
276 if (@UntranslatedKeys > 0) {
277 print "Still " . 0 + @UntranslatedKeys . " $type messages found in $ARGV[1]\n";
278 for my $l (@UntranslatedKeys) {
279 print "> line $l: \"" . $rExtra->{$l} . "\"\n";