3 # this script is stolen and modified from Merkaartor
4 # you are welcome if you make pure java utility instead of this
10 my $basename = 'nevernote';
17 # don't copy these from files
19 "X-Generator" => "Merkaartor translation convert",
20 "MIME-Version" => "1.0",
21 "Content-Type" => "text/plain; charset=UTF-8",
22 "Content-Transfer-Encoding" => "8bit",
23 "Project-Id-Version" => "nevernote 1.0",
24 "Report-Msgid-Bugs-To" => $mail,
25 "POT-Creation-Date" => getdate(),
26 "PO-Revision-Date" => getdate(),
27 # "Last-Translator" => $mail,
28 # "Language-Team" => $mail,
29 # "X-Launchpad-Export-Date" => getdate(),
37 return sprintf("%04d-%02d-%02d %02d:%02d+0000",
38 1900+$t[5],$t[4]+1,$t[3],$t[2],$t[1]);
45 my ($lang,$keys,@files) = @_;
46 foreach my $file (@files)
48 die "Could not open file $file." if(!open FILE,"<:utf8",$file);
51 if($file =~ /[-_](.._..)\.po$/ || $file =~ /^(?:.*\/)?(.._..)\.po$/ ||
52 $file =~ /[-_](...?)\.po$/ || $file =~ /^(?:.*\/)?(..)\.po$/)
56 my %postate = (last => "", type => "");
61 my $fn = "$file:$linenum";
65 checkpo(\%postate, \%all, $l, "line $linenum in $file", $keys, 1);
66 $postate{fuzzy} = 1 if ($_ =~ /fuzzy/);
68 elsif($_ =~ /^"(.*)"$/) {$postate{last} .= $1;}
69 elsif($_ =~ /^(msg.+) "(.*)"$/)
71 my ($n, $d) = ($1, $2);
72 my $new = !${postate}{fuzzy} && (($n eq "msgid" && $postate{type} ne "msgctxt") || ($n eq "msgctxt"));
73 checkpo(\%postate, \%all, $l, "line $linenum in $file", $keys, $new);
76 $postate{src} = $fn if $new;
80 die "Strange line $linenum in $file: $_.";
83 checkpo(\%postate, \%all, $l, "line $linenum in $file", $keys, 1);
85 elsif($file =~ /\.ts$/)
101 if(/<name>(.*)<\/name>/) { $ctx = $1; }
102 elsif(/<location filename="(.*?)" line="(.*?)"\/>/) { $loc = "$1:$2"; }
103 elsif(/context>/){$ctx = undef;}
104 elsif(/message( numerus="yes")?>/)
107 die "No language found in file $file." if !$l;
110 $source = maketxt($source);
113 my $txt = "line $linenum in $file";
114 $txt .= ", $loc" if($loc);
115 for($i = 0; $i <= $#trans; ++$i)
117 copystring(\%all, $source, $i ? "$l.$i" : $l, maketxt($trans[$i]), $txt, $ctx, 0);
119 if(defined($numerus))
121 copystring(\%all, $source, "en.1", $source, $txt, $ctx, 0);
124 copystring(\%all, $source, "_file", $loc, $txt, $ctx, 0) if $loc;
125 copystring(\%all, $source, "_src.$l", "$file:$linenum", $txt, $ctx, 0);
128 $loc = $issource = $istrans = $source = $numerus = $fuzzy = undef;
131 elsif(/<TS .* language="(.*)">/) { $l = getlang($1); ++$lang->{$l}; }
132 elsif(/<TS version.*>/) {}
133 elsif(/<\?xml/ || /<!DOCTYPE/ || /<\/TS>/ || /<defaultcodec>/){} # ignore
134 elsif(/comment>(.*)<\/comment>/){} # ignore
136 elsif(/<source>(.*)<\/source>/){$source = $1;}
137 elsif(/<source>(.*)/){$source = "$1\n"; $issource = 1;}
138 elsif($issource && /(.*)<\/source>/){$source .= $1; $issource = undef;}
139 elsif($issource){$source .= $_;}
141 elsif(defined($numerus) && /translation(?: type="(unfinished|obsolete)")?>/) {$fuzzy=$1;}
142 elsif(defined($numerus) && /<numerusform>(.*)<\/numerusform>/){$trans[$numerus++] = $1;}
143 elsif(defined($numerus) && /<numerusform>(.*)/){$trans[$numerus] = "$1\n"; $istrans = 1;}
144 elsif(defined($numerus) && $istrans && /(.*)<\/numerusform>/){$trans[$numerus++] .= $1; $istrans = undef;}
145 elsif(/<translation(?: type="(unfinished|obsolete)")?>(.*)<\/translation>/){$trans[0] = $2;$fuzzy=$1;}
146 elsif(/<translation(?: type="(unfinished|obsolete)")?>(.*)/){$trans[0] = "$2\n"; $istrans = 1;$fuzzy=$1;}
147 elsif($istrans && /(.*)<\/translation>/){$trans[0] .= $1; $istrans = undef;}
148 elsif($istrans){$trans[$numerus ? $numerus : 0] .= $_;}
149 elsif(/<translatorcomment>(.*)<\/translatorcomment>/){} #ignore
152 die "Strange line $linenum in $file: $_.";
159 die "File format not supported for file $file.";
170 sub copystring($$$$$$$)
172 my ($data, $en, $l, $str, $txt, $context, $ispo) = @_;
174 $en = "___${context}___$en" if $context && !$nocontext;
176 if(exists($data->{$en}{$l}) && $data->{$en}{$l} ne $str)
181 $data->{$en}{$l} .= ";$str" if !($data->{$en}{$l} =~ /$str/);
183 elsif(!$data->{$en}{$l})
185 $data->{$en}{$l} = $str;
190 my $f = $data->{$en}{_file} || "";
191 $f = ($f ? "$f;".$data->{$en}{"_src.$l"} : $data->{$en}{"_src.$l"}) if $data->{$en}{"_src.$l"};
192 my $isotherpo = ($f =~ /\.po\:/);
193 my $pomode = ($ispo && !$isotherpo) || (!$ispo && $isotherpo);
195 my $mis = "String mismatch for '$en' **$str** ($txt) != **$data->{$en}{$l}** ($f)\n";
198 if(($conflicts{$l}{$str} || "") eq $data->{$en}{$l}) {}
199 elsif($pomode && $alwaysup) { $replace=$isotherpo; }
200 elsif($pomode && $alwayspo) { $replace=$ispo; }
201 elsif($noask) { print $mis; ++$waswarn; }
204 ReadMode 4; # Turn off controls keys
205 my $arg = "(l)eft, (r)ight";
206 $arg .= ", (p)o, (u)pstream[ts], all p(o), all up(s)tream" if $pomode;
207 $arg .= ", e(x)it: ";
209 while((my $c = getc()))
211 if($c eq "l") { $replace=1; }
213 elsif($c eq "p" && $pomode) { $replace=$ispo; }
214 elsif($c eq "u" && $pomode) { $replace=$isotherpo; }
215 elsif($c eq "o" && $pomode) { $alwayspo = 1; $replace=$ispo; }
216 elsif($c eq "s" && $pomode) { $alwaysup = 1; $replace=$isotherpo; }
217 elsif($c eq "x") { $noask = 1; ++$waswarn; }
218 else { print "\n$arg"; next; }
222 ReadMode 0; # Turn on controls keys
228 $data->{$en}{$l} = $str;
229 $conflicts{$l}{$data->{$en}{$l}} = $str;
233 $conflicts{$l}{$str} = $data->{$en}{$l};
240 $data->{$en}{$l} = $str;
246 my ($postate, $data, $l, $txt, $keys, $new) = @_;
248 if($postate->{type} eq "msgid") {$postate->{msgid} = $postate->{last};}
249 elsif($postate->{type} eq "msgid_plural") {$postate->{msgid_1} = $postate->{last};}
250 elsif($postate->{type} =~ /^msgstr(\[0\])?$/) {$postate->{msgstr} = $postate->{last};}
251 elsif($postate->{type} =~ /^msgstr\[(.+)\]$/) {$postate->{"msgstr_$1"} = $postate->{last};}
252 elsif($postate->{type} eq "msgctxt") {$postate->{context} = $postate->{last};}
253 elsif($postate->{type}) { die "Strange type $postate->{type} found\n" }
257 if((!$postate->{fuzzy}) && $postate->{msgstr} && $postate->{msgid})
259 copystring($data, $postate->{msgid}, $l, $postate->{msgstr},$txt,$postate->{context}, 1);
260 for($i = 1; exists($postate->{"msgstr_$i"}); ++$i)
261 { copystring($data, $postate->{msgid}, "$l.$i", $postate->{"msgstr_$i"},$txt,$postate->{context}, 1); }
262 if($postate->{msgid_1})
263 { copystring($data, $postate->{msgid}, "en.1", $postate->{msgid_1},$txt,$postate->{context}, 1); }
264 copystring($data, $postate->{msgid}, "_src.$l", $postate->{src},$txt,$postate->{context}, 1);
266 elsif($postate->{msgstr} && !$postate->{msgid})
268 my %k = ($postate->{msgstr} =~ /(.+?): +(.+?)\\n/g);
269 # take the first one!
270 for $a (sort keys %k)
272 $keys->{$l}{$a} = $k{$a} if !$keys->{$l}{$a};
275 foreach my $k (keys %{$postate})
277 delete $postate->{$k};
279 $postate->{type} = $postate->{last} = "";
285 my ($data, $keys, @files) = @_;
286 foreach my $file (@files)
290 if($file =~ /[-_](.._..)\.po$/ || $file =~ /^(?:.*\/)?(.._..)\.po$/ ||
291 $file =~ /[-_](...?)\.po$/ || $file =~ /^(?:.*\/)?(..)\.po$/)
294 $head = "# translation into language $la file $file\n";
296 elsif($file =~ /\.pot$/)
299 $head = "# template file $file\n";
303 die "Language for file $file unknown.";
305 die "Could not open outfile $file\n" if !open FILE,">:utf8",$file;
306 print FILE "${head}msgid \"\"\nmsgstr \"\"\n";
308 foreach my $k (keys %{$keys->{$la}}) { $k{$k} = $keys->{$la}{$k}; }
309 foreach my $k (keys %defkeys) { $k{$k} = $defkeys{$k}; }
310 foreach my $k (sort keys %k)
312 print FILE "\"$k: $k{$k}\\n\"\n";
316 foreach my $en (sort keys %{$data})
320 $ctx = $1 if $ennc =~ s/^___(.*)___//;
321 my $str = ($la ne "en" && exists($data->{$en}{$la})) ? $data->{$en}{$la} : "";
322 if($data->{$en}{_file})
324 foreach my $f (split ";",$data->{$en}{_file})
332 # print FILE "#: unknown:0\n"
334 if($ennc =~ /\%[0-9n]/)
335 { print FILE "#, c-format, qt-format\n"; }
336 elsif($ennc =~ /\%[ds]/)
337 { print FILE "#, c-format\n"; }
338 print FILE "msgctxt \"$ctx\"\n" if $ctx;
339 print FILE "msgid \"$ennc\"\n";
340 print FILE "msgid_plural \"$data->{$en}{\"en.1\"}\"\n" if $data->{$en}{"en.1"};
341 if($la ne "en" && (exists($data->{$en}{"$la.1"}) || $data->{$en}{"en.1"}))
343 print FILE "msgstr[0] \"$str\"\n";
344 for($i = 1; exists($data->{$en}{"$la.$i"}); ++$i)
345 { print FILE "msgstr[$i] \"$data->{$en}{\"$la.$i\"}\"\n"; }
349 print FILE "msgstr \"$str\"\n";
363 $str =~ s/"/\\"/g;
364 $str =~ s/'/'/g;
376 $str =~ s/\\"/"/g;
377 $str =~ s/'/'/g;
385 if($l eq "ru_RU") {$l = "ru";}
386 elsif($l eq "pl_PL") {$l = "pl";}
390 sub makenumerus($$$$)
392 my ($data, $first, $last,$l) = @_;
393 my $repl = $first.makexml($data->{$l}).$last;
394 for($i = 1; exists($data->{"$l.$i"}); ++$i)
396 $repl .= "\n".$first.makexml($data->{"$l.$i"}).$last;
401 sub convert_ts_message($$$$)
403 my ($content,$data,$l,$ctx) = @_;
404 $content =~ /<source>(.*)<\/source>/s;
405 my $source = ($ctx ? "___${ctx}___" : "") .maketxt($1);
406 if(exists($data->{$source}{$l}))
408 if($content =~ /numerus="yes"/)
410 if(!($content =~ s/( +<numerusform>).*(<\/numerusform>)/makenumerus($data->{$source},$1,$2,$l)/se))
412 die sprintf "Could not replace string '%.10s'",$source;
417 my $repl = makexml($data->{$source}{$l});
418 if(!($content =~ s/(<translation).*(<\/translation>)/$1>$repl$2/s))
420 die sprintf "Could not replace string '%.10s'",$source;
427 sub convert_ts_context($$$)
429 my ($content,$data,$l) = @_;
431 $ctx = $1 if(!$nocontext && $content =~ /<name>(.*)<\/name>/);
432 $content =~ s/(<message.*?>.*?<\/message>)/convert_ts_message($1,$data,$l,$ctx)/seg;
438 my ($data, @files) = @_;
440 foreach my $file (@files)
444 die "Could not open $file\n" if !open FILE,"<:utf8",$file;
445 my $content = <FILE>;
447 if(!($content =~ /<TS .* language="(.*)">/))
449 die "Could not find language for $file.";
452 $content =~ s/(<context>.*?<\/context>)/convert_ts_context($1,$data,$l)/seg;
454 die "Could not open output $file\n" if !open FILE,">:utf8",$file;
465 foreach my $f (@ARGV)
467 if($f =~ /\*/) { printf "Skipping $f\n"; }
468 elsif($f =~ /\.po$/) { push(@po, $f); }
469 elsif($f =~ /\.ts$/) { push(@ts, $f); }
470 else { die "unknown file extension."; }
472 my %data = loadfiles(\%lang,\%pokeys, @ts,@po);
474 foreach my $la (sort keys %lang)
476 push(@cpo, "${basename}_$la.po");
478 push(@cpo, "$basename.pot");
479 die "There have been warning. No output.\n" if $waswarn;
480 createpos(\%data, \%pokeys, @cpo);
481 createts(\%data, @ts) if @ts;