4 # The Intltool Message Merger
6 # Copyright (C) 2000, 2003 Free Software Foundation.
7 # Copyright (C) 2000, 2001 Eazel, Inc
9 # Intltool is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # version 2 published by the Free Software Foundation.
13 # Intltool is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 # As a special exception to the GNU General Public License, if you
23 # distribute this file as part of a program that contains a
24 # configuration script generated by Autoconf, you may include it under
25 # the same distribution terms that you use for the rest of that program.
27 # Authors: Maciej Stachowiak <mjs@noisehavoc.org>
28 # Kenneth Christiansen <kenneth@gnu.org>
29 # Darin Adler <darin@bentspoon.com>
31 # Proper XML UTF-8'ification written by Cyrille Chepelov <chepelov@calixo.net>
34 ## Release information
35 my $PROGRAM = "intltool-merge";
36 my $PACKAGE = "intltool";
45 my $must_end_tag = -1;
47 my $translation_depth = -1;
50 my @translation_strings = ();
51 my $leading_space = "";
53 ## Scalars used by the option stuff
57 my $XML_STYLE_ARG = 0;
58 my $KEYS_STYLE_ARG = 0;
59 my $DESKTOP_STYLE_ARG = 0;
60 my $SCHEMAS_STYLE_ARG = 0;
61 my $RFC822DEB_STYLE_ARG = 0;
63 my $PASS_THROUGH_ARG = 0;
65 my $MULTIPLE_OUTPUT = 0;
72 "version" => \$VERSION_ARG,
73 "quiet|q" => \$QUIET_ARG,
74 "oaf-style|o" => \$BA_STYLE_ARG, ## for compatibility
75 "ba-style|b" => \$BA_STYLE_ARG,
76 "xml-style|x" => \$XML_STYLE_ARG,
77 "keys-style|k" => \$KEYS_STYLE_ARG,
78 "desktop-style|d" => \$DESKTOP_STYLE_ARG,
79 "schemas-style|s" => \$SCHEMAS_STYLE_ARG,
80 "rfc822deb-style|r" => \$RFC822DEB_STYLE_ARG,
81 "pass-through|p" => \$PASS_THROUGH_ARG,
82 "utf8|u" => \$UTF8_ARG,
83 "multiple-output|m" => \$MULTIPLE_OUTPUT,
84 "cache|c=s" => \$cache_file
91 my %po_files_by_lang = ();
92 my %translations = ();
93 my $iconv = $ENV{"INTLTOOL_ICONV"} || "iconv";
95 # Use this instead of \w for XML files to handle more possible characters.
96 my $w = "[-A-Za-z0-9._:]";
98 # XML quoted string contents
101 ## Check for options.
111 elsif ($BA_STYLE_ARG && @ARGV > 2)
115 &ba_merge_translations;
118 elsif ($XML_STYLE_ARG && @ARGV > 2)
128 elsif ($KEYS_STYLE_ARG && @ARGV > 2)
133 &keys_merge_translations;
136 elsif ($DESKTOP_STYLE_ARG && @ARGV > 2)
140 &desktop_merge_translations;
143 elsif ($SCHEMAS_STYLE_ARG && @ARGV > 2)
147 &schemas_merge_translations;
150 elsif ($RFC822DEB_STYLE_ARG && @ARGV > 2)
154 &rfc822deb_merge_translations;
164 ## Sub for printing release information
168 ${PROGRAM} (${PACKAGE}) ${VERSION}
169 Written by Maciej Stachowiak, Darin Adler and Kenneth Christiansen.
171 Copyright (C) 2000-2003 Free Software Foundation, Inc.
172 Copyright (C) 2000-2001 Eazel, Inc.
173 This is free software; see the source for copying conditions. There is NO
174 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
179 ## Sub for printing usage information
183 Usage: ${PROGRAM} [OPTION]... PO_DIRECTORY FILENAME OUTPUT_FILE
184 Generates an output file that includes some localized attributes from an
185 untranslated source file.
187 Mandatory options: (exactly one must be specified)
188 -b, --ba-style includes translations in the bonobo-activation style
189 -d, --desktop-style includes translations in the desktop style
190 -k, --keys-style includes translations in the keys style
191 -s, --schemas-style includes translations in the schemas style
192 -r, --rfc822deb-style includes translations in the RFC822 style
193 -x, --xml-style includes translations in the standard xml style
196 -u, --utf8 convert all strings to UTF-8 before merging
197 -p, --pass-through use strings as found in .po files, without
198 conversion (STRONGLY unrecommended with -x)
199 -m, --multiple-output output one localized file per locale, instead of
200 a single file containing all localized elements
201 -c, --cache=FILE specify cache file name
202 (usually \$top_builddir/po/.intltool-merge-cache)
203 -q, --quiet suppress most messages
204 --help display this help and exit
205 --version output version information and exit
207 Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE")
208 or send email to <xml-i18n-tools\@gnome.org>.
214 ## Sub for printing error messages
217 print STDERR "Try `${PROGRAM} --help' for more information.\n";
224 print "Merging translations into $OUTFILE.\n" unless $QUIET_ARG;
235 &get_translation_database;
238 # General-purpose code for looking up translations in .po files
243 $tmp =~ s/^.*\/(.*)\.po$/$1/;
249 for my $po_file (glob "$PO_DIR/*.po") {
250 $po_files_by_lang{po_file2lang($po_file)} = $po_file;
254 sub get_local_charset
257 my $alias_file = $ENV{"G_CHARSET_ALIAS"} || "/usr/lib/charset.alias";
259 # seek character encoding aliases in charset.alias (glib)
261 if (open CHARSET_ALIAS, $alias_file)
263 while (<CHARSET_ALIAS>)
266 return $1 if (/^\s*([-._a-zA-Z0-9]+)\s+$encoding\b/i)
272 # if not found, return input string
279 my ($in_po_file) = @_;
282 open IN_PO_FILE, $in_po_file or die;
285 ## example: "Content-Type: text/plain; charset=ISO-8859-1\n"
286 if (/Content-Type\:.*charset=([-a-zA-Z0-9]+)\\n/)
296 print STDERR "Warning: no encoding found in $in_po_file. Assuming ISO-8859-1\n" unless $QUIET_ARG;
297 $encoding = "ISO-8859-1";
300 system ("$iconv -f $encoding -t UTF-8 </dev/null 2>/dev/null");
302 $encoding = get_local_charset($encoding);
308 sub utf8_sanity_check
312 if (!$PASS_THROUGH_ARG)
314 $PASS_THROUGH_ARG="1";
319 sub get_translation_database
322 &get_cached_translation_database;
324 &create_translation_database;
328 sub get_newest_po_age
332 foreach my $file (values %po_files_by_lang)
334 my $file_age = -M $file;
335 $newest_age = $file_age if !$newest_age || $file_age < $newest_age;
338 $newest_age = 0 if !$newest_age;
345 print "Generating and caching the translation database\n" unless $QUIET_ARG;
347 &create_translation_database;
349 open CACHE, ">$cache_file" || die;
350 print CACHE join "\x01", %translations;
356 print "Found cached translation database\n" unless $QUIET_ARG;
359 open CACHE, "<$cache_file" || die;
365 %translations = split "\x01", $contents;
368 sub get_cached_translation_database
370 my $cache_file_age = -M $cache_file;
371 if (defined $cache_file_age)
373 if ($cache_file_age <= &get_newest_po_age)
378 print "Found too-old cached translation database\n" unless $QUIET_ARG;
384 sub create_translation_database
386 for my $lang (keys %po_files_by_lang)
388 my $po_file = $po_files_by_lang{$lang};
392 my $encoding = get_po_encoding ($po_file);
394 if (lc $encoding eq "utf-8")
396 open PO_FILE, "<$po_file";
400 print STDERR "WARNING: $po_file is not in UTF-8 but $encoding, converting...\n" unless $QUIET_ARG;;
402 open PO_FILE, "$iconv -f $encoding -t UTF-8 $po_file|";
407 open PO_FILE, "<$po_file";
418 $nextfuzzy = 1 if /^#, fuzzy/;
420 if (/^msgid "((\\.|[^\\])*)"/ )
422 $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr;
429 $msgid = unescape_po_string($1);
436 if (/^msgstr "((\\.|[^\\])*)"/)
438 $msgstr = unescape_po_string($1);
443 if (/^"((\\.|[^\\])*)"/)
445 $msgid .= unescape_po_string($1) if $inmsgid;
446 $msgstr .= unescape_po_string($1) if $inmsgstr;
449 $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr;
457 sub unescape_one_sequence
461 return "\\" if $sequence eq "\\\\";
462 return "\"" if $sequence eq "\\\"";
463 return "\n" if $sequence eq "\\n";
465 # gettext also handles \n, \t, \b, \r, \f, \v, \a, \xxx (octal),
466 # \xXX (hex) and has a comment saying they want to handle \u and \U.
471 sub unescape_po_string
475 $string =~ s/(\\.)/unescape_one_sequence($1)/eg;
480 ## NOTE: deal with < - < but not > - > because it seems its ok to have
481 ## > in the entity. For further info please look at #84738.
494 # entity_encode: (string)
496 # Encode the given string to XML format (encode '<' etc). It also
497 # encodes high bit if not in UTF-8 mode.
501 my ($pre_encoded) = @_;
503 my @list_of_chars = unpack ('C*', $pre_encoded);
505 if ($PASS_THROUGH_ARG)
507 return join ('', map (&entity_encode_int_even_high_bit, @list_of_chars));
511 # with UTF-8 we only encode minimalistic
512 return join ('', map (&entity_encode_int_minimalist, @list_of_chars));
516 sub entity_encode_int_minimalist
518 return """ if $_ == 34;
519 return "&" if $_ == 38;
520 return "'" if $_ == 39;
521 return "<" if $_ == 60;
525 sub entity_encode_int_even_high_bit
527 if ($_ > 127 || $_ == 34 || $_ == 38 || $_ == 39 || $_ == 60)
529 # the ($_ > 127) should probably be removed
530 return "&#" . $_ . ";";
538 sub entity_encoded_translation
540 my ($lang, $string) = @_;
542 my $translation = $translations{$lang, $string};
543 return $string if !$translation;
544 return entity_encode ($translation);
547 ## XML (bonobo-activation specific) merge code
549 sub ba_merge_translations
554 local $/; # slurp mode
555 open INPUT, "<$FILE" or die "can't open $FILE: $!";
560 open OUTPUT, ">$OUTFILE" or die "can't open $OUTFILE: $!";
562 while ($source =~ s|^(.*?)([ \t]*<\s*$w+\s+($w+\s*=\s*"$q"\s*)+/?>)([ \t]*\n)?||s)
566 my $node = $2 . "\n";
570 while (s/(\s)_($w+\s*=\s*"($q)")/$1$2/s) {
571 push @strings, entity_decode($3);
576 for my $string (@strings)
578 for my $lang (keys %po_files_by_lang)
580 $langs{$lang} = 1 if $translations{$lang, $string};
584 for my $lang (sort keys %langs)
587 s/(\sname\s*=\s*)"($q)"/$1"$2-$lang"/s;
588 s/(\s)_($w+\s*=\s*")($q)"/$1 . $2 . entity_encoded_translation($lang, $3) . '"'/seg;
593 print OUTPUT $source;
599 ## XML (non-bonobo-activation) merge code
605 my $depth = shift || 0;
606 my $language = shift || "";
610 $entered_tag[$depth + 1] = 0;
612 # Check to see if this is not a CDATA element.
614 foreach my $sub (@{ $ref }) {
615 if (ref $sub eq 'ARRAY') {
620 foreach my $sub (@{ $ref }) {
622 # Handle empty nodes.
628 if (ref $sub eq 'ARRAY') {
634 # Check to see if current tag has any elements that need to be translated.
636 if ($translation_depth == -1) {
637 foreach my $subsub (@{ $sub } ) {
638 if (ref $subsub eq 'HASH') {
639 foreach my $e (reverse(keys %{ $subsub })) {
641 $translation_depth = $depth;
644 } elsif (ref $subsub eq 'ARRAY') {
650 my $current_tag = pop(@tag_stack);
651 push @tag_stack, $current_tag;
652 @translation_strings = ();
654 $must_end_tag = $depth;
655 print $fh "<", $current_tag;
657 parseTree($fh, $sub, $depth + 1, $language);
659 # Close any open tags
661 if ($must_end_tag != -1) {
662 if ($must_end_tag < $depth) {
667 if ($depth == $translation_depth) {
668 $translation_depth = -1;
674 # Add ending tag(s), if needed
676 if ($entered_tag[$depth + 1] == 1) {
677 while ($last_depth > $depth) {
680 print $fh "</", pop(@tag_stack), ">";
683 $last_depth = $depth;
685 # If beginning a translation block, then process for each language.
687 if ($translation_depth == $depth) {
690 # Skip languages that do not have translations. Since we only
691 # do this check when $translation_depth == $depth, it will never
692 # happen for nodes inside a node with a translated element.
694 for my $lang (sort keys %po_files_by_lang) {
695 if ($has_children == 1) {
698 # Skip this language if there is no translation
701 foreach my $string (@translation_strings) {
702 my $decode_string = entity_decode($string);
703 my $translation = $translations{$lang, $decode_string};
711 if ($do_language == 0) {
716 $leading_space =~ s/.*\n//g;
717 print $fh $leading_space;
718 if ($MULTIPLE_OUTPUT && $lang ne "$language") {
722 print $fh "<", $current_tag, " xml:lang=\"", $lang, "\"";
723 $must_end_tag = $depth;
725 parseTree($fh, $sub, $depth + 1, $lang);
727 # Close any open tags
729 if ($must_end_tag != -1) {
730 if ($must_end_tag < $depth) {
735 if ($depth == $translation_depth) {
736 $translation_depth = -1;
742 # Add ending tag(s), if needed
744 if ($entered_tag[$depth + 1] == 1) {
745 while ($last_depth > $depth + 1) {
748 print $fh "</", pop(@tag_stack), ">";
750 print $fh "</", $current_tag, ">";
753 $translation_depth = -1;
754 $last_depth = $depth;
758 } elsif (ref $sub eq 'HASH') {
760 # Process tag elements
762 foreach my $e (reverse(keys %{ $sub })) {
764 my $string = $sub->{$e};
767 $string =~ s/^[\s]+//;
768 $string =~ s/[\s]+$//;
770 if ($string =~ /^'.*'$/)
774 $string =~ s/^['"]//g;
775 $string =~ s/['"]$//g;
784 my $decode_string = entity_decode($string);
785 my $translation = $translations{$language, $decode_string};
787 $translation = entity_encode($translation);
788 $string = $translation;
793 print $fh " $key=$quote$string$quote";
798 # Handle tags and CDATA values
800 # Mark parent tag as having been entered.
802 $entered_tag[$depth] = 1;
804 # The last_depth flag allows us to determine if this tag should be
805 # closed with "/>" or ">"
807 $last_depth = $depth;
809 # Close any open tags
811 if ($must_end_tag != -1) {
812 if ($must_end_tag < $depth) {
821 if ($sub =~ /^[\s]*$/) {
822 $leading_space .= $sub;
824 } elsif ($not_cdata) {
834 $temp_tag =~ s|^_||g;
836 if ($translation_depth == -1) {
837 $translation_depth = $depth;
841 # Push the tag on the stack, it will get handled when the ARRAY
842 # for this tag is processed.
844 push(@tag_stack, $temp_tag);
852 $string =~ s/^[\s]+//;
853 $string =~ s/[\s]+$//;
855 push(@translation_strings, $string);
863 my $decode_string = entity_decode($string);
864 my $translation = $translations{$language, $decode_string};
866 $translation = entity_encode($translation);
867 $string = $translation;
877 sub intltool_tree_char
881 my $clist = $expat->{Curlist};
884 # Use original_string so that we retain escaped entities
887 if ($pos > 0 and $clist->[$pos - 1] eq '0') {
888 $clist->[$pos] .= $expat->original_string();
890 push @$clist, 0 => $expat->original_string();
894 sub intltool_tree_start
900 # Use original_string so that we retain escaped entities
901 # in attribute values. We must convert the string to an
902 # @origlist array to conform to the structure of the Tree
905 my @original_array = split /\x/, $expat->original_string();
906 my $source = $expat->original_string();
908 # Remove leading tag.
910 $source =~ s|^\s*<\s*(\S+)||s;
912 # Grab attribute key/value pairs and push onto @origlist array.
916 if ($source =~ /^\s*([\w:-]+)\s*[=]\s*["]/)
918 $source =~ s|^\s*([\w:-]+)\s*[=]\s*["]([^"]*)["]||s;
920 push @origlist, '"' . $2 . '"';
922 elsif ($source =~ /^\s*([\w:-]+)\s*[=]\s*[']/)
924 $source =~ s|^\s*([\w:-]+)\s*[=]\s*[']([^']*)[']||s;
926 push @origlist, "'" . $2 . "'";
934 my $ol = [ { @origlist } ];
936 push @{ $expat->{Lists} }, $expat->{Curlist};
937 push @{ $expat->{Curlist} }, $tag => $ol;
938 $expat->{Curlist} = $ol;
943 my $filename = shift || return;
945 die "ERROR Cannot find filename: $filename\n";
948 my $ret = eval 'require XML::Parser';
950 die "You must have XML::Parser installed to run $0\n\n";
952 my $xp = new XML::Parser(Style => 'Tree');
953 $xp->setHandlers(Char => \&intltool_tree_char);
954 $xp->setHandlers(Start => \&intltool_tree_start);
955 my $tree = $xp->parsefile($filename);
957 # <foo><head id="a">Hello <em>there</em></head><bar>Howdy<ref/></bar>do</foo>
959 # [foo, [{}, head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], bar, [{},
960 # 0, "Howdy", ref, [{}]], 0, "do" ] ]
972 die "ERROR Cannot find filename: $infile\n";
975 print $fh qq{<?xml version="1.0" encoding="UTF-8"?>\n};
978 open DOCINPUT, "<${FILE}" or die;
979 $source = <DOCINPUT>;
982 if ($source =~ /(<!DOCTYPE.*\[.*\]\s*>)/s)
986 elsif ($source =~ /(<!DOCTYPE[^>]*>)/s)
996 if ($MULTIPLE_OUTPUT) {
997 for my $lang (sort keys %po_files_by_lang) {
999 mkdir $lang or die "Cannot create subdirectory $lang: $!\n";
1001 open OUTPUT, ">$lang/$OUTFILE" or die "Cannot open $lang/$OUTFILE: $!\n";
1002 my $tree = readXml($FILE);
1003 print_header($FILE, \*OUTPUT);
1004 parseTree(\*OUTPUT, $tree, 0, $lang);
1006 print "CREATED $lang/$OUTFILE\n" unless $QUIET_ARG;
1009 open OUTPUT, ">$OUTFILE" or die "Cannot open $OUTFILE: $!\n";
1010 my $tree = readXml($FILE);
1011 print_header($FILE, \*OUTPUT);
1012 parseTree(\*OUTPUT, $tree, 0);
1014 print "CREATED $OUTFILE\n" unless $QUIET_ARG;
1017 sub keys_merge_translations
1019 open INPUT, "<${FILE}" or die;
1020 open OUTPUT, ">${OUTFILE}" or die;
1024 if (s/^(\s*)_(\w+=(.*))/$1$2/)
1030 my $non_translated_line = $_;
1032 for my $lang (sort keys %po_files_by_lang)
1034 my $translation = $translations{$lang, $string};
1035 next if !$translation;
1037 $_ = $non_translated_line;
1038 s/(\w+)=.*/[$lang]$1=$translation/;
1052 sub desktop_merge_translations
1054 open INPUT, "<${FILE}" or die;
1055 open OUTPUT, ">${OUTFILE}" or die;
1059 if (s/^(\s*)_(\w+=(.*))/$1$2/)
1065 my $non_translated_line = $_;
1067 for my $lang (sort keys %po_files_by_lang)
1069 my $translation = $translations{$lang, $string};
1070 next if !$translation;
1072 $_ = $non_translated_line;
1073 s/(\w+)=.*/${1}[$lang]=$translation/;
1087 sub schemas_merge_translations
1092 local $/; # slurp mode
1093 open INPUT, "<$FILE" or die "can't open $FILE: $!";
1098 open OUTPUT, ">$OUTFILE" or die;
1100 # FIXME: support attribute translations
1102 # Empty nodes never need translation, so unmark all of them.
1103 # For example, <_foo/> is just replaced by <foo/>.
1104 $source =~ s|<\s*_($w+)\s*/>|<$1/>|g;
1106 while ($source =~ s/
1108 (\s+)(<locale\ name="C">(\s*)
1109 (<default>\s*(.*?)\s*<\/default>)?(\s*)
1110 (<short>\s*(.*?)\s*<\/short>)?(\s*)
1111 (<long>\s*(.*?)\s*<\/long>)?(\s*)
1117 my $locale_start_spaces = $2 ? $2 : '';
1118 my $default_spaces = $4 ? $4 : '';
1119 my $short_spaces = $7 ? $7 : '';
1120 my $long_spaces = $10 ? $10 : '';
1121 my $locale_end_spaces = $13 ? $13 : '';
1122 my $c_default_block = $3 ? $3 : '';
1123 my $default_string = $6 ? $6 : '';
1124 my $short_string = $9 ? $9 : '';
1125 my $long_string = $12 ? $12 : '';
1127 $c_default_block =~ s/default>\[.*?\]/default>/s;
1129 print OUTPUT "$locale_start_spaces$c_default_block";
1131 $default_string =~ s/\s+/ /g;
1132 $default_string = entity_decode($default_string);
1133 $short_string =~ s/\s+/ /g;
1134 $short_string = entity_decode($short_string);
1135 $long_string =~ s/\s+/ /g;
1136 $long_string = entity_decode($long_string);
1138 for my $lang (sort keys %po_files_by_lang)
1140 my $default_translation = $translations{$lang, $default_string};
1141 my $short_translation = $translations{$lang, $short_string};
1142 my $long_translation = $translations{$lang, $long_string};
1144 next if (!$default_translation && !$short_translation &&
1145 !$long_translation);
1147 print OUTPUT "\n$locale_start_spaces<locale name=\"$lang\">";
1149 print OUTPUT "$default_spaces";
1151 if ($default_translation)
1153 $default_translation = entity_encode($default_translation);
1154 print OUTPUT "<default>$default_translation</default>";
1157 print OUTPUT "$short_spaces";
1159 if ($short_translation)
1161 $short_translation = entity_encode($short_translation);
1162 print OUTPUT "<short>$short_translation</short>";
1165 print OUTPUT "$long_spaces";
1167 if ($long_translation)
1169 $long_translation = entity_encode($long_translation);
1170 print OUTPUT "<long>$long_translation</long>";
1173 print OUTPUT "$locale_end_spaces</locale>";
1177 print OUTPUT $source;
1182 sub rfc822deb_merge_translations
1185 for my $lang (keys %po_files_by_lang) {
1186 $encodings{$lang} = ($UTF8_ARG ? 'UTF-8' : get_po_encoding($po_files_by_lang{$lang}));
1191 $Text::Wrap::huge = 'overflow';
1192 $Text::Wrap::break = qr/\n|\s(?=\S)/;
1195 local $/; # slurp mode
1196 open INPUT, "<$FILE" or die "can't open $FILE: $!";
1201 open OUTPUT, ">${OUTFILE}" or die;
1203 while ($source =~ /(^|\n+)(_*)([^:\s]+)(:[ \t]*)(.*?)(?=\n[\S\n]|$)/sg)
1206 my $non_translated_line = $3.$4;
1208 my $underscore = length($2);
1209 next if $underscore eq 0 && $non_translated_line =~ /^#/;
1210 # Remove [] dummy strings
1211 my $stripped = $string;
1212 $stripped =~ s/\[\s[^\[\]]*\],/,/g if $underscore eq 2;
1213 $stripped =~ s/\[\s[^\[\]]*\]$//;
1214 $non_translated_line .= $stripped;
1216 print OUTPUT $sep.$non_translated_line;
1220 my @str_list = rfc822deb_split($underscore, $string);
1222 for my $lang (sort keys %po_files_by_lang)
1224 my $is_translated = 1;
1225 my $str_translated = '';
1228 for my $str (@str_list)
1230 my $translation = $translations{$lang, $str};
1238 # $translation may also contain [] dummy
1239 # strings, mostly to indicate an empty string
1240 $translation =~ s/\[\s[^\[\]]*\]$//;
1244 if ($underscore eq 2)
1246 $str_translated .= $translation;
1251 Text::Tabs::expand($translation) .
1257 if ($underscore eq 2)
1259 $str_translated .= ', ' . $translation;
1263 $str_translated .= Text::Tabs::expand(
1264 Text::Wrap::wrap(' ', ' ', $translation)) .
1270 # To fix some problems with Text::Wrap::wrap
1271 $str_translated =~ s/(\n )+\n/\n .\n/g;
1273 next unless $is_translated;
1275 $str_translated =~ s/\n \.\n$//;
1276 $str_translated =~ s/\s+$//;
1278 $_ = $non_translated_line;
1279 s/^(\w+):\s*.*/$sep${1}-$lang.$encodings{$lang}: $str_translated/s;
1292 # Debian defines a special way to deal with rfc822-style files:
1293 # when a value contain newlines, it consists of
1294 # 1. a short form (first line)
1295 # 2. a long description, all lines begin with a space,
1296 # and paragraphs are separated by a single dot on a line
1297 # This routine returns an array of all paragraphs, and reformat
1299 # When first argument is 2, the string is a comma separated list of
1303 $text =~ s/^[ \t]//mg;
1304 return (split(/, */, $text, 0)) if $type ne 1;
1305 return ($text) if $text !~ /\n/;
1307 $text =~ s/([^\n]*)\n//;
1311 for my $line (split (/\n/, $text))
1314 if ($line =~ /^\.\s*$/)
1321 elsif ($line =~ /^\s/)
1323 # Line which must not be reformatted
1324 $str .= "\n" if length ($str) && $str !~ /\n$/;
1330 # Continuation line, remove newline
1331 $str .= " " if length ($str) && $str !~ /\n$/;
1337 push(@list, $str) if length ($str);