1 #!@XML_I18N_TOOLS_PERL@ -w
4 # The XML Translation Merge Tool
6 # Copyright (C) 2000 Free Software Foundation.
7 # Copyright (C) 2000, 2001 Eazel, Inc
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License as
11 # published by the Free Software Foundation; either version 2 of the
12 # License, or (at your option) any later version.
14 # This script is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this library; if not, write to the Free Software
21 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 # Authors: Maciej Stachowiak <mjs@eazel.com>
24 # Kenneth Christiansen <kenneth@gnu.org>
25 # Darin Adler <darin@eazel.com>
29 ## Release information
30 my $PROGRAM = "xml-i18n-merge";
31 my $PACKAGE = "xml-i18n-tools";
34 ## Script options - Enable by setting value to 1
42 ## Scalars used by the option stuff
44 my $VERSION_ARG = "0";
45 my $OAF_STYLE_ARG = "0";
46 my $XML_STYLE_ARG = "0";
47 my $KEYS_STYLE_ARG = "0";
48 my $DESKTOP_STYLE_ARG = "0";
54 "help|h|?" => \$HELP_ARG,
55 "version|v" => \$VERSION_ARG,
56 "quiet|q" => \$QUIET_ARG,
57 "oaf-style|o" => \$OAF_STYLE_ARG,
58 "xml-style|x" => \$XML_STYLE_ARG,
59 "keys-style|k" => \$KEYS_STYLE_ARG,
60 "desktop-style|d" => \$DESKTOP_STYLE_ARG
69 my %po_files_by_lang = ();
70 my %translations = ();
76 ## This section will check for the different options.
78 sub split_on_argument {
85 } elsif ($OAF_STYLE_ARG && @ARGV > 2) {
89 &oaf_merge_translations;
90 } elsif ($XML_STYLE_ARG && @ARGV > 2) {
94 &xml_merge_translations;
95 } elsif ($KEYS_STYLE_ARG && @ARGV > 2) {
99 &keys_merge_translations;
100 } elsif ($DESKTOP_STYLE_ARG && @ARGV > 2) {
104 &desktop_merge_translations;
118 ## Sub for printing release information
120 print "${PROGRAM} (${PACKAGE}) ${VERSION}\n";
121 print "Written by Maciej Stachowiak and Kenneth Christiansen, 2000.\n\n";
122 print "Copyright (C) 2000 Free Software Foundation, Inc.\n";
123 print "Copyright (C) 2000, 2001 Eazel, Inc.\n";
124 print "This is free software; see the source for copying conditions. There is NO\n";
125 print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
129 ## Sub for printing usage information
131 print "Usage: ${PROGRAM} [OPTIONS] PO_DIRECTORY FILENAME OUTPUT_FILE\n";
132 print "Generates an xml file that includes translated versions of some attributes,\n";
133 print "from an untranslated source and a po directory that includes translations.\n";
134 print " -v, --version shows the version\n";
135 print " -h, --help shows this help page\n";
136 print " -q, --quiet quiet mode\n";
137 print " -o, --oaf-style includes translations in the oaf style\n";
138 print " -x, --xml-style includes translations in the xml style\n";
139 print " -k, --keys-style includes translations in the keys style\n";
140 print " -d, --desktop-style includes translations in the desktop style\n";
141 print "\nReport bugs to <mjs\@eazel.com>.\n";
146 ## Sub for printing error messages
148 # print "xml-i18n-merge: invalid option @ARGV\n";
149 print "Try `${PROGRAM} --help' for more information.\n";
155 print "Merging translations into $OUTFILE.\n" unless $QUIET_ARG;
162 &create_translation_database;
167 # General-purpose code for looking up translations in .po files
171 my @po_files = glob("${PO_DIR}/*.po");
173 @languages = map (&po_file2lang, @po_files);
175 foreach my $lang (@languages) {
176 $po_files_by_lang{$lang} = shift (@po_files);
183 $tmp =~ s/^.*\/(.*)\.po$/$1/;
188 sub create_translation_database
190 foreach my $lang (@languages) {
192 my $po_file = $po_files_by_lang{$lang};
194 open PO_FILE, "<$po_file";
198 $_ = <PO_FILE>; next;
200 if (/^msgid "(.*)"/ ) {
204 if (/^msgstr "(.+)"/) {
206 $translations{$lang . "|" . $msgid} = $msgstr;
207 # print "[$lang]$msgstr\n";
214 sub lookup_translations
220 foreach my $lang (@languages) {
221 my $translation = lookup_translation ($value, $lang);
224 $transmap{$lang} = $translation;
232 sub lookup_translation
234 my ($string, $lang) = @_;
235 $string =~ s/\+/\\+/g;
237 my $salt = "$lang|$string";
239 if ($translations{$salt}) {
240 return $translations{$salt};
247 sub entity_encode_translations
251 foreach my $key (keys %transmap) {
252 $transmap{$key} = entity_encode ($transmap{$key});
261 my ($pre_encoded) = @_;
263 $pre_encoded =~ s/\\(.)/$1/g;
264 my @list_of_chars = unpack ('C*', $pre_encoded);
266 return join ('', map (&entity_encode_int, @list_of_chars));
269 sub entity_encode_int
271 if ($_ > 127 || $_ == 34 || $_ == 38) {
272 return "&#" . $_ . ";";
279 ## XML/OAF-specific merge code
281 sub oaf_merge_translations
285 local $/; # slurp mode
286 open INPUT, "<$FILE" or die "can't open $FILE: $!";
287 $xml_source = <INPUT>;
291 open OUTPUT, ">$OUTFILE";
293 while ($xml_source =~ /[ \t]*<[^<]*\s_\w+="[^"]*"[^<]*>/m) {
298 my $non_translated_line = $orig_node;
299 $non_translated_line =~ s/_(\w+)="/$1="/;
301 my $new_node = $non_translated_line;
303 my $value_str = $orig_node;
304 $value_str =~ s/.*_\w+="([^"]*)".*/$1/s;
307 my %value_translation_map = entity_encode_translations
308 (lookup_translations ($value_str));
310 foreach my $key (sort keys %value_translation_map) {
311 my $translation = $value_translation_map{$key};
313 my $translated_line = $orig_node;
314 $translated_line =~ s/name="([^"]*)"/name="$1-$key"/;
315 $translated_line =~ s/(\s*)_(\w+)="[^"]*"/$1$2="$translation"/;
317 $new_node .= "\n$translated_line";
321 $xml_source = $new_node . $xml_source;
324 print OUTPUT $xml_source;
330 ## XML (non-OAF) merge code
332 sub xml_merge_translations
336 local $/; # slurp mode
337 open INPUT, "<$FILE" or die "can't open $FILE: $!";
338 $xml_source = <INPUT>;
342 open OUTPUT, ">$OUTFILE";
344 # FIXME: support attribute translations
346 # First just unmark for translation all empty nodes
347 # for example <_foo/> is just replaced by <foo/>
348 $xml_source =~ s/<_(\w+)\/>/<$1\/>/mg;
350 # Support for XML <_foo>blah</_foo> style translations
351 while ($xml_source =~ /([ \t]*)<_(\w+)>([^<]+)<\/_\2>/m) {
359 my $non_translated_line = "$spaces<$tag_name>$value_str</$tag_name>";
361 my $new_node = $non_translated_line;
364 my %value_translation_map = entity_encode_translations
365 (lookup_translations ($value_str));
367 foreach my $key (sort keys %value_translation_map) {
368 my $translation = $value_translation_map{$key};
370 $new_node .= "\n$spaces<$tag_name xml:lang=\"$key\">$translation</$tag_name>";
374 $xml_source = $new_node . $xml_source;
377 print OUTPUT $xml_source;
382 sub keys_merge_translations
384 open INPUT, "<${FILE}";
386 open OUTPUT, ">${OUTFILE}";
393 my $non_translated_line = $orig_line;
394 $non_translated_line =~ s/_([^="]*)=/$1=/;
396 print OUTPUT "${non_translated_line}\n";
398 my $value_str = $orig_line;
399 $value_str =~ s/.*_\w+=(.*)/$1/;
402 my %value_translation_map = lookup_translations ($value_str);
404 foreach my $key (sort keys %value_translation_map) {
405 my $translation = $value_translation_map{$key};
407 my $translated_line = $orig_line;
408 $translated_line =~ s/_([^="]*)=([^\n]*)/\[$key]$1=$translation/;
409 print OUTPUT "$translated_line\n";
421 sub desktop_merge_translations
423 open INPUT, "<${FILE}";
425 open OUTPUT, ">${OUTFILE}";
432 my $non_translated_line = $orig_line;
433 $non_translated_line =~ s/_([^="]*)=/$1=/;
435 print OUTPUT "${non_translated_line}\n";
437 my $value_str = $orig_line;
438 $value_str =~ s/.*_\w+=(.*)/$1/;
441 my %value_translation_map = lookup_translations ($value_str);
443 foreach my $key (sort keys %value_translation_map) {
444 my $translation = $value_translation_map{$key};
446 my $translated_line = $orig_line;
447 $translated_line =~ s/^_([^="]*)=([^\n]*)/$1\[$key]=$translation/;
448 print OUTPUT "$translated_line\n";