#! /usr/bin/perl -w use utf8; use encoding "utf8"; use Term::ReadKey; use Encode; my $waswarn = 0; main(); sub getdate { my @t=gmtime(); return sprintf("%04d-%02d-%02d %02d:%02d+0000", 1900+$t[5],$t[4]+1,$t[3],$t[2],$t[1]); } sub loadfiles($@) { my $desc; my $all; my ($lang,@files) = @_; foreach my $file (@files) { die "Could not open file $file." if(!open FILE,"<:utf8",$file); my $linenum = 0; if($file =~ /\/(.._..)\.po$/ || $file =~ /\/(..)\.po$/) { my $l = $1; ++$lang->{$l}; my %postate = (last => "", type => ""); my $linenum = 0; print "Reading file $file\n"; while() { ++$linenum; my $fn = "$file:$linenum"; chomp; if($_ =~ /^#/ || !$_) { checkpo(\%postate, \%all, $l, "line $linenum in $file", $keys, 1); $postate{fuzzy} = 1 if ($_ =~ /fuzzy/); } elsif($_ =~ /^"(.*)"$/) {$postate{last} .= $1;} elsif($_ =~ /^(msg.+) "(.*)"$/) { my ($n, $d) = ($1, $2); my $new = ($n eq "msgid" && $postate{type} ne "msgctxt") || ($n eq "msgctxt"); checkpo(\%postate, \%all, $l, "line $linenum in $file", $keys, $new); $postate{last} = $d; $postate{type} = $n; $postate{src} = $fn if $new; } else { die "Strange line $linenum in $file: $_."; } } checkpo(\%postate, \%all, $l, "line $linenum in $file", $keys, 1); } else { die "File format not supported for file $file."; } close(FILE); } return %all; } my $alwayspo = 0; my $alwaysup = 0; my $noask = 0; my $conflicts; sub copystring($$$$$$$) { my ($data, $en, $l, $str, $txt, $context, $ispo) = @_; $en = "___${context}___$en" if $context; if(exists($data->{$en}{$l}) && $data->{$en}{$l} ne $str) { return if !$str; if($l =~ /^_/) { $data->{$en}{$l} .= ";$str" if !($data->{$en}{$l} =~ /$str/); } elsif(!$data->{$en}{$l}) { $data->{$en}{$l} = $str; } else { my $f = $data->{$en}{_file} || ""; $f = ($f ? "$f;".$data->{$en}{"_src.$l"} : $data->{$en}{"_src.$l"}) if $data->{$en}{"_src.$l"}; my $isotherpo = ($f =~ /\.po\:/); my $pomode = ($ispo && !$isotherpo) || (!$ispo && $isotherpo); my $mis = "String mismatch for '$en' **$str** ($txt) != **$data->{$en}{$l}** ($f)\n"; my $replace = 0; if(($conflicts{$l}{$str} || "") eq $data->{$en}{$l}) {} elsif($pomode && $alwaysup) { $replace=$isotherpo; } elsif($pomode && $alwayspo) { $replace=$ispo; } elsif($noask) { print $mis; ++$waswarn; } else { ReadMode 4; # Turn off controls keys my $arg = "(l)eft, (r)ight"; $arg .= ", (p)o, (u)pstream[ts/mat], all p(o), all up(s)tream" if $pomode; $arg .= ", e(x)it: "; print "$mis$arg"; while((my $c = getc())) { if($c eq "l") { $replace=1; } elsif($c eq "r") {} elsif($c eq "p" && $pomode) { $replace=$ispo; } elsif($c eq "u" && $pomode) { $replace=$isotherpo; } elsif($c eq "o" && $pomode) { $alwayspo = 1; $replace=$ispo; } elsif($c eq "s" && $pomode) { $alwaysup = 1; $replace=$isotherpo; } elsif($c eq "x") { $noask = 1; ++$waswarn; } else { print "\n$arg"; next; } last; } print("\n"); ReadMode 0; # Turn on controls keys } if(!$noask) { if($replace) { $data->{$en}{$l} = $str; $conflicts{$l}{$data->{$en}{$l}} = $str; } else { $conflicts{$l}{$str} = $data->{$en}{$l}; } } } } else { $data->{$en}{$l} = $str; } } sub checkpo($$$$$$) { my ($postate, $data, $l, $txt, $keys, $new) = @_; if($postate->{type} eq "msgid") {$postate->{msgid} = $postate->{last};} elsif($postate->{type} eq "msgid_plural") {$postate->{msgid_1} = $postate->{last};} elsif($postate->{type} =~ /^msgstr(\[0\])?$/) {$postate->{msgstr} = $postate->{last};} elsif($postate->{type} =~ /^msgstr\[(.+)\]$/) {$postate->{"msgstr_$1"} = $postate->{last};} elsif($postate->{type} eq "msgctxt") {$postate->{context} = $postate->{last};} elsif($postate->{type}) { die "Strange type $postate->{type} found\n" } if($new) { if((!$postate->{fuzzy}) && $postate->{msgstr} && $postate->{msgid}) { copystring($data, $postate->{msgid}, $l, $postate->{msgstr},$txt,$postate->{context}, 1); for($i = 1; exists($postate->{"msgstr_$i"}); ++$i) { copystring($data, $postate->{msgid}, "$l.$i", $postate->{"msgstr_$i"},$txt,$postate->{context}, 1); } if($postate->{msgid_1}) { copystring($data, $postate->{msgid}, "en.1", $postate->{msgid_1},$txt,$postate->{context}, 1); } copystring($data, $postate->{msgid}, "_src.$l", $postate->{src},$txt,$postate->{context}, 1); } elsif($postate->{msgstr} && !$postate->{msgid}) { my %k = ($postate->{msgstr} =~ /(.+?): +(.+?)\\n/g); # take the first one! for $a (sort keys %k) { $keys->{$l}{$a} = $k{$a} if !$keys->{$l}{$a}; } } foreach my $k (keys %{$postate}) { delete $postate->{$k}; } $postate->{type} = $postate->{last} = ""; } } sub createlang($@) { my ($data, @files) = @_; foreach my $file (@files) { my $la; if($file =~ /[-_](.._..)\.lang$/ || $file =~ /^(?:.*\/)?(.._..)\.lang$/ || $file =~ /[-_](...?)\.lang$/ || $file =~ /^(?:.*\/)?(..)\.lang$/) { print "Creating file $file\n"; $la = $1; } else { die "Language for file $file unknown."; } die "Could not open outfile $file\n" if !open FILE,">:raw",$file; foreach my $en (sort keys %{$data}) { next if $data->{$en}{"en.1"}; my $val; if($la eq "en") { $val = $en; $val =~ s/^___(.*)___/_:$1\n/; } else { my $ennoctx = $en; $ennoctx =~ s/^___(.*)___//; $val = (exists($data->{$en}{$la})) ? $data->{$en}{$la} : ""; $val = "" if($ennoctx eq $val); } $val = encode("utf8", $val); print FILE pack("n",length($val)).$val; } print FILE pack "n",0xFFFF; foreach my $en (sort keys %{$data}) { next if !$data->{$en}{"en.1"}; my $num; for($num = 1; exists($data->{$en}{"$la.$num"}); ++$num) { } my $val; if($la eq "en") { $val = $en; $val =~ s/^___(.*)___/_:$1\n/; } else { $val = (exists($data->{$en}{$la})) ? $data->{$en}{$la} : ""; --$num if(!$val); if($num == 2) { my $ennoctx = $en; $ennoctx =~ s/^___(.*)___//; $num = 0 if $val eq $ennoctx && $data->{$en}{"$la.1"} eq $data->{$en}{"en.1"}; } } print FILE pack "C",$num; if($num) { $val = encode("utf8", $val); print FILE pack("n",length($val)).$val; for($num = 1; exists($data->{$en}{"$la.$num"}); ++$num) { $val = encode("utf8", $data->{$en}{"$la.$num"}); print FILE pack("n",length($val)).$val; } } } close FILE; } } sub main { my %lang; my @po; my $basename = shift @ARGV; foreach my $arg (@ARGV) { foreach my $f (glob $arg) { if($f =~ /\*/) { printf "Skipping $f\n"; } elsif($f =~ /\.po$/) { push(@po, $f); } else { die "unknown file extension."; } } } my %data = loadfiles(\%lang,@po); my @clang; foreach my $la (sort keys %lang) { push(@clang, "${basename}$la.lang"); } push(@clang, "${basename}en.lang"); die "There have been warning. No output.\n" if $waswarn; createlang(\%data, @clang); }