hgbook

changeset 623:082bb76417f1

Add Po4a 0.37-dev(2009-03-08)
author Dongsheng Song <dongsheng.song@gmail.com>
date Thu Mar 12 15:43:56 2009 +0800 (2009-03-12)
parents 2180358c32c4
children 3c5e1c03cc3e
files .hgignore tools/po4a/lib/Locale/Po4a/Chooser.pm tools/po4a/lib/Locale/Po4a/Common.pm tools/po4a/lib/Locale/Po4a/Docbook.pm tools/po4a/lib/Locale/Po4a/Po.pm tools/po4a/lib/Locale/Po4a/TransTractor.pm tools/po4a/lib/Locale/Po4a/Xml.pm tools/po4a/po4a-translate tools/po4a/po4a-updatepo
line diff
     1.1 --- a/.hgignore	Thu Mar 12 15:40:40 2009 +0800
     1.2 +++ b/.hgignore	Thu Mar 12 15:43:56 2009 +0800
     1.3 @@ -1,4 +1,5 @@
     1.4 -[^/]+/htdocs/
     1.5 +^htdocs/
     1.6 +^tools/fop/
     1.7  
     1.8  syntax: glob
     1.9  
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tools/po4a/lib/Locale/Po4a/Chooser.pm	Thu Mar 12 15:43:56 2009 +0800
     2.3 @@ -0,0 +1,148 @@
     2.4 +# Locale::Po4a::Pod -- Convert POD data to PO file, for translation.
     2.5 +# $Id: Chooser.pm,v 1.41 2008-07-20 16:31:55 nekral-guest Exp $
     2.6 +#
     2.7 +# This program is free software; you may redistribute it and/or modify it
     2.8 +# under the terms of GPL (see COPYING).
     2.9 +#
    2.10 +# This module converts POD to PO file, so that it becomes possible to 
    2.11 +# translate POD formatted documentation. See gettext documentation for
    2.12 +# more info about PO files.
    2.13 +
    2.14 +############################################################################
    2.15 +# Modules and declarations
    2.16 +############################################################################
    2.17 +
    2.18 +
    2.19 +package Locale::Po4a::Chooser;
    2.20 +
    2.21 +use 5.006;
    2.22 +use strict;
    2.23 +use warnings;
    2.24 +use Locale::Po4a::Common;
    2.25 +
    2.26 +sub new {
    2.27 +    my ($module)=shift;
    2.28 +    my (%options)=@_;
    2.29 +
    2.30 +    die wrap_mod("po4a::chooser", gettext("Need to provide a module name"))
    2.31 +      unless defined $module;
    2.32 +
    2.33 +    my $modname;
    2.34 +    if ($module eq 'kernelhelp') {
    2.35 +        $modname = 'KernelHelp';
    2.36 +    } elsif ($module eq 'newsdebian') {
    2.37 +        $modname = 'NewsDebian';
    2.38 +    } elsif ($module eq 'latex') {
    2.39 +        $modname = 'LaTeX';
    2.40 +    } elsif ($module eq 'bibtex') {
    2.41 +        $modname = 'BibTex';
    2.42 +    } elsif ($module eq 'tex') {
    2.43 +        $modname = 'TeX';
    2.44 +    } else {
    2.45 +        $modname = ucfirst($module);
    2.46 +    }
    2.47 +    if (! UNIVERSAL::can("Locale::Po4a::$modname", 'new')) {
    2.48 +        eval qq{use Locale::Po4a::$modname};
    2.49 +        if ($@) {
    2.50 +            my $error=$@;
    2.51 +            warn wrap_msg(gettext("Unknown format type: %s."), $module);
    2.52 +	    warn wrap_mod("po4a::chooser",
    2.53 +		gettext("Module loading error: %s"), $error)
    2.54 +	      if defined $options{'verbose'} && $options{'verbose'} > 0;
    2.55 +            list(1);
    2.56 +        }
    2.57 +    }
    2.58 +    return "Locale::Po4a::$modname"->new(%options);
    2.59 +}
    2.60 +
    2.61 +sub list {
    2.62 +    warn wrap_msg(gettext("List of valid formats:")
    2.63 +#	."\n  - ".gettext("bibtex: BibTex bibliography format.")
    2.64 +	."\n  - ".gettext("dia: uncompressed Dia diagrams.")
    2.65 +	."\n  - ".gettext("docbook: Docbook XML.")
    2.66 +	."\n  - ".gettext("guide: Gentoo Linux's xml documentation format.")
    2.67 +#	."\n  - ".gettext("html: HTML documents (EXPERIMENTAL).")
    2.68 +	."\n  - ".gettext("ini: .INI format.")
    2.69 +	."\n  - ".gettext("kernelhelp: Help messages of each kernel compilation option.")
    2.70 +	."\n  - ".gettext("latex: LaTeX format.")
    2.71 +	."\n  - ".gettext("man: Good old manual page format.")
    2.72 +	."\n  - ".gettext("pod: Perl Online Documentation format.")
    2.73 +	."\n  - ".gettext("sgml: either debiandoc or docbook DTD.")
    2.74 +	."\n  - ".gettext("texinfo: The info page format.")
    2.75 +	."\n  - ".gettext("tex: generic TeX documents (see also latex).")
    2.76 +	."\n  - ".gettext("text: simple text document.")
    2.77 +	."\n  - ".gettext("wml: WML documents.")
    2.78 +	."\n  - ".gettext("xhtml: XHTML documents.")
    2.79 +	."\n  - ".gettext("xml: generic XML documents (see also docbook).")
    2.80 +    );
    2.81 +    exit shift;
    2.82 +}
    2.83 +##############################################################################
    2.84 +# Module return value and documentation
    2.85 +##############################################################################
    2.86 +
    2.87 +1;
    2.88 +__END__
    2.89 +
    2.90 +=head1 NAME
    2.91 +
    2.92 +Locale::Po4a::Chooser - Manage po4a modules
    2.93 +
    2.94 +=head1 DESCRIPTION
    2.95 +
    2.96 +Locale::Po4a::Chooser is a module to manage po4a modules. Before, all po4a
    2.97 +binaries used to know all po4a modules (pod, man, sgml, etc). This made the
    2.98 +add of a new module boring, to make sure the documentation is synchronized
    2.99 +in all modules, and that each of them can access the new module.
   2.100 +
   2.101 +Now, you just have to call the Locale::Po4a::Chooser::new() function,
   2.102 +passing the name of module as argument.
   2.103 +
   2.104 +You also have the Locale::Po4a::Chooser::list() function which lists the
   2.105 +available format and exits on the value passed as argument.
   2.106 +
   2.107 +=head1 SEE ALSO
   2.108 +
   2.109 +=over 4
   2.110 +
   2.111 +=item About po4a:
   2.112 +
   2.113 +L<po4a(7)|po4a.7>, 
   2.114 +L<Locale::Po4a::TransTractor(3pm)>,
   2.115 +L<Locale::Po4a::Po(3pm)>
   2.116 +
   2.117 +=item About modules:
   2.118 +
   2.119 +L<Locale::Po4a::Dia(3pm)>,
   2.120 +L<Locale::Po4a::Docbook(3pm)>,
   2.121 +L<Locale::Po4a::Guide(3pm)>,
   2.122 +L<Locale::Po4a::Halibut(3pm)>,
   2.123 +L<Locale::Po4a::Ini(3pm)>,
   2.124 +L<Locale::Po4a::KernelHelp(3pm)>,
   2.125 +L<Locale::Po4a::LaTeX(3pm)>,
   2.126 +L<Locale::Po4a::Man(3pm)>,
   2.127 +L<Locale::Po4a::Pod(3pm)>,
   2.128 +L<Locale::Po4a::Sgml(3pm)>,
   2.129 +L<Locale::Po4a::TeX(3pm)>,
   2.130 +L<Locale::Po4a::Texinfo(3pm)>,
   2.131 +L<Locale::Po4a::Text(3pm)>,
   2.132 +L<Locale::Po4a::Wml(3pm)>.
   2.133 +L<Locale::Po4a::Xhtml(3pm)>,
   2.134 +L<Locale::Po4a::Xml(3pm)>,
   2.135 +L<Locale::Po4a::Wml(3pm)>.
   2.136 +
   2.137 +=back
   2.138 +
   2.139 +=head1 AUTHORS
   2.140 +
   2.141 + Denis Barbier <barbier@linuxfr.org>
   2.142 + Martin Quinson (mquinson#debian.org)
   2.143 +
   2.144 +=head1 COPYRIGHT AND LICENSE
   2.145 +
   2.146 +Copyright 2002,2003,2004,2005 by SPI, inc.
   2.147 +
   2.148 +This program is free software; you may redistribute it and/or modify it
   2.149 +under the terms of GPL (see the COPYING file).
   2.150 +
   2.151 +=cut
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/tools/po4a/lib/Locale/Po4a/Common.pm	Thu Mar 12 15:43:56 2009 +0800
     3.3 @@ -0,0 +1,246 @@
     3.4 +# Locale::Po4a::Common -- Common parts of the po4a scripts and utils
     3.5 +# $Id: Common.pm,v 1.20 2009-02-13 23:16:44 nekral-guest Exp $
     3.6 +#
     3.7 +# Copyright 2005 by Jordi Vilalta <jvprat@gmail.com>
     3.8 +#
     3.9 +# This program is free software; you may redistribute it and/or modify it
    3.10 +# under the terms of GPL (see COPYING).
    3.11 +#
    3.12 +# This module has common utilities for the various scripts of po4a
    3.13 +
    3.14 +=head1 NAME
    3.15 +
    3.16 +Locale::Po4a::Common - Common parts of the po4a scripts and utils
    3.17 +
    3.18 +=head1 DESCRIPTION
    3.19 +
    3.20 +Locale::Po4a::Common contains common parts of the po4a scripts and some useful
    3.21 +functions used along the other modules.
    3.22 +
    3.23 +In order to use Locale::Po4a programatically, one may want to disable
    3.24 +the use of Text::WrapI18N, by writing e.g.
    3.25 +
    3.26 +    use Locale::Po4a::Common qw(nowrapi18n);
    3.27 +    use Locale::Po4a::Text;
    3.28 +
    3.29 +instead of:
    3.30 +
    3.31 +    use Locale::Po4a::Text;
    3.32 +
    3.33 +Ordering is important here: as most Locale::Po4a modules themselves
    3.34 +load Locale::Po4a::Common, the first time this module is loaded
    3.35 +determines whether Text::WrapI18N is used.
    3.36 +
    3.37 +=cut
    3.38 +
    3.39 +package Locale::Po4a::Common;
    3.40 +
    3.41 +require Exporter;
    3.42 +use vars qw(@ISA @EXPORT);
    3.43 +@ISA = qw(Exporter);
    3.44 +@EXPORT = qw(wrap_msg wrap_mod wrap_ref_mod textdomain gettext dgettext);
    3.45 +
    3.46 +use 5.006;
    3.47 +use strict;
    3.48 +use warnings;
    3.49 +
    3.50 +sub import {
    3.51 +    my $class=shift;
    3.52 +
    3.53 +    my $wrapi18n=1;
    3.54 +    if (exists $_[0] && defined $_[0] && $_[0] eq 'nowrapi18n') {
    3.55 +        shift;
    3.56 +        $wrapi18n=0;
    3.57 +    }
    3.58 +    $class->export_to_level(1, $class, @_);
    3.59 +
    3.60 +    return if defined &wrapi18n;
    3.61 +
    3.62 +    if ($wrapi18n && -t STDERR && -t STDOUT && eval { require Text::WrapI18N }) {
    3.63 +    
    3.64 +        # Don't bother determining the wrap column if we cannot wrap.
    3.65 +        my $col=$ENV{COLUMNS};
    3.66 +        if (!defined $col) {
    3.67 +            my @term=eval "use Term::ReadKey; Term::ReadKey::GetTerminalSize()";
    3.68 +            $col=$term[0] if (!$@);
    3.69 +            # If GetTerminalSize() failed we will fallback to a safe default.
    3.70 +            # This can happen if Term::ReadKey is not available
    3.71 +            # or this is a terminal-less build or such strange condition.
    3.72 +        }
    3.73 +        $col=76 if (!defined $col);
    3.74 +        
    3.75 +        eval ' use Text::WrapI18N qw($columns);
    3.76 +               $columns = $col;
    3.77 +             ';
    3.78 +       
    3.79 +        eval ' sub wrapi18n($$$) { Text::WrapI18N::wrap($_[0],$_[1],$_[2]) } '
    3.80 +    } else {
    3.81 +    
    3.82 +        # If we cannot wrap, well, that's too bad. Survive anyway.
    3.83 +        eval ' sub wrapi18n($$$) { $_[0].$_[2] } '
    3.84 +    }
    3.85 +}
    3.86 +
    3.87 +sub min($$) {
    3.88 +    return $_[0] < $_[1] ? $_[0] : $_[1];
    3.89 +}
    3.90 +
    3.91 +=head1 FUNCTIONS
    3.92 +
    3.93 +=head2 Showing output messages
    3.94 +
    3.95 +=over
    3.96 +
    3.97 +=item 
    3.98 +
    3.99 +show_version($)
   3.100 +
   3.101 +Shows the current version of the script, and a short copyright message. It
   3.102 +takes the name of the script as an argument.
   3.103 +
   3.104 +=cut
   3.105 +
   3.106 +sub show_version {
   3.107 +    my $name = shift;
   3.108 +
   3.109 +    print sprintf(gettext(
   3.110 +	"%s version %s.\n".
   3.111 +	"written by Martin Quinson and Denis Barbier.\n\n".
   3.112 +	"Copyright (C) 2002, 2003, 2004 Software of Public Interest, Inc.\n".
   3.113 +	"This is free software; see source code for copying\n".
   3.114 +	"conditions. There is NO warranty; not even for\n".
   3.115 +	"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
   3.116 +	), $name, $Locale::Po4a::TransTractor::VERSION)."\n";
   3.117 +}
   3.118 +
   3.119 +=item 
   3.120 +
   3.121 +wrap_msg($@)
   3.122 +
   3.123 +This function displays a message the same way than sprintf() does, but wraps
   3.124 +the result so that they look nice on the terminal.
   3.125 +
   3.126 +=cut
   3.127 +
   3.128 +sub wrap_msg($@) {
   3.129 +    my $msg = shift;
   3.130 +    my @args = @_;
   3.131 +
   3.132 +    return wrapi18n("", "", sprintf($msg, @args))."\n";
   3.133 +}
   3.134 +
   3.135 +=item 
   3.136 +
   3.137 +wrap_mod($$@)
   3.138 +
   3.139 +This function works like wrap_msg(), but it takes a module name as the first
   3.140 +argument, and leaves a space at the left of the message.
   3.141 +
   3.142 +=cut
   3.143 +
   3.144 +sub wrap_mod($$@) {
   3.145 +    my ($mod, $msg) = (shift, shift);
   3.146 +    my @args = @_;
   3.147 +
   3.148 +    $mod .= ": ";
   3.149 +    my $spaces = " " x min(length($mod), 15);
   3.150 +    return wrapi18n($mod, $spaces, sprintf($msg, @args))."\n";
   3.151 +}
   3.152 +
   3.153 +=item 
   3.154 +
   3.155 +wrap_ref_mod($$$@)
   3.156 +
   3.157 +This function works like wrap_msg(), but it takes a file:line reference as the
   3.158 +first argument, a module name as the second one, and leaves a space at the left
   3.159 +of the message.
   3.160 +
   3.161 +=back
   3.162 +
   3.163 +=cut
   3.164 +
   3.165 +sub wrap_ref_mod($$$@) {
   3.166 +    my ($ref, $mod, $msg) = (shift, shift, shift);
   3.167 +    my @args = @_;
   3.168 +
   3.169 +    if (!$mod) {
   3.170 +	# If we don't get a module name, show the message like wrap_mod does
   3.171 +	return wrap_mod($ref, $msg, @args);
   3.172 +    } else {
   3.173 +	$ref .= ": ";
   3.174 +	my $spaces = " " x min(length($ref), 15);
   3.175 +	$msg = "$ref($mod)\n$msg";
   3.176 +	return wrapi18n("", $spaces, sprintf($msg, @args))."\n";
   3.177 +    }
   3.178 +}
   3.179 +
   3.180 +=head2 Wrappers for other modules
   3.181 +
   3.182 +=over 
   3.183 +
   3.184 +=item 
   3.185 +
   3.186 +Locale::Gettext
   3.187 +
   3.188 +When the Locale::Gettext module cannot be loaded, this module provide dummy
   3.189 +(empty) implementation of the following functions. In that case, po4a
   3.190 +messages won't get translated but the program will continue to work.
   3.191 +
   3.192 +If Locale::gettext is present, this wrapper also calls
   3.193 +setlocale(LC_MESSAGES, "") so callers don't depend on the POSIX module
   3.194 +either.
   3.195 +
   3.196 +=over
   3.197 +
   3.198 +=item 
   3.199 +
   3.200 +bindtextdomain($$)
   3.201 +
   3.202 +=item 
   3.203 +
   3.204 +textdomain($)
   3.205 +
   3.206 +=item 
   3.207 +
   3.208 +gettext($)
   3.209 +
   3.210 +=item 
   3.211 +
   3.212 +dgettext($$)
   3.213 +
   3.214 +=back
   3.215 +
   3.216 +=back
   3.217 +
   3.218 +=cut
   3.219 +
   3.220 +BEGIN {
   3.221 +    if (eval { require Locale::gettext }) {
   3.222 +       import Locale::gettext;
   3.223 +       require POSIX;
   3.224 +       POSIX::setlocale(&POSIX::LC_MESSAGES, '');
   3.225 +    } else {
   3.226 +       eval '
   3.227 +           sub bindtextdomain($$) { }
   3.228 +           sub textdomain($) { }
   3.229 +           sub gettext($) { shift }
   3.230 +           sub dgettext($$) { return $_[1] }
   3.231 +       '
   3.232 +    }
   3.233 +}
   3.234 +
   3.235 +1;
   3.236 +__END__
   3.237 +
   3.238 +=head1 AUTHORS
   3.239 +
   3.240 + Jordi Vilalta <jvprat@gmail.com>
   3.241 +
   3.242 +=head1 COPYRIGHT AND LICENSE
   3.243 +
   3.244 +Copyright 2005 by SPI, inc.
   3.245 +
   3.246 +This program is free software; you may redistribute it and/or modify it
   3.247 +under the terms of GPL (see the COPYING file).
   3.248 +
   3.249 +=cut
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/tools/po4a/lib/Locale/Po4a/Docbook.pm	Thu Mar 12 15:43:56 2009 +0800
     4.3 @@ -0,0 +1,2040 @@
     4.4 +#!/usr/bin/perl
     4.5 +# aptitude: cmdsynopsis => missing removal of leading spaces
     4.6 +
     4.7 +# Po4a::Docbook.pm 
     4.8 +# 
     4.9 +# extract and translate translatable strings from Docbook XML documents.
    4.10 +# 
    4.11 +# This code extracts plain text from tags and attributes on Docbook XML
    4.12 +# documents.
    4.13 +#
    4.14 +# Copyright (c) 2004 by Jordi Vilalta  <jvprat@gmail.com>
    4.15 +# Copyright (c) 2007-2009 by Nicolas François <nicolas.francois@centraliens.net>
    4.16 +#
    4.17 +# This program is free software; you can redistribute it and/or modify
    4.18 +# it under the terms of the GNU General Public License as published by
    4.19 +# the Free Software Foundation; either version 2 of the License, or
    4.20 +# (at your option) any later version.
    4.21 +#
    4.22 +# This program is distributed in the hope that it will be useful,
    4.23 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.24 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.25 +# GNU General Public License for more details.
    4.26 +#
    4.27 +# You should have received a copy of the GNU General Public License
    4.28 +# along with this program; if not, write to the Free Software
    4.29 +# Foundation, Inc.,
    4.30 +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    4.31 +#
    4.32 +########################################################################
    4.33 +
    4.34 +=head1 NAME
    4.35 +
    4.36 +Locale::Po4a::Docbook - Convert Docbook XML documents from/to PO files
    4.37 +
    4.38 +=head1 DESCRIPTION
    4.39 +
    4.40 +The po4a (po for anything) project goal is to ease translations (and more
    4.41 +interestingly, the maintenance of translations) using gettext tools on
    4.42 +areas where they were not expected like documentation.
    4.43 +
    4.44 +Locale::Po4a::Docbook is a module to help the translation of DocBook XML 
    4.45 +documents into other [human] languages.
    4.46 +
    4.47 +Please note that this module is still under heavy development, and not 
    4.48 +distributed in official po4a release since we don't feel it to be mature 
    4.49 +enough. If you insist on trying, check the CVS out.
    4.50 +
    4.51 +=head1 STATUS OF THIS MODULE
    4.52 +
    4.53 +This module is fully functional, as it relies in the L<Locale::Po4a::Xml>
    4.54 +module. This only defines the translatable tags and attributes.
    4.55 +
    4.56 +The only known issue is that it doesn't handle entities yet, and this includes
    4.57 +the file inclusion entities, but you can translate most of those files alone
    4.58 +(except the typical entities files), and it's usually better to maintain them
    4.59 +separated.
    4.60 +
    4.61 +=head1 SEE ALSO
    4.62 +
    4.63 +L<po4a(7)|po4a.7>, L<Locale::Po4a::TransTractor(3pm)>, L<Locale::Po4a::Xml(3pm)>.
    4.64 +
    4.65 +=head1 AUTHORS
    4.66 +
    4.67 + Jordi Vilalta <jvprat@gmail.com>
    4.68 +
    4.69 +=head1 COPYRIGHT AND LICENSE
    4.70 +
    4.71 + Copyright (c) 2004 by Jordi Vilalta  <jvprat@gmail.com>
    4.72 + Copyright (c) 2007-2009 by Nicolas François <nicolas.francois@centraliens.net>
    4.73 +
    4.74 +This program is free software; you may redistribute it and/or modify it
    4.75 +under the terms of GPL (see the COPYING file).
    4.76 +
    4.77 +=cut
    4.78 +
    4.79 +package Locale::Po4a::Docbook;
    4.80 +
    4.81 +use 5.006;
    4.82 +use strict;
    4.83 +use warnings;
    4.84 +
    4.85 +use Locale::Po4a::Xml;
    4.86 +
    4.87 +use vars qw(@ISA);
    4.88 +@ISA = qw(Locale::Po4a::Xml);
    4.89 +
    4.90 +sub initialize {
    4.91 +	my $self = shift;
    4.92 +	my %options = @_;
    4.93 +
    4.94 +	$self->SUPER::initialize(%options);
    4.95 +	$self->{options}{'wrap'}=1;
    4.96 +	$self->{options}{'doctype'}=$self->{options}{'doctype'} || 'docbook xml';
    4.97 +
    4.98 +# AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    4.99 +
   4.100 +	# abbrev; contains text; Formatted inline
   4.101 +	$self->{options}{'_default_translated'} .= " <abbrev>";
   4.102 +	$self->{options}{'_default_inline'} .= " <abbrev>";
   4.103 +
   4.104 +	# abstract; does not contain text; Formatted as a displayed block
   4.105 +	$self->{options}{'_default_untranslated'} .= " <abstract>";
   4.106 +	$self->{options}{'_default_break'} .= " <abstract>";
   4.107 +
   4.108 +	# accel; contains text; Formatted inline
   4.109 +	$self->{options}{'_default_translated'} .= " <accel>";
   4.110 +	$self->{options}{'_default_inline'} .= " <accel>";
   4.111 +
   4.112 +	# ackno; does not contain text; Formatted as a displayed block
   4.113 +	# Replaced by acknowledgements in Docbook v5.0
   4.114 +	$self->{options}{'_default_untranslated'} .= " <ackno>";
   4.115 +	$self->{options}{'_default_break'} .= " <ackno>";
   4.116 +	# acknowledgements; does not contain text; Formatted as a displayed block
   4.117 +	$self->{options}{'_default_untranslated'} .= " <acknowledgements>";
   4.118 +	$self->{options}{'_default_break'} .= " <acknowledgements>";
   4.119 +
   4.120 +	# acronym; contains text; Formatted inline
   4.121 +	$self->{options}{'_default_translated'} .= " <acronym>";
   4.122 +	$self->{options}{'_default_inline'} .= " <acronym>";
   4.123 +
   4.124 +	# action; contains text; Formatted inline; v4, not in v5
   4.125 +	$self->{options}{'_default_translated'} .= " <action>";
   4.126 +	$self->{options}{'_default_inline'} .= " <action>";
   4.127 +
   4.128 +	# address; contains text; Formatted as a displayed block; verbatim
   4.129 +	$self->{options}{'_default_translated'} .= " W<address>";
   4.130 +	$self->{options}{'_default_placeholder'} .= " <address>";
   4.131 +
   4.132 +	# affiliation; does not contain text; Formatted inline or as a
   4.133 +	# displayed block depending on context
   4.134 +	$self->{options}{'_default_untranslated'} .= " <affiliation>";
   4.135 +	$self->{options}{'_default_inline'} .= " <affiliation>";
   4.136 +
   4.137 +	# alt; contains text; Formatted inline or as a
   4.138 +	# displayed block depending on context
   4.139 +	$self->{options}{'_default_translated'} .= " <alt>";
   4.140 +	$self->{options}{'_default_inline'} .= " <alt>";
   4.141 +
   4.142 +	# anchor; does not contain text; Produces no output
   4.143 +	$self->{options}{'_default_untranslated'} .= " <anchor>";
   4.144 +	$self->{options}{'_default_inline'} .= " <anchor>";
   4.145 +
   4.146 +	# annotation; does not contain text;
   4.147 +	$self->{options}{'_default_untranslated'} .= " <annotation>";
   4.148 +	$self->{options}{'_default_placeholder'} .= " <annotation>";
   4.149 +
   4.150 +	# answer; does not contain text;
   4.151 +	$self->{options}{'_default_untranslated'} .= " <answer>";
   4.152 +	$self->{options}{'_default_break'} .= " <answer>";
   4.153 +
   4.154 +	# appendix; does not contain text; Formatted as a displayed block
   4.155 +	$self->{options}{'_default_untranslated'} .= " <appendix>";
   4.156 +	$self->{options}{'_default_break'} .= " <appendix>";
   4.157 +
   4.158 +	# appendixinfo; does not contain text; v4, not in v5
   4.159 +	$self->{options}{'_default_untranslated'} .= " <appendixinfo>";
   4.160 +	$self->{options}{'_default_placeholder'} .= " <appendixinfo>";
   4.161 +
   4.162 +	# application; contains text; Formatted inline
   4.163 +	$self->{options}{'_default_translated'} .= " <application>";
   4.164 +	$self->{options}{'_default_inline'} .= " <application>";
   4.165 +
   4.166 +	# arc; does not contain text;
   4.167 +	$self->{options}{'_default_untranslated'} .= " <arc>";
   4.168 +	$self->{options}{'_default_inline'} .= " <arc>";
   4.169 +
   4.170 +	# area; does not contain text;
   4.171 +	# NOTE: the area is not translatable as is, but the coords
   4.172 +	# attribute might be.
   4.173 +	$self->{options}{'_default_untranslated'} .= " <area>";
   4.174 +	$self->{options}{'_default_inline'} .= " <area>";
   4.175 +
   4.176 +	# areaset; does not contain text;
   4.177 +	# NOTE: the areaset is not translatable as is. depending on the
   4.178 +	# language there might be more or less area tags inside.
   4.179 +	$self->{options}{'_default_untranslated'} .= " <areaset>";
   4.180 +	$self->{options}{'_default_inline'} .= " <areaset>";
   4.181 +
   4.182 +	# areaspec; does not contain text;
   4.183 +	# NOTE: see area and areaset
   4.184 +	$self->{options}{'_default_translated'} .= " <areaspec>";
   4.185 +	$self->{options}{'_default_break'} .= " <areaspec>";
   4.186 +
   4.187 +	# arg; contains text; Formatted inline or as a
   4.188 +	# displayed block depending on context
   4.189 +	$self->{options}{'_default_translated'} .= " <arg>";
   4.190 +	$self->{options}{'_default_inline'} .= " <arg>";
   4.191 +
   4.192 +	# artheader; does not contain text; renamed to articleinfo in v4.0
   4.193 +	$self->{options}{'_default_untranslated'} .= " <artheader>";
   4.194 +	$self->{options}{'_default_placeholder'} .= " <artheader>";
   4.195 +
   4.196 +	# article; does not contain text; Formatted as a displayed block
   4.197 +	$self->{options}{'_default_untranslated'} .= " <article>";
   4.198 +	$self->{options}{'_default_break'} .= " <article>";
   4.199 +
   4.200 +	# articleinfo; does not contain text; v4 only
   4.201 +	$self->{options}{'_default_untranslated'} .= " <articleinfo>";
   4.202 +	$self->{options}{'_default_placeholder'} .= " <articleinfo>";
   4.203 +
   4.204 +	# artpagenums; contains text; Formatted inline
   4.205 +	# NOTE: could be in the break class
   4.206 +	$self->{options}{'_default_translated'} .= " <artpagenums>";
   4.207 +	$self->{options}{'_default_inline'} .= " <artpagenums>";
   4.208 +
   4.209 +	# attribution; contains text; Formatted inline or as a
   4.210 +	# displayed block depending on context
   4.211 +	$self->{options}{'_default_translated'} .= " <attribution>";
   4.212 +	$self->{options}{'_default_inline'} .= " <attribution>";
   4.213 +
   4.214 +	# audiodata; does not contain text;
   4.215 +	# NOTE: the attributes might be translated
   4.216 +	$self->{options}{'_default_translated'} .= " <audiodata>";
   4.217 +	$self->{options}{'_default_placeholder'} .= " <audiodata>";
   4.218 +	$self->{options}{'_default_attributes'}.=' <audiodata>fileref';
   4.219 +
   4.220 +	# audioobject; does not contain text;
   4.221 +	# NOTE: might be contaioned in a inlinemediaobject
   4.222 +	$self->{options}{'_default_translated'} .= " <audioobject>";
   4.223 +	$self->{options}{'_default_placeholder'} .= " <audioobject>";
   4.224 +
   4.225 +	# author; does not contain text; Formatted inline or as a
   4.226 +	# displayed block depending on context
   4.227 +	$self->{options}{'_default_untranslated'} .= " <author>";
   4.228 +	$self->{options}{'_default_inline'} .= " <author>";
   4.229 +
   4.230 +	# authorblurb; does not contain text; Formatted as a displayed block.
   4.231 +	# v4, not in v5
   4.232 +	$self->{options}{'_default_untranslated'} .= " <authorblurb>";
   4.233 +	$self->{options}{'_default_placeholder'} .= " <authorblurb>";
   4.234 +
   4.235 +	# authorgroup; does not contain text; Formatted inline or as a
   4.236 +	# displayed block depending on context
   4.237 +	# NOTE: given the possible parents, it is probably very rarely
   4.238 +	#       inlined
   4.239 +	$self->{options}{'_default_untranslated'} .= " <authorgroup>";
   4.240 +	$self->{options}{'_default_break'} .= " <authorgroup>";
   4.241 +
   4.242 +	# authorinitials; contains text; Formatted inline
   4.243 +	$self->{options}{'_default_translated'} .= " <authorinitials>";
   4.244 +	$self->{options}{'_default_inline'} .= " <authorinitials>";
   4.245 +
   4.246 +# BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
   4.247 +
   4.248 +	# beginpage; does not contain text; v4, not in v5
   4.249 +	$self->{options}{'_default_untranslated'} .= " <beginpage>";
   4.250 +	$self->{options}{'_default_break'} .= " <beginpage>";
   4.251 +
   4.252 +	# bibliocoverage; contains text; Formatted inline
   4.253 +	# NOTE: could be in the break class
   4.254 +	$self->{options}{'_default_translated'} .= " <bibliocoverage>";
   4.255 +	$self->{options}{'_default_inline'} .= " <bibliocoverage>";
   4.256 +
   4.257 +	# bibliodiv; does not contain text; Formatted as a displayed block
   4.258 +	$self->{options}{'_default_untranslated'} .= " <bibliodiv>";
   4.259 +	$self->{options}{'_default_break'} .= " <bibliodiv>";
   4.260 +
   4.261 +	# biblioentry; does not contain text; Formatted as a displayed block
   4.262 +	$self->{options}{'_default_untranslated'} .= " <biblioentry>";
   4.263 +	$self->{options}{'_default_break'} .= " <biblioentry>";
   4.264 +
   4.265 +	# bibliography; does not contain text; Formatted as a displayed block
   4.266 +	$self->{options}{'_default_untranslated'} .= " <bibliography>";
   4.267 +	$self->{options}{'_default_break'} .= " <bibliography>";
   4.268 +
   4.269 +	# bibliographyinfo; does not contain text; v4, not in v5
   4.270 +	$self->{options}{'_default_untranslated'} .= " <bibliographyinfo>";
   4.271 +	$self->{options}{'_default_placeholder'} .= " <bibliographyinfo>";
   4.272 +
   4.273 +	# biblioid; contains text; Formatted inline
   4.274 +	# NOTE: could be in the break class
   4.275 +	$self->{options}{'_default_translated'} .= " <biblioid>";
   4.276 +	$self->{options}{'_default_inline'} .= " <biblioid>";
   4.277 +
   4.278 +	# bibliolist; does not contain text; Formatted as a displayed block
   4.279 +	$self->{options}{'_default_untranslated'} .= " <bibliolist>";
   4.280 +	$self->{options}{'_default_break'} .= " <bibliolist>";
   4.281 +
   4.282 +	# bibliomisc; contains text; Formatted inline
   4.283 +	# NOTE: could be in the break class
   4.284 +	$self->{options}{'_default_translated'} .= " <bibliomisc>";
   4.285 +	$self->{options}{'_default_inline'} .= " <bibliomisc>";
   4.286 +
   4.287 +	# bibliomixed; contains text; Formatted as a displayed block
   4.288 +	$self->{options}{'_default_translated'} .= " <bibliomixed>";
   4.289 +	$self->{options}{'_default_placeholder'} .= " <bibliomixed>";
   4.290 +
   4.291 +	# bibliomset; contains text; Formatted as a displayed block
   4.292 +	# NOTE: content might need to be inlined, e.g. <bibliomset><title>
   4.293 +	$self->{options}{'_default_translated'} .= " <bibliomset>";
   4.294 +	$self->{options}{'_default_placeholder'} .= " <bibliomset>";
   4.295 +
   4.296 +	# biblioref; does not contain text; Formatted inline
   4.297 +	$self->{options}{'_default_untranslated'} .= " <biblioref>";
   4.298 +	$self->{options}{'_default_inline'} .= " <biblioref>";
   4.299 +
   4.300 +	# bibliorelation; does not contain text; Formatted inline
   4.301 +	$self->{options}{'_default_translated'} .= " <bibliorelation>";
   4.302 +	$self->{options}{'_default_inline'} .= " <bibliorelation>";
   4.303 +
   4.304 +	# biblioset; does not contain text; Formatted as a displayed block
   4.305 +	$self->{options}{'_default_untranslated'} .= " <biblioset>";
   4.306 +	$self->{options}{'_default_break'} .= " <biblioset>";
   4.307 +
   4.308 +	# bibliosource; contains text; Formatted inline
   4.309 +	# NOTE: could be in the break class
   4.310 +	$self->{options}{'_default_translated'} .= " <bibliosource>";
   4.311 +	$self->{options}{'_default_inline'} .= " <bibliosource>";
   4.312 +
   4.313 +	# blockinfo; does not contain text; v4.2, not in v5
   4.314 +	$self->{options}{'_default_untranslated'} .= " <blockinfo>";
   4.315 +	$self->{options}{'_default_placeholder'} .= " <blockinfo>";
   4.316 +
   4.317 +	# blockquote; does not contain text; Formatted as a displayed block
   4.318 +	$self->{options}{'_default_untranslated'} .= " <blockquote>";
   4.319 +	$self->{options}{'_default_break'} .= " <blockquote>";
   4.320 +
   4.321 +	# book; does not contain text; Formatted as a displayed block
   4.322 +	$self->{options}{'_default_untranslated'} .= " <book>";
   4.323 +	$self->{options}{'_default_break'} .= " <book>";
   4.324 +
   4.325 +	# bookbiblio; does not contain text; Formatted as a displayed block
   4.326 +	# Removed in v4.0
   4.327 +	$self->{options}{'_default_untranslated'} .= " <bookbiblio>";
   4.328 +	$self->{options}{'_default_break'} .= " <bookbiblio>";
   4.329 +
   4.330 +	# bookinfo; does not contain text; v4, not in v5
   4.331 +	$self->{options}{'_default_untranslated'} .= " <bookinfo>";
   4.332 +	$self->{options}{'_default_placeholder'} .= " <bookinfo>";
   4.333 +
   4.334 +	# bridgehead; contains text; Formatted as a displayed block
   4.335 +	$self->{options}{'_default_translated'} .= " <bridgehead>";
   4.336 +	$self->{options}{'_default_break'} .= " <bridgehead>";
   4.337 +
   4.338 +# CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
   4.339 +
   4.340 +	# callout; does not contain text; Formatted as a displayed block
   4.341 +	$self->{options}{'_default_untranslated'} .= " <callout>";
   4.342 +	$self->{options}{'_default_break'} .= " <callout>";
   4.343 +
   4.344 +	# calloutlist; does not contain text; Formatted as a displayed block
   4.345 +	$self->{options}{'_default_untranslated'} .= " <calloutlist>";
   4.346 +	$self->{options}{'_default_break'} .= " <calloutlist>";
   4.347 +
   4.348 +	# caption; does not contain text; Formatted as a displayed block
   4.349 +	$self->{options}{'_default_untranslated'} .= " <caption>";
   4.350 +	$self->{options}{'_default_break'} .= " <caption>";
   4.351 +
   4.352 +	# caption (db.html.caption); contains text; Formatted as a displayed block
   4.353 +	# TODO: Check if this works
   4.354 +	$self->{options}{'_default_translated'} .= " <table><caption>";
   4.355 +	$self->{options}{'_default_break'} .= " <table><caption>";
   4.356 +
   4.357 +	# caution; does not contain text; Formatted as a displayed block
   4.358 +	$self->{options}{'_default_untranslated'} .= " <caution>";
   4.359 +	$self->{options}{'_default_break'} .= " <caution>";
   4.360 +
   4.361 +	# chapter; does not contain text; Formatted as a displayed block
   4.362 +	$self->{options}{'_default_untranslated'} .= " <chapter>";
   4.363 +	$self->{options}{'_default_break'} .= " <chapter>";
   4.364 +
   4.365 +	# chapterinfo; does not contain text; v4, not in v5
   4.366 +	$self->{options}{'_default_untranslated'} .= " <chapterinfo>";
   4.367 +	$self->{options}{'_default_placeholder'} .= " <chapterinfo>";
   4.368 +
   4.369 +	# citation; contains text; Formatted inline
   4.370 +	$self->{options}{'_default_translated'} .= " <citation>";
   4.371 +	$self->{options}{'_default_inline'} .= " <citation>";
   4.372 +
   4.373 +	# citebiblioid; contains text; Formatted inline
   4.374 +	# NOTE: maybe untranslated?
   4.375 +	$self->{options}{'_default_translated'} .= " <citebiblioid>";
   4.376 +	$self->{options}{'_default_inline'} .= " <citebiblioid>";
   4.377 +
   4.378 +	# citerefentry; contains text; Formatted inline
   4.379 +	$self->{options}{'_default_translated'} .= " <citerefentry>";
   4.380 +	$self->{options}{'_default_inline'} .= " <citerefentry>";
   4.381 +
   4.382 +	# citetitle; contains text; Formatted inline
   4.383 +	$self->{options}{'_default_translated'} .= " <citetitle>";
   4.384 +	$self->{options}{'_default_inline'} .= " <citetitle>";
   4.385 +
   4.386 +	# city; contains text; Formatted inline
   4.387 +	$self->{options}{'_default_translated'} .= " <city>";
   4.388 +	$self->{options}{'_default_inline'} .= " <city>";
   4.389 +
   4.390 +	# classname; contains text; Formatted inline
   4.391 +	$self->{options}{'_default_translated'} .= " <classname>";
   4.392 +	$self->{options}{'_default_inline'} .= " <classname>";
   4.393 +
   4.394 +	# classsynopsis; does not contain text; may be in a para
   4.395 +	# NOTE: It may contain a classsynopsisinfo, which should be
   4.396 +	#       verbatim
   4.397 +	# XXX: since it is in untranslated class, does the W flag takes
   4.398 +	#      effect?
   4.399 +	$self->{options}{'_default_untranslated'} .= " W<classsynopsis>";
   4.400 +	$self->{options}{'_default_placeholder'} .= " <classsynopsis>";
   4.401 +
   4.402 +	# classsynopsisinfo; contains text;
   4.403 +	# NOTE: see above
   4.404 +	$self->{options}{'_default_translated'} .= " W<classsynopsisinfo>";
   4.405 +	$self->{options}{'_default_inline'} .= " <classsynopsisinfo>";
   4.406 +
   4.407 +	# cmdsynopsis; does not contain text; may be in a para
   4.408 +	# NOTE: It may be clearer as a verbatim block
   4.409 +	# XXX: since it is in untranslated class, does the W flag takes
   4.410 +	#      effect? => not completely. Rewrap afterward?
   4.411 +	$self->{options}{'_default_untranslated'} .= " W<cmdsynopsis>";
   4.412 +	$self->{options}{'_default_placeholder'} .= " <cmdsynopsis>";
   4.413 +
   4.414 +	# co; does not contain text; Formatted inline
   4.415 +	# XXX: tranlsated or not? (label attribute)
   4.416 +	$self->{options}{'_default_translated'} .= " <co>";
   4.417 +	$self->{options}{'_default_inline'} .= " <co>";
   4.418 +
   4.419 +	# code; contains text; Formatted inline
   4.420 +	$self->{options}{'_default_translated'} .= " <code>";
   4.421 +	$self->{options}{'_default_inline'} .= " <code>";
   4.422 +
   4.423 +	# col; does not contain text;
   4.424 +	# NOTE: could be translated to change the layout in a translation
   4.425 +	#       To be done on colgroup in that case.
   4.426 +	$self->{options}{'_default_untranslated'} .= " <col>";
   4.427 +	$self->{options}{'_default_break'} .= " <col>";
   4.428 +
   4.429 +	# colgroup; does not contain text;
   4.430 +	# NOTE: could be translated to change the layout in a translation
   4.431 +	$self->{options}{'_default_untranslated'} .= " <colgroup>";
   4.432 +	$self->{options}{'_default_break'} .= " <colgroup>";
   4.433 +
   4.434 +	# collab; does not contain text; Formatted inline or as a
   4.435 +	# displayed block depending on context
   4.436 +	# NOTE: could be in the break class
   4.437 +	$self->{options}{'_default_untranslated'} .= " <collab>";
   4.438 +	$self->{options}{'_default_inline'} .= " <collab>";
   4.439 +
   4.440 +	# collabname; contains text; Formatted inline or as a
   4.441 +	# displayed block depending on context; v4, not in v5
   4.442 +	$self->{options}{'_default_translated'} .= " <collabname>";
   4.443 +	$self->{options}{'_default_inline'} .= " <collabname>";
   4.444 +
   4.445 +	# colophon; does not contain text; Formatted as a displayed block
   4.446 +	$self->{options}{'_default_untranslated'} .= " <colophon>";
   4.447 +	$self->{options}{'_default_break'} .= " <colophon>";
   4.448 +
   4.449 +	# colspec; does not contain text;
   4.450 +	# NOTE: could be translated to change the layout in a translation
   4.451 +	$self->{options}{'_default_untranslated'} .= " <colspec>";
   4.452 +	$self->{options}{'_default_break'} .= " <colspec>";
   4.453 +
   4.454 +	# command; contains text; Formatted inline
   4.455 +	$self->{options}{'_default_translated'} .= " <command>";
   4.456 +	$self->{options}{'_default_inline'} .= " <command>";
   4.457 +
   4.458 +	# comment; contains text; Formatted inline or as a displayed block
   4.459 +	# Renamed to remark in v4.0
   4.460 +	$self->{options}{'_default_translated'} .= " <comment>";
   4.461 +	$self->{options}{'_default_inline'} .= " <comment>";
   4.462 +
   4.463 +	# computeroutput; contains text; Formatted inline
   4.464 +	# NOTE: "is not a verbatim environment, but an inline."
   4.465 +	$self->{options}{'_default_translated'} .= " <computeroutput>";
   4.466 +	$self->{options}{'_default_inline'} .= " <computeroutput>";
   4.467 +
   4.468 +	# confdates; contains text; Formatted inline or as a
   4.469 +	# displayed block depending on context
   4.470 +	$self->{options}{'_default_translated'} .= " <confdates>";
   4.471 +	$self->{options}{'_default_inline'} .= " <confdates>";
   4.472 +
   4.473 +	# confgroup; does not contain text; Formatted inline or as a
   4.474 +	# displayed block depending on context
   4.475 +	# NOTE: could be in the break class
   4.476 +	$self->{options}{'_default_untranslated'} .= " <confgroup>";
   4.477 +	$self->{options}{'_default_inline'} .= " <confgroup>";
   4.478 +
   4.479 +	# confnum; contains text; Formatted inline or as a
   4.480 +	# displayed block depending on context
   4.481 +	$self->{options}{'_default_translated'} .= " <confnum>";
   4.482 +	$self->{options}{'_default_inline'} .= " <confnum>";
   4.483 +
   4.484 +	# confsponsor; contains text; Formatted inline or as a
   4.485 +	# displayed block depending on context
   4.486 +	$self->{options}{'_default_translated'} .= " <confsponsor>";
   4.487 +	$self->{options}{'_default_inline'} .= " <confsponsor>";
   4.488 +
   4.489 +	# conftitle; contains text; Formatted inline or as a
   4.490 +	# displayed block depending on context
   4.491 +	$self->{options}{'_default_translated'} .= " <conftitle>";
   4.492 +	$self->{options}{'_default_inline'} .= " <conftitle>";
   4.493 +
   4.494 +	# constant; contains text; Formatted inline
   4.495 +	$self->{options}{'_default_translated'} .= " <constant>";
   4.496 +	$self->{options}{'_default_inline'} .= " <constant>";
   4.497 +
   4.498 +	# constraint; does not contain text;
   4.499 +	# NOTE: it might be better to have the production as verbatim
   4.500 +	#       Keeping the constrainst inline to have it close to the
   4.501 +	#       lhs or rhs.
   4.502 +	#       The attribute is translatable
   4.503 +	$self->{options}{'_default_untranslated'} .= " <constraint>";
   4.504 +	$self->{options}{'_default_break'} .= " <constraint>";
   4.505 +
   4.506 +	# constraintdef; does not contain text; Formatted as a displayed block
   4.507 +	$self->{options}{'_default_untranslated'} .= " <constraintdef>";
   4.508 +	$self->{options}{'_default_break'} .= " <constraintdef>";
   4.509 +
   4.510 +	# constructorsynopsis; does not contain text; may be in a para
   4.511 +	# NOTE: It may be clearer as a verbatim block
   4.512 +	# XXX: since it is in untranslated class, does the W flag takes
   4.513 +	#      effect?
   4.514 +	$self->{options}{'_default_untranslated'} .= " W<constructorsynopsis>";
   4.515 +	$self->{options}{'_default_placeholder'} .= " <constructorsynopsis>";
   4.516 +
   4.517 +	# contractnum; contains text; Formatted inline or as a displayed block
   4.518 +	# NOTE: could be in the break class
   4.519 +	$self->{options}{'_default_translated'} .= " <contractnum>";
   4.520 +	$self->{options}{'_default_inline'} .= " <contractnum>";
   4.521 +
   4.522 +	# contractsponsor; contains text; Formatted inline or as a displayed block
   4.523 +	# NOTE: could be in the break class
   4.524 +	$self->{options}{'_default_translated'} .= " <contractsponsor>";
   4.525 +	$self->{options}{'_default_inline'} .= " <contractsponsor>";
   4.526 +
   4.527 +	# contrib; contains text; Formatted inline or as a displayed block
   4.528 +	$self->{options}{'_default_translated'} .= " <contrib>";
   4.529 +	$self->{options}{'_default_inline'} .= " <contrib>";
   4.530 +
   4.531 +	# copyright; contains text; Formatted inline or as a displayed block
   4.532 +	# NOTE: could be in the break class
   4.533 +	$self->{options}{'_default_translated'} .= " <copyright>";
   4.534 +	$self->{options}{'_default_inline'} .= " <copyright>";
   4.535 +
   4.536 +	# coref; does not contain text; Formatted inline
   4.537 +	# XXX: tranlsated or not? (label attribute)
   4.538 +	$self->{options}{'_default_translated'} .= " <coref>";
   4.539 +	$self->{options}{'_default_inline'} .= " <coref>";
   4.540 +
   4.541 +	# corpauthor; contains text; Formatted inline or as a
   4.542 +	# displayed block depending on context; v4, not in v5
   4.543 +	$self->{options}{'_default_translated'} .= " <corpauthor>";
   4.544 +	$self->{options}{'_default_inline'} .= " <corpauthor>";
   4.545 +
   4.546 +	# corpcredit; contains text; Formatted inline or as a
   4.547 +	# displayed block depending on context; v4, not in v5
   4.548 +	$self->{options}{'_default_translated'} .= " <corpcredit>";
   4.549 +	$self->{options}{'_default_inline'} .= " <corpcredit>";
   4.550 +
   4.551 +	# corpname; contains text; Formatted inline or as a
   4.552 +	# displayed block depending on context; v4, not in v5
   4.553 +	$self->{options}{'_default_translated'} .= " <corpname>";
   4.554 +	$self->{options}{'_default_inline'} .= " <corpname>";
   4.555 +
   4.556 +	# country; contains text; Formatted inline
   4.557 +	$self->{options}{'_default_translated'} .= " <country>";
   4.558 +	$self->{options}{'_default_inline'} .= " <country>";
   4.559 +
   4.560 +	# cover; does not contain text; Formatted as a displayed block
   4.561 +	$self->{options}{'_default_untranslated'} .= " <cover>";
   4.562 +	$self->{options}{'_default_break'} .= " <cover>";
   4.563 +
   4.564 +# DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
   4.565 +
   4.566 +	# database; contains text; Formatted inline
   4.567 +	$self->{options}{'_default_translated'} .= " <database>";
   4.568 +	$self->{options}{'_default_inline'} .= " <database>";
   4.569 +
   4.570 +	# date; contains text; Formatted inline
   4.571 +	$self->{options}{'_default_translated'} .= " <date>";
   4.572 +	$self->{options}{'_default_inline'} .= " <date>";
   4.573 +
   4.574 +	# dedication; contains text; Formatted as a displayed block
   4.575 +	$self->{options}{'_default_translated'} .= " <dedication>";
   4.576 +	$self->{options}{'_default_break'} .= " <dedication>";
   4.577 +
   4.578 +	# destructorsynopsis; does not contain text; may be in a para
   4.579 +	# NOTE: It may be clearer as a verbatim block
   4.580 +	# XXX: since it is in untranslated class, does the W flag takes
   4.581 +	#      effect?
   4.582 +	$self->{options}{'_default_untranslated'} .= " W<destructorsynopsis>";
   4.583 +	$self->{options}{'_default_placeholder'} .= " <destructorsynopsis>";
   4.584 +
   4.585 +	# docinfo; does not contain text; removed in v4.0
   4.586 +	$self->{options}{'_default_untranslated'} .= " <docinfo>";
   4.587 +	$self->{options}{'_default_placeholder'} .= " <docinfo>";
   4.588 +
   4.589 +# EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
   4.590 +
   4.591 +	# edition; contains text; Formatted inline or as a displayed block
   4.592 +	# NOTE: could be in the break class
   4.593 +	$self->{options}{'_default_translated'} .= " <edition>";
   4.594 +	$self->{options}{'_default_inline'} .= " <edition>";
   4.595 +
   4.596 +	# editor; does not contain text; Formatted inline or as a
   4.597 +	# displayed block depending on context
   4.598 +	$self->{options}{'_default_untranslated'} .= " <editor>";
   4.599 +	$self->{options}{'_default_inline'} .= " <editor>";
   4.600 +
   4.601 +	# email; contains text; Formatted inline
   4.602 +	$self->{options}{'_default_translated'} .= " <email>";
   4.603 +	$self->{options}{'_default_inline'} .= " <email>";
   4.604 +
   4.605 +	# emphasis; contains text; Formatted inline
   4.606 +	$self->{options}{'_default_translated'} .= " <emphasis>";
   4.607 +	$self->{options}{'_default_inline'} .= " <emphasis>";
   4.608 +
   4.609 +	# entry; contains text;
   4.610 +	$self->{options}{'_default_translated'} .= " <entry>";
   4.611 +	$self->{options}{'_default_break'} .= " <entry>";
   4.612 +
   4.613 +	# entrytbl; does not contain text;
   4.614 +	$self->{options}{'_default_untranslated'} .= " <entrytbl>";
   4.615 +	$self->{options}{'_default_break'} .= " <entrytbl>";
   4.616 +
   4.617 +	# envar; contains text; Formatted inline
   4.618 +	$self->{options}{'_default_translated'} .= " <envar>";
   4.619 +	$self->{options}{'_default_inline'} .= " <envar>";
   4.620 +
   4.621 +	# epigraph; contains text; Formatted as a displayed block.
   4.622 +	# NOTE: maybe contained in a para
   4.623 +	$self->{options}{'_default_translated'} .= " <epigraph>";
   4.624 +	$self->{options}{'_default_placeholder'} .= " <epigraph>";
   4.625 +
   4.626 +	# equation; does not contain text; Formatted as a displayed block.
   4.627 +	$self->{options}{'_default_untranslated'} .= " <equation>";
   4.628 +	$self->{options}{'_default_break'} .= " <equation>";
   4.629 +
   4.630 +	# errorcode; contains text; Formatted inline
   4.631 +	$self->{options}{'_default_translated'} .= " <errorcode>";
   4.632 +	$self->{options}{'_default_inline'} .= " <errorcode>";
   4.633 +
   4.634 +	# errorname; contains text; Formatted inline
   4.635 +	$self->{options}{'_default_translated'} .= " <errorname>";
   4.636 +	$self->{options}{'_default_inline'} .= " <errorname>";
   4.637 +
   4.638 +	# errortext; contains text; Formatted inline
   4.639 +	$self->{options}{'_default_translated'} .= " <errortext>";
   4.640 +	$self->{options}{'_default_inline'} .= " <errortext>";
   4.641 +
   4.642 +	# errortype; contains text; Formatted inline
   4.643 +	$self->{options}{'_default_translated'} .= " <errortype>";
   4.644 +	$self->{options}{'_default_inline'} .= " <errortype>";
   4.645 +
   4.646 +	# example; does not contain text; Formatted as a displayed block.
   4.647 +	# NOTE: maybe contained in a para
   4.648 +	$self->{options}{'_default_untranslated'} .= " <example>";
   4.649 +	$self->{options}{'_default_placeholder'} .= " <example>";
   4.650 +
   4.651 +	# exceptionname; contains text; Formatted inline
   4.652 +	$self->{options}{'_default_translated'} .= " <exceptionname>";
   4.653 +	$self->{options}{'_default_inline'} .= " <exceptionname>";
   4.654 +
   4.655 +	# extendedlink; does not contain text;
   4.656 +	$self->{options}{'_default_untranslated'} .= " <extendedlink>";
   4.657 +	$self->{options}{'_default_inline'} .= " <extendedlink>";
   4.658 +
   4.659 +# FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
   4.660 +
   4.661 +	# fax; contains text; Formatted inline
   4.662 +	$self->{options}{'_default_translated'} .= " <fax>";
   4.663 +	$self->{options}{'_default_inline'} .= " <fax>";
   4.664 +
   4.665 +	# fieldsynopsis; does not contain text; may be in a para
   4.666 +	$self->{options}{'_default_untranslated'} .= " <fieldsynopsis>";
   4.667 +	$self->{options}{'_default_inline'} .= " <fieldsynopsis>";
   4.668 +
   4.669 +	# figure; does not contain text; Formatted as a displayed block.
   4.670 +	# NOTE: maybe contained in a para
   4.671 +	$self->{options}{'_default_untranslated'} .= " <figure>";
   4.672 +	$self->{options}{'_default_placeholder'} .= " <figure>";
   4.673 +
   4.674 +	# filename; contains text; Formatted inline
   4.675 +	$self->{options}{'_default_translated'} .= " <filename>";
   4.676 +	$self->{options}{'_default_inline'} .= " <filename>";
   4.677 +
   4.678 +	# firstname; contains text; Formatted inline
   4.679 +	$self->{options}{'_default_translated'} .= " <firstname>";
   4.680 +	$self->{options}{'_default_inline'} .= " <firstname>";
   4.681 +
   4.682 +	# firstterm; contains text; Formatted inline
   4.683 +	$self->{options}{'_default_translated'} .= " <firstterm>";
   4.684 +	$self->{options}{'_default_inline'} .= " <firstterm>";
   4.685 +
   4.686 +	# footnote; contains text;
   4.687 +	$self->{options}{'_default_translated'} .= " <footnote>";
   4.688 +	$self->{options}{'_default_placeholder'} .= " <footnote>";
   4.689 +
   4.690 +	# footnoteref; contains text;
   4.691 +	$self->{options}{'_default_translated'} .= " <footnoteref>";
   4.692 +	$self->{options}{'_default_inline'} .= " <footnoteref>";
   4.693 +
   4.694 +	# foreignphrase; contains text;
   4.695 +	$self->{options}{'_default_translated'} .= " <foreignphrase>";
   4.696 +	$self->{options}{'_default_inline'} .= " <foreignphrase>";
   4.697 +
   4.698 +	# formalpara; does not contain text; Formatted as a displayed block.
   4.699 +	$self->{options}{'_default_untranslated'} .= " <formalpara>";
   4.700 +	$self->{options}{'_default_break'} .= " <formalpara>";
   4.701 +
   4.702 +	# funcdef; contains text; Formatted inline
   4.703 +	$self->{options}{'_default_translated'} .= " <funcdef>";
   4.704 +	$self->{options}{'_default_inline'} .= " <funcdef>";
   4.705 +
   4.706 +	# funcparams; contains text; Formatted inline
   4.707 +	$self->{options}{'_default_translated'} .= " <funcparams>";
   4.708 +	$self->{options}{'_default_inline'} .= " <funcparams>";
   4.709 +
   4.710 +	# funcprototype; does not contain text;
   4.711 +	# NOTE: maybe contained in a funcsynopsis, contained in a para
   4.712 +	$self->{options}{'_default_untranslated'} .= " <funcprototype>";
   4.713 +	$self->{options}{'_default_placeholder'} .= " <funcprototype>";
   4.714 +
   4.715 +	# funcsynopsis; does not contain text;
   4.716 +	# NOTE: maybe contained in a para
   4.717 +	$self->{options}{'_default_untranslated'} .= " <funcsynopsis>";
   4.718 +	$self->{options}{'_default_placeholder'} .= " <funcsynopsis>";
   4.719 +
   4.720 +	# funcsynopsisinfo; contains text; verbatim
   4.721 +	# NOTE: maybe contained in a funcsynopsis, contained in a para
   4.722 +	$self->{options}{'_default_translated'} .= " W<funcsynopsisinfo>";
   4.723 +	$self->{options}{'_default_placeholder'} .= " <funcsynopsisinfo>";
   4.724 +
   4.725 +	# function; contains text; Formatted inline
   4.726 +	$self->{options}{'_default_translated'} .= " <function>";
   4.727 +	$self->{options}{'_default_inline'} .= " <function>";
   4.728 +
   4.729 +# GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
   4.730 +
   4.731 +	# glossary; does not contain text; Formatted as a displayed block.
   4.732 +	$self->{options}{'_default_untranslated'} .= " <glossary>";
   4.733 +	$self->{options}{'_default_break'} .= " <glossary>";
   4.734 +
   4.735 +	# glossaryinfo; does not contain text; v4, not in v5
   4.736 +	$self->{options}{'_default_untranslated'} .= " <glossaryinfo>";
   4.737 +	$self->{options}{'_default_placeholder'} .= " <glossaryinfo>";
   4.738 +
   4.739 +	# glossdef; does not contain text; Formatted as a displayed block.
   4.740 +	$self->{options}{'_default_untranslated'} .= " <glossdef>";
   4.741 +	$self->{options}{'_default_break'} .= " <glossdef>";
   4.742 +
   4.743 +	# glossdiv; does not contain text; Formatted as a displayed block.
   4.744 +	$self->{options}{'_default_untranslated'} .= " <glossdiv>";
   4.745 +	$self->{options}{'_default_break'} .= " <glossdiv>";
   4.746 +
   4.747 +	# glossentry; does not contain text; Formatted as a displayed block.
   4.748 +	$self->{options}{'_default_untranslated'} .= " <glossentry>";
   4.749 +	$self->{options}{'_default_break'} .= " <glossentry>";
   4.750 +
   4.751 +	# glosslist; does not contain text; Formatted as a displayed block.
   4.752 +	$self->{options}{'_default_untranslated'} .= " <glosslist>";
   4.753 +	$self->{options}{'_default_break'} .= " <glosslist>";
   4.754 +
   4.755 +	# glosssee; contains text; Formatted as a displayed block.
   4.756 +	$self->{options}{'_default_translated'} .= " <glosssee>";
   4.757 +	$self->{options}{'_default_break'} .= " <glosssee>";
   4.758 +
   4.759 +	# glossseealso; contains text; Formatted as a displayed block.
   4.760 +	$self->{options}{'_default_translated'} .= " <glossseealso>";
   4.761 +	$self->{options}{'_default_break'} .= " <glossseealso>";
   4.762 +
   4.763 +	# glossterm; contains text; Formatted inline
   4.764 +	$self->{options}{'_default_translated'} .= " <glossterm>";
   4.765 +	$self->{options}{'_default_inline'} .= " <glossterm>";
   4.766 +
   4.767 +	# graphic; does not contain text; Formatted as a displayed block
   4.768 +	# v4, not in v5
   4.769 +	$self->{options}{'_default_untranslated'} .= " <graphic>";
   4.770 +	$self->{options}{'_default_inline'} .= " <graphic>";
   4.771 +	$self->{options}{'_default_attributes'}.=' <graphic>fileref';
   4.772 +
   4.773 +	# graphicco; does not contain text; Formatted as a displayed block.
   4.774 +	# v4, not in v5
   4.775 +	$self->{options}{'_default_untranslated'} .= " <graphicco>";
   4.776 +	$self->{options}{'_default_placeholder'} .= " <graphicco>";
   4.777 +
   4.778 +	# group; does not contain text; Formatted inline
   4.779 +	$self->{options}{'_default_untranslated'} .= " W<group>";
   4.780 +	$self->{options}{'_default_inline'} .= " <group>";
   4.781 +
   4.782 +	# guibutton; contains text; Formatted inline
   4.783 +	$self->{options}{'_default_translated'} .= " <guibutton>";
   4.784 +	$self->{options}{'_default_inline'} .= " <guibutton>";
   4.785 +
   4.786 +	# guiicon; contains text; Formatted inline
   4.787 +	$self->{options}{'_default_translated'} .= " <guiicon>";
   4.788 +	$self->{options}{'_default_inline'} .= " <guiicon>";
   4.789 +
   4.790 +	# guilabel; contains text; Formatted inline
   4.791 +	$self->{options}{'_default_translated'} .= " <guilabel>";
   4.792 +	$self->{options}{'_default_inline'} .= " <guilabel>";
   4.793 +
   4.794 +	# guimenu; contains text; Formatted inline
   4.795 +	$self->{options}{'_default_translated'} .= " <guimenu>";
   4.796 +	$self->{options}{'_default_inline'} .= " <guimenu>";
   4.797 +
   4.798 +	# guimenuitem; contains text; Formatted inline
   4.799 +	$self->{options}{'_default_translated'} .= " <guimenuitem>";
   4.800 +	$self->{options}{'_default_inline'} .= " <guimenuitem>";
   4.801 +
   4.802 +	# guisubmenu; contains text; Formatted inline
   4.803 +	$self->{options}{'_default_translated'} .= " <guisubmenu>";
   4.804 +	$self->{options}{'_default_inline'} .= " <guisubmenu>";
   4.805 +
   4.806 +# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
   4.807 +
   4.808 +	# hardware; contains text; Formatted inline
   4.809 +	$self->{options}{'_default_translated'} .= " <hardware>";
   4.810 +	$self->{options}{'_default_inline'} .= " <hardware>";
   4.811 +
   4.812 +	# highlights; does not contain text; Formatted inline
   4.813 +	# v4, not in v5
   4.814 +	$self->{options}{'_default_untranslated'} .= " <highlights>";
   4.815 +	$self->{options}{'_default_break'} .= " <highlights>";
   4.816 +
   4.817 +	# holder; contains text;
   4.818 +	# NOTE: may depend on the copyright container
   4.819 +	$self->{options}{'_default_translated'} .= " <holder>";
   4.820 +	$self->{options}{'_default_inline'} .= " <holder>";
   4.821 +
   4.822 +	# honorific; contains text; Formatted inline
   4.823 +	$self->{options}{'_default_translated'} .= " <honorific>";
   4.824 +	$self->{options}{'_default_inline'} .= " <honorific>";
   4.825 +
   4.826 +	# html:button; contains text; Formatted inline
   4.827 +	$self->{options}{'_default_translated'} .= " <html:button>";
   4.828 +	$self->{options}{'_default_inline'} .= " <html:button>";
   4.829 +
   4.830 +	# html:fieldset; contains text; Formatted inline
   4.831 +	$self->{options}{'_default_translated'} .= " <html:fieldset>";
   4.832 +	$self->{options}{'_default_inline'} .= " <html:fieldset>";
   4.833 +
   4.834 +	# html:form; does not contain text;
   4.835 +	$self->{options}{'_default_translated'} .= " <html:form>";
   4.836 +	$self->{options}{'_default_inline'} .= " <html:form>";
   4.837 +
   4.838 +	# html:input; does not contain text; Formatted inline
   4.839 +	# NOTE: attributes are translatable
   4.840 +	$self->{options}{'_default_translated'} .= " <html:input>";
   4.841 +	$self->{options}{'_default_inline'} .= " <html:input>";
   4.842 +
   4.843 +	# html:label; contains text; Formatted inline
   4.844 +	$self->{options}{'_default_translated'} .= " <html:label>";
   4.845 +	$self->{options}{'_default_inline'} .= " <html:label>";
   4.846 +
   4.847 +	# html:legend; contains text; Formatted inline
   4.848 +	$self->{options}{'_default_translated'} .= " <html:legend>";
   4.849 +	$self->{options}{'_default_inline'} .= " <html:legend>";
   4.850 +
   4.851 +	# html:option; contains text; Formatted inline
   4.852 +	$self->{options}{'_default_translated'} .= " <html:option>";
   4.853 +	$self->{options}{'_default_inline'} .= " <html:option>";
   4.854 +
   4.855 +	# html:select; does not contain text; Formatted inline
   4.856 +	$self->{options}{'_default_translated'} .= " <html:select>";
   4.857 +	$self->{options}{'_default_inline'} .= " <html:select>";
   4.858 +
   4.859 +	# html:textarea; contains text; Formatted as a displayed block.
   4.860 +	$self->{options}{'_default_translated'} .= " <html:textarea>";
   4.861 +	$self->{options}{'_default_placeholder'} .= " <html:textarea>";
   4.862 +
   4.863 +	# imagedata; does not contain text; May be formatted inline or
   4.864 +	# as a displayed block, depending on context
   4.865 +	$self->{options}{'_default_translated'} .= " <imagedata>";
   4.866 +	$self->{options}{'_default_inline'} .= " <imagedata>";
   4.867 +	$self->{options}{'_default_attributes'}.=' <imagedata>fileref';
   4.868 +
   4.869 +	# imageobject; does not contain text; May be formatted inline or
   4.870 +	# as a displayed block, depending on context
   4.871 +	$self->{options}{'_default_untranslated'} .= " <imageobject>";
   4.872 +	$self->{options}{'_default_inline'} .= " <imageobject>";
   4.873 +
   4.874 +	# imageobjectco; does not contain text; Formatted as a displayed block
   4.875 +	# NOTE: may be in a inlinemediaobject
   4.876 +	# TODO: check if this works when the inlinemediaobject is defined
   4.877 +	# as inline
   4.878 +	$self->{options}{'_default_untranslated'} .= " <imageobjectco>";
   4.879 +	$self->{options}{'_default_break'} .= " <imageobjectco>";
   4.880 +
   4.881 +	# important; does not contain text; Formatted as a displayed block.
   4.882 +	$self->{options}{'_default_untranslated'} .= " <important>";
   4.883 +	$self->{options}{'_default_break'} .= " <important>";
   4.884 +
   4.885 +	# index; does not contain text; Formatted as a displayed block.
   4.886 +	$self->{options}{'_default_untranslated'} .= " <index>";
   4.887 +	$self->{options}{'_default_break'} .= " <index>";
   4.888 +
   4.889 +	# indexdiv; does not contain text; Formatted as a displayed block.
   4.890 +	$self->{options}{'_default_untranslated'} .= " <indexdiv>";
   4.891 +	$self->{options}{'_default_break'} .= " <indexdiv>";
   4.892 +
   4.893 +	# indexentry; does not contain text; Formatted as a displayed block.
   4.894 +	$self->{options}{'_default_untranslated'} .= " <indexentry>";
   4.895 +	$self->{options}{'_default_break'} .= " <indexentry>";
   4.896 +
   4.897 +	# indexinfo; does not contain text; v4, not in v5
   4.898 +	$self->{options}{'_default_untranslated'} .= " <indexinfo>";
   4.899 +	$self->{options}{'_default_placeholder'} .= " <indexinfo>";
   4.900 +
   4.901 +	# indexterm; does not contain text; 
   4.902 +	$self->{options}{'_default_untranslated'} .= " <indexterm>";
   4.903 +	$self->{options}{'_default_placeholder'} .= " <indexterm>";
   4.904 +
   4.905 +	# info; does not contain text; 
   4.906 +	$self->{options}{'_default_untranslated'} .= " <info>";
   4.907 +	$self->{options}{'_default_placeholder'} .= " <info>";
   4.908 +
   4.909 +	# informalequation; does not contain text; Formatted as a displayed block.
   4.910 +	$self->{options}{'_default_untranslated'} .= " <informalequation>";
   4.911 +	$self->{options}{'_default_placeholder'} .= " <informalequation>";
   4.912 +
   4.913 +	# informalexample; does not contain text; Formatted as a displayed block.
   4.914 +	# NOTE: can be in a para
   4.915 +	$self->{options}{'_default_untranslated'} .= " <informalexample>";
   4.916 +	$self->{options}{'_default_break'} .= " <informalexample>";
   4.917 +
   4.918 +	# informalfigure; does not contain text; Formatted as a displayed block.
   4.919 +	# NOTE: can be in a para
   4.920 +	$self->{options}{'_default_untranslated'} .= " <informalfigure>";
   4.921 +	$self->{options}{'_default_break'} .= " <informalfigure>";
   4.922 +
   4.923 +	# informaltable; does not contain text; Formatted as a displayed block.
   4.924 +	# NOTE: can be in a para
   4.925 +	$self->{options}{'_default_untranslated'} .= " <informaltable>";
   4.926 +	$self->{options}{'_default_break'} .= " <informaltable>";
   4.927 +
   4.928 +	# initializer; contains text; Formatted inline
   4.929 +	$self->{options}{'_default_translated'} .= " <initializer>";
   4.930 +	$self->{options}{'_default_inline'} .= " <initializer>";
   4.931 +
   4.932 +	# inlineequation; does not contain text; Formatted inline
   4.933 +	$self->{options}{'_default_translated'} .= " W<inlineequation>";
   4.934 +	$self->{options}{'_default_placeholder'} .= " <inlineequation>";
   4.935 +
   4.936 +	# inlinegraphic; does not contain text; Formatted inline
   4.937 +	# empty; v4, not in v5
   4.938 +	$self->{options}{'_default_translated'} .= " W<inlinegraphic>";
   4.939 +	$self->{options}{'_default_inline'} .= " <inlinegraphic>";
   4.940 +
   4.941 +	# inlinemediaobject; does not contain text; Formatted inline
   4.942 +	$self->{options}{'_default_translated'} .= " <inlinemediaobject>";
   4.943 +	$self->{options}{'_default_placeholder'} .= " <inlinemediaobject>";
   4.944 +
   4.945 +	# interface; contains text; Formatted inline; v4, not in v5
   4.946 +	$self->{options}{'_default_translated'} .= " <interface>";
   4.947 +	$self->{options}{'_default_inline'} .= " <interface>";
   4.948 +
   4.949 +	# interfacedefinition; contains text; Formatted inline
   4.950 +	# Removed in v4.0
   4.951 +	$self->{options}{'_default_translated'} .= " <interfacedefinition>";
   4.952 +	$self->{options}{'_default_inline'} .= " <interfacedefinition>";
   4.953 +
   4.954 +	# interfacename; contains text; Formatted inline
   4.955 +	$self->{options}{'_default_translated'} .= " <interfacename>";
   4.956 +	$self->{options}{'_default_inline'} .= " <interfacename>";
   4.957 +
   4.958 +	# invpartnumber; contains text; Formatted inline; v4, not in v5
   4.959 +	$self->{options}{'_default_translated'} .= " <invpartnumber>";
   4.960 +	$self->{options}{'_default_inline'} .= " <invpartnumber>";
   4.961 +
   4.962 +	# isbn; contains text; Formatted inline; v4, not in v5
   4.963 +	$self->{options}{'_default_translated'} .= " <isbn>";
   4.964 +	$self->{options}{'_default_inline'} .= " <isbn>";
   4.965 +
   4.966 +	# issn; contains text; Formatted inline; v4, not in v5
   4.967 +	$self->{options}{'_default_translated'} .= " <issn>";
   4.968 +	$self->{options}{'_default_inline'} .= " <issn>";
   4.969 +
   4.970 +	# issuenum; contains text; Formatted inline or as a displayed block
   4.971 +	# NOTE: could be in the break class
   4.972 +	$self->{options}{'_default_translated'} .= " <issuenum>";
   4.973 +	$self->{options}{'_default_inline'} .= " <issuenum>";
   4.974 +
   4.975 +	# itemizedlist; does not contain text; Formatted as a displayed block.
   4.976 +	$self->{options}{'_default_untranslated'} .= " <itemizedlist>";
   4.977 +	$self->{options}{'_default_break'} .= " <itemizedlist>";
   4.978 +
   4.979 +	# itermset; does not contain text;
   4.980 +	# FIXME
   4.981 +	$self->{options}{'_default_untranslated'} .= " <itermset>";
   4.982 +	$self->{options}{'_default_inline'} .= " <itermset>";
   4.983 +
   4.984 +# JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
   4.985 +
   4.986 +	# jobtitle; contains text; Formatted inline or as a displayed block
   4.987 +	# NOTE: can be in a para
   4.988 +	$self->{options}{'_default_translated'} .= " <jobtitle>";
   4.989 +	$self->{options}{'_default_inline'} .= " <jobtitle>";
   4.990 +
   4.991 +# KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
   4.992 +
   4.993 +	# keycap; contains text; Formatted inline
   4.994 +	$self->{options}{'_default_translated'} .= " <keycap>";
   4.995 +	$self->{options}{'_default_inline'} .= " <keycap>";
   4.996 +
   4.997 +	# keycode; contains text; Formatted inline
   4.998 +	$self->{options}{'_default_translated'} .= " <keycode>";
   4.999 +	$self->{options}{'_default_inline'} .= " <keycode>";
  4.1000 +
  4.1001 +	# keycombo; does not contain text; Formatted inline
  4.1002 +	$self->{options}{'_default_translated'} .= " <keycombo>";
  4.1003 +	$self->{options}{'_default_inline'} .= " <keycombo>";
  4.1004 +
  4.1005 +	# keysym; contains text; Formatted inline
  4.1006 +	$self->{options}{'_default_translated'} .= " <keysym>";
  4.1007 +	$self->{options}{'_default_inline'} .= " <keysym>";
  4.1008 +
  4.1009 +	# keyword; contains text;
  4.1010 +	# NOTE: could be inline
  4.1011 +	$self->{options}{'_default_translated'} .= " <keyword>";
  4.1012 +	$self->{options}{'_default_break'} .= " <keyword>";
  4.1013 +
  4.1014 +	# keywordset; contains text; Formatted inline or as a displayed block
  4.1015 +	# NOTE: could be placeholder/break
  4.1016 +	$self->{options}{'_default_translated'} .= " <keywordset>";
  4.1017 +	$self->{options}{'_default_break'} .= " <keywordset>";
  4.1018 +
  4.1019 +# LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
  4.1020 +
  4.1021 +	# label; contains text; Formatted as a displayed block
  4.1022 +	$self->{options}{'_default_translated'} .= " <label>";
  4.1023 +	$self->{options}{'_default_break'} .= " <label>";
  4.1024 +
  4.1025 +	# legalnotice; contains text; Formatted as a displayed block
  4.1026 +	$self->{options}{'_default_translated'} .= " <legalnotice>";
  4.1027 +	$self->{options}{'_default_break'} .= " <legalnotice>";
  4.1028 +
  4.1029 +	# lhs; contains text; Formatted as a displayed block.
  4.1030 +	# NOTE: it might be better to have the production as verbatim
  4.1031 +	#       Keeping the constrainst inline to have it close to the
  4.1032 +	#       lhs or rhs.
  4.1033 +	$self->{options}{'_default_translated'} .= " <lhs>";
  4.1034 +	$self->{options}{'_default_break'} .= " <lhs>";
  4.1035 +
  4.1036 +	# lineage; contains text; Formatted inline
  4.1037 +	$self->{options}{'_default_translated'} .= " <lineage>";
  4.1038 +	$self->{options}{'_default_inline'} .= " <lineage>";
  4.1039 +
  4.1040 +	# lineannotation; contains text; Formatted inline
  4.1041 +	$self->{options}{'_default_translated'} .= " <lineannotation>";
  4.1042 +	$self->{options}{'_default_inline'} .= " <lineannotation>";
  4.1043 +
  4.1044 +	# link; contains text; Formatted inline
  4.1045 +	$self->{options}{'_default_translated'} .= " <link>";
  4.1046 +	$self->{options}{'_default_inline'} .= " <link>";
  4.1047 +
  4.1048 +	# listitem; does not contain text; Formatted as a displayed block.
  4.1049 +	$self->{options}{'_default_untranslated'} .= " <listitem>";
  4.1050 +	$self->{options}{'_default_break'} .= " <listitem>";
  4.1051 +
  4.1052 +	# literal; contains text; Formatted inline
  4.1053 +	$self->{options}{'_default_translated'} .= " <literal>";
  4.1054 +	$self->{options}{'_default_inline'} .= " <literal>";
  4.1055 +
  4.1056 +	# literallayout; contains text; verbatim
  4.1057 +	$self->{options}{'_default_translated'} .= " W<literallayout>";
  4.1058 +	$self->{options}{'_default_placeholder'} .= " <literallayout>";
  4.1059 +
  4.1060 +	# locator; does not contain text;
  4.1061 +	$self->{options}{'_default_untranslated'} .= " <locator>";
  4.1062 +	$self->{options}{'_default_inline'} .= " <locator>";
  4.1063 +
  4.1064 +	# lot; does not contain text; Formatted as a displayed block.
  4.1065 +	# v4, not in v5
  4.1066 +	$self->{options}{'_default_untranslated'} .= " <lot>";
  4.1067 +	$self->{options}{'_default_break'} .= " <lot>";
  4.1068 +
  4.1069 +	# lotentry; contains text; Formatted as a displayed block.
  4.1070 +	# v4, not in v5
  4.1071 +	$self->{options}{'_default_translated'} .= " <lotentry>";
  4.1072 +	$self->{options}{'_default_break'} .= " <lotentry>";
  4.1073 +
  4.1074 +# MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
  4.1075 +
  4.1076 +	# manvolnum; contains text;
  4.1077 +	$self->{options}{'_default_translated'} .= " <manvolnum>";
  4.1078 +	$self->{options}{'_default_inline'} .= " <manvolnum>";
  4.1079 +
  4.1080 +	# markup; contains text; Formatted inline
  4.1081 +	$self->{options}{'_default_translated'} .= " <markup>";
  4.1082 +	$self->{options}{'_default_inline'} .= " <markup>";
  4.1083 +
  4.1084 +	# mathphrase; contains text; Formatted inline
  4.1085 +	$self->{options}{'_default_translated'} .= " <mathphrase>";
  4.1086 +	$self->{options}{'_default_inline'} .= " <mathphrase>";
  4.1087 +
  4.1088 +	# medialabel; contains text; Formatted inline
  4.1089 +	# v4, not in v5
  4.1090 +	$self->{options}{'_default_translated'} .= " <medialabel>";
  4.1091 +	$self->{options}{'_default_inline'} .= " <medialabel>";
  4.1092 +
  4.1093 +	# mediaobject; does not contain text; Formatted as a displayed block.
  4.1094 +	$self->{options}{'_default_untranslated'} .= " <mediaobject>";
  4.1095 +	$self->{options}{'_default_placeholder'} .= " <mediaobject>";
  4.1096 +
  4.1097 +	# mediaobjectco; does not contain text; Formatted as a displayed block.
  4.1098 +	$self->{options}{'_default_untranslated'} .= " <mediaobjectco>";
  4.1099 +	$self->{options}{'_default_placeholder'} .= " <mediaobjectco>";
  4.1100 +
  4.1101 +	# member; contains text; Formatted inline
  4.1102 +	$self->{options}{'_default_translated'} .= " <member>";
  4.1103 +	$self->{options}{'_default_inline'} .= " <member>";
  4.1104 +
  4.1105 +	# menuchoice; does not contain text; Formatted inline
  4.1106 +	$self->{options}{'_default_translated'} .= " <menuchoice>";
  4.1107 +	$self->{options}{'_default_inline'} .= " <menuchoice>";
  4.1108 +
  4.1109 +	# methodname; contains text; Formatted inline
  4.1110 +	$self->{options}{'_default_translated'} .= " <methodname>";
  4.1111 +	$self->{options}{'_default_inline'} .= " <methodname>";
  4.1112 +
  4.1113 +	# methodparam; does not contain text; Formatted inline
  4.1114 +	$self->{options}{'_default_translated'} .= " <methodparam>";
  4.1115 +	$self->{options}{'_default_inline'} .= " <methodparam>";
  4.1116 +
  4.1117 +	# methodsynopsis; does not contain text; Formatted inline
  4.1118 +	$self->{options}{'_default_translated'} .= " <methodsynopsis>";
  4.1119 +	$self->{options}{'_default_inline'} .= " <methodsynopsis>";
  4.1120 +
  4.1121 +	# modifier; contains text; Formatted inline
  4.1122 +	$self->{options}{'_default_translated'} .= " <modifier>";
  4.1123 +	$self->{options}{'_default_inline'} .= " <modifier>";
  4.1124 +
  4.1125 +	# mousebutton; contains text; Formatted inline
  4.1126 +	$self->{options}{'_default_translated'} .= " <mousebutton>";
  4.1127 +	$self->{options}{'_default_inline'} .= " <mousebutton>";
  4.1128 +
  4.1129 +	# msg; does not contain text; Formatted as a displayed block.
  4.1130 +	$self->{options}{'_default_untranslated'} .= " <msg>";
  4.1131 +	$self->{options}{'_default_break'} .= " <msg>";
  4.1132 +
  4.1133 +	# msgaud; contains text; Formatted as a displayed block.
  4.1134 +	$self->{options}{'_default_translated'} .= " <msgaud>";
  4.1135 +	$self->{options}{'_default_break'} .= " <msgaud>";
  4.1136 +
  4.1137 +	# msgentry; does not contain text; Formatted as a displayed block.
  4.1138 +	$self->{options}{'_default_untranslated'} .= " <msgentry>";
  4.1139 +	$self->{options}{'_default_break'} .= " <msgentry>";
  4.1140 +
  4.1141 +	# msgexplan; does not contain text; Formatted as a displayed block.
  4.1142 +	$self->{options}{'_default_untranslated'} .= " <msgexplan>";
  4.1143 +	$self->{options}{'_default_break'} .= " <msgexplan>";
  4.1144 +
  4.1145 +	# msginfo; does not contain text; Formatted as a displayed block.
  4.1146 +	$self->{options}{'_default_untranslated'} .= " <msginfo>";
  4.1147 +	$self->{options}{'_default_break'} .= " <msginfo>";
  4.1148 +
  4.1149 +	# msglevel; contains text; Formatted as a displayed block.
  4.1150 +	$self->{options}{'_default_translated'} .= " <msglevel>";
  4.1151 +	$self->{options}{'_default_break'} .= " <msglevel>";
  4.1152 +
  4.1153 +	# msgmain; does not contain text; Formatted as a displayed block.
  4.1154 +	$self->{options}{'_default_untranslated'} .= " <msgmain>";
  4.1155 +	$self->{options}{'_default_break'} .= " <msgmain>";
  4.1156 +
  4.1157 +	# msgorig; contains text; Formatted as a displayed block.
  4.1158 +	$self->{options}{'_default_translated'} .= " <msgorig>";
  4.1159 +	$self->{options}{'_default_break'} .= " <msgorig>";
  4.1160 +
  4.1161 +	# msgrel; does not contain text; Formatted as a displayed block.
  4.1162 +	$self->{options}{'_default_untranslated'} .= " <msgrel>";
  4.1163 +	$self->{options}{'_default_break'} .= " <msgrel>";
  4.1164 +
  4.1165 +	# msgset; does not contain text; Formatted as a displayed block.
  4.1166 +	$self->{options}{'_default_untranslated'} .= " <msgset>";
  4.1167 +	$self->{options}{'_default_placeholder'} .= " <msgset>";
  4.1168 +
  4.1169 +	# msgsub; does not contain text; Formatted as a displayed block.
  4.1170 +	$self->{options}{'_default_untranslated'} .= " <msgsub>";
  4.1171 +	$self->{options}{'_default_break'} .= " <msgsub>";
  4.1172 +
  4.1173 +	# msgtext; does not contain text; Formatted as a displayed block.
  4.1174 +	$self->{options}{'_default_untranslated'} .= " <msgtext>";
  4.1175 +	$self->{options}{'_default_break'} .= " <msgtext>";
  4.1176 +
  4.1177 +# NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
  4.1178 +
  4.1179 +	# nonterminal; contains text; Formatted inline
  4.1180 +	$self->{options}{'_default_translated'} .= " <nonterminal>";
  4.1181 +	$self->{options}{'_default_inline'} .= " <nonterminal>";
  4.1182 +
  4.1183 +	# note; does not contain text; Formatted inline
  4.1184 +	# NOTE: can be in a para
  4.1185 +	$self->{options}{'_default_untranslated'} .= " <note>";
  4.1186 +	$self->{options}{'_default_inline'} .= " <note>";
  4.1187 +
  4.1188 +# OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
  4.1189 +
  4.1190 +	# objectinfo; does not contain text; v3.1 -> v4, not in v5
  4.1191 +	$self->{options}{'_default_untranslated'} .= " <objectinfo>";
  4.1192 +	$self->{options}{'_default_placeholder'} .= " <objectinfo>";
  4.1193 +
  4.1194 +	# olink; contains text; Formatted inline
  4.1195 +	$self->{options}{'_default_translated'} .= " <olink>";
  4.1196 +	$self->{options}{'_default_inline'} .= " <olink>";
  4.1197 +
  4.1198 +	# ooclass; does not contain text; Formatted inline
  4.1199 +	$self->{options}{'_default_translated'} .= " <ooclass>";
  4.1200 +	$self->{options}{'_default_inline'} .= " <ooclass>";
  4.1201 +
  4.1202 +	# ooexception; contains text; Formatted inline
  4.1203 +	$self->{options}{'_default_translated'} .= " <ooexception>";
  4.1204 +	$self->{options}{'_default_inline'} .= " <ooexception>";
  4.1205 +
  4.1206 +	# oointerface; contains text; Formatted inline
  4.1207 +	$self->{options}{'_default_translated'} .= " <oointerface>";
  4.1208 +	$self->{options}{'_default_inline'} .= " <oointerface>";
  4.1209 +
  4.1210 +	# option; contains text; Formatted inline
  4.1211 +	$self->{options}{'_default_translated'} .= " <option>";
  4.1212 +	$self->{options}{'_default_inline'} .= " <option>";
  4.1213 +
  4.1214 +	# optional; contains text; Formatted inline
  4.1215 +	$self->{options}{'_default_translated'} .= " <optional>";
  4.1216 +	$self->{options}{'_default_inline'} .= " <optional>";
  4.1217 +
  4.1218 +	# orderedlist; does not contain text; Formatted as a displayed block.
  4.1219 +	$self->{options}{'_default_untranslated'} .= " <orderedlist>";
  4.1220 +	$self->{options}{'_default_placeholder'} .= " <orderedlist>";
  4.1221 +
  4.1222 +	# org; does not contain text; Formatted inline or as a
  4.1223 +	# displayed block depending on context
  4.1224 +	$self->{options}{'_default_untranslated'} .= " <org>";
  4.1225 +	$self->{options}{'_default_inline'} .= " <org>";
  4.1226 +
  4.1227 +	# orgdiv; contains text; Formatted inline
  4.1228 +	$self->{options}{'_default_translated'} .= " <orgdiv>";
  4.1229 +	$self->{options}{'_default_inline'} .= " <orgdiv>";
  4.1230 +
  4.1231 +	# orgname; contains text; Formatted inline
  4.1232 +	$self->{options}{'_default_translated'} .= " <orgname>";
  4.1233 +	$self->{options}{'_default_inline'} .= " <orgname>";
  4.1234 +
  4.1235 +	# otheraddr; contains text; Formatted inline
  4.1236 +	$self->{options}{'_default_translated'} .= " <otheraddr>";
  4.1237 +	$self->{options}{'_default_inline'} .= " <otheraddr>";
  4.1238 +
  4.1239 +	# othercredit; does not contain text; Formatted inline or as a
  4.1240 +	# displayed block depending on context
  4.1241 +	$self->{options}{'_default_untranslated'} .= " <othercredit>";
  4.1242 +	$self->{options}{'_default_inline'} .= " <othercredit>";
  4.1243 +
  4.1244 +	# othername; contains text; Formatted inline
  4.1245 +	$self->{options}{'_default_translated'} .= " <othername>";
  4.1246 +	$self->{options}{'_default_inline'} .= " <othername>";
  4.1247 +
  4.1248 +# PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
  4.1249 +
  4.1250 +	# package; contains text; Formatted inline
  4.1251 +	$self->{options}{'_default_translated'} .= " <package>";
  4.1252 +	$self->{options}{'_default_inline'} .= " <package>";
  4.1253 +
  4.1254 +	# pagenums; contains text; Formatted inline
  4.1255 +	$self->{options}{'_default_translated'} .= " <pagenums>";
  4.1256 +	$self->{options}{'_default_inline'} .= " <pagenums>";
  4.1257 +
  4.1258 +	# para; contains text; Formatted as a displayed block
  4.1259 +	$self->{options}{'_default_translated'} .= " <para>";
  4.1260 +	$self->{options}{'_default_break'} .= " <para>";
  4.1261 +
  4.1262 +	# paramdef; contains text; Formatted inline
  4.1263 +	$self->{options}{'_default_translated'} .= " <paramdef>";
  4.1264 +	$self->{options}{'_default_inline'} .= " <paramdef>";
  4.1265 +
  4.1266 +	# parameter; contains text; Formatted inline
  4.1267 +	$self->{options}{'_default_translated'} .= " <parameter>";
  4.1268 +	$self->{options}{'_default_inline'} .= " <parameter>";
  4.1269 +
  4.1270 +	# part; does not contain text; Formatted as a displayed block.
  4.1271 +	$self->{options}{'_default_untranslated'} .= " <part>";
  4.1272 +	$self->{options}{'_default_break'} .= " <part>";
  4.1273 +
  4.1274 +	# partinfo; does not contain text; v4, not in v5
  4.1275 +	$self->{options}{'_default_untranslated'} .= " <partinfo>";
  4.1276 +	$self->{options}{'_default_placeholder'} .= " <partinfo>";
  4.1277 +
  4.1278 +	# partintro; does not contain text; Formatted as a displayed block.
  4.1279 +	$self->{options}{'_default_untranslated'} .= " <partintro>";
  4.1280 +	$self->{options}{'_default_break'} .= " <partintro>";
  4.1281 +
  4.1282 +	# person; does not contain text; Formatted inline or as a
  4.1283 +	# displayed block depending on context
  4.1284 +	$self->{options}{'_default_untranslated'} .= " <person>";
  4.1285 +	$self->{options}{'_default_inline'} .= " <person>";
  4.1286 +
  4.1287 +	# personblurb; does not contain text; Formatted as a displayed block.
  4.1288 +	$self->{options}{'_default_untranslated'} .= " <personblurb>";
  4.1289 +	$self->{options}{'_default_placeholder'} .= " <personblurb>";
  4.1290 +
  4.1291 +	# personname; contains text; Formatted inline
  4.1292 +	$self->{options}{'_default_translated'} .= " <personname>";
  4.1293 +	$self->{options}{'_default_inline'} .= " <personname>";
  4.1294 +
  4.1295 +	# phone; contains text; Formatted inline
  4.1296 +	$self->{options}{'_default_translated'} .= " <phone>";
  4.1297 +	$self->{options}{'_default_inline'} .= " <phone>";
  4.1298 +
  4.1299 +	# phrase; contains text; Formatted inline
  4.1300 +	$self->{options}{'_default_translated'} .= " <phrase>";
  4.1301 +	$self->{options}{'_default_inline'} .= " <phrase>";
  4.1302 +
  4.1303 +	# pob; contains text; Formatted inline
  4.1304 +	$self->{options}{'_default_translated'} .= " <pob>";
  4.1305 +	$self->{options}{'_default_inline'} .= " <pob>";
  4.1306 +
  4.1307 +	# postcode; contains text; Formatted inline
  4.1308 +	$self->{options}{'_default_translated'} .= " <postcode>";
  4.1309 +	$self->{options}{'_default_inline'} .= " <postcode>";
  4.1310 +
  4.1311 +	# preface; does not contain text; Formatted as a displayed block.
  4.1312 +	$self->{options}{'_default_untranslated'} .= " <preface>";
  4.1313 +	$self->{options}{'_default_break'} .= " <preface>";
  4.1314 +
  4.1315 +	# prefaceinfo; does not contain text; v4, not in v5
  4.1316 +	$self->{options}{'_default_untranslated'} .= " <prefaceinfo>";
  4.1317 +	$self->{options}{'_default_placeholder'} .= " <prefaceinfo>";
  4.1318 +
  4.1319 +	# primary; contains text;
  4.1320 +	$self->{options}{'_default_translated'} .= " <primary>";
  4.1321 +	$self->{options}{'_default_break'} .= " <primary>";
  4.1322 +
  4.1323 +	# primaryie; contains text; Formatted as a displayed block.
  4.1324 +	$self->{options}{'_default_translated'} .= " <primaryie>";
  4.1325 +	$self->{options}{'_default_break'} .= " <primaryie>";
  4.1326 +
  4.1327 +	# printhistory; does not contain text; Formatted as a displayed block.
  4.1328 +	$self->{options}{'_default_untranslated'} .= " <printhistory>";
  4.1329 +	$self->{options}{'_default_break'} .= " <printhistory>";
  4.1330 +
  4.1331 +	# procedure; does not contain text; Formatted as a displayed block.
  4.1332 +	$self->{options}{'_default_untranslated'} .= " <procedure>";
  4.1333 +	$self->{options}{'_default_placeholder'} .= " <procedure>";
  4.1334 +
  4.1335 +	# production; doesnot contain text;
  4.1336 +	# NOTE: it might be better to have the production as verbatim
  4.1337 +	#       Keeping the constrainst inline to have it close to the
  4.1338 +	#       lhs or rhs.
  4.1339 +	$self->{options}{'_default_untranslated'} .= " <production>";
  4.1340 +	$self->{options}{'_default_break'} .= " <production>";
  4.1341 +
  4.1342 +	# productionrecap; does not contain text; like production
  4.1343 +	$self->{options}{'_default_untranslated'} .= " <productionrecap>";
  4.1344 +	$self->{options}{'_default_break'} .= " <productionrecap>";
  4.1345 +
  4.1346 +	# productionset; does not contain text; Formatted as a displayed block.
  4.1347 +	$self->{options}{'_default_untranslated'} .= " <productionset>";
  4.1348 +	$self->{options}{'_default_placeholder'} .= " <productionset>";
  4.1349 +
  4.1350 +	# productname; contains text; Formatted inline
  4.1351 +	$self->{options}{'_default_translated'} .= " <productname>";
  4.1352 +	$self->{options}{'_default_inline'} .= " <productname>";
  4.1353 +
  4.1354 +	# productnumber; contains text; Formatted inline
  4.1355 +	$self->{options}{'_default_translated'} .= " <productnumber>";
  4.1356 +	$self->{options}{'_default_inline'} .= " <productnumber>";
  4.1357 +
  4.1358 +	# programlisting; contains text; Formatted as a displayed block.
  4.1359 +	$self->{options}{'_default_translated'} .= " W<programlisting>";
  4.1360 +	$self->{options}{'_default_placeholder'} .= " <programlisting>";
  4.1361 +
  4.1362 +	# programlistingco; contains text; Formatted as a displayed block.
  4.1363 +	$self->{options}{'_default_untranslated'} .= " <programlistingco>";
  4.1364 +	$self->{options}{'_default_placeholder'} .= " <programlistingco>";
  4.1365 +
  4.1366 +	# prompt; contains text; Formatted inline
  4.1367 +	$self->{options}{'_default_translated'} .= " <prompt>";
  4.1368 +	$self->{options}{'_default_inline'} .= " <prompt>";
  4.1369 +
  4.1370 +	# property; contains text; Formatted inline
  4.1371 +	$self->{options}{'_default_translated'} .= " <property>";
  4.1372 +	$self->{options}{'_default_inline'} .= " <property>";
  4.1373 +
  4.1374 +	# pubdate; contains text; Formatted inline
  4.1375 +	$self->{options}{'_default_translated'} .= " <pubdate>";
  4.1376 +	$self->{options}{'_default_inline'} .= " <pubdate>";
  4.1377 +
  4.1378 +	# publisher; does not contain text; Formatted inline or as a displayed block
  4.1379 +	# NOTE: could be in the break class
  4.1380 +	$self->{options}{'_default_translated'} .= " <publisher>";
  4.1381 +	$self->{options}{'_default_inline'} .= " <publisher>";
  4.1382 +
  4.1383 +	# publishername; contains text; Formatted inline or as a displayed block
  4.1384 +	$self->{options}{'_default_translated'} .= " <publishername>";
  4.1385 +	$self->{options}{'_default_inline'} .= " <publishername>";
  4.1386 +
  4.1387 +# QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
  4.1388 +
  4.1389 +	# qandadiv; does not contain text; Formatted as a displayed block.
  4.1390 +	$self->{options}{'_default_untranslated'} .= " <qandadiv>";
  4.1391 +	$self->{options}{'_default_break'} .= " <qandadiv>";
  4.1392 +
  4.1393 +	# qandaentry; does not contain text; Formatted as a displayed block.
  4.1394 +	$self->{options}{'_default_untranslated'} .= " <qandaentry>";
  4.1395 +	$self->{options}{'_default_break'} .= " <qandaentry>";
  4.1396 +
  4.1397 +	# qandaset; does not contain text; Formatted as a displayed block.
  4.1398 +	$self->{options}{'_default_untranslated'} .= " <qandaset>";
  4.1399 +	$self->{options}{'_default_break'} .= " <qandaset>";
  4.1400 +
  4.1401 +	# question; does not contain text;
  4.1402 +	$self->{options}{'_default_untranslated'} .= " <question>";
  4.1403 +	$self->{options}{'_default_break'} .= " <question>";
  4.1404 +
  4.1405 +	# quote; contains text; Formatted inline
  4.1406 +	$self->{options}{'_default_translated'} .= " <quote>";
  4.1407 +	$self->{options}{'_default_inline'} .= " <quote>";
  4.1408 +
  4.1409 +# RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
  4.1410 +
  4.1411 +	# refclass; contains text; Formatted inline or as a displayed block
  4.1412 +	# NOTE: could be in the inline class
  4.1413 +	$self->{options}{'_default_translated'} .= " <refclass>";
  4.1414 +	$self->{options}{'_default_break'} .= " <refclass>";
  4.1415 +
  4.1416 +	# refdescriptor; contains text; Formatted inline or as a displayed block
  4.1417 +	# NOTE: could be in the inline class
  4.1418 +	$self->{options}{'_default_translated'} .= " <refdescriptor>";
  4.1419 +	$self->{options}{'_default_break'} .= " <refdescriptor>";
  4.1420 +
  4.1421 +	# refentry; does not contain text; Formatted as a displayed block
  4.1422 +	$self->{options}{'_default_untranslated'} .= " <refentry>";
  4.1423 +	$self->{options}{'_default_break'} .= " <refentry>";
  4.1424 +
  4.1425 +	# refentryinfo; does not contain text; v4, not in v5
  4.1426 +	$self->{options}{'_default_untranslated'} .= " <refentryinfo>";
  4.1427 +	$self->{options}{'_default_placeholder'} .= " <refentryinfo>";
  4.1428 +
  4.1429 +	# refentrytitle; contains text; Formatted as a displayed block
  4.1430 +# FIXME: do not seems to be a block
  4.1431 +	$self->{options}{'_default_translated'} .= " <refentrytitle>";
  4.1432 +	$self->{options}{'_default_inline'} .= " <refentrytitle>";
  4.1433 +
  4.1434 +	# reference; does not contain text; Formatted as a displayed block
  4.1435 +	$self->{options}{'_default_untranslated'} .= " <reference>";
  4.1436 +	$self->{options}{'_default_break'} .= " <reference>";
  4.1437 +
  4.1438 +	# referenceinfo; does not contain text; v4, not in v5
  4.1439 +	$self->{options}{'_default_untranslated'} .= " <referenceinfo>";
  4.1440 +	$self->{options}{'_default_placeholder'} .= " <referenceinfo>";
  4.1441 +
  4.1442 +	# refmeta; does not contains text; 
  4.1443 +	# NOTE: could be in the inline class
  4.1444 +	$self->{options}{'_default_untranslated'} .= " <refmeta>";
  4.1445 +	$self->{options}{'_default_break'} .= " <refmeta>";
  4.1446 +
  4.1447 +	# refmiscinfo; contains text; Formatted inline or as a displayed block
  4.1448 +	# NOTE: could be in the inline class
  4.1449 +	$self->{options}{'_default_translated'} .= " <refmiscinfo>";
  4.1450 +	$self->{options}{'_default_break'} .= " <refmiscinfo>";
  4.1451 +
  4.1452 +	# refname; contains text; Formatted inline or as a displayed block
  4.1453 +	# NOTE: could be in the inline class
  4.1454 +	$self->{options}{'_default_translated'} .= " <refname>";
  4.1455 +	$self->{options}{'_default_break'} .= " <refname>";
  4.1456 +
  4.1457 +	# refnamediv; does not contain text; Formatted as a displayed block
  4.1458 +	$self->{options}{'_default_untranslated'} .= " <refnamediv>";
  4.1459 +	$self->{options}{'_default_break'} .= " <refnamediv>";
  4.1460 +
  4.1461 +	# refpurpose; contains text; Formatted inline
  4.1462 +	$self->{options}{'_default_translated'} .= " <refpurpose>";
  4.1463 +	$self->{options}{'_default_inline'} .= " <refpurpose>";
  4.1464 +
  4.1465 +	# refsect1; does not contain text; Formatted as a displayed block
  4.1466 +	$self->{options}{'_default_untranslated'} .= " <refsect1>";
  4.1467 +	$self->{options}{'_default_break'} .= " <refsect1>";
  4.1468 +
  4.1469 +	# refsect1info; does not contain text; v4, not in v5
  4.1470 +	$self->{options}{'_default_untranslated'} .= " <refsect1info>";
  4.1471 +	$self->{options}{'_default_placeholder'} .= " <refsect1info>";
  4.1472 +
  4.1473 +	# refsect2; does not contain text; Formatted as a displayed block
  4.1474 +	$self->{options}{'_default_untranslated'} .= " <refsect2>";
  4.1475 +	$self->{options}{'_default_break'} .= " <refsect2>";
  4.1476 +
  4.1477 +	# refsect2info; does not contain text; v4, not in v5
  4.1478 +	$self->{options}{'_default_untranslated'} .= " <refsect2info>";
  4.1479 +	$self->{options}{'_default_placeholder'} .= " <refsect2info>";
  4.1480 +
  4.1481 +	# refsect3; does not contain text; Formatted as a displayed block
  4.1482 +	$self->{options}{'_default_untranslated'} .= " <refsect3>";
  4.1483 +	$self->{options}{'_default_break'} .= " <refsect3>";
  4.1484 +
  4.1485 +	# refsect3info; does not contain text; v4, not in v5
  4.1486 +	$self->{options}{'_default_untranslated'} .= " <refsect3info>";
  4.1487 +	$self->{options}{'_default_placeholder'} .= " <refsect3info>";
  4.1488 +
  4.1489 +	# refsection; does not contain text; Formatted as a displayed block
  4.1490 +	$self->{options}{'_default_untranslated'} .= " <refsection>";
  4.1491 +	$self->{options}{'_default_break'} .= " <refsection>";
  4.1492 +
  4.1493 +	# refsectioninfo; does not contain text; v4, not in v5
  4.1494 +	$self->{options}{'_default_untranslated'} .= " <refsectioninfo>";
  4.1495 +	$self->{options}{'_default_placeholder'} .= " <refsectioninfo>";
  4.1496 +
  4.1497 +	# refsynopsisdiv; does not contain text; Formatted as a displayed block
  4.1498 +	$self->{options}{'_default_untranslated'} .= " <refsynopsisdiv>";
  4.1499 +	$self->{options}{'_default_break'} .= " <refsynopsisdiv>";
  4.1500 +
  4.1501 +	# refsynopsisdivinfo; does not contain text; v4, not in v5
  4.1502 +	$self->{options}{'_default_untranslated'} .= " <refsynopsisdivinfo>";
  4.1503 +	$self->{options}{'_default_placeholder'} .= " <refsynopsisdivinfo>";
  4.1504 +
  4.1505 +	# releaseinfo; contains text; Formatted inline or as a displayed block
  4.1506 +	# NOTE: could be in the inline class
  4.1507 +	$self->{options}{'_default_translated'} .= " <releaseinfo>";
  4.1508 +	$self->{options}{'_default_break'} .= " <releaseinfo>";
  4.1509 +
  4.1510 +	# remark; contains text; Formatted inline or as a displayed block
  4.1511 +	$self->{options}{'_default_translated'} .= " <remark>";
  4.1512 +	$self->{options}{'_default_inline'} .= " <remark>";
  4.1513 +
  4.1514 +	# replaceable; contains text; Formatted inline
  4.1515 +	$self->{options}{'_default_translated'} .= " <replaceable>";
  4.1516 +	$self->{options}{'_default_inline'} .= " <replaceable>";
  4.1517 +
  4.1518 +	# returnvalue; contains text; Formatted inline
  4.1519 +	$self->{options}{'_default_translated'} .= " <returnvalue>";
  4.1520 +	$self->{options}{'_default_inline'} .= " <returnvalue>";
  4.1521 +
  4.1522 +	# revdescription; contains text; Formatted inline or as a displayed block
  4.1523 +	$self->{options}{'_default_translated'} .= " <revdescription>";
  4.1524 +	$self->{options}{'_default_break'} .= " <revdescription>";
  4.1525 +
  4.1526 +	# revhistory; does not contain text; Formatted as a displayed block
  4.1527 +	$self->{options}{'_default_untranslated'} .= " <revhistory>";
  4.1528 +	$self->{options}{'_default_break'} .= " <revhistory>";
  4.1529 +
  4.1530 +	# revision; does not contain text;
  4.1531 +	$self->{options}{'_default_untranslated'} .= " <revision>";
  4.1532 +	$self->{options}{'_default_break'} .= " <revision>";
  4.1533 +
  4.1534 +	# revnumber; contains text; Formatted inline
  4.1535 +	$self->{options}{'_default_translated'} .= " <revnumber>";
  4.1536 +	$self->{options}{'_default_inline'} .= " <revnumber>";
  4.1537 +
  4.1538 +	# revremark; contains text; Formatted inline or as a displayed block
  4.1539 +	$self->{options}{'_default_translated'} .= " <revremark>";
  4.1540 +	$self->{options}{'_default_break'} .= " <revremark>";
  4.1541 +
  4.1542 +	# rhs; contains text; Formatted as a displayed block.
  4.1543 +	# NOTE: it might be better to have the production as verbatim
  4.1544 +	#       Keeping the constrainst inline to have it close to the
  4.1545 +	#       lhs or rhs.
  4.1546 +	$self->{options}{'_default_translated'} .= " <rhs>";
  4.1547 +	$self->{options}{'_default_break'} .= " <rhs>";
  4.1548 +
  4.1549 +	# row; does not contain text;
  4.1550 +	$self->{options}{'_default_untranslated'} .= " <row>";
  4.1551 +	$self->{options}{'_default_break'} .= " <row>";
  4.1552 +
  4.1553 +# SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
  4.1554 +
  4.1555 +	# sbr; does not contain text; line break
  4.1556 +	$self->{options}{'_default_untranslated'} .= " <sbr>";
  4.1557 +	$self->{options}{'_default_break'} .= " <sbr>";
  4.1558 +
  4.1559 +	# screen; contains text; verbatim
  4.1560 +	$self->{options}{'_default_translated'} .= " W<screen>";
  4.1561 +	$self->{options}{'_default_placeholder'} .= " <screen>";
  4.1562 +
  4.1563 +	# screenco; does not contain text; Formatted as a displayed block.
  4.1564 +	$self->{options}{'_default_untranslated'} .= " <screenco>";
  4.1565 +	$self->{options}{'_default_placeholder'} .= " <screenco>";
  4.1566 +
  4.1567 +	# screeninfo; does not contain text; v4, not in v5
  4.1568 +	$self->{options}{'_default_untranslated'} .= " <screeninfo>";
  4.1569 +	$self->{options}{'_default_placeholder'} .= " <screeninfo>";
  4.1570 +
  4.1571 +	# screenshot; does not contain text; Formatted as a displayed block.
  4.1572 +	$self->{options}{'_default_untranslated'} .= " <screenshot>";
  4.1573 +	$self->{options}{'_default_placeholder'} .= " <screenshot>";
  4.1574 +
  4.1575 +	# secondary; contains text; 
  4.1576 +	$self->{options}{'_default_translated'} .= " <secondary>";
  4.1577 +	$self->{options}{'_default_break'} .= " <secondary>";
  4.1578 +
  4.1579 +	# secondaryie; contains text; Formatted as a displayed block.
  4.1580 +	$self->{options}{'_default_translated'} .= " <secondaryie>";
  4.1581 +	$self->{options}{'_default_break'} .= " <secondaryie>";
  4.1582 +
  4.1583 +	# sect1; does not contain text; Formatted as a displayed block.
  4.1584 +	$self->{options}{'_default_untranslated'} .= " <sect1>";
  4.1585 +	$self->{options}{'_default_break'} .= " <sect1>";
  4.1586 +
  4.1587 +	# sect1info; does not contain text; v4, not in v5
  4.1588 +	$self->{options}{'_default_untranslated'} .= " <sect1info>";
  4.1589 +	$self->{options}{'_default_placeholder'} .= " <sect1info>";
  4.1590 +
  4.1591 +	# sect2; does not contain text; Formatted as a displayed block.
  4.1592 +	$self->{options}{'_default_untranslated'} .= " <sect2>";
  4.1593 +	$self->{options}{'_default_break'} .= " <sect2>";
  4.1594 +
  4.1595 +	# sect2info; does not contain text; v4, not in v5
  4.1596 +	$self->{options}{'_default_untranslated'} .= " <sect2info>";
  4.1597 +	$self->{options}{'_default_placeholder'} .= " <sect2info>";
  4.1598 +
  4.1599 +	# sect3; does not contain text; Formatted as a displayed block.
  4.1600 +	$self->{options}{'_default_untranslated'} .= " <sect3>";
  4.1601 +	$self->{options}{'_default_break'} .= " <sect3>";
  4.1602 +
  4.1603 +	# sect3info; does not contain text; v4, not in v5
  4.1604 +	$self->{options}{'_default_untranslated'} .= " <sect3info>";
  4.1605 +	$self->{options}{'_default_placeholder'} .= " <sect3info>";
  4.1606 +
  4.1607 +	# sect4; does not contain text; Formatted as a displayed block.
  4.1608 +	$self->{options}{'_default_untranslated'} .= " <sect4>";
  4.1609 +	$self->{options}{'_default_break'} .= " <sect4>";
  4.1610 +
  4.1611 +	# sect4info; does not contain text; v4, not in v5
  4.1612 +	$self->{options}{'_default_untranslated'} .= " <sect4info>";
  4.1613 +	$self->{options}{'_default_placeholder'} .= " <sect4info>";
  4.1614 +
  4.1615 +	# sect5; does not contain text; Formatted as a displayed block.
  4.1616 +	$self->{options}{'_default_untranslated'} .= " <sect5>";
  4.1617 +	$self->{options}{'_default_break'} .= " <sect5>";
  4.1618 +
  4.1619 +	# sect5info; does not contain text; v4, not in v5
  4.1620 +	$self->{options}{'_default_untranslated'} .= " <sect5info>";
  4.1621 +	$self->{options}{'_default_placeholder'} .= " <sect5info>";
  4.1622 +
  4.1623 +	# section; does not contain text; Formatted as a displayed block.
  4.1624 +	$self->{options}{'_default_untranslated'} .= " <section>";
  4.1625 +	$self->{options}{'_default_break'} .= " <section>";
  4.1626 +
  4.1627 +	# sectioninfo; does not contain text; v3.1 -> v4, not in v5
  4.1628 +	$self->{options}{'_default_untranslated'} .= " <sectioninfo>";
  4.1629 +	$self->{options}{'_default_placeholder'} .= " <sectioninfo>";
  4.1630 +
  4.1631 +	# see; contains text; 
  4.1632 +	$self->{options}{'_default_translated'} .= " <see>";
  4.1633 +	$self->{options}{'_default_break'} .= " <see>";
  4.1634 +
  4.1635 +	# seealso; contains text; 
  4.1636 +	$self->{options}{'_default_translated'} .= " <seealso>";
  4.1637 +	$self->{options}{'_default_break'} .= " <seealso>";
  4.1638 +
  4.1639 +	# seealsoie; contains text; Formatted as a displayed block.
  4.1640 +	$self->{options}{'_default_translated'} .= " <seealsoie>";
  4.1641 +	$self->{options}{'_default_break'} .= " <seealsoie>";
  4.1642 +
  4.1643 +	# seeie; contains text; Formatted as a displayed block.
  4.1644 +	$self->{options}{'_default_translated'} .= " <seeie>";
  4.1645 +	$self->{options}{'_default_break'} .= " <seeie>";
  4.1646 +
  4.1647 +	# seg; contains text;
  4.1648 +	$self->{options}{'_default_translated'} .= " <seg>";
  4.1649 +	$self->{options}{'_default_break'} .= " <seg>";
  4.1650 +
  4.1651 +	# seglistitem; does not contain text;
  4.1652 +	$self->{options}{'_default_untranslated'} .= " <seglistitem>";
  4.1653 +	$self->{options}{'_default_break'} .= " <seglistitem>";
  4.1654 +
  4.1655 +	# segmentedlist; does not contain text;
  4.1656 +	$self->{options}{'_default_untranslated'} .= " <segmentedlist>";
  4.1657 +	$self->{options}{'_default_break'} .= " <segmentedlist>";
  4.1658 +
  4.1659 +	# segtitle; contains text;
  4.1660 +	$self->{options}{'_default_translated'} .= " <segtitle>";
  4.1661 +	$self->{options}{'_default_break'} .= " <segtitle>";
  4.1662 +
  4.1663 +	# seriesinfo; does not contain text;
  4.1664 +	# Removed in v4.0
  4.1665 +	$self->{options}{'_default_untranslated'} .= " <seriesinfo>";
  4.1666 +	$self->{options}{'_default_placeholder'} .= " <seriesinfo>";
  4.1667 +
  4.1668 +	# seriesvolnums; contains text; Formatted inline
  4.1669 +	# NOTE: could be in the break class
  4.1670 +	$self->{options}{'_default_translated'} .= " <seriesvolnums>";
  4.1671 +	$self->{options}{'_default_inline'} .= " <seriesvolnums>";
  4.1672 +
  4.1673 +	# set; does not contain text; Formatted as a displayed block.
  4.1674 +	$self->{options}{'_default_untranslated'} .= " <set>";
  4.1675 +	$self->{options}{'_default_break'} .= " <set>";
  4.1676 +
  4.1677 +	# setindex; does not contain text; Formatted as a displayed block.
  4.1678 +	$self->{options}{'_default_untranslated'} .= " <setindex>";
  4.1679 +	$self->{options}{'_default_break'} .= " <setindex>";
  4.1680 +
  4.1681 +	# setindexinfo; does not contain text; v4, not in v5
  4.1682 +	$self->{options}{'_default_untranslated'} .= " <setindexinfo>";
  4.1683 +	$self->{options}{'_default_placeholder'} .= " <setindexinfo>";
  4.1684 +
  4.1685 +	# setinfo; does not contain text; v4, not in v5
  4.1686 +	$self->{options}{'_default_untranslated'} .= " <setinfo>";
  4.1687 +	$self->{options}{'_default_placeholder'} .= " <setinfo>";
  4.1688 +
  4.1689 +	# sgmltag; contains text; Formatted inline; v4, not in v5
  4.1690 +	$self->{options}{'_default_translated'} .= " <sgmltag>";
  4.1691 +	$self->{options}{'_default_inline'} .= " <sgmltag>";
  4.1692 +
  4.1693 +	# shortaffil; contains text; Formatted inline or as a
  4.1694 +	# displayed block depending on context
  4.1695 +	$self->{options}{'_default_translated'} .= " <shortaffil>";
  4.1696 +	$self->{options}{'_default_inline'} .= " <shortaffil>";
  4.1697 +
  4.1698 +	# shortcut; does not contain text; Formatted inline
  4.1699 +	$self->{options}{'_default_untranslated'} .= " <shortcut>";
  4.1700 +	$self->{options}{'_default_inline'} .= " <shortcut>";
  4.1701 +
  4.1702 +	# sidebar; does not contain text; Formatted as a displayed block.
  4.1703 +	$self->{options}{'_default_untranslated'} .= " <sidebar>";
  4.1704 +	$self->{options}{'_default_break'} .= " <sidebar>";
  4.1705 +
  4.1706 +	# sidebarinfo; does not contain text; v4, not in v5
  4.1707 +	$self->{options}{'_default_untranslated'} .= " <sidebarinfo>";
  4.1708 +	$self->{options}{'_default_placeholder'} .= " <sidebarinfo>";
  4.1709 +
  4.1710 +	# simpara; contains text; Formatted as a displayed block.
  4.1711 +	$self->{options}{'_default_translated'} .= " <simpara>";
  4.1712 +	$self->{options}{'_default_break'} .= " <simpara>";
  4.1713 +
  4.1714 +	# simplelist; does not contain text;
  4.1715 +	$self->{options}{'_default_untranslated'} .= " <simplelist>";
  4.1716 +	$self->{options}{'_default_inline'} .= " <simplelist>";
  4.1717 +
  4.1718 +	# simplemsgentry; does not contain text; Formatted as a displayed block.
  4.1719 +	$self->{options}{'_default_untranslated'} .= " <simplemsgentry>";
  4.1720 +	$self->{options}{'_default_break'} .= " <simplemsgentry>";
  4.1721 +
  4.1722 +	# simplesect; does not contain text; Formatted as a displayed block.
  4.1723 +	$self->{options}{'_default_untranslated'} .= " <simplesect>";
  4.1724 +	$self->{options}{'_default_break'} .= " <simplesect>";
  4.1725 +
  4.1726 +	# spanspec; does not contain text; Formatted as a displayed block.
  4.1727 +	$self->{options}{'_default_untranslated'} .= " <spanspec>";
  4.1728 +	$self->{options}{'_default_break'} .= " <spanspec>";
  4.1729 +
  4.1730 +	# state; contains text; Formatted inline
  4.1731 +	$self->{options}{'_default_translated'} .= " <state>";
  4.1732 +	$self->{options}{'_default_inline'} .= " <state>";
  4.1733 +
  4.1734 +	# step; does not contain text; Formatted as a displayed block.
  4.1735 +	$self->{options}{'_default_untranslated'} .= " <step>";
  4.1736 +	$self->{options}{'_default_break'} .= " <step>";
  4.1737 +
  4.1738 +	# stepalternatives; does not contain text; Formatted as a displayed block.
  4.1739 +	$self->{options}{'_default_untranslated'} .= " <stepalternatives>";
  4.1740 +	$self->{options}{'_default_break'} .= " <stepalternatives>";
  4.1741 +
  4.1742 +	# street; contains text; Formatted inline
  4.1743 +	$self->{options}{'_default_translated'} .= " <street>";
  4.1744 +	$self->{options}{'_default_inline'} .= " <street>";
  4.1745 +
  4.1746 +	# structfield; contains text; Formatted inline; v4, not in v5
  4.1747 +	$self->{options}{'_default_translated'} .= " <structfield>";
  4.1748 +	$self->{options}{'_default_inline'} .= " <structfield>";
  4.1749 +
  4.1750 +	# structname; contains text; Formatted inline; v4, not in v5
  4.1751 +	$self->{options}{'_default_translated'} .= " <structname>";
  4.1752 +	$self->{options}{'_default_inline'} .= " <structname>";
  4.1753 +
  4.1754 +	# subject; does not contain text; Formatted inline or as a displayed block
  4.1755 +	# NOTE: could be in the inline class
  4.1756 +	$self->{options}{'_default_untranslated'} .= " <subject>";
  4.1757 +	$self->{options}{'_default_break'} .= " <subject>";
  4.1758 +
  4.1759 +	# subjectset; does not contain text; Formatted inline or as a displayed block
  4.1760 +	# NOTE: could be in the inline class
  4.1761 +	$self->{options}{'_default_untranslated'} .= " <subjectset>";
  4.1762 +	$self->{options}{'_default_break'} .= " <subjectset>";
  4.1763 +
  4.1764 +	# subjectterm; contains text; Formatted inline or as a displayed block
  4.1765 +	# NOTE: could be in the inline class
  4.1766 +	$self->{options}{'_default_translated'} .= " <subjectterm>";
  4.1767 +	$self->{options}{'_default_break'} .= " <subjectterm>";
  4.1768 +
  4.1769 +	# subscript; contains text; Formatted inline
  4.1770 +	$self->{options}{'_default_translated'} .= " <subscript>";
  4.1771 +	$self->{options}{'_default_inline'} .= " <subscript>";
  4.1772 +
  4.1773 +	# substeps; does not contain text; Formatted as a displayed block.
  4.1774 +	$self->{options}{'_default_untranslated'} .= " <substeps>";
  4.1775 +	$self->{options}{'_default_break'} .= " <substeps>";
  4.1776 +
  4.1777 +	# subtitle; contains text; Formatted as a displayed block.
  4.1778 +	$self->{options}{'_default_translated'} .= " <subtitle>";
  4.1779 +	$self->{options}{'_default_break'} .= " <subtitle>";
  4.1780 +
  4.1781 +	# superscript; contains text; Formatted inline
  4.1782 +	$self->{options}{'_default_translated'} .= " <superscript>";
  4.1783 +	$self->{options}{'_default_inline'} .= " <superscript>";
  4.1784 +
  4.1785 +	# surname; contains text; Formatted inline
  4.1786 +	$self->{options}{'_default_translated'} .= " <surname>";
  4.1787 +	$self->{options}{'_default_inline'} .= " <surname>";
  4.1788 +
  4.1789 +#svg:svg
  4.1790 +
  4.1791 +	# symbol; contains text; Formatted inline
  4.1792 +	$self->{options}{'_default_translated'} .= " <symbol>";
  4.1793 +	$self->{options}{'_default_inline'} .= " <symbol>";
  4.1794 +
  4.1795 +	# synopfragment; does not contain text; Formatted as a displayed block.
  4.1796 +	$self->{options}{'_default_untranslated'} .= " <synopfragment>";
  4.1797 +	$self->{options}{'_default_placeholder'} .= " <synopfragment>";
  4.1798 +
  4.1799 +	# synopfragmentref; contains text; Formatted inline
  4.1800 +	$self->{options}{'_default_translated'} .= " <synopfragmentref>";
  4.1801 +	$self->{options}{'_default_inline'} .= " <synopfragmentref>";
  4.1802 +
  4.1803 +	# synopsis; contains text; verbatim
  4.1804 +	$self->{options}{'_default_translated'} .= " W<synopsis>";
  4.1805 +	$self->{options}{'_default_placeholder'} .= " <synopsis>";
  4.1806 +
  4.1807 +	# systemitem; contains text; Formatted inline
  4.1808 +	$self->{options}{'_default_translated'} .= " <systemitem>";
  4.1809 +	$self->{options}{'_default_inline'} .= " <systemitem>";
  4.1810 +
  4.1811 +# TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
  4.1812 +
  4.1813 +	# table; does not contain text; Formatted as a displayed block.
  4.1814 +	$self->{options}{'_default_untranslated'} .= " <table>";
  4.1815 +	$self->{options}{'_default_placeholder'} .= " <table>";
  4.1816 +
  4.1817 +	# tag; contains text; Formatted inline
  4.1818 +	$self->{options}{'_default_translated'} .= " <tag>";
  4.1819 +	$self->{options}{'_default_inline'} .= " <tag>";
  4.1820 +
  4.1821 +	# task; does not contain text; Formatted as a displayed block.
  4.1822 +	$self->{options}{'_default_untranslated'} .= " <task>";
  4.1823 +	$self->{options}{'_default_placeholder'} .= " <task>";
  4.1824 +
  4.1825 +	# taskprerequisites; does not contain text; Formatted as a displayed block.
  4.1826 +	$self->{options}{'_default_untranslated'} .= " <taskprerequisites>";
  4.1827 +	$self->{options}{'_default_break'} .= " <taskprerequisites>";
  4.1828 +
  4.1829 +	# taskrelated; does not contain text; Formatted as a displayed block.
  4.1830 +	$self->{options}{'_default_untranslated'} .= " <taskrelated>";
  4.1831 +	$self->{options}{'_default_break'} .= " <taskrelated>";
  4.1832 +
  4.1833 +	# tasksummary; does not contain text; Formatted as a displayed block.
  4.1834 +	$self->{options}{'_default_untranslated'} .= " <tasksummary>";
  4.1835 +	$self->{options}{'_default_break'} .= " <tasksummary>";
  4.1836 +
  4.1837 +	# tbody; does not contain text;
  4.1838 +	$self->{options}{'_default_untranslated'} .= " <tbody>";
  4.1839 +	$self->{options}{'_default_break'} .= " <tbody>";
  4.1840 +
  4.1841 +	# td; contains text;
  4.1842 +	$self->{options}{'_default_translated'} .= " <td>";
  4.1843 +	$self->{options}{'_default_break'} .= " <td>";
  4.1844 +
  4.1845 +	# term; contains text; Formatted as a displayed block.
  4.1846 +	$self->{options}{'_default_translated'} .= " <term>";
  4.1847 +	$self->{options}{'_default_break'} .= " <term>";
  4.1848 +
  4.1849 +	# termdef; contains text; Formatted inline
  4.1850 +	$self->{options}{'_default_translated'} .= " <termdef>";
  4.1851 +	$self->{options}{'_default_inline'} .= " <termdef>";
  4.1852 +
  4.1853 +	# tertiary; contains text; Suppressed
  4.1854 +	$self->{options}{'_default_translated'} .= " <tertiary>";
  4.1855 +	$self->{options}{'_default_placeholder'} .= " <tertiary>";
  4.1856 +
  4.1857 +	# tertiaryie; contains text; Formatted as a displayed block.
  4.1858 +	$self->{options}{'_default_translated'} .= " <tertiaryie>";
  4.1859 +	$self->{options}{'_default_break'} .= " <tertiaryie>";
  4.1860 +
  4.1861 +	# textdata; does not contain text; Formatted inline or as a displayed block
  4.1862 +	# NOTE: could be in the inline class
  4.1863 +	$self->{options}{'_default_untranslated'} .= " <textdata>";
  4.1864 +	$self->{options}{'_default_break'} .= " <textdata>";
  4.1865 +	$self->{options}{'_default_attributes'}.=' <textdata>fileref';
  4.1866 +
  4.1867 +	# textobject; does not contain text; Formatted inline or as a displayed block
  4.1868 +	# NOTE: could be in the inline class
  4.1869 +	$self->{options}{'_default_untranslated'} .= " <textobject>";
  4.1870 +	$self->{options}{'_default_break'} .= " <textobject>";
  4.1871 +
  4.1872 +	# tfoot; does not contain text;
  4.1873 +	$self->{options}{'_default_untranslated'} .= " <tfoot>";
  4.1874 +	$self->{options}{'_default_break'} .= " <tfoot>";
  4.1875 +
  4.1876 +	# tgroup; does not contain text;
  4.1877 +	$self->{options}{'_default_untranslated'} .= " <tgroup>";
  4.1878 +	$self->{options}{'_default_break'} .= " <tgroup>";
  4.1879 +
  4.1880 +	# th; contains text;
  4.1881 +	$self->{options}{'_default_translated'} .= " <th>";
  4.1882 +	$self->{options}{'_default_break'} .= " <th>";
  4.1883 +
  4.1884 +	# thead; does not contain text;
  4.1885 +	$self->{options}{'_default_untranslated'} .= " <thead>";
  4.1886 +	$self->{options}{'_default_break'} .= " <thead>";
  4.1887 +
  4.1888 +	# tip; does not contain text; Formatted as a displayed block.
  4.1889 +	$self->{options}{'_default_untranslated'} .= " <tip>";
  4.1890 +	$self->{options}{'_default_break'} .= " <tip>";
  4.1891 +
  4.1892 +	# title; contains text; Formatted as a displayed block.
  4.1893 +	$self->{options}{'_default_translated'} .= " <title>";
  4.1894 +	$self->{options}{'_default_break'} .= " <title>";
  4.1895 +
  4.1896 +	# titleabbrev; contains text; Formatted inline or as a displayed block
  4.1897 +	# NOTE: could be in the inline class
  4.1898 +	$self->{options}{'_default_translated'} .= " <titleabbrev>";
  4.1899 +	$self->{options}{'_default_break'} .= " <titleabbrev>";
  4.1900 +
  4.1901 +	# toc; does not contain text; Formatted as a displayed block.
  4.1902 +	$self->{options}{'_default_untranslated'} .= " <toc>";
  4.1903 +	$self->{options}{'_default_break'} .= " <toc>";
  4.1904 +
  4.1905 +	# tocback; contains text; Formatted as a displayed block.
  4.1906 +	$self->{options}{'_default_translated'} .= " <tocback>";
  4.1907 +	$self->{options}{'_default_break'} .= " <tocback>";
  4.1908 +
  4.1909 +	# tocchap; does not contain text; Formatted as a displayed block.
  4.1910 +	$self->{options}{'_default_translated'} .= " <tocchap>";
  4.1911 +	$self->{options}{'_default_break'} .= " <tocchap>";
  4.1912 +
  4.1913 +	# tocdiv; does not contain text; Formatted as a displayed block.
  4.1914 +	$self->{options}{'_default_untranslated'} .= " <tocdiv>";
  4.1915 +	$self->{options}{'_default_break'} .= " <tocdiv>";
  4.1916 +
  4.1917 +	# tocentry; contains text; Formatted as a displayed block.
  4.1918 +	$self->{options}{'_default_translated'} .= " <tocentry>";
  4.1919 +	$self->{options}{'_default_break'} .= " <tocentry>";
  4.1920 +
  4.1921 +	# tocfront; does not contain text; Formatted as a displayed block.
  4.1922 +	$self->{options}{'_default_translated'} .= " <tocfront>";
  4.1923 +	$self->{options}{'_default_break'} .= " <tocfront>";
  4.1924 +
  4.1925 +	# toclevel1; does not contain text; Formatted as a displayed block.
  4.1926 +	$self->{options}{'_default_untranslated'} .= " <toclevel1>";
  4.1927 +	$self->{options}{'_default_break'} .= " <toclevel1>";
  4.1928 +
  4.1929 +	# toclevel2; does not contain text; Formatted as a displayed block.
  4.1930 +	$self->{options}{'_default_untranslated'} .= " <toclevel2>";
  4.1931 +	$self->{options}{'_default_break'} .= " <toclevel2>";
  4.1932 +
  4.1933 +	# toclevel3; does not contain text; Formatted as a displayed block.
  4.1934 +	$self->{options}{'_default_untranslated'} .= " <toclevel3>";
  4.1935 +	$self->{options}{'_default_break'} .= " <toclevel3>";
  4.1936 +
  4.1937 +	# toclevel4; does not contain text; Formatted as a displayed block.
  4.1938 +	$self->{options}{'_default_untranslated'} .= " <toclevel4>";
  4.1939 +	$self->{options}{'_default_break'} .= " <toclevel4>";
  4.1940 +
  4.1941 +	# toclevel5; does not contain text; Formatted as a displayed block.
  4.1942 +	$self->{options}{'_default_untranslated'} .= " <toclevel5>";
  4.1943 +	$self->{options}{'_default_break'} .= " <toclevel5>";
  4.1944 +
  4.1945 +	# tocpart; does not contain text; Formatted as a displayed block.
  4.1946 +	$self->{options}{'_default_untranslated'} .= " <tocpart>";
  4.1947 +	$self->{options}{'_default_break'} .= " <tocpart>";
  4.1948 +
  4.1949 +	# token; contains text; Formatted inline
  4.1950 +	$self->{options}{'_default_translated'} .= " <token>";
  4.1951 +	$self->{options}{'_default_inline'} .= " <token>";
  4.1952 +
  4.1953 +	# tr; does not contain text;
  4.1954 +	$self->{options}{'_default_untranslated'} .= " <tr>";
  4.1955 +	$self->{options}{'_default_break'} .= " <tr>";
  4.1956 +
  4.1957 +	# trademark; contains text; Formatted inline
  4.1958 +	$self->{options}{'_default_translated'} .= " <trademark>";
  4.1959 +	$self->{options}{'_default_inline'} .= " <trademark>";
  4.1960 +
  4.1961 +	# type; contains text; Formatted inline
  4.1962 +	$self->{options}{'_default_translated'} .= " <type>";
  4.1963 +	$self->{options}{'_default_inline'} .= " <type>";
  4.1964 +
  4.1965 +# UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
  4.1966 +
  4.1967 +	# ulink; contains text; Formatted inline; v4, not in v5
  4.1968 +	$self->{options}{'_default_translated'} .= " <ulink>";
  4.1969 +	$self->{options}{'_default_inline'} .= " <ulink>";
  4.1970 +
  4.1971 +	# uri; contains text; Formatted inline
  4.1972 +	$self->{options}{'_default_translated'} .= " <uri>";
  4.1973 +	$self->{options}{'_default_inline'} .= " <uri>";
  4.1974 +
  4.1975 +	# userinput; contains text; Formatted inline
  4.1976 +	$self->{options}{'_default_translated'} .= " <userinput>";
  4.1977 +	$self->{options}{'_default_inline'} .= " <userinput>";
  4.1978 +
  4.1979 +# VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
  4.1980 +
  4.1981 +	# varargs; empty element;
  4.1982 +	$self->{options}{'_default_untranslated'} .= " <varargs>";
  4.1983 +	$self->{options}{'_default_inline'} .= " <varargs>";
  4.1984 +
  4.1985 +	# variablelist; does not contain text; Formatted as a displayed block.
  4.1986 +	$self->{options}{'_default_untranslated'} .= " <variablelist>";
  4.1987 +	$self->{options}{'_default_placeholder'} .= " <variablelist>";
  4.1988 +
  4.1989 +	# varlistentry; does not contain text; Formatted as a displayed block.
  4.1990 +	$self->{options}{'_default_untranslated'} .= " <varlistentry>";
  4.1991 +	$self->{options}{'_default_break'} .= " <varlistentry>";
  4.1992 +
  4.1993 +	# varname; contains text; Formatted inline
  4.1994 +	$self->{options}{'_default_translated'} .= " <varname>";
  4.1995 +	$self->{options}{'_default_inline'} .= " <varname>";
  4.1996 +
  4.1997 +	# videodata; contains text; Formatted inline or as a displayed block
  4.1998 +	$self->{options}{'_default_untranslated'} .= " <videodata>";
  4.1999 +	$self->{options}{'_default_break'} .= " <videodata>";
  4.2000 +	$self->{options}{'_default_attributes'}.=' <videodata>fileref';
  4.2001 +
  4.2002 +	# videoobject; contains text; Formatted inline or as a displayed block
  4.2003 +	$self->{options}{'_default_untranslated'} .= " <videoobject>";
  4.2004 +	$self->{options}{'_default_break'} .= " <videoobject>";
  4.2005 +
  4.2006 +	# void; empty element;
  4.2007 +	$self->{options}{'_default_untranslated'} .= " <void>";
  4.2008 +	$self->{options}{'_default_inline'} .= " <void>";
  4.2009 +
  4.2010 +	# volumenum; contains text; Formatted inline
  4.2011 +	$self->{options}{'_default_translated'} .= " <volumenum>";
  4.2012 +	$self->{options}{'_default_inline'} .= " <volumenum>";
  4.2013 +
  4.2014 +# WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
  4.2015 +
  4.2016 +	# warning; does not contain text; Formatted as a displayed block.
  4.2017 +	$self->{options}{'_default_untranslated'} .= " <warning>";
  4.2018 +	$self->{options}{'_default_break'} .= " <warning>";
  4.2019 +
  4.2020 +	# wordasword; contains text; Formatted inline
  4.2021 +	$self->{options}{'_default_translated'} .= " <wordasword>";
  4.2022 +	$self->{options}{'_default_inline'} .= " <wordasword>";
  4.2023 +
  4.2024 +# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  4.2025 +
  4.2026 +	# xref; empty element;
  4.2027 +	$self->{options}{'_default_untranslated'} .= " <xref>";
  4.2028 +	$self->{options}{'_default_inline'} .= " <xref>";
  4.2029 +
  4.2030 +# YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
  4.2031 +
  4.2032 +	# year; contains text; Formatted inline
  4.2033 +	$self->{options}{'_default_translated'} .= " <year>";
  4.2034 +	$self->{options}{'_default_inline'} .= " <year>";
  4.2035 +
  4.2036 +# ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  4.2037 +
  4.2038 +	$self->{options}{'_default_attributes'}.='
  4.2039 +		lang
  4.2040 +		xml:lang';
  4.2041 +
  4.2042 +	$self->treat_options;
  4.2043 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/tools/po4a/lib/Locale/Po4a/Po.pm	Thu Mar 12 15:43:56 2009 +0800
     5.3 @@ -0,0 +1,1580 @@
     5.4 +# Locale::Po4a::Po -- manipulation of po files
     5.5 +# $Id: Po.pm,v 1.95 2009-02-28 22:18:39 nekral-guest Exp $
     5.6 +#
     5.7 +# This program is free software; you may redistribute it and/or modify it
     5.8 +# under the terms of GPL (see COPYING).
     5.9 +
    5.10 +############################################################################
    5.11 +# Modules and declarations
    5.12 +############################################################################
    5.13 +
    5.14 +=head1 NAME
    5.15 +
    5.16 +Locale::Po4a::Po - po file manipulation module
    5.17 +
    5.18 +=head1 SYNOPSIS
    5.19 +
    5.20 +    use Locale::Po4a::Po;
    5.21 +    my $pofile=Locale::Po4a::Po->new();
    5.22 +
    5.23 +    # Read po file
    5.24 +    $pofile->read('file.po');
    5.25 +
    5.26 +    # Add an entry
    5.27 +    $pofile->push('msgid' => 'Hello', 'msgstr' => 'bonjour',
    5.28 +                  'flags' => "wrap", 'reference'=>'file.c:46');
    5.29 +
    5.30 +    # Extract a translation
    5.31 +    $pofile->gettext("Hello"); # returns 'bonjour'
    5.32 +
    5.33 +    # Write back to a file
    5.34 +    $pofile->write('otherfile.po');
    5.35 +
    5.36 +=head1 DESCRIPTION
    5.37 +
    5.38 +Locale::Po4a::Po is a module that allows you to manipulate message
    5.39 +catalogs. You can load and write from/to a file (which extension is often
    5.40 +I<po>), you can build new entries on the fly or request for the translation
    5.41 +of a string.
    5.42 +
    5.43 +For a more complete description of message catalogs in the po format and
    5.44 +their use, please refer to the documentation of the gettext program.
    5.45 +
    5.46 +This module is part of the PO4A project, which objective is to use po files
    5.47 +(designed at origin to ease the translation of program messages) to
    5.48 +translate everything, including documentation (man page, info manual),
    5.49 +package description, debconf templates, and everything which may benefit
    5.50 +from this.
    5.51 +
    5.52 +=head1 OPTIONS ACCEPTED BY THIS MODULE
    5.53 +
    5.54 +=over 4
    5.55 +
    5.56 +=item porefs
    5.57 +
    5.58 +This specifies the reference format. It can be one of 'none' to not produce
    5.59 +any reference, 'noline' to not specify the line number, and 'full' to
    5.60 +include complete references.
    5.61 +
    5.62 +=back
    5.63 +
    5.64 +=cut
    5.65 +
    5.66 +use IO::File;
    5.67 +
    5.68 +
    5.69 +require Exporter;
    5.70 +
    5.71 +package Locale::Po4a::Po;
    5.72 +use DynaLoader;
    5.73 +
    5.74 +use Locale::Po4a::Common qw(wrap_msg wrap_mod wrap_ref_mod dgettext);
    5.75 +
    5.76 +use subs qw(makespace);
    5.77 +use vars qw(@ISA @EXPORT_OK);
    5.78 +@ISA = qw(Exporter DynaLoader);
    5.79 +@EXPORT = qw(%debug);
    5.80 +@EXPORT_OK = qw(&move_po_if_needed);
    5.81 +
    5.82 +use Locale::Po4a::TransTractor;
    5.83 +# Try to use a C extension if present.
    5.84 +eval("bootstrap Locale::Po4a::Po $Locale::Po4a::TransTractor::VERSION");
    5.85 +
    5.86 +use 5.006;
    5.87 +use strict;
    5.88 +use warnings;
    5.89 +
    5.90 +use Carp qw(croak);
    5.91 +use File::Path; # mkdir before write
    5.92 +use File::Copy; # move
    5.93 +use POSIX qw(strftime floor);
    5.94 +use Time::Local;
    5.95 +
    5.96 +use Encode;
    5.97 +
    5.98 +my @known_flags=qw(wrap no-wrap c-format fuzzy);
    5.99 +
   5.100 +our %debug=('canonize'  => 0,
   5.101 +            'quote'     => 0,
   5.102 +            'escape'    => 0,
   5.103 +            'encoding'  => 0,
   5.104 +            'filter'    => 0);
   5.105 +
   5.106 +=head1 Functions about whole message catalogs
   5.107 +
   5.108 +=over 4
   5.109 +
   5.110 +=item new()
   5.111 +
   5.112 +Creates a new message catalog. If an argument is provided, it's the name of
   5.113 +a po file we should load.
   5.114 +
   5.115 +=cut
   5.116 +
   5.117 +sub new {
   5.118 +    my ($this, $options) = (shift, shift);
   5.119 +    my $class = ref($this) || $this;
   5.120 +    my $self = {};
   5.121 +    bless $self, $class;
   5.122 +    $self->initialize($options);
   5.123 +
   5.124 +    my $filename = shift;
   5.125 +    $self->read($filename) if defined($filename) && length($filename);
   5.126 +    return $self;
   5.127 +}
   5.128 +
   5.129 +# Return the numerical timezone (e.g. +0200)
   5.130 +# Neither the %z nor the %s formats of strftime are portable:
   5.131 +# '%s' is not supported on Solaris and '%z' indicates
   5.132 +# "2006-10-25 19:36E. Europe Standard Time" on MS Windows.
   5.133 +sub timezone {
   5.134 +    my @g = gmtime();
   5.135 +    my @l = localtime();
   5.136 +
   5.137 +    my $diff;
   5.138 +    $diff  = floor(timelocal(@l)/60 +0.5);
   5.139 +    $diff -= floor(timelocal(@g)/60 +0.5);
   5.140 +
   5.141 +    my $h = floor($diff / 60) + $l[8]; # $l[8] indicates if we are currently
   5.142 +                                       # in a daylight saving time zone
   5.143 +    my $m = $diff%60;
   5.144 +
   5.145 +    return sprintf "%+03d%02d\n", $h, $m;
   5.146 +}
   5.147 +
   5.148 +sub initialize {
   5.149 +    my ($self, $options) = (shift, shift);
   5.150 +    my $date = strftime("%Y-%m-%d %H:%M", localtime).timezone();
   5.151 +    chomp $date;
   5.152 +#    $options = ref($options) || $options;
   5.153 +
   5.154 +    $self->{options}{'porefs'}= 'full';
   5.155 +    $self->{options}{'msgid-bugs-address'}= undef;
   5.156 +    $self->{options}{'copyright-holder'}= "Free Software Foundation, Inc.";
   5.157 +    $self->{options}{'package-name'}= "PACKAGE";
   5.158 +    $self->{options}{'package-version'}= "VERSION";
   5.159 +    foreach my $opt (keys %$options) {
   5.160 +        if ($options->{$opt}) {
   5.161 +            die wrap_mod("po4a::po",
   5.162 +                         dgettext ("po4a", "Unknown option: %s"), $opt)
   5.163 +                unless exists $self->{options}{$opt};
   5.164 +            $self->{options}{$opt} = $options->{$opt};
   5.165 +        }
   5.166 +    }
   5.167 +    $self->{options}{'porefs'} =~ /^(full|noline|none)$/ ||
   5.168 +        die wrap_mod("po4a::po",
   5.169 +                     dgettext ("po4a",
   5.170 +                               "Invalid value for option 'porefs' ('%s' is ".
   5.171 +                               "not one of 'full', 'noline' or 'none')"),
   5.172 +                     $self->{options}{'porefs'});
   5.173 +
   5.174 +    $self->{po}=();
   5.175 +    $self->{count}=0;  # number of msgids in the PO
   5.176 +    # count_doc: number of strings in the document
   5.177 +    # (duplicate strings counted multiple times)
   5.178 +    $self->{count_doc}=0;
   5.179 +    $self->{header_comment}=
   5.180 +                     " SOME DESCRIPTIVE TITLE\n"
   5.181 +                    ." Copyright (C) YEAR ".
   5.182 +                     $self->{options}{'copyright-holder'}."\n"
   5.183 +                    ." This file is distributed under the same license ".
   5.184 +                     "as the ".$self->{options}{'package-name'}." package.\n"
   5.185 +                    ." FIRST AUTHOR <EMAIL\@ADDRESS>, YEAR.\n"
   5.186 +                    ."\n"
   5.187 +                    .", fuzzy";
   5.188 +#    $self->header_tag="fuzzy";
   5.189 +    $self->{header}=escape_text("Project-Id-Version: ".
   5.190 +                                $self->{options}{'package-name'}." ".
   5.191 +                                $self->{options}{'package-version'}."\n".
   5.192 +                        ((defined $self->{options}{'msgid-bugs-address'})?
   5.193 +        "Report-Msgid-Bugs-To: ".$self->{options}{'msgid-bugs-address'}."\n":
   5.194 +                                "").
   5.195 +                                "POT-Creation-Date: $date\n".
   5.196 +                                "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n".
   5.197 +                                "Last-Translator: FULL NAME <EMAIL\@ADDRESS>\n".
   5.198 +                                "Language-Team: LANGUAGE <LL\@li.org>\n".
   5.199 +                                "MIME-Version: 1.0\n".
   5.200 +                                "Content-Type: text/plain; charset=CHARSET\n".
   5.201 +                                "Content-Transfer-Encoding: ENCODING");
   5.202 +
   5.203 +    $self->{encoder}=find_encoding("ascii");
   5.204 +
   5.205 +    # To make stats about gettext hits
   5.206 +    $self->stats_clear();
   5.207 +}
   5.208 +
   5.209 +=item read($)
   5.210 +
   5.211 +Reads a po file (which name is given as argument).  Previously existing
   5.212 +entries in self are not removed, the new ones are added to the end of the
   5.213 +catalog.
   5.214 +
   5.215 +=cut
   5.216 +
   5.217 +sub read {
   5.218 +    my $self=shift;
   5.219 +    my $filename=shift
   5.220 +        or croak wrap_mod("po4a::po",
   5.221 +                          dgettext("po4a",
   5.222 +                                   "Please provide a non-null filename"));
   5.223 +
   5.224 +    my $fh;
   5.225 +    if ($filename eq '-') {
   5.226 +        $fh=*STDIN;
   5.227 +    } else {
   5.228 +        open $fh,"<$filename"
   5.229 +            or croak wrap_mod("po4a::po",
   5.230 +                              dgettext("po4a", "Can't read from %s: %s"),
   5.231 +                              $filename, $!);
   5.232 +    }
   5.233 +
   5.234 +    ## Read paragraphs line-by-line
   5.235 +    my $pofile="";
   5.236 +    my $textline;
   5.237 +    while (defined ($textline = <$fh>)) {
   5.238 +        $pofile .= $textline;
   5.239 +    }
   5.240 +#    close INPUT
   5.241 +#        or croak (sprintf(dgettext("po4a",
   5.242 +#                                   "Can't close %s after reading: %s"),
   5.243 +#                          $filename,$!)."\n");
   5.244 +
   5.245 +    my $linenum=0;
   5.246 +
   5.247 +    foreach my $msg (split (/\n\n/,$pofile)) {
   5.248 +        my ($msgid,$msgstr,$comment,$automatic,$reference,$flags,$buffer);
   5.249 +        my ($msgid_plural, $msgstr_plural);
   5.250 +        foreach my $line (split (/\n/,$msg)) {
   5.251 +            $linenum++;
   5.252 +            if ($line =~ /^#\. ?(.*)$/) {  # Automatic comment
   5.253 +                $automatic .= (defined($automatic) ? "\n" : "").$1;
   5.254 +
   5.255 +            } elsif ($line =~ /^#: ?(.*)$/) { # reference
   5.256 +                $reference .= (defined($reference) ? "\n" : "").$1;
   5.257 +
   5.258 +            } elsif ($line =~ /^#, ?(.*)$/) { # flags
   5.259 +                $flags .= (defined($flags) ? "\n" : "").$1;
   5.260 +
   5.261 +            } elsif ($line =~ /^#(.*)$/) {  # Translator comments
   5.262 +                $comment .= (defined($comment) ? "\n" : "").($1||"");
   5.263 +
   5.264 +            } elsif ($line =~ /^msgid (".*")$/) { # begin of msgid
   5.265 +                $buffer = $1;
   5.266 +
   5.267 +            } elsif ($line =~ /^msgid_plural (".*")$/) {
   5.268 +                # begin of msgid_plural, end of msgid
   5.269 +
   5.270 +                $msgid = $buffer;
   5.271 +                $buffer = $1;
   5.272 +
   5.273 +            } elsif ($line =~ /^msgstr (".*")$/) {
   5.274 +                # begin of msgstr, end of msgid
   5.275 +
   5.276 +                $msgid = $buffer;
   5.277 +                $buffer = "$1";
   5.278 +
   5.279 +            } elsif ($line =~ /^msgstr\[([0-9]+)\] (".*")$/) {
   5.280 +                # begin of msgstr[x], end of msgid_plural or msgstr[x-1]
   5.281 +
   5.282 +                # Note: po4a cannot uses plural forms
   5.283 +                # (no integer to use the plural form)
   5.284 +                #   * drop the msgstr[x] where x >= 2
   5.285 +                #   * use msgstr[0] as the translation of msgid
   5.286 +                #   * use msgstr[1] as the translation of msgid_plural
   5.287 +
   5.288 +                if ($1 eq "0") {
   5.289 +                    $msgid_plural = $buffer;
   5.290 +                    $buffer = "$2";
   5.291 +                } elsif ($1 eq "1") {
   5.292 +                    $msgstr = $buffer;
   5.293 +                    $buffer = "$2";
   5.294 +                } elsif ($1 eq "2") {
   5.295 +                    $msgstr_plural = $buffer;
   5.296 +                    warn wrap_ref_mod("$filename:$linenum",
   5.297 +                                      "po4a::po",
   5.298 +                                      dgettext("po4a", "Messages with more than 2 plural forms are not supported."));
   5.299 +                }
   5.300 +            } elsif ($line =~ /^(".*")$/) {
   5.301 +                # continuation of a line
   5.302 +                $buffer .= "\n$1";
   5.303 +
   5.304 +            } else {
   5.305 +                warn wrap_ref_mod("$filename:$linenum",
   5.306 +                                  "po4a::po",
   5.307 +                                  dgettext("po4a", "Strange line: -->%s<--"),
   5.308 +                                  $line);
   5.309 +            }
   5.310 +        }
   5.311 +        $linenum++;
   5.312 +        if (defined $msgid_plural) {
   5.313 +            $msgstr_plural=$buffer;
   5.314 +
   5.315 +            $msgid = unquote_text($msgid) if (defined($msgid));
   5.316 +            $msgstr = unquote_text($msgstr) if (defined($msgstr));
   5.317 +
   5.318 +            $self->push_raw ('msgid'     => $msgid,
   5.319 +                             'msgstr'    => $msgstr,
   5.320 +                             'reference' => $reference,
   5.321 +                             'flags'     => $flags,
   5.322 +                             'comment'   => $comment,
   5.323 +                             'automatic' => $automatic,
   5.324 +                             'plural'    => 0);
   5.325 +
   5.326 +            $msgid_plural = unquote_text($msgid_plural)
   5.327 +                if (defined($msgid_plural));
   5.328 +            $msgstr_plural = unquote_text($msgstr_plural)
   5.329 +                if (defined($msgstr_plural));
   5.330 +
   5.331 +            $self->push_raw ('msgid'     => $msgid_plural,
   5.332 +                             'msgstr'    => $msgstr_plural,
   5.333 +                             'reference' => $reference,
   5.334 +                             'flags'     => $flags,
   5.335 +                             'comment'   => $comment,
   5.336 +                             'automatic' => $automatic,
   5.337 +                             'plural'    => 1);
   5.338 +        } else {
   5.339 +            $msgstr=$buffer;
   5.340 +
   5.341 +            $msgid = unquote_text($msgid) if (defined($msgid));
   5.342 +            $msgstr = unquote_text($msgstr) if (defined($msgstr));
   5.343 +
   5.344 +            $self->push_raw ('msgid'     => $msgid,
   5.345 +                             'msgstr'    => $msgstr,
   5.346 +                             'reference' => $reference,
   5.347 +                             'flags'     => $flags,
   5.348 +                             'comment'   => $comment,
   5.349 +                             'automatic' => $automatic);
   5.350 +        }
   5.351 +    }
   5.352 +}
   5.353 +
   5.354 +=item write($)
   5.355 +
   5.356 +Writes the current catalog to the given file.
   5.357 +
   5.358 +=cut
   5.359 +
   5.360 +sub write{
   5.361 +    my $self=shift;
   5.362 +    my $filename=shift
   5.363 +        or croak dgettext("po4a","Can't write to a file without filename")."\n";
   5.364 +
   5.365 +    my $fh;
   5.366 +    if ($filename eq '-') {
   5.367 +        $fh=\*STDOUT;
   5.368 +    } else {
   5.369 +        # make sure the directory in which we should write the localized
   5.370 +        # file exists
   5.371 +        my $dir = $filename;
   5.372 +        if ($dir =~ m|/|) {
   5.373 +            $dir =~ s|/[^/]*$||;
   5.374 +
   5.375 +            File::Path::mkpath($dir, 0, 0755) # Croaks on error
   5.376 +                if (length ($dir) && ! -e $dir);
   5.377 +        }
   5.378 +        open $fh,">$filename"
   5.379 +            or croak wrap_mod("po4a::po",
   5.380 +                              dgettext("po4a", "Can't write to %s: %s"),
   5.381 +                              $filename, $!);
   5.382 +    }
   5.383 +
   5.384 +    print $fh "".format_comment($self->{header_comment},"")
   5.385 +        if defined($self->{header_comment}) && length($self->{header_comment});
   5.386 +
   5.387 +    print $fh "msgid \"\"\n";
   5.388 +    print $fh "msgstr ".quote_text($self->{header})."\n\n";
   5.389 +
   5.390 +
   5.391 +    my $buf_msgstr_plural; # USed to keep the first msgstr of plural forms
   5.392 +    my $first=1;
   5.393 +    foreach my $msgid ( sort { ($self->{po}{"$a"}{'pos'}) <=>
   5.394 +                               ($self->{po}{"$b"}{'pos'})
   5.395 +                             }  keys %{$self->{po}}) {
   5.396 +        my $output="";
   5.397 +
   5.398 +        if ($first) {
   5.399 +            $first=0;
   5.400 +        } else {
   5.401 +            $output .= "\n";
   5.402 +        }
   5.403 +
   5.404 +        $output .= format_comment($self->{po}{$msgid}{'comment'},"")
   5.405 +            if    defined($self->{po}{$msgid}{'comment'})
   5.406 +               && length ($self->{po}{$msgid}{'comment'});
   5.407 +        if (   defined($self->{po}{$msgid}{'automatic'})
   5.408 +            && length ($self->{po}{$msgid}{'automatic'})) {
   5.409 +            foreach my $comment (split(/\\n/,$self->{po}{$msgid}{'automatic'}))
   5.410 +            {
   5.411 +                $output .= format_comment($comment, ". ")
   5.412 +            }
   5.413 +        }
   5.414 +        $output .= format_comment($self->{po}{$msgid}{'type'},". type: ")
   5.415 +            if    defined($self->{po}{$msgid}{'type'})
   5.416 +               && length ($self->{po}{$msgid}{'type'});
   5.417 +        $output .= format_comment($self->{po}{$msgid}{'reference'},": ")
   5.418 +            if    defined($self->{po}{$msgid}{'reference'})
   5.419 +               && length ($self->{po}{$msgid}{'reference'});
   5.420 +        $output .= "#, ". join(", ", sort split(/\s+/,$self->{po}{$msgid}{'flags'}))."\n"
   5.421 +            if    defined($self->{po}{$msgid}{'flags'})
   5.422 +               && length ($self->{po}{$msgid}{'flags'});
   5.423 +
   5.424 +        if (exists $self->{po}{$msgid}{'plural'}) {
   5.425 +            if ($self->{po}{$msgid}{'plural'} == 0) {
   5.426 +                if ($self->get_charset =~ /^utf-8$/i) {
   5.427 +                    my $msgstr = Encode::decode_utf8($self->{po}{$msgid}{'msgstr'});
   5.428 +                    $msgid = Encode::decode_utf8($msgid);
   5.429 +                    $output .= Encode::encode_utf8("msgid ".quote_text($msgid)."\n");
   5.430 +                    $buf_msgstr_plural = Encode::encode_utf8("msgstr[0] ".quote_text($msgstr)."\n");
   5.431 +                } else {
   5.432 +                    $output = "msgid ".quote_text($msgid)."\n";
   5.433 +                    $buf_msgstr_plural = "msgstr[0] ".quote_text($self->{po}{$msgid}{'msgstr'})."\n";
   5.434 +                }
   5.435 +            } elsif ($self->{po}{$msgid}{'plural'} == 1) {
   5.436 +# TODO: there may be only one plural form
   5.437 +                if ($self->get_charset =~ /^utf-8$/i) {
   5.438 +                    my $msgstr = Encode::decode_utf8($self->{po}{$msgid}{'msgstr'});
   5.439 +                    $msgid = Encode::decode_utf8($msgid);
   5.440 +                    $output = Encode::encode_utf8("msgid_plural ".quote_text($msgid)."\n");
   5.441 +                    $output .= $buf_msgstr_plural;
   5.442 +                    $output .= Encode::encode_utf8("msgstr[1] ".quote_text($msgstr)."\n");
   5.443 +                    $buf_msgstr_plural = "";
   5.444 +                } else {
   5.445 +                    $output = "msgid_plural ".quote_text($msgid)."\n";
   5.446 +                    $output .= $buf_msgstr_plural;
   5.447 +                    $output .= "msgstr[1] ".quote_text($self->{po}{$msgid}{'msgstr'})."\n";
   5.448 +                }
   5.449 +            } else {
   5.450 +                die wrap_msg(dgettext("po4a","Can't write PO files with more than two plural forms."));
   5.451 +            }
   5.452 +        } else {
   5.453 +            if ($self->get_charset =~ /^utf-8$/i) {
   5.454 +                my $msgstr = Encode::decode_utf8($self->{po}{$msgid}{'msgstr'});
   5.455 +                $msgid = Encode::decode_utf8($msgid);
   5.456 +                $output .= Encode::encode_utf8("msgid ".quote_text($msgid)."\n");
   5.457 +                $output .= Encode::encode_utf8("msgstr ".quote_text($msgstr)."\n");
   5.458 +            } else {
   5.459 +                $output .= "msgid ".quote_text($msgid)."\n";
   5.460 +                $output .= "msgstr ".quote_text($self->{po}{$msgid}{'msgstr'})."\n";
   5.461 +            }
   5.462 +        }
   5.463 +
   5.464 +        print $fh $output;
   5.465 +    }
   5.466 +#    print STDERR "$fh";
   5.467 +#    if ($filename ne '-') {
   5.468 +#        close $fh
   5.469 +#            or croak (sprintf(dgettext("po4a",
   5.470 +#                                       "Can't close %s after writing: %s\n"),
   5.471 +#                              $filename,$!));
   5.472 +#    }
   5.473 +}
   5.474 +
   5.475 +=item write_if_needed($$)
   5.476 +
   5.477 +Like write, but if the PO or POT file already exists, the object will be
   5.478 +written in a temporary file which will be compared with the existing file
   5.479 +to check that the update is needed (this avoids to change a POT just to
   5.480 +update a line reference or the POT-Creation-Date field).
   5.481 +
   5.482 +=cut
   5.483 +
   5.484 +sub move_po_if_needed {
   5.485 +    my ($new_po, $old_po, $backup) = (shift, shift, shift);
   5.486 +    my $diff;
   5.487 +
   5.488 +    if (-e $old_po) {
   5.489 +        my $diff_ignore = "-I'^#:' "
   5.490 +                         ."-I'^\"POT-Creation-Date:' "
   5.491 +                         ."-I'^\"PO-Revision-Date:'";
   5.492 +        $diff = qx(diff -q $diff_ignore $old_po $new_po);
   5.493 +        if ( $diff eq "" ) {
   5.494 +            unlink $new_po
   5.495 +                or die wrap_msg(dgettext("po4a","Can't unlink %s: %s."),
   5.496 +                                $new_po, $!);
   5.497 +            # touch the old PO
   5.498 +            my ($atime, $mtime) = (time,time);
   5.499 +            utime $atime, $mtime, $old_po;
   5.500 +        } else {
   5.501 +            if ($backup) {
   5.502 +                copy $old_po, $old_po."~"
   5.503 +                    or die wrap_msg(dgettext("po4a","Can't copy %s to %s: %s."),
   5.504 +                                    $old_po, $old_po."~", $!);
   5.505 +            } else {
   5.506 +            }
   5.507 +            move $new_po, $old_po
   5.508 +                or die wrap_msg(dgettext("po4a","Can't move %s to %s: %s."),
   5.509 +                                $new_po, $old_po, $!);
   5.510 +        }
   5.511 +    } else {
   5.512 +        move $new_po, $old_po
   5.513 +            or die wrap_msg(dgettext("po4a","Can't move %s to %s: %s."),
   5.514 +                            $new_po, $old_po, $!);
   5.515 +    }
   5.516 +}
   5.517 +
   5.518 +sub write_if_needed {
   5.519 +    my $self=shift;
   5.520 +    my $filename=shift
   5.521 +        or croak dgettext("po4a","Can't write to a file without filename")."\n";
   5.522 +
   5.523 +    if (-e $filename) {
   5.524 +        my ($tmp_filename);
   5.525 +        (undef,$tmp_filename)=File::Temp->tempfile($filename."XXXX",
   5.526 +                                                   DIR    => "/tmp",
   5.527 +                                                   OPEN   => 0,
   5.528 +                                                   UNLINK => 0);
   5.529 +        $self->write($tmp_filename);
   5.530 +        move_po_if_needed($tmp_filename, $filename);
   5.531 +    } else {
   5.532 +        $self->write($filename);
   5.533 +    }
   5.534 +}
   5.535 +
   5.536 +=item gettextize($$)
   5.537 +
   5.538 +This function produces one translated message catalog from two catalogs, an
   5.539 +original and a translation. This process is described in L<po4a(7)|po4a.7>,
   5.540 +section I<Gettextization: how does it work?>.
   5.541 +
   5.542 +=cut
   5.543 +
   5.544 +sub gettextize {
   5.545 +    my $this = shift;
   5.546 +    my $class = ref($this) || $this;
   5.547 +    my ($poorig,$potrans)=(shift,shift);
   5.548 +
   5.549 +    my $pores=Locale::Po4a::Po->new();
   5.550 +
   5.551 +    my $please_fail = 0;
   5.552 +    my $toobad = dgettext("po4a",
   5.553 +        "\nThe gettextization failed (once again). Don't give up, ".
   5.554 +        "gettextizing is a subtle art, but this is only needed once ".
   5.555 +        "to convert a project to the gorgeous luxus offered by po4a ".
   5.556 +        "to translators.".
   5.557 +        "\nPlease refer to the po4a(7) documentation, the section ".
   5.558 +        "\"HOWTO convert a pre-existing translation to po4a?\" ".
   5.559 +        "contains several hints to help you in your task");
   5.560 +
   5.561 +    # Don't fail right now when the entry count does not match. Instead, give
   5.562 +    # it a try so that the user can see where we fail (which is probably where
   5.563 +    # the problem is).
   5.564 +    if ($poorig->count_entries_doc() > $potrans->count_entries_doc()) {
   5.565 +        warn wrap_mod("po4a gettextize", dgettext("po4a",
   5.566 +            "Original has more strings than the translation (%d>%d). ".
   5.567 +            "Please fix it by editing the translated version to add ".
   5.568 +            "some dummy entry."),
   5.569 +                      $poorig->count_entries_doc(),
   5.570 +                      $potrans->count_entries_doc());
   5.571 +        $please_fail = 1;
   5.572 +    } elsif ($poorig->count_entries_doc() < $potrans->count_entries_doc()) {
   5.573 +        warn wrap_mod("po4a gettextize", dgettext("po4a",
   5.574 +            "Original has less strings than the translation (%d<%d). ".
   5.575 +            "Please fix it by removing the extra entry from the ".
   5.576 +            "translated file. You may need an addendum (cf po4a(7)) ".
   5.577 +            "to reput the chunk in place after gettextization. A ".
   5.578 +            "possible cause is that a text duplicated in the original ".
   5.579 +            "is not translated the same way each time. Remove one of ".
   5.580 +            "the translations, and you're fine."),
   5.581 +                      $poorig->count_entries_doc(),
   5.582 +                      $potrans->count_entries_doc());
   5.583 +        $please_fail = 1;
   5.584 +    }
   5.585 +
   5.586 +    if ( $poorig->get_charset =~ /^utf-8$/i ) {
   5.587 +        $potrans->to_utf8;
   5.588 +        $pores->set_charset("utf-8");
   5.589 +    } else {
   5.590 +        if ($potrans->get_charset eq "CHARSET") {
   5.591 +            $pores->set_charset("ascii");
   5.592 +        } else {
   5.593 +            $pores->set_charset($potrans->get_charset);
   5.594 +        }
   5.595 +    }
   5.596 +    print "Po character sets:\n".
   5.597 +        "  original=".$poorig->get_charset."\n".
   5.598 +        "  translated=".$potrans->get_charset."\n".
   5.599 +        "  result=".$pores->get_charset."\n"
   5.600 +            if $debug{'encoding'};
   5.601 +
   5.602 +    for (my ($o,$t)=(0,0) ;
   5.603 +         $o<$poorig->count_entries_doc() && $t<$potrans->count_entries_doc();
   5.604 +         $o++,$t++) {
   5.605 +        #
   5.606 +        # Extract some informations
   5.607 +
   5.608 +        my ($orig,$trans)=($poorig->msgid_doc($o),$potrans->msgid_doc($t));
   5.609 +#       print STDERR "Matches [[$orig]]<<$trans>>\n";
   5.610 +
   5.611 +        my ($reforig,$reftrans)=($poorig->{po}{$orig}{'reference'},
   5.612 +                                 $potrans->{po}{$trans}{'reference'});
   5.613 +        my ($typeorig,$typetrans)=($poorig->{po}{$orig}{'type'},
   5.614 +                                   $potrans->{po}{$trans}{'type'});
   5.615 +
   5.616 +        #
   5.617 +        # Make sure the type of both string exist
   5.618 +        #
   5.619 +        die wrap_mod("po4a gettextize",
   5.620 +                     "Internal error: type of original string number %s ".
   5.621 +                     "isn't provided", $o)
   5.622 +            if ($typeorig eq '');
   5.623 +
   5.624 +        die wrap_mod("po4a gettextize",
   5.625 +                     "Internal error: type of translated string number %s ".
   5.626 +                     "isn't provided", $o)
   5.627 +            if ($typetrans eq '');
   5.628 +
   5.629 +        #
   5.630 +        # Make sure both type are the same
   5.631 +        #
   5.632 +        if ($typeorig ne $typetrans){
   5.633 +            $pores->write("gettextization.failed.po");
   5.634 +            die wrap_msg(dgettext("po4a",
   5.635 +                         "po4a gettextization: Structure disparity between ".
   5.636 +                         "original and translated files:\n".
   5.637 +                         "msgid (at %s) is of type '%s' while\n".
   5.638 +                         "msgstr (at %s) is of type '%s'.\n".
   5.639 +                         "Original text: %s\n".
   5.640 +                         "Translated text: %s\n".
   5.641 +                         "(result so far dumped to gettextization.failed.po)").
   5.642 +                         "%s",
   5.643 +                         $reforig, $typeorig,
   5.644 +                         $reftrans, $typetrans,
   5.645 +                         $orig,
   5.646 +                         $trans,
   5.647 +                         $toobad);
   5.648 +        }
   5.649 +
   5.650 +        #
   5.651 +        # Push the entry
   5.652 +        #
   5.653 +        my $flags;
   5.654 +        if (defined $poorig->{po}{$orig}{'flags'}) {
   5.655 +            $flags = $poorig->{po}{$orig}{'flags'}." fuzzy";
   5.656 +        } else {
   5.657 +            $flags = "fuzzy";
   5.658 +        }
   5.659 +        $pores->push_raw('msgid'     => $orig,
   5.660 +                         'msgstr'    => $trans,
   5.661 +                         'flags'     => $flags,
   5.662 +                         'type'      => $typeorig,
   5.663 +                         'reference' => $reforig,
   5.664 +                         'conflict'  => 1,
   5.665 +                         'transref'  => $potrans->{po}{$trans}{'reference'})
   5.666 +            unless (defined($pores->{po}{$orig})
   5.667 +                    and ($pores->{po}{$orig}{'msgstr'} eq $trans))
   5.668 +        # FIXME: maybe we should be smarter about what reference should be
   5.669 +        #        sent to push_raw.
   5.670 +    }
   5.671 +
   5.672 +    # make sure we return a useful error message when entry count differ
   5.673 +    die "$toobad\n" if $please_fail;
   5.674 +
   5.675 +    return $pores;
   5.676 +}
   5.677 +
   5.678 +=item filter($)
   5.679 +
   5.680 +This function extracts a catalog from an existing one. Only the entries having
   5.681 +a reference in the given file will be placed in the resulting catalog.
   5.682 +
   5.683 +This function parses its argument, converts it to a perl function definition,
   5.684 +eval this definition and filter the fields for which this function returns
   5.685 +true.
   5.686 +
   5.687 +I love perl sometimes ;)
   5.688 +
   5.689 +=cut
   5.690 +
   5.691 +sub filter {
   5.692 +    my $self=shift;
   5.693 +    our $filter=shift;
   5.694 +
   5.695 +    my $res;
   5.696 +    $res = Locale::Po4a::Po->new();
   5.697 +
   5.698 +    # Parse the filter
   5.699 +    our $code="sub apply { return ";
   5.700 +    our $pos=0;
   5.701 +    our $length = length $filter;
   5.702 +
   5.703 +    # explode chars to parts. How to subscript a string in Perl?
   5.704 +    our @filter = split(//,$filter);
   5.705 +
   5.706 +    sub gloups {
   5.707 +        my $fmt=shift;
   5.708 +        my $space = "";
   5.709 +        for (1..$pos){
   5.710 +            $space .= ' ';
   5.711 +        }
   5.712 +        die wrap_msg("$fmt\n$filter\n$space^ HERE");
   5.713 +    }
   5.714 +    sub showmethecode {
   5.715 +        return unless $debug{'filter'};
   5.716 +        my $fmt=shift;
   5.717 +        my $space="";
   5.718 +        for (1..$pos){
   5.719 +            $space .= ' ';
   5.720 +        }
   5.721 +        print STDERR "$filter\n$space^ $fmt\n";#"$code\n";
   5.722 +    }
   5.723 +
   5.724 +    # I dream of a lex in perl :-/
   5.725 +    sub parse_expression {
   5.726 +        showmethecode("Begin expression")
   5.727 +            if $debug{'filter'};
   5.728 +
   5.729 +        gloups("Begin of expression expected, got '%s'",$filter[$pos])
   5.730 +            unless ($filter[$pos] eq '(');
   5.731 +        $pos ++; # pass the '('
   5.732 +        if ($filter[$pos] eq '&') {
   5.733 +            # AND
   5.734 +            $pos++;
   5.735 +            showmethecode("Begin of AND")
   5.736 +                if $debug{'filter'};
   5.737 +            $code .= "(";
   5.738 +            while (1) {
   5.739 +                gloups ("Unfinished AND statement.")
   5.740 +                    if ($pos == $length);
   5.741 +                parse_expression();
   5.742 +                if ($filter[$pos] eq '(') {
   5.743 +                    $code .= " && ";
   5.744 +                } elsif ($filter[$pos] eq ')') {
   5.745 +                    last; # do not eat that char
   5.746 +                } else {
   5.747 +                    gloups("End of AND or begin of sub-expression expected, got '%s'", $filter[$pos]);
   5.748 +                }
   5.749 +            }
   5.750 +            $code .= ")";
   5.751 +        } elsif ($filter[$pos] eq '|') {
   5.752 +            # OR
   5.753 +            $pos++;
   5.754 +            $code .= "(";
   5.755 +            while (1) {
   5.756 +                gloups("Unfinished OR statement.")
   5.757 +                    if ($pos == $length);
   5.758 +                parse_expression();
   5.759 +                if ($filter[$pos] eq '(') {
   5.760 +                    $code .= " || ";
   5.761 +                } elsif ($filter[$pos] eq ')') {
   5.762 +                    last; # do not eat that char
   5.763 +                } else {
   5.764 +                    gloups("End of OR or begin of sub-expression expected, got '%s'",$filter[$pos]);
   5.765 +                }
   5.766 +            }
   5.767 +            $code .= ")";
   5.768 +        } elsif ($filter[$pos] eq '!') {
   5.769 +            # NOT
   5.770 +            $pos++;
   5.771 +            $code .= "(!";
   5.772 +            gloups("Missing sub-expression in NOT statement.")
   5.773 +                if ($pos == $length);
   5.774 +            parse_expression();
   5.775 +            $code .= ")";
   5.776 +        } else {
   5.777 +            # must be an equal. Let's get field and argument
   5.778 +            my ($field,$arg,$done);
   5.779 +            $field = substr($filter,$pos);
   5.780 +            gloups("EQ statement contains no '=' or invalid field name")
   5.781 +                unless ($field =~ /([a-z]*)=/i);
   5.782 +            $field = lc($1);
   5.783 +            $pos += (length $field) + 1;
   5.784 +
   5.785 +            # check that we've got a valid field name,
   5.786 +            # and the number it referes to
   5.787 +            # DO NOT CHANGE THE ORDER
   5.788 +            my @names=qw(msgid msgstr reference flags comment automatic);
   5.789 +            my $fieldpos;
   5.790 +            for ($fieldpos = 0;
   5.791 +                 $fieldpos < scalar @names && $field ne $names[$fieldpos];
   5.792 +                 $fieldpos++) {}
   5.793 +            gloups("Invalid field name: %s",$field)
   5.794 +                if $fieldpos == scalar @names; # not found
   5.795 +
   5.796 +            # Now, get the argument value. It has to be between quotes,
   5.797 +            # which can be escaped
   5.798 +            # We point right on the first char of the argument
   5.799 +            # (first quote already eaten)
   5.800 +            my $escaped = 0;
   5.801 +            my $quoted = 0;
   5.802 +            if ($filter[$pos] eq '"') {
   5.803 +                $pos++;
   5.804 +                $quoted = 1;
   5.805 +            }
   5.806 +            showmethecode(($quoted?"Quoted":"Unquoted")." argument of field '$field'")
   5.807 +                if $debug{'filter'};
   5.808 +
   5.809 +            while (!$done) {
   5.810 +                gloups("Unfinished EQ argument.")
   5.811 +                    if ($pos == $length);
   5.812 +
   5.813 +                if ($quoted) {
   5.814 +                    if ($filter[$pos] eq '\\') {
   5.815 +                        if ($escaped) {
   5.816 +                            $arg .= '\\';
   5.817 +                            $escaped = 0;
   5.818 +                        } else {
   5.819 +                            $escaped = 1;
   5.820 +                        }
   5.821 +                    } elsif ($escaped) {
   5.822 +                        if ($filter[$pos] eq '"') {
   5.823 +                            $arg .= '"';
   5.824 +                            $escaped = 0;
   5.825 +                        } else {
   5.826 +                            gloups("Invalid escape sequence in argument: '\\%s'",$filter[$pos]);
   5.827 +                        }
   5.828 +                    } else {
   5.829 +                        if ($filter[$pos] eq '"') {
   5.830 +                            $done = 1;
   5.831 +                        } else {
   5.832 +                            $arg .= $filter[$pos];
   5.833 +                        }
   5.834 +                    }
   5.835 +                } else {
   5.836 +                    if ($filter[$pos] eq ')') {
   5.837 +                        # counter the next ++ since we don't want to eat
   5.838 +                        # this char
   5.839 +                        $pos--;
   5.840 +                        $done = 1;
   5.841 +                    } else {
   5.842 +                        $arg .= $filter[$pos];
   5.843 +                    }
   5.844 +                }
   5.845 +                $pos++;
   5.846 +            }
   5.847 +            # and now, add the code to check this equality
   5.848 +            $code .= "(\$_[$fieldpos] =~ m/$arg/)";
   5.849 +
   5.850 +        }
   5.851 +        showmethecode("End of expression")
   5.852 +            if $debug{'filter'};
   5.853 +        gloups("Unfinished statement.")
   5.854 +            if ($pos == $length);
   5.855 +        gloups("End of expression expected, got '%s'",$filter[$pos])
   5.856 +            unless ($filter[$pos] eq ')');
   5.857 +        $pos++;
   5.858 +    }
   5.859 +    # And now, launch the beast, finish the function and use eval
   5.860 +    # to construct this function.
   5.861 +    # Ok, the lack of lexer is a fair price for the eval ;)
   5.862 +    parse_expression();
   5.863 +    gloups("Garbage at the end of the expression")
   5.864 +        if ($pos != $length);
   5.865 +    $code .= "; }";
   5.866 +    print STDERR "CODE = $code\n"
   5.867 +        if $debug{'filter'};
   5.868 +    eval $code;
   5.869 +    die wrap_mod("po4a::po", dgettext("po4a", "Eval failure: %s"), $@)
   5.870 +        if $@;
   5.871 +
   5.872 +    for (my $cpt=(0) ;
   5.873 +         $cpt<$self->count_entries();
   5.874 +         $cpt++) {
   5.875 +
   5.876 +        my ($msgid,$ref,$msgstr,$flags,$type,$comment,$automatic);
   5.877 +
   5.878 +        $msgid = $self->msgid($cpt);
   5.879 +        $ref=$self->{po}{$msgid}{'reference'};
   5.880 +
   5.881 +        $msgstr= $self->{po}{$msgid}{'msgstr'};
   5.882 +        $flags =  $self->{po}{$msgid}{'flags'};
   5.883 +        $type = $self->{po}{$msgid}{'type'};
   5.884 +        $comment = $self->{po}{$msgid}{'comment'};
   5.885 +        $automatic = $self->{po}{$msgid}{'automatic'};
   5.886 +
   5.887 +        # DO NOT CHANGE THE ORDER
   5.888 +        $res->push_raw('msgid' => $msgid,
   5.889 +                       'msgstr' => $msgstr,
   5.890 +                       'flags' => $flags,
   5.891 +                       'type'  => $type,
   5.892 +                       'reference' => $ref,
   5.893 +                       'comment' => $comment,
   5.894 +                       'automatic' => $automatic)
   5.895 +               if (apply($msgid,$msgstr,$ref,$flags,$comment,$automatic));
   5.896 +    }
   5.897 +    # delete the apply subroutine
   5.898 +    # otherwise it will be redefined.
   5.899 +    undef &apply;
   5.900 +    return $res;
   5.901 +}
   5.902 +
   5.903 +=item to_utf8()
   5.904 +
   5.905 +Recodes to utf-8 the po's msgstrs. Does nothing if the charset is not
   5.906 +specified in the po file ("CHARSET" value), or if it's already utf-8 or
   5.907 +ascii.
   5.908 +
   5.909 +=cut
   5.910 +
   5.911 +sub to_utf8 {
   5.912 +    my $this = shift;
   5.913 +    my $charset = $this->get_charset();
   5.914 +
   5.915 +    unless ($charset eq "CHARSET" or
   5.916 +            $charset =~ /^ascii$/i or
   5.917 +            $charset =~ /^utf-8$/i) {
   5.918 +        foreach my $msgid ( keys %{$this->{po}} ) {
   5.919 +            Encode::from_to($this->{po}{$msgid}{'msgstr'}, $charset, "utf-8");
   5.920 +        }
   5.921 +        $this->set_charset("utf-8");
   5.922 +    }
   5.923 +}
   5.924 +
   5.925 +=back
   5.926 +
   5.927 +=head1 Functions to use a message catalog for translations
   5.928 +
   5.929 +=over 4
   5.930 +
   5.931 +=item gettext($%)
   5.932 +
   5.933 +Request the translation of the string given as argument in the current catalog.
   5.934 +The function returns the original (untranslated) string if the string was not
   5.935 +found.
   5.936 +
   5.937 +After the string to translate, you can pass a hash of extra
   5.938 +arguments. Here are the valid entries:
   5.939 +
   5.940 +=over
   5.941 +
   5.942 +=item wrap
   5.943 +
   5.944 +boolean indicating whether we can consider that whitespaces in string are
   5.945 +not important. If yes, the function canonizes the string before looking for
   5.946 +a translation, and wraps the result.
   5.947 +
   5.948 +=item wrapcol
   5.949 +
   5.950 +The column at which we should wrap (default: 76).
   5.951 +
   5.952 +=back
   5.953 +
   5.954 +=cut
   5.955 +
   5.956 +sub gettext {
   5.957 +    my $self=shift;
   5.958 +    my $text=shift;
   5.959 +    my (%opt)=@_;
   5.960 +    my $res;
   5.961 +
   5.962 +    return "" unless defined($text) && length($text); # Avoid returning the header.
   5.963 +    my $validoption="reference wrap wrapcol";
   5.964 +    my %validoption;
   5.965 +
   5.966 +    map { $validoption{$_}=1 } (split(/ /,$validoption));
   5.967 +    foreach (keys %opt) {
   5.968 +        Carp::confess "internal error:  unknown arg $_.\n".
   5.969 +                      "Here are the valid options: $validoption.\n"
   5.970 +            unless $validoption{$_};
   5.971 +    }
   5.972 +
   5.973 +    $text=canonize($text)
   5.974 +        if ($opt{'wrap'});
   5.975 +
   5.976 +    my $esc_text=escape_text($text);
   5.977 +
   5.978 +    $self->{gettextqueries}++;
   5.979 +
   5.980 +    if (    defined $self->{po}{$esc_text}
   5.981 +        and defined $self->{po}{$esc_text}{'msgstr'}
   5.982 +        and length $self->{po}{$esc_text}{'msgstr'}
   5.983 +        and (   not defined $self->{po}{$esc_text}{'flags'}
   5.984 +             or $self->{po}{$esc_text}{'flags'} !~ /fuzzy/)) {
   5.985 +
   5.986 +        $self->{gettexthits}++;
   5.987 +        $res = unescape_text($self->{po}{$esc_text}{'msgstr'});
   5.988 +        if (defined $self->{po}{$esc_text}{'plural'}) {
   5.989 +            if ($self->{po}{$esc_text}{'plural'} eq "0") {
   5.990 +                warn wrap_mod("po4a gettextize", dgettext("po4a",
   5.991 +                              "'%s' is the singular form of a message, ".
   5.992 +                              "po4a will use the msgstr[0] translation (%s)."),
   5.993 +                              $esc_text, $res);
   5.994 +            } else {
   5.995 +                warn wrap_mod("po4a gettextize", dgettext("po4a",
   5.996 +                              "'%s' is the plural form of a message, ".
   5.997 +                              "po4a will use the msgstr[1] translation (%s)."),
   5.998 +                              $esc_text, $res);
   5.999 +            }
  5.1000 +        }
  5.1001 +    } else {
  5.1002 +        $res = $text;
  5.1003 +    }
  5.1004 +
  5.1005 +    if ($opt{'wrap'}) {
  5.1006 +        if ($self->get_charset =~ /^utf-8$/i) {
  5.1007 +            $res=Encode::decode_utf8($res);
  5.1008 +            $res=wrap ($res, $opt{'wrapcol'} || 76);
  5.1009 +            $res=Encode::encode_utf8($res);
  5.1010 +        } else {
  5.1011 +            $res=wrap ($res, $opt{'wrapcol'} || 76);
  5.1012 +        }
  5.1013 +    }
  5.1014 +#    print STDERR "Gettext >>>$text<<<(escaped=$esc_text)=[[[$res]]]\n\n";
  5.1015 +    return $res;
  5.1016 +}
  5.1017 +
  5.1018 +=item stats_get()
  5.1019 +
  5.1020 +Returns statistics about the hit ratio of gettext since the last time that
  5.1021 +stats_clear() was called. Please note that it's not the same
  5.1022 +statistics than the one printed by msgfmt --statistic. Here, it's statistics
  5.1023 +about recent usage of the po file, while msgfmt reports the status of the
  5.1024 +file.  Example of use:
  5.1025 +
  5.1026 +    [some use of the po file to translate stuff]
  5.1027 +
  5.1028 +    ($percent,$hit,$queries) = $pofile->stats_get();
  5.1029 +    print "So far, we found translations for $percent\%  ($hit of $queries) of strings.\n";
  5.1030 +
  5.1031 +=cut
  5.1032 +
  5.1033 +sub stats_get() {
  5.1034 +    my $self=shift;
  5.1035 +    my ($h,$q)=($self->{gettexthits},$self->{gettextqueries});
  5.1036 +    my $p = ($q == 0 ? 100 : int($h/$q*10000)/100);
  5.1037 +
  5.1038 +#    $p =~ s/\.00//;
  5.1039 +#    $p =~ s/(\..)0/$1/;
  5.1040 +
  5.1041 +    return ( $p,$h,$q );
  5.1042 +}
  5.1043 +
  5.1044 +=item stats_clear()
  5.1045 +
  5.1046 +Clears the statistics about gettext hits.
  5.1047 +
  5.1048 +=cut
  5.1049 +
  5.1050 +sub stats_clear {
  5.1051 +    my $self = shift;
  5.1052 +    $self->{gettextqueries} = 0;
  5.1053 +    $self->{gettexthits} = 0;
  5.1054 +}
  5.1055 +
  5.1056 +=back
  5.1057 +
  5.1058 +=head1 Functions to build a message catalog
  5.1059 +
  5.1060 +=over 4
  5.1061 +
  5.1062 +=item push(%)
  5.1063 +
  5.1064 +Push a new entry at the end of the current catalog. The arguments should
  5.1065 +form a hash table. The valid keys are:
  5.1066 +
  5.1067 +=over 4
  5.1068 +
  5.1069 +=item msgid
  5.1070 +
  5.1071 +the string in original language.
  5.1072 +
  5.1073 +=item msgstr
  5.1074 +
  5.1075 +the translation.
  5.1076 +
  5.1077 +=item reference
  5.1078 +
  5.1079 +an indication of where this string was found. Example: file.c:46 (meaning
  5.1080 +in 'file.c' at line 46). It can be a space-separated list in case of
  5.1081 +multiple occurrences.
  5.1082 +
  5.1083 +=item comment
  5.1084 +
  5.1085 +a comment added here manually (by the translators). The format here is free.
  5.1086 +
  5.1087 +=item automatic
  5.1088 +
  5.1089 +a comment which was automatically added by the string extraction
  5.1090 +program. See the I<--add-comments> option of the B<xgettext> program for
  5.1091 +more information.
  5.1092 +
  5.1093 +=item flags
  5.1094 +
  5.1095 +space-separated list of all defined flags for this entry.
  5.1096 +
  5.1097 +Valid flags are: c-text, python-text, lisp-text, elisp-text, librep-text,
  5.1098 +smalltalk-text, java-text, awk-text, object-pascal-text, ycp-text,
  5.1099 +tcl-text, wrap, no-wrap and fuzzy.
  5.1100 +
  5.1101 +See the gettext documentation for their meaning.
  5.1102 +
  5.1103 +=item type
  5.1104 +
  5.1105 +This is mostly an internal argument: it is used while gettextizing
  5.1106 +documents. The idea here is to parse both the original and the translation
  5.1107 +into a po object, and merge them, using one's msgid as msgid and the
  5.1108 +other's msgid as msgstr. To make sure that things get ok, each msgid in po
  5.1109 +objects are given a type, based on their structure (like "chapt", "sect1",
  5.1110 +"p" and so on in docbook). If the types of strings are not the same, that
  5.1111 +means that both files do not share the same structure, and the process
  5.1112 +reports an error.
  5.1113 +
  5.1114 +This information is written as automatic comment in the po file since this
  5.1115 +gives to translators some context about the strings to translate.
  5.1116 +
  5.1117 +=item wrap
  5.1118 +
  5.1119 +boolean indicating whether whitespaces can be mangled in cosmetic
  5.1120 +reformattings. If true, the string is canonized before use.
  5.1121 +
  5.1122 +This information is written to the po file using the 'wrap' or 'no-wrap' flag.
  5.1123 +
  5.1124 +=item wrapcol
  5.1125 +
  5.1126 +The column at which we should wrap (default: 76).
  5.1127 +
  5.1128 +This information is not written to the po file.
  5.1129 +
  5.1130 +=back
  5.1131 +
  5.1132 +=cut
  5.1133 +
  5.1134 +sub push {
  5.1135 +    my $self=shift;
  5.1136 +    my %entry=@_;
  5.1137 +
  5.1138 +    my $validoption="wrap wrapcol type msgid msgstr automatic flags reference";
  5.1139 +    my %validoption;
  5.1140 +
  5.1141 +    map { $validoption{$_}=1 } (split(/ /,$validoption));
  5.1142 +    foreach (keys %entry) {
  5.1143 +        Carp::confess "internal error:  unknown arg $_.\n".
  5.1144 +                      "Here are the valid options: $validoption.\n"
  5.1145 +            unless $validoption{$_};
  5.1146 +    }
  5.1147 +
  5.1148 +    unless ($entry{'wrap'}) {
  5.1149 +        $entry{'flags'} .= " no-wrap";
  5.1150 +    }
  5.1151 +    if (defined ($entry{'msgid'})) {
  5.1152 +        $entry{'msgid'} = canonize($entry{'msgid'})
  5.1153 +            if ($entry{'wrap'});
  5.1154 +
  5.1155 +        $entry{'msgid'} = escape_text($entry{'msgid'});
  5.1156 +    }
  5.1157 +    if (defined ($entry{'msgstr'})) {
  5.1158 +        $entry{'msgstr'} = canonize($entry{'msgstr'})
  5.1159 +            if ($entry{'wrap'});
  5.1160 +
  5.1161 +        $entry{'msgstr'} = escape_text($entry{'msgstr'});
  5.1162 +    }
  5.1163 +
  5.1164 +    $self->push_raw(%entry);
  5.1165 +}
  5.1166 +
  5.1167 +# The same as push(), but assuming that msgid and msgstr are already escaped
  5.1168 +sub push_raw {
  5.1169 +    my $self=shift;
  5.1170 +    my %entry=@_;
  5.1171 +    my ($msgid,$msgstr,$reference,$comment,$automatic,$flags,$type,$transref)=
  5.1172 +        ($entry{'msgid'},$entry{'msgstr'},
  5.1173 +         $entry{'reference'},$entry{'comment'},$entry{'automatic'},
  5.1174 +         $entry{'flags'},$entry{'type'},$entry{'transref'});
  5.1175 +    my $keep_conflict = $entry{'conflict'};
  5.1176 +
  5.1177 +#    print STDERR "Push_raw\n";
  5.1178 +#    print STDERR " msgid=>>>$msgid<<<\n" if $msgid;
  5.1179 +#    print STDERR " msgstr=[[[$msgstr]]]\n" if $msgstr;
  5.1180 +#    Carp::cluck " flags=$flags\n" if $flags;
  5.1181 +
  5.1182 +    return unless defined($entry{'msgid'});
  5.1183 +
  5.1184 +    #no msgid => header definition
  5.1185 +    unless (length($entry{'msgid'})) {
  5.1186 +#       if (defined($self->{header}) && $self->{header} =~ /\S/) {
  5.1187 +#           warn dgettext("po4a","Redefinition of the header. ".
  5.1188 +#                                "The old one will be discarded\n");
  5.1189 +#       } FIXME: do that iff the header isn't the default one.
  5.1190 +        $self->{header}=$msgstr;
  5.1191 +        $self->{header_comment}=$comment;
  5.1192 +        my $charset = $self->get_charset;
  5.1193 +        if ($charset ne "CHARSET") {
  5.1194 +            $self->{encoder}=find_encoding($charset);
  5.1195 +        } else {
  5.1196 +            $self->{encoder}=find_encoding("ascii");
  5.1197 +        }
  5.1198 +        return;
  5.1199 +    }
  5.1200 +
  5.1201 +    if ($self->{options}{'porefs'} eq "none") {
  5.1202 +        $reference = "";
  5.1203 +    } elsif ($self->{options}{'porefs'} eq "noline") {
  5.1204 +        $reference =~ s/:[0-9]*/:1/g;
  5.1205 +    }
  5.1206 +
  5.1207 +    if (defined($self->{po}{$msgid})) {
  5.1208 +        warn wrap_mod("po4a::po",
  5.1209 +                      dgettext("po4a","msgid defined twice: %s"),
  5.1210 +                      $msgid)
  5.1211 +            if (0); # FIXME: put a verbose stuff
  5.1212 +        if (    defined $msgstr
  5.1213 +            and defined $self->{po}{$msgid}{'msgstr'}
  5.1214 +            and $self->{po}{$msgid}{'msgstr'} ne $msgstr) {
  5.1215 +            my $txt=quote_text($msgid);
  5.1216 +            my ($first,$second)=
  5.1217 +                (format_comment(". ",$self->{po}{$msgid}{'reference'}).
  5.1218 +                 quote_text($self->{po}{$msgid}{'msgstr'}),
  5.1219 +
  5.1220 +                 format_comment(". ",$reference).
  5.1221 +                 quote_text($msgstr));
  5.1222 +
  5.1223 +            if ($keep_conflict) {
  5.1224 +                if ($self->{po}{$msgid}{'msgstr'} =~ m/^#-#-#-#-#  .*  #-#-#-#-#\\n/s) {
  5.1225 +                    $msgstr = $self->{po}{$msgid}{'msgstr'}.
  5.1226 +                              "\\n#-#-#-#-#  $transref  #-#-#-#-#\\n".
  5.1227 +                              $msgstr;
  5.1228 +                } else {
  5.1229 +                    $msgstr = "#-#-#-#-#  ".
  5.1230 +                              $self->{po}{$msgid}{'transref'}.
  5.1231 +                              "  #-#-#-#-#\\n".
  5.1232 +                              $self->{po}{$msgid}{'msgstr'}."\\n".
  5.1233 +                              "#-#-#-#-#  $transref  #-#-#-#-#\\n".
  5.1234 +                              $msgstr;
  5.1235 +                }
  5.1236 +                # Every msgid will have the same list of references.
  5.1237 +                # Only keep the last list.
  5.1238 +                $self->{po}{$msgid}{'reference'} = "";
  5.1239 +            } else {
  5.1240 +            warn wrap_msg(dgettext("po4a",
  5.1241 +                                   "Translations don't match for:\n".
  5.1242 +                                   "%s\n".
  5.1243 +                                   "-->First translation:\n".
  5.1244 +                                   "%s\n".
  5.1245 +                                   " Second translation:\n".
  5.1246 +                                   "%s\n".
  5.1247 +                                   " Old translation discarded."),
  5.1248 +                          $txt,$first,$second);
  5.1249 +            }
  5.1250 +        }
  5.1251 +    }
  5.1252 +    if (defined $transref) {
  5.1253 +        $self->{po}{$msgid}{'transref'} = $transref;
  5.1254 +    }
  5.1255 +    if (defined $reference) {
  5.1256 +        if (defined $self->{po}{$msgid}{'reference'}) {
  5.1257 +            $self->{po}{$msgid}{'reference'} .= " ".$reference;
  5.1258 +        } else {
  5.1259 +            $self->{po}{$msgid}{'reference'} = $reference;
  5.1260 +        }
  5.1261 +    }
  5.1262 +    $self->{po}{$msgid}{'msgstr'} = $msgstr;
  5.1263 +    $self->{po}{$msgid}{'comment'} = $comment;
  5.1264 +    $self->{po}{$msgid}{'automatic'} = $automatic;
  5.1265 +    if (defined($self->{po}{$msgid}{'pos_doc'})) {
  5.1266 +        $self->{po}{$msgid}{'pos_doc'} .= " ".$self->{count_doc}++;
  5.1267 +    } else {
  5.1268 +        $self->{po}{$msgid}{'pos_doc'}  = $self->{count_doc}++;
  5.1269 +    }
  5.1270 +    unless (defined($self->{po}{$msgid}{'pos'})) {
  5.1271 +        $self->{po}{$msgid}{'pos'} = $self->{count}++;
  5.1272 +    }
  5.1273 +    $self->{po}{$msgid}{'type'} = $type;
  5.1274 +    $self->{po}{$msgid}{'plural'} = $entry{'plural'}
  5.1275 +        if defined $entry{'plural'};
  5.1276 +
  5.1277 +    if (defined($flags)) {
  5.1278 +        $flags = " $flags ";
  5.1279 +        $flags =~ s/,/ /g;
  5.1280 +        foreach my $flag (@known_flags) {
  5.1281 +            if ($flags =~ /\s$flag\s/) { # if flag to be set
  5.1282 +                unless (   defined($self->{po}{$msgid}{'flags'})
  5.1283 +                        && $self->{po}{$msgid}{'flags'} =~ /\b$flag\b/) {
  5.1284 +                    # flag not already set
  5.1285 +                    if (defined $self->{po}{$msgid}{'flags'}) {
  5.1286 +                        $self->{po}{$msgid}{'flags'} .= " ".$flag;
  5.1287 +                    } else {
  5.1288 +                        $self->{po}{$msgid}{'flags'} = $flag;
  5.1289 +                    }
  5.1290 +                }
  5.1291 +            }
  5.1292 +        }
  5.1293 +    }
  5.1294 +#    print STDERR "stored ((($msgid)))=>(((".$self->{po}{$msgid}{'msgstr'}.")))\n\n";
  5.1295 +
  5.1296 +}
  5.1297 +
  5.1298 +=back
  5.1299 +
  5.1300 +=head1 Miscellaneous functions
  5.1301 +
  5.1302 +=over 4
  5.1303 +
  5.1304 +=item count_entries()
  5.1305 +
  5.1306 +Returns the number of entries in the catalog (without the header).
  5.1307 +
  5.1308 +=cut
  5.1309 +
  5.1310 +sub count_entries($) {
  5.1311 +    my $self=shift;
  5.1312 +    return $self->{count};
  5.1313 +}
  5.1314 +
  5.1315 +=item count_entries_doc()
  5.1316 +
  5.1317 +Returns the number of entries in document. If a string appears multiple times
  5.1318 +in the document, it will be counted multiple times
  5.1319 +
  5.1320 +=cut
  5.1321 +
  5.1322 +sub count_entries_doc($) {
  5.1323 +    my $self=shift;
  5.1324 +    return $self->{count_doc};
  5.1325 +}
  5.1326 +
  5.1327 +=item msgid($)
  5.1328 +
  5.1329 +Returns the msgid of the given number.
  5.1330 +
  5.1331 +=cut
  5.1332 +
  5.1333 +sub msgid($$) {
  5.1334 +    my $self=shift;
  5.1335 +    my $num=shift;
  5.1336 +
  5.1337 +    foreach my $msgid ( keys %{$self->{po}} ) {
  5.1338 +        return $msgid if ($self->{po}{$msgid}{'pos'} eq $num);
  5.1339 +    }
  5.1340 +    return undef;
  5.1341 +}
  5.1342 +
  5.1343 +=item msgid_doc($)
  5.1344 +
  5.1345 +Returns the msgid with the given position in the document.
  5.1346 +
  5.1347 +=cut
  5.1348 +
  5.1349 +sub msgid_doc($$) {
  5.1350 +    my $self=shift;
  5.1351 +    my $num=shift;
  5.1352 +
  5.1353 +    foreach my $msgid ( keys %{$self->{po}} ) {
  5.1354 +        foreach my $pos (split / /, $self->{po}{$msgid}{'pos_doc'}) {
  5.1355 +            return $msgid if ($pos eq $num);
  5.1356 +        }
  5.1357 +    }
  5.1358 +    return undef;
  5.1359 +}
  5.1360 +
  5.1361 +=item get_charset()
  5.1362 +
  5.1363 +Returns the character set specified in the po header. If it hasn't been
  5.1364 +set, it will return "CHARSET".
  5.1365 +
  5.1366 +=cut
  5.1367 +
  5.1368 +sub get_charset() {
  5.1369 +    my $self=shift;
  5.1370 +
  5.1371 +    $self->{header} =~ /charset=(.*?)[\s\\]/;
  5.1372 +
  5.1373 +    if (defined $1) {
  5.1374 +        return $1;
  5.1375 +    } else {
  5.1376 +        return "CHARSET";
  5.1377 +    }
  5.1378 +}
  5.1379 +
  5.1380 +=item set_charset($)
  5.1381 +
  5.1382 +This sets the character set of the po header to the value specified in its
  5.1383 +first argument. If you never call this function (and no file with a specified
  5.1384 +character set is read), the default value is left to "CHARSET". This value
  5.1385 +doesn't change the behavior of this module, it's just used to fill that field
  5.1386 +in the header, and to return it in get_charset().
  5.1387 +
  5.1388 +=cut
  5.1389 +
  5.1390 +sub set_charset() {
  5.1391 +    my $self=shift;
  5.1392 +
  5.1393 +    my ($newchar,$oldchar);
  5.1394 +    $newchar = shift;
  5.1395 +    $oldchar = $self->get_charset();
  5.1396 +
  5.1397 +    $self->{header} =~ s/$oldchar/$newchar/;
  5.1398 +    $self->{encoder}=find_encoding($newchar);
  5.1399 +}
  5.1400 +
  5.1401 +#----[ helper functions ]---------------------------------------------------
  5.1402 +
  5.1403 +# transforme the string from its po file representation to the form which
  5.1404 +#   should be used to print it
  5.1405 +sub unescape_text {
  5.1406 +    my $text = shift;
  5.1407 +
  5.1408 +    print STDERR "\nunescape [$text]====" if $debug{'escape'};
  5.1409 +    $text = join("",split(/\n/,$text));
  5.1410 +    $text =~ s/\\"/"/g;
  5.1411 +    # unescape newlines
  5.1412 +    #   NOTE on \G:
  5.1413 +    #   The following regular expression introduce newlines.
  5.1414 +    #   Thus, ^ doesn't match all beginnings of lines.
  5.1415 +    #   \G is a zero-width assertion that matches the position
  5.1416 +    #   of the previous substitution with s///g. As every
  5.1417 +    #   substitution ends by a newline, it always matches a
  5.1418 +    #   position just after a newline.
  5.1419 +    $text =~ s/(           # $1:
  5.1420 +                (\G|[^\\]) #    beginning of the line or any char
  5.1421 +                           #    different from '\'
  5.1422 +                (\\\\)*    #    followed by any even number of '\'
  5.1423 +               )\\n        # and followed by an escaped newline
  5.1424 +              /$1\n/sgx;   # single string, match globally, allow comments
  5.1425 +    # unescape tabulations
  5.1426 +    $text =~ s/(          # $1:
  5.1427 +                (\G|[^\\])#    beginning of the line or any char
  5.1428 +                          #    different from '\'
  5.1429 +                (\\\\)*   #    followed by any even number of '\'
  5.1430 +               )\\t       # and followed by an escaped tabulation
  5.1431 +              /$1\t/mgx;  # multilines string, match globally, allow comments
  5.1432 +    # and unescape the escape character
  5.1433 +    $text =~ s/\\\\/\\/g;
  5.1434 +    print STDERR ">$text<\n" if $debug{'escape'};
  5.1435 +
  5.1436 +    return $text;
  5.1437 +}
  5.1438 +
  5.1439 +# transform the string to its representation as it should be written in po
  5.1440 +# files
  5.1441 +sub escape_text {
  5.1442 +    my $text = shift;
  5.1443 +
  5.1444 +    print STDERR "\nescape [$text]====" if $debug{'escape'};
  5.1445 +    $text =~ s/\\/\\\\/g;
  5.1446 +    $text =~ s/"/\\"/g;
  5.1447 +    $text =~ s/\n/\\n/g;
  5.1448 +    $text =~ s/\t/\\t/g;
  5.1449 +    print STDERR ">$text<\n" if $debug{'escape'};
  5.1450 +
  5.1451 +    return $text;
  5.1452 +}
  5.1453 +
  5.1454 +# put quotes around the string on each lines (without escaping it)
  5.1455 +# It does also normalize the text (ie, make sure its representation is wraped
  5.1456 +#   on the 80th char, but without changing the meaning of the string)
  5.1457 +sub quote_text {
  5.1458 +    my $string = shift;
  5.1459 +
  5.1460 +    return '""' unless defined($string) && length($string);
  5.1461 +
  5.1462 +    print STDERR "\nquote [$string]====" if $debug{'quote'};
  5.1463 +    # break lines on newlines, if any
  5.1464 +    # see unescape_text for an explanation on \G
  5.1465 +    $string =~ s/(           # $1:
  5.1466 +                  (\G|[^\\]) #    beginning of the line or any char
  5.1467 +                             #    different from '\'
  5.1468 +                  (\\\\)*    #    followed by any even number of '\'
  5.1469 +                 \\n)        # and followed by an escaped newline
  5.1470 +                /$1\n/sgx;   # single string, match globally, allow comments
  5.1471 +    $string = wrap($string);
  5.1472 +    my @string = split(/\n/,$string);
  5.1473 +    $string = join ("\"\n\"",@string);
  5.1474 +    $string = "\"$string\"";
  5.1475 +    if (scalar @string > 1 && $string[0] ne '') {
  5.1476 +        $string = "\"\"\n".$string;
  5.1477 +    }
  5.1478 +
  5.1479 +    print STDERR ">$string<\n" if $debug{'quote'};
  5.1480 +    return $string;
  5.1481 +}
  5.1482 +
  5.1483 +# undo the work of the quote_text function
  5.1484 +sub unquote_text {
  5.1485 +    my $string = shift;
  5.1486 +    print STDERR "\nunquote [$string]====" if $debug{'quote'};
  5.1487 +    $string =~ s/^""\\n//s;
  5.1488 +    $string =~ s/^"(.*)"$/$1/s;
  5.1489 +    $string =~ s/"\n"//gm;
  5.1490 +    # Note: an even number of '\' could precede \\n, but I could not build a
  5.1491 +    # document to test this
  5.1492 +    $string =~ s/([^\\])\\n\n/$1!!DUMMYPOPM!!/gm;
  5.1493 +    $string =~ s|!!DUMMYPOPM!!|\\n|gm;
  5.1494 +    print STDERR ">$string<\n" if $debug{'quote'};
  5.1495 +    return $string;
  5.1496 +}
  5.1497 +
  5.1498 +# canonize the string: write it on only one line, changing consecutive
  5.1499 +# whitespace to only one space.
  5.1500 +# Warning, it changes the string and should only be called if the string is
  5.1501 +# plain text
  5.1502 +sub canonize {
  5.1503 +    my $text=shift;
  5.1504 +    print STDERR "\ncanonize [$text]====" if $debug{'canonize'};
  5.1505 +    $text =~ s/^ *//s;
  5.1506 +    $text =~ s/^[ \t]+/  /gm;
  5.1507 +    # if ($text eq "\n"), it messed up the first string (header)
  5.1508 +    $text =~ s/\n/  /gm if ($text ne "\n");
  5.1509 +    $text =~ s/([.)])  +/$1  /gm;
  5.1510 +    $text =~ s/([^.)])  */$1 /gm;
  5.1511 +    $text =~ s/ *$//s;
  5.1512 +    print STDERR ">$text<\n" if $debug{'canonize'};
  5.1513 +    return $text;
  5.1514 +}
  5.1515 +
  5.1516 +# wraps the string. We don't use Text::Wrap since it mangles whitespace at
  5.1517 +# the end of splited line
  5.1518 +sub wrap {
  5.1519 +    my $text=shift;
  5.1520 +    return "0" if ($text eq '0');
  5.1521 +    my $col=shift || 76;
  5.1522 +    my @lines=split(/\n/,"$text");
  5.1523 +    my $res="";
  5.1524 +    my $first=1;
  5.1525 +    while (defined(my $line=shift @lines)) {
  5.1526 +        if ($first && length($line) > $col - 10) {
  5.1527 +            unshift @lines,$line;
  5.1528 +            $first=0;
  5.1529 +            next;
  5.1530 +        }
  5.1531 +        if (length($line) > $col) {
  5.1532 +            my $pos=rindex($line," ",$col);
  5.1533 +            while (substr($line,$pos-1,1) eq '.' && $pos != -1) {
  5.1534 +                $pos=rindex($line," ",$pos-1);
  5.1535 +            }
  5.1536 +            if ($pos == -1) {
  5.1537 +                # There are no spaces in the first $col chars, pick-up the
  5.1538 +                # first space
  5.1539 +                $pos = index($line," ");
  5.1540 +            }
  5.1541 +            if ($pos != -1) {
  5.1542 +                my $end=substr($line,$pos+1);
  5.1543 +                $line=substr($line,0,$pos+1);
  5.1544 +                if ($end =~ s/^( +)//) {
  5.1545 +                    $line .= $1;
  5.1546 +                }
  5.1547 +                unshift @lines,$end;
  5.1548 +            }
  5.1549 +        }
  5.1550 +        $first=0;
  5.1551 +        $res.="$line\n";
  5.1552 +    }
  5.1553 +    # Restore the original trailing spaces
  5.1554 +    $res =~ s/\s+$//s;
  5.1555 +    if ($text =~ m/(\s+)$/s) {
  5.1556 +        $res .= $1;
  5.1557 +    }
  5.1558 +    return $res;
  5.1559 +}
  5.1560 +
  5.1561 +# outputs properly a '# ... ' line to be put in the po file
  5.1562 +sub format_comment {
  5.1563 +    my $comment=shift;
  5.1564 +    my $char=shift;
  5.1565 +    my $result = "#". $char . $comment;
  5.1566 +    $result =~ s/\n/\n#$char/gs;
  5.1567 +    $result =~ s/^#$char$/#/gm;
  5.1568 +    $result .= "\n";
  5.1569 +    return $result;
  5.1570 +}
  5.1571 +
  5.1572 +
  5.1573 +1;
  5.1574 +__END__
  5.1575 +
  5.1576 +=back
  5.1577 +
  5.1578 +=head1 AUTHORS
  5.1579 +
  5.1580 + Denis Barbier <barbier@linuxfr.org>
  5.1581 + Martin Quinson (mquinson#debian.org)
  5.1582 +
  5.1583 +=cut
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/tools/po4a/lib/Locale/Po4a/TransTractor.pm	Thu Mar 12 15:43:56 2009 +0800
     6.3 @@ -0,0 +1,1100 @@
     6.4 +#!/usr/bin/perl -w
     6.5 +
     6.6 +require Exporter;
     6.7 +
     6.8 +package Locale::Po4a::TransTractor;
     6.9 +use DynaLoader;
    6.10 +
    6.11 +use 5.006;
    6.12 +use strict;
    6.13 +use warnings;
    6.14 +
    6.15 +use subs qw(makespace);
    6.16 +use vars qw($VERSION @ISA @EXPORT);
    6.17 +$VERSION="0.36";
    6.18 +@ISA = qw(DynaLoader);
    6.19 +@EXPORT = qw(new process translate 
    6.20 +             read write readpo writepo
    6.21 +             getpoout setpoout);
    6.22 +
    6.23 +# Try to use a C extension if present.
    6.24 +eval("bootstrap Locale::Po4a::TransTractor $VERSION");
    6.25 +
    6.26 +use Carp qw(croak);
    6.27 +use Locale::Po4a::Po;
    6.28 +use Locale::Po4a::Common;
    6.29 +
    6.30 +use File::Path; # mkdir before write
    6.31 +
    6.32 +use Encode;
    6.33 +use Encode::Guess;
    6.34 +
    6.35 +=head1 NAME
    6.36 +
    6.37 +Locale::Po4a::TransTractor - Generic trans(lator ex)tractor.
    6.38 +
    6.39 +=head1 DESCRIPTION
    6.40 +
    6.41 +The po4a (po for anything) project goal is to ease translations (and more
    6.42 +interestingly, the maintenance of translations) using gettext tools on
    6.43 +areas where they were not expected like documentation.
    6.44 +
    6.45 +This class is the ancestor of every po4a parsers used to parse a document to
    6.46 +search translatable strings, extract them to a po file and replace them by
    6.47 +their translation in the output document. 
    6.48 +
    6.49 +More formally, it takes the following arguments as input:
    6.50 +
    6.51 +=over 2
    6.52 +
    6.53 +=item -
    6.54 +
    6.55 +a document to translate ;
    6.56 +
    6.57 +=item -
    6.58 +
    6.59 +a po file containing the translations to use.
    6.60 +
    6.61 +=back
    6.62 +
    6.63 +As output, it produces:
    6.64 +
    6.65 +=over 2
    6.66 +
    6.67 +=item -
    6.68 +
    6.69 +another po file, resulting of the extraction of translatable strings from
    6.70 +the input document ;
    6.71 +
    6.72 +=item -
    6.73 +
    6.74 +a translated document, with the same structure than the one in input, but
    6.75 +with all translatable strings replaced with the translations found in the
    6.76 +po file provided in input.
    6.77 +
    6.78 +=back
    6.79 +
    6.80 +Here is a graphical representation of this:
    6.81 +
    6.82 +   Input document --\                             /---> Output document
    6.83 +                     \                           /       (translated)
    6.84 +                      +-> parse() function -----+
    6.85 +                     /                           \
    6.86 +   Input po --------/                             \---> Output po
    6.87 +                                                         (extracted)
    6.88 +
    6.89 +=head1 FUNCTIONS YOUR PARSER SHOULD OVERRIDE
    6.90 +
    6.91 +=over 4
    6.92 +
    6.93 +=item parse()
    6.94 +
    6.95 +This is where all the work takes place: the parsing of input documents, the
    6.96 +generation of output, and the extraction of the translatable strings. This
    6.97 +is pretty simple using the provided functions presented in the section
    6.98 +"INTERNAL FUNCTIONS" below. See also the synopsis, which present an
    6.99 +example.
   6.100 +
   6.101 +This function is called by the process() function bellow, but if you choose
   6.102 +to use the new() function, and to add content manually to your document,
   6.103 +you will have to call this function yourself.
   6.104 +
   6.105 +=item docheader()
   6.106 +
   6.107 +This function returns the header we should add to the produced document,
   6.108 +quoted properly to be a comment in the target language.  See the section
   6.109 +"Educating developers about translations", from L<po4a(7)|po4a.7>, for what
   6.110 +it is good for.
   6.111 +
   6.112 +=back
   6.113 +
   6.114 +=cut
   6.115 +
   6.116 +sub docheader {}
   6.117 +
   6.118 +sub parse {}
   6.119 +
   6.120 +=head1 SYNOPSIS
   6.121 +
   6.122 +The following example parses a list of paragraphs beginning with "<p>". For the sake
   6.123 +of simplicity, we assume that the document is well formatted, i.e. that '<p>'
   6.124 +tags are the only tags present, and that this tag is at the very beginning
   6.125 +of each paragraph.
   6.126 +
   6.127 + sub parse {
   6.128 +   my $self = shift;
   6.129 +
   6.130 +   PARAGRAPH: while (1) {
   6.131 +       my ($paragraph,$pararef)=("","");
   6.132 +       my $first=1;
   6.133 +       my ($line,$lref)=$self->shiftline();
   6.134 +       while (defined($line)) {
   6.135 +	   if ($line =~ m/<p>/ && !$first--; ) {
   6.136 +	       # Not the first time we see <p>. 
   6.137 +	       # Reput the current line in input,
   6.138 +	       #  and put the built paragraph to output
   6.139 +	       $self->unshiftline($line,$lref);
   6.140 +	      
   6.141 +	       # Now that the document is formed, translate it:
   6.142 +	       #   - Remove the leading tag
   6.143 +	       $paragraph =~ s/^<p>//s;
   6.144 +
   6.145 +	       #   - push to output the leading tag (untranslated) and the
   6.146 +	       #     rest of the paragraph (translated)
   6.147 +	       $self->pushline(  "<p>"
   6.148 +                               . $document->translate($paragraph,$pararef)
   6.149 +                               );
   6.150 +
   6.151 + 	       next PARAGRAPH;
   6.152 +	   } else {
   6.153 +	       # Append to the paragraph
   6.154 +	       $paragraph .= $line;
   6.155 +	       $pararef = $lref unless(length($pararef));
   6.156 +	   }
   6.157 +
   6.158 +           # Reinit the loop
   6.159 +           ($line,$lref)=$self->shiftline();
   6.160 +       }
   6.161 +       # Did not get a defined line? End of input file.
   6.162 +       return;
   6.163 +   }
   6.164 + } 
   6.165 +
   6.166 +Once you've implemented the parse function, you can use your document
   6.167 +class, using the public interface presented in the next section.
   6.168 +
   6.169 +=head1 PUBLIC INTERFACE for scripts using your parser
   6.170 +
   6.171 +=head2 Constructor
   6.172 +
   6.173 +=over 4
   6.174 +
   6.175 +=item process(%)
   6.176 +
   6.177 +This function can do all you need to do with a po4a document in one
   6.178 +invocation. Its arguments must be packed as a hash. ACTIONS:
   6.179 +
   6.180 +=over 3
   6.181 +
   6.182 +=item a.
   6.183 +
   6.184 +Reads all the po files specified in po_in_name
   6.185 +
   6.186 +=item b.
   6.187 +
   6.188 +Reads all original documents specified in file_in_name
   6.189 +
   6.190 +=item c.
   6.191 +
   6.192 +Parses the document
   6.193 +
   6.194 +=item d.
   6.195 +
   6.196 +Reads and applies all the addenda specified
   6.197 +
   6.198 +=item e.
   6.199 +
   6.200 +Writes the translated document to file_out_name (if given)
   6.201 +
   6.202 +=item f.
   6.203 +
   6.204 +Writes the extracted po file to po_out_name (if given)
   6.205 +
   6.206 +=back
   6.207 +
   6.208 +ARGUMENTS, beside the ones accepted by new() (with expected type):
   6.209 +
   6.210 +=over 4
   6.211 +
   6.212 +=item file_in_name (@)
   6.213 +
   6.214 +List of filenames where we should read the input document.
   6.215 +
   6.216 +=item file_in_charset ($)
   6.217 +
   6.218 +Charset used in the input document (if it isn't specified, it will try
   6.219 +to detect it from the input document).
   6.220 +
   6.221 +=item file_out_name ($)
   6.222 +
   6.223 +Filename where we should write the output document.
   6.224 +
   6.225 +=item file_out_charset ($)
   6.226 +
   6.227 +Charset used in the output document (if it isn't specified, it will use
   6.228 +the po file charset).
   6.229 +
   6.230 +=item po_in_name (@)
   6.231 +
   6.232 +List of filenames where we should read the input po files from, containing
   6.233 +the translation which will be used to translate the document.
   6.234 +
   6.235 +=item po_out_name ($)
   6.236 +
   6.237 +Filename where we should write the output po file, containing the strings
   6.238 +extracted from the input document.
   6.239 +
   6.240 +=item addendum (@)
   6.241 +
   6.242 +List of filenames where we should read the addenda from.
   6.243 +
   6.244 +=item addendum_charset ($)
   6.245 +
   6.246 +Charset for the addenda.
   6.247 +
   6.248 +=back
   6.249 +
   6.250 +=item new(%)
   6.251 +
   6.252 +Create a new Po4a document. Accepted options (but be in a hash):
   6.253 +
   6.254 +=over 4
   6.255 +
   6.256 +=item verbose ($)
   6.257 +
   6.258 +Sets the verbosity.
   6.259 +
   6.260 +=item debug ($)
   6.261 +
   6.262 +Sets the debugging.
   6.263 +
   6.264 +=back
   6.265 +
   6.266 +=cut
   6.267 +
   6.268 +sub process {
   6.269 +    ## Determine if we were called via an object-ref or a classname
   6.270 +    my $self = shift;
   6.271 +
   6.272 +    ## Any remaining arguments are treated as initial values for the
   6.273 +    ## hash that is used to represent this object.
   6.274 +    my %params = @_;
   6.275 +    
   6.276 +    # Build the args for new()
   6.277 +    my %newparams = ();
   6.278 +    foreach (keys %params) {
   6.279 +	next if ($_ eq 'po_in_name' ||
   6.280 +		 $_ eq 'po_out_name' ||
   6.281 +		 $_ eq 'file_in_name' ||
   6.282 +		 $_ eq 'file_in_charset' ||
   6.283 +		 $_ eq 'file_out_name' ||
   6.284 +		 $_ eq 'file_out_charset' ||
   6.285 +		 $_ eq 'addendum' ||
   6.286 +		 $_ eq 'addendum_charset');
   6.287 +	$newparams{$_}=$params{$_};
   6.288 +    }
   6.289 +
   6.290 +    $self->detected_charset($params{'file_in_charset'});
   6.291 +    $self->{TT}{'file_out_charset'}=$params{'file_out_charset'};
   6.292 +    if (defined($self->{TT}{'file_out_charset'}) and
   6.293 +	length($self->{TT}{'file_out_charset'})) {
   6.294 +	$self->{TT}{'file_out_encoder'} = find_encoding($self->{TT}{'file_out_charset'});
   6.295 +    }
   6.296 +    $self->{TT}{'addendum_charset'}=$params{'addendum_charset'};
   6.297 +
   6.298 +    foreach my $file (@{$params{'po_in_name'}}) {
   6.299 +	print STDERR "readpo($file)... " if $self->debug();
   6.300 +	$self->readpo($file);
   6.301 +	print STDERR "done.\n" if $self->debug()
   6.302 +    }
   6.303 +    foreach my $file (@{$params{'file_in_name'}}) {
   6.304 +	print STDERR "read($file)..." if $self->debug();
   6.305 +	$self->read($file);
   6.306 +	print STDERR "done.\n"  if $self->debug();
   6.307 +    }
   6.308 +    print STDERR "parse..." if $self->debug();
   6.309 +    $self->parse();
   6.310 +    print STDERR "done.\n" if $self->debug();
   6.311 +    foreach my $file (@{$params{'addendum'}}) {
   6.312 +	print STDERR "addendum($file)..." if $self->debug();
   6.313 +	$self->addendum($file) || die "An addendum failed\n";
   6.314 +	print STDERR "done.\n" if $self->debug();
   6.315 +    }
   6.316 +    if (defined $params{'file_out_name'}) {
   6.317 +	print STDERR "write(".$params{'file_out_name'}.")... " 
   6.318 +	    if $self->debug();
   6.319 +	$self->write($params{'file_out_name'});
   6.320 +	print STDERR "done.\n" if $self->debug();
   6.321 +    }
   6.322 +    if (defined $params{'po_out_name'}) {
   6.323 +	print STDERR "writepo(".$params{'po_out_name'}.")... "
   6.324 +	     if $self->debug();
   6.325 +	$self->writepo($params{'po_out_name'});
   6.326 +	print STDERR "done.\n" if $self->debug();
   6.327 +    }
   6.328 +    return $self;
   6.329 +}
   6.330 +
   6.331 +sub new {
   6.332 +    ## Determine if we were called via an object-ref or a classname
   6.333 +    my $this = shift;
   6.334 +    my $class = ref($this) || $this;
   6.335 +    my $self = { };
   6.336 +    my %options=@_;
   6.337 +    ## Bless ourselves into the desired class and perform any initialization
   6.338 +    bless $self, $class;
   6.339 +    
   6.340 +    ## initialize the plugin
   6.341 +    # prevent the plugin from croaking on the options intended for Po.pm
   6.342 +    $self->{options}{'porefs'} = '';
   6.343 +    # let the plugin parse the options and such
   6.344 +    $self->initialize(%options);
   6.345 +
   6.346 +    ## Create our private data
   6.347 +    my %po_options;
   6.348 +    $po_options{'porefs'} = $self->{options}{'porefs'};
   6.349 +    
   6.350 +    # private data
   6.351 +    $self->{TT}=(); 
   6.352 +    $self->{TT}{po_in}=Locale::Po4a::Po->new();
   6.353 +    $self->{TT}{po_out}=Locale::Po4a::Po->new(\%po_options);
   6.354 +    # Warning, this is an array of array:
   6.355 +    #  The document is splited on lines, and for each
   6.356 +    #  [0] is the line content, [1] is the reference [2] the type
   6.357 +    $self->{TT}{doc_in}=();
   6.358 +    $self->{TT}{doc_out}=();
   6.359 +    if (defined $options{'verbose'}) {
   6.360 +	$self->{TT}{verbose}  =  $options{'verbose'};
   6.361 +    }
   6.362 +    if (defined $options{'debug'}) {
   6.363 +	$self->{TT}{debug}  =  $options{'debug'};
   6.364 +    }
   6.365 +    # Input document is in ascii until we prove the opposite (in read())
   6.366 +    $self->{TT}{ascii_input}=1;
   6.367 +    # We try not to use utf unless it's forced from the outside (in case the
   6.368 +    # document isn't in ascii)
   6.369 +    $self->{TT}{utf_mode}=0;
   6.370 +
   6.371 +    
   6.372 +    return $self;
   6.373 +}
   6.374 +
   6.375 +=back
   6.376 +
   6.377 +=head2 Manipulating document files
   6.378 +
   6.379 +=over 4
   6.380 +
   6.381 +=item read($)
   6.382 +
   6.383 +Add another input document at the end of the existing one. The argument is
   6.384 +the filename to read. 
   6.385 +
   6.386 +Please note that it does not parse anything. You should use the parse()
   6.387 +function when you're done with packing input files into the document. 
   6.388 +
   6.389 +=cut
   6.390 +
   6.391 +#'
   6.392 +sub read() {
   6.393 +    my $self=shift;
   6.394 +    my $filename=shift
   6.395 +	or croak wrap_msg(dgettext("po4a", "Can't read from file without having a filename"));
   6.396 +    my $linenum=0;
   6.397 +
   6.398 +    open INPUT,"<$filename" 
   6.399 +	or croak wrap_msg(dgettext("po4a", "Can't read from %s: %s"), $filename, $!);
   6.400 +    while (defined (my $textline = <INPUT>)) {
   6.401 +	$linenum++;
   6.402 +	my $ref="$filename:$linenum";
   6.403 +	my @entry=($textline,$ref);
   6.404 +	push @{$self->{TT}{doc_in}}, @entry;
   6.405 +
   6.406 +	if (!defined($self->{TT}{'file_in_charset'})) {
   6.407 +	    # Detect if this file has non-ascii characters
   6.408 +	    if($self->{TT}{ascii_input}) {
   6.409 +		my $decoder = guess_encoding($textline);
   6.410 +		if (!ref($decoder) or $decoder !~ /Encode::XS=/) {
   6.411 +		    # We have detected a non-ascii line
   6.412 +		    $self->{TT}{ascii_input} = 0;
   6.413 +		    # Save the reference for future error message
   6.414 +		    $self->{TT}{non_ascii_ref} ||= $ref;
   6.415 +		}
   6.416 +	    }
   6.417 +	}
   6.418 +    }
   6.419 +    close INPUT 
   6.420 +	or croak wrap_msg(dgettext("po4a", "Can't close %s after reading: %s"), $filename, $!);
   6.421 +
   6.422 +}
   6.423 +
   6.424 +=item write($)
   6.425 +
   6.426 +Write the translated document to the given filename.
   6.427 +
   6.428 +=cut
   6.429 +
   6.430 +sub write {
   6.431 +    my $self=shift;
   6.432 +    my $filename=shift
   6.433 +	or croak wrap_msg(dgettext("po4a", "Can't write to a file without filename"));
   6.434 +
   6.435 +    my $fh;
   6.436 +    if ($filename eq '-') {
   6.437 +	$fh=\*STDOUT;
   6.438 +    } else {
   6.439 +	# make sure the directory in which we should write the localized file exists
   6.440 +	my $dir = $filename;
   6.441 +	if ($dir =~ m|/|) {
   6.442 +	    $dir =~ s|/[^/]*$||;
   6.443 +	
   6.444 +	    File::Path::mkpath($dir, 0, 0755) # Croaks on error
   6.445 +	      if (length ($dir) && ! -e $dir);
   6.446 +	}
   6.447 +	open $fh,">$filename"
   6.448 +	    or croak wrap_msg(dgettext("po4a", "Can't write to %s: %s"), $filename, $!);
   6.449 +    }
   6.450 +    
   6.451 +    map { print $fh $_ } $self->docheader();
   6.452 +    map { print $fh $_ } @{$self->{TT}{doc_out}};
   6.453 +
   6.454 +    if ($filename ne '-') {
   6.455 +	close $fh or croak wrap_msg(dgettext("po4a", "Can't close %s after writing: %s"), $filename, $!);
   6.456 +    }
   6.457 +
   6.458 +}
   6.459 +
   6.460 +=back
   6.461 +
   6.462 +=head2 Manipulating po files
   6.463 +
   6.464 +=over 4 
   6.465 +
   6.466 +=item readpo($)
   6.467 +
   6.468 +Add the content of a file (which name is passed in argument) to the
   6.469 +existing input po. The old content is not discarded.
   6.470 +
   6.471 +=item writepo($)
   6.472 +
   6.473 +Write the extracted po file to the given filename.
   6.474 +
   6.475 +=item stats()
   6.476 +
   6.477 +Returns some statistics about the translation done so far. Please note that
   6.478 +it's not the same statistics than the one printed by msgfmt
   6.479 +--statistic. Here, it's stats about recent usage of the po file, while
   6.480 +msgfmt reports the status of the file. It is a wrapper to the
   6.481 +Locale::Po4a::Po::stats_get function applied to the input po file. Example
   6.482 +of use:
   6.483 +
   6.484 +    [normal use of the po4a document...]
   6.485 +
   6.486 +    ($percent,$hit,$queries) = $document->stats();
   6.487 +    print "We found translations for $percent\%  ($hit from $queries) of strings.\n";
   6.488 +
   6.489 +=back
   6.490 +
   6.491 +=cut
   6.492 +
   6.493 +sub getpoout {
   6.494 +    return $_[0]->{TT}{po_out};
   6.495 +}
   6.496 +sub setpoout {
   6.497 +    $_[0]->{TT}{po_out} = $_[1];
   6.498 +}
   6.499 +sub readpo  { 
   6.500 +    $_[0]->{TT}{po_in}->read($_[1]);        
   6.501 +}
   6.502 +sub writepo { 
   6.503 +    $_[0]->{TT}{po_out}->write( $_[1] );    
   6.504 +}
   6.505 +sub stats   { 
   6.506 +    return $_[0]->{TT}{po_in}->stats_get(); 
   6.507 +}
   6.508 +
   6.509 +=head2 Manipulating addenda
   6.510 +
   6.511 +=over 4
   6.512 +
   6.513 +=item addendum($)
   6.514 +
   6.515 +Please refer to L<po4a(7)|po4a.7> for more information on what addenda are,
   6.516 +and how translators should write them. To apply an addendum to the translated
   6.517 +document, simply pass its filename to this function and you are done ;)
   6.518 +
   6.519 +This function returns a non-null integer on error.
   6.520 +
   6.521 +=cut
   6.522 +
   6.523 +# Internal function to read the header.
   6.524 +sub addendum_parse {
   6.525 +    my ($filename,$header)=shift;
   6.526 +
   6.527 +    my ($errcode,$mode,$position,$boundary,$bmode,$content)=
   6.528 +	(1,"","","","","");
   6.529 +
   6.530 +    unless (open (INS, "<$filename")) {
   6.531 +	warn wrap_msg(dgettext("po4a", "Can't read from %s: %s"), $filename, $!);
   6.532 +	goto END_PARSE_ADDFILE;
   6.533 +    } 
   6.534 +
   6.535 +    unless (defined ($header=<INS>) && $header)  {
   6.536 +	warn wrap_msg(dgettext("po4a", "Can't read Po4a header from %s."), $filename);
   6.537 +	goto END_PARSE_ADDFILE;
   6.538 +    }
   6.539 +
   6.540 +    unless ($header =~ s/PO4A-HEADER://i) {
   6.541 +	warn wrap_msg(dgettext("po4a", "First line of %s does not look like a Po4a header."), $filename);
   6.542 +	goto END_PARSE_ADDFILE;
   6.543 +    }
   6.544 +    foreach my $part (split(/;/,$header)) {
   6.545 +	unless ($part =~ m/^\s*([^=]*)=(.*)$/) {
   6.546 +	    warn wrap_msg(dgettext("po4a", "Syntax error in Po4a header of %s, near \"%s\""), $filename, $part);
   6.547 +	    goto END_PARSE_ADDFILE;
   6.548 +	}
   6.549 +	my ($key,$value)=($1,$2);
   6.550 +	$key=lc($key);
   6.551 +  	     if ($key eq 'mode')     {  $mode=lc($value);
   6.552 +	} elsif ($key eq 'position') {  $position=$value;
   6.553 +	} elsif ($key eq 'endboundary') {  
   6.554 +	    $boundary=$value;
   6.555 +	    $bmode='after';
   6.556 +	} elsif ($key eq 'beginboundary') {  
   6.557 +	    $boundary=$value;
   6.558 +	    $bmode='before';
   6.559 +	} else { 
   6.560 +	    warn wrap_msg(dgettext("po4a", "Invalid argument in the Po4a header of %s: %s"), $filename, $key);
   6.561 +	    goto END_PARSE_ADDFILE;
   6.562 +	}
   6.563 +    }
   6.564 +
   6.565 +    unless (length($mode)) {
   6.566 +	warn wrap_msg(dgettext("po4a", "The Po4a header of %s does not define the mode."), $filename);
   6.567 +	goto END_PARSE_ADDFILE;
   6.568 +    }
   6.569 +    unless ($mode eq "before" || $mode eq "after") {
   6.570 +	warn wrap_msg(dgettext("po4a", "Mode invalid in the Po4a header of %s: should be 'before' or 'after' not %s."), $filename, $mode);
   6.571 +	goto END_PARSE_ADDFILE;
   6.572 +    }
   6.573 +
   6.574 +    unless (length($position)) {
   6.575 +	warn wrap_msg(dgettext("po4a", "The Po4a header of %s does not define the position."), $filename);
   6.576 +	goto END_PARSE_ADDFILE;
   6.577 +    }
   6.578 +    unless ($mode eq "before" || length($boundary)) {
   6.579 +    	warn wrap_msg(dgettext("po4a", "No ending boundary given in the Po4a header, but mode=after."));
   6.580 +	goto END_PARSE_ADDFILE;
   6.581 +    }
   6.582 +
   6.583 +    while (defined(my $line = <INS>)) {
   6.584 +	$content .= $line;
   6.585 +    }
   6.586 +    close INS;
   6.587 +
   6.588 +    $errcode=0;
   6.589 +  END_PARSE_ADDFILE: 
   6.590 +      return ($errcode,$mode,$position,$boundary,$bmode,$content);
   6.591 +}
   6.592 +
   6.593 +sub mychomp {
   6.594 +    my ($str) = shift;
   6.595 +    chomp($str);
   6.596 +    return $str;
   6.597 +}
   6.598 +
   6.599 +sub addendum {
   6.600 +    my ($self,$filename) = @_;
   6.601 +
   6.602 +    print STDERR "Apply addendum $filename..." if $self->debug();
   6.603 +    unless ($filename) {
   6.604 +	warn wrap_msg(dgettext("po4a",
   6.605 +	    "Can't apply addendum when not given the filename"));
   6.606 +	return 0;
   6.607 +    }
   6.608 +    die wrap_msg(dgettext("po4a", "Addendum %s does not exist."), $filename)
   6.609 +      unless -e $filename;
   6.610 +  
   6.611 +    my ($errcode,$mode,$position,$boundary,$bmode,$content)=
   6.612 +	addendum_parse($filename);
   6.613 +    return 0 if ($errcode);
   6.614 +
   6.615 +    print STDERR "mode=$mode;pos=$position;bound=$boundary;bmode=$bmode;ctn=$content\n"
   6.616 +      if $self->debug();
   6.617 +    
   6.618 +    # We only recode the addendum if an origin charset is specified, else we
   6.619 +    # suppose it's already in the output document's charset
   6.620 +    if (defined($self->{TT}{'addendum_charset'}) &&
   6.621 +        length($self->{TT}{'addendum_charset'})) {
   6.622 +	Encode::from_to($content,$self->{TT}{'addendum_charset'},
   6.623 +	    $self->get_out_charset);
   6.624 +    }
   6.625 +
   6.626 +    my $found = scalar grep { /$position/ } @{$self->{TT}{doc_out}};
   6.627 +    if ($found == 0) {
   6.628 +	warn wrap_msg(dgettext("po4a",
   6.629 +	    "No candidate position for the addendum %s."), $filename);
   6.630 +	return 0;
   6.631 +    }
   6.632 +    if ($found > 1) {
   6.633 +	warn wrap_msg(dgettext("po4a",
   6.634 +	    "More than one candidate position found for the addendum %s."), $filename);
   6.635 +	return 0;
   6.636 +    }
   6.637 +
   6.638 +    if ($mode eq "before") {
   6.639 +	if ($self->verbose() > 1 || $self->debug() ) {
   6.640 +	    map { print STDERR wrap_msg(dgettext("po4a", "Addendum '%s' applied before this line: %s"), $filename, $_) if (/$position/);
   6.641 + 	        } @{$self->{TT}{doc_out}};
   6.642 +	}
   6.643 +	@{$self->{TT}{doc_out}} = map { /$position/ ? ($content,$_) : $_ 
   6.644 +                                        }  @{$self->{TT}{doc_out}};
   6.645 +    } else {
   6.646 +	my @newres=();
   6.647 +
   6.648 +	do {
   6.649 +	    # make sure it doesnt whine on empty document
   6.650 +	    my $line = scalar @{$self->{TT}{doc_out}} ? shift @{$self->{TT}{doc_out}} : "";
   6.651 +	    push @newres,$line;
   6.652 +	    my $outline=mychomp($line);
   6.653 +	    $outline =~ s/^[ \t]*//;
   6.654 +	      
   6.655 +	    if ($line =~ m/$position/) {
   6.656 +		while ($line=shift @{$self->{TT}{doc_out}}) {
   6.657 +		    last if ($line=~/$boundary/);
   6.658 +		    push @newres,$line;
   6.659 +		}
   6.660 +		if (defined $line) {
   6.661 +		    if ($bmode eq 'before') {
   6.662 +			print wrap_msg(dgettext("po4a",
   6.663 +			    "Addendum '%s' applied before this line: %s"),
   6.664 +			    $filename, $outline)
   6.665 +			  if ($self->verbose() > 1 || $self->debug());
   6.666 +			push @newres,$content;
   6.667 +			push @newres,$line;
   6.668 +		    } else {
   6.669 +			print wrap_msg(dgettext("po4a",
   6.670 +			    "Addendum '%s' applied after the line: %s."),
   6.671 +			    $filename, $outline)
   6.672 +			  if ($self->verbose() > 1 || $self->debug());
   6.673 +			push @newres,$line;
   6.674 +			push @newres,$content;
   6.675 +		    }
   6.676 +		} else {
   6.677 +		    print wrap_msg(dgettext("po4a", "Addendum '%s' applied at the end of the file."), $filename)
   6.678 +		      if ($self->verbose() > 1 || $self->debug());
   6.679 +		    push @newres,$content;
   6.680 +		}
   6.681 +	    }
   6.682 +	} while (scalar @{$self->{TT}{doc_out}});
   6.683 +	@{$self->{TT}{doc_out}} = @newres;
   6.684 +    }
   6.685 +    print STDERR "done.\n" if $self->debug();
   6.686 +    return 1;
   6.687 +}
   6.688 +
   6.689 +=back
   6.690 +
   6.691 +=head1 INTERNAL FUNCTIONS used to write derivated parsers
   6.692 +
   6.693 +=head2 Getting input, providing output
   6.694 +
   6.695 +Four functions are provided to get input and return output. They are very
   6.696 +similar to shift/unshift and push/pop. The first pair is about input, while
   6.697 +the second is about output. Mnemonic: in input, you are interested in the
   6.698 +first line, what shift gives, and in output you want to add your result at
   6.699 +the end, like push does.
   6.700 +
   6.701 +=over 4
   6.702 +
   6.703 +=item shiftline()
   6.704 +
   6.705 +This function returns the next line of the doc_in to be parsed and its
   6.706 +reference (packed as an array).
   6.707 +
   6.708 +=item unshiftline($$)
   6.709 +
   6.710 +Unshifts a line of the input document and its reference. 
   6.711 +
   6.712 +=item pushline($)
   6.713 +
   6.714 +Push a new line to the doc_out.
   6.715 +
   6.716 +=item popline()
   6.717 +
   6.718 +Pop the last pushed line from the doc_out.
   6.719 +
   6.720 +=back
   6.721 +
   6.722 +=cut
   6.723 +
   6.724 +sub shiftline   {  
   6.725 +    my ($line,$ref)=(shift @{$_[0]->{TT}{doc_in}},
   6.726 +		     shift @{$_[0]->{TT}{doc_in}}); 
   6.727 +    return ($line,$ref);
   6.728 +}
   6.729 +sub unshiftline {
   6.730 +	my $self = shift;
   6.731 +	unshift @{$self->{TT}{doc_in}},@_;
   6.732 +}
   6.733 +
   6.734 +sub pushline    {  push @{$_[0]->{TT}{doc_out}}, $_[1] if defined $_[1]; }
   6.735 +sub popline     {  return pop @{$_[0]->{TT}{doc_out}};            }
   6.736 +
   6.737 +=head2 Marking strings as translatable
   6.738 +
   6.739 +One function is provided to handle the text which should be translated. 
   6.740 +
   6.741 +=over 4
   6.742 +
   6.743 +=item translate($$$)
   6.744 +
   6.745 +Mandatory arguments:
   6.746 +
   6.747 +=over 2
   6.748 +
   6.749 +=item -
   6.750 +
   6.751 +A string to translate
   6.752 +
   6.753 +=item -
   6.754 +
   6.755 +The reference of this string (ie, position in inputfile)
   6.756 +
   6.757 +=item -
   6.758 +
   6.759 +The type of this string (ie, the textual description of its structural role
   6.760 +; used in Locale::Po4a::Po::gettextization() ; see also L<po4a(7)|po4a.7>,
   6.761 +section I<Gettextization: how does it work?>)
   6.762 +
   6.763 +=back
   6.764 +
   6.765 +This function can also take some extra arguments. They must be organized as
   6.766 +a hash. For example:
   6.767 +
   6.768 +  $self->translate("string","ref","type",
   6.769 +		   'wrap' => 1);
   6.770 +
   6.771 +=over
   6.772 +
   6.773 +=item wrap
   6.774 +
   6.775 +boolean indicating whether we can consider that whitespaces in string are
   6.776 +not important. If yes, the function canonizes the string before looking for
   6.777 +a translation or extracting it, and wraps the translation.
   6.778 +
   6.779 +=item wrapcol
   6.780 +
   6.781 +The column at which we should wrap (default: 76).
   6.782 +
   6.783 +=item comment
   6.784 +
   6.785 +An extra comment to add to the entry.
   6.786 +
   6.787 +=back
   6.788 +
   6.789 +Actions:
   6.790 +
   6.791 +=over 2
   6.792 +
   6.793 +=item -
   6.794 +
   6.795 +Pushes the string, reference and type to po_out.
   6.796 +
   6.797 +=item -
   6.798 +
   6.799 +Returns the translation of the string (as found in po_in) so that the
   6.800 +parser can build the doc_out.
   6.801 +
   6.802 +=item -
   6.803 +
   6.804 +Handles the charsets to recode the strings before sending them to
   6.805 +po_out and before returning the translations.
   6.806 +
   6.807 +=back
   6.808 +
   6.809 +=back
   6.810 +
   6.811 +=cut
   6.812 +
   6.813 +sub translate {
   6.814 +    my $self=shift;
   6.815 +    my ($string,$ref,$type)=(shift,shift,shift);
   6.816 +    my (%options)=@_;
   6.817 +
   6.818 +    # my $validoption="wrap wrapcol";
   6.819 +    # my %validoption;
   6.820 +
   6.821 +    return "" unless defined($string) && length($string);
   6.822 +
   6.823 +    # map { $validoption{$_}=1 } (split(/ /,$validoption));
   6.824 +    # foreach (keys %options) {
   6.825 +    #	Carp::confess "internal error: translate() called with unknown arg $_. Valid options: $validoption"
   6.826 +    #	    unless $validoption{$_};
   6.827 +    # }
   6.828 +
   6.829 +    my $in_charset;
   6.830 +    if ($self->{TT}{ascii_input}) {
   6.831 +	$in_charset = "ascii";
   6.832 +    } else {
   6.833 +	if (defined($self->{TT}{'file_in_charset'}) and
   6.834 +	    length($self->{TT}{'file_in_charset'}) and
   6.835 +	    $self->{TT}{'file_in_charset'} !~ m/ascii/i) {
   6.836 +	    $in_charset=$self->{TT}{'file_in_charset'};
   6.837 +	} else {
   6.838 +	    # FYI, the document charset have to be determined *before* we see the first
   6.839 +	    # string to recode.
   6.840 +	    die wrap_mod("po4a", dgettext("po4a", "Couldn't determine the input document's charset. Please specify it on the command line. (non-ascii char at %s)"), $self->{TT}{non_ascii_ref})
   6.841 +	}
   6.842 +    }
   6.843 +
   6.844 +    if ($self->{TT}{po_in}->get_charset ne "CHARSET") {
   6.845 +	$string = encode_from_to($string,
   6.846 +	                         $self->{TT}{'file_in_encoder'},
   6.847 +	                         $self->{TT}{po_in}{encoder});
   6.848 +    }
   6.849 +
   6.850 +    if (defined $options{'wrapcol'} && $options{'wrapcol'} < 0) {
   6.851 +# FIXME: should be the parameter given with --width
   6.852 +        $options{'wrapcol'} = 76 + $options{'wrapcol'};
   6.853 +    }
   6.854 +    my $transstring = $self->{TT}{po_in}->gettext($string,
   6.855 +					'wrap'      => $options{'wrap'}||0,
   6.856 +					'wrapcol'   => $options{'wrapcol'});
   6.857 +
   6.858 +    if ($self->{TT}{po_in}->get_charset ne "CHARSET") {
   6.859 +	my $out_encoder = $self->{TT}{'file_out_encoder'};
   6.860 +	unless (defined $out_encoder) {
   6.861 +	    $out_encoder = find_encoding($self->get_out_charset)
   6.862 +	}
   6.863 +	$transstring = encode_from_to($transstring,
   6.864 +	                              $self->{TT}{po_in}{encoder},
   6.865 +	                              $out_encoder);
   6.866 +    }
   6.867 +
   6.868 +    # If the input document isn't completely in ascii, we should see what to
   6.869 +    # do with the current string
   6.870 +    unless ($self->{TT}{ascii_input}) {
   6.871 +        my $out_charset = $self->{TT}{po_out}->get_charset;
   6.872 +	# We set the output po charset 
   6.873 +        if ($out_charset eq "CHARSET") {
   6.874 +	    if ($self->{TT}{utf_mode}) {
   6.875 +		$out_charset="utf-8";
   6.876 +	    } else {
   6.877 +		$out_charset=$in_charset;
   6.878 +	    }
   6.879 +	    $self->{TT}{po_out}->set_charset($out_charset);
   6.880 +	}
   6.881 +	if ( $in_charset !~ /^$out_charset$/i ) {
   6.882 +	    Encode::from_to($string,$in_charset,$out_charset);
   6.883 +	    if (defined($options{'comment'}) and length($options{'comment'})) {
   6.884 +		Encode::from_to($options{'comment'},$in_charset,$out_charset);
   6.885 +	    }
   6.886 +	}
   6.887 +    }
   6.888 +
   6.889 +    # the comments provided by the modules are automatic comments from the PO point of view
   6.890 +    $self->{TT}{po_out}->push('msgid'     => $string,
   6.891 +			      'reference' => $ref,
   6.892 +			      'type'      => $type,
   6.893 +	                      'automatic' => $options{'comment'},
   6.894 +			      'wrap'      => $options{'wrap'}||0,
   6.895 +			      'wrapcol'   => $options{'wrapcol'});
   6.896 +
   6.897 +#    if ($self->{TT}{po_in}->get_charset ne "CHARSET") {
   6.898 +#	Encode::from_to($transstring,$self->{TT}{po_in}->get_charset,
   6.899 +#	    $self->get_out_charset);
   6.900 +#    }
   6.901 +
   6.902 +    if ($options{'wrap'}||0) {
   6.903 +        $transstring =~ s/( *)$//s;
   6.904 +        my $trailing_spaces = $1||"";
   6.905 +        $transstring =~ s/ *$//gm;
   6.906 +        $transstring .= $trailing_spaces;
   6.907 +    }
   6.908 +
   6.909 +    return $transstring;
   6.910 +}
   6.911 +
   6.912 +=head2 Misc functions
   6.913 +
   6.914 +=over 4
   6.915 +
   6.916 +=item verbose()
   6.917 +
   6.918 +Returns if the verbose option was passed during the creation of the
   6.919 +TransTractor.
   6.920 +
   6.921 +=cut
   6.922 +
   6.923 +sub verbose {
   6.924 +    if (defined $_[1]) {
   6.925 +	$_[0]->{TT}{verbose} = $_[1];
   6.926 +    } else {
   6.927 +	return $_[0]->{TT}{verbose} || 0; # undef and 0 have the same meaning, but one generates warnings
   6.928 +    }
   6.929 +}
   6.930 +
   6.931 +=item debug()
   6.932 +
   6.933 +Returns if the debug option was passed during the creation of the
   6.934 +TransTractor.
   6.935 +
   6.936 +=cut
   6.937 +
   6.938 +sub debug {
   6.939 +    return $_[0]->{TT}{debug};
   6.940 +}
   6.941 +
   6.942 +=item detected_charset($)
   6.943 +
   6.944 +This tells TransTractor that a new charset (the first argument) has been
   6.945 +detected from the input document. It can usually be read from the document
   6.946 +header. Only the first charset will remain, coming either from the
   6.947 +process() arguments or detected from the document.
   6.948 +
   6.949 +=cut
   6.950 +
   6.951 +sub detected_charset {
   6.952 +    my ($self,$charset)=(shift,shift);
   6.953 +    unless (defined($self->{TT}{'file_in_charset'}) and
   6.954 +            length($self->{TT}{'file_in_charset'}) ) {
   6.955 +        $self->{TT}{'file_in_charset'}=$charset;
   6.956 +        if (defined $charset) {
   6.957 +            $self->{TT}{'file_in_encoder'}=find_encoding($charset);
   6.958 +        }
   6.959 +    }
   6.960 +
   6.961 +    if (defined $self->{TT}{'file_in_charset'} and
   6.962 +        length $self->{TT}{'file_in_charset'} and
   6.963 +        $self->{TT}{'file_in_charset'} !~ m/ascii/i) {
   6.964 +	$self->{TT}{ascii_input}=0;
   6.965 +    }
   6.966 +}
   6.967 +
   6.968 +=item get_out_charset()
   6.969 +
   6.970 +This function will return the charset that should be used in the output
   6.971 +document (usually useful to substitute the input document's detected charset
   6.972 +where it has been found).
   6.973 +
   6.974 +It will use the output charset specified in the command line. If it wasn't
   6.975 +specified, it will use the input po's charset, and if the input po has the
   6.976 +default "CHARSET", it will return the input document's charset, so that no
   6.977 +encoding is performed.
   6.978 +
   6.979 +=cut
   6.980 +
   6.981 +sub get_out_charset {
   6.982 +    my $self=shift;
   6.983 +    my $charset;
   6.984 +
   6.985 +    # Use the value specified at the command line
   6.986 +    if (defined($self->{TT}{'file_out_charset'}) and
   6.987 +	length($self->{TT}{'file_out_charset'})) {
   6.988 +	$charset=$self->{TT}{'file_out_charset'};
   6.989 +    } else {
   6.990 +	if ($self->{TT}{utf_mode} && $self->{TT}{ascii_input}) {
   6.991 +	    $charset="utf-8";
   6.992 +	} else {
   6.993 +	    $charset=$self->{TT}{po_in}->get_charset;
   6.994 +	    $charset=$self->{TT}{'file_in_charset'}
   6.995 +		if $charset eq "CHARSET" and
   6.996 +		    defined($self->{TT}{'file_in_charset'}) and
   6.997 +		    length($self->{TT}{'file_in_charset'});
   6.998 +	    $charset="ascii"
   6.999 +		if $charset eq "CHARSET";
  6.1000 +	}
  6.1001 +    }
  6.1002 +    return $charset;
  6.1003 +}
  6.1004 +
  6.1005 +=item recode_skipped_text($)
  6.1006 +
  6.1007 +This function returns the recoded text passed as argument, from the input
  6.1008 +document's charset to the output document's one. This isn't needed when
  6.1009 +translating a string (translate() recodes everything itself), but it is when
  6.1010 +you skip a string from the input document and you want the output document to
  6.1011 +be consistent with the global encoding.
  6.1012 +
  6.1013 +=cut
  6.1014 +
  6.1015 +sub recode_skipped_text {
  6.1016 +    my ($self,$text)=(shift,shift);
  6.1017 +    unless ($self->{TT}{'ascii_input'}) {
  6.1018 +	if(defined($self->{TT}{'file_in_charset'}) and
  6.1019 +	    length($self->{TT}{'file_in_charset'}) ) {
  6.1020 +	    $text = encode_from_to($text,
  6.1021 +	                           $self->{TT}{'file_in_encoder'},
  6.1022 +	                           find_encoding($self->get_out_charset));
  6.1023 +	} else {
  6.1024 +	    die wrap_mod("po4a", dgettext("po4a", "Couldn't determine the input document's charset. Please specify it on the command line. (non-ascii char at %s)"), $self->{TT}{non_ascii_ref})
  6.1025 +	}
  6.1026 +    }
  6.1027 +    return $text;
  6.1028 +}
  6.1029 +
  6.1030 +
  6.1031 +# encode_from_to($,$,$)
  6.1032 +#
  6.1033 +# Encode the given text from one encoding to another one.
  6.1034 +# It differs from Encode::from_to because it does not take the name of the
  6.1035 +# encoding in argument, but the encoders (as returned by the
  6.1036 +# Encode::find_encoding(<name>) method). Thus it permits to save a bunch
  6.1037 +# of call to find_encoding.
  6.1038 +#
  6.1039 +# If the "from" encoding is undefined, it is considered as UTF-8 (or
  6.1040 +# ascii).
  6.1041 +# If the "to" encoding is undefined, it is considered as UTF-8.
  6.1042 +#
  6.1043 +sub encode_from_to {
  6.1044 +    my ($text,$from,$to) = (shift,shift,shift);
  6.1045 +
  6.1046 +    if (not defined $from) {
  6.1047 +        # for ascii and UTF-8, no conversion needed to get an utf-8
  6.1048 +        # string.
  6.1049 +    } else {
  6.1050 +        $text = $from->decode($text, 0);
  6.1051 +    }
  6.1052 +
  6.1053 +    if (not defined $to) {
  6.1054 +        # Already in UTF-8, no conversion needed
  6.1055 +    } else {
  6.1056 +        $text = $to->encode($text, 0);
  6.1057 +    }
  6.1058 +
  6.1059 +    return $text;
  6.1060 +}
  6.1061 +
  6.1062 +=back
  6.1063 +
  6.1064 +=head1 FUTURE DIRECTIONS
  6.1065 +
  6.1066 +One shortcoming of the current TransTractor is that it can't handle
  6.1067 +translated document containing all languages, like debconf templates, or
  6.1068 +.desktop files.
  6.1069 +
  6.1070 +To address this problem, the only interface changes needed are:
  6.1071 +
  6.1072 +=over 2
  6.1073 +
  6.1074 +=item -
  6.1075 +
  6.1076 +take a hash as po_in_name (a list per language)
  6.1077 +
  6.1078 +=item -
  6.1079 +
  6.1080 +add an argument to translate to indicate the target language
  6.1081 +
  6.1082 +=item -
  6.1083 +
  6.1084 +make a pushline_all function, which would make pushline of its content for
  6.1085 +all language, using a map-like syntax:
  6.1086 +
  6.1087 +    $self->pushline_all({ "Description[".$langcode."]=".
  6.1088 +			  $self->translate($line,$ref,$langcode) 
  6.1089 +		        });
  6.1090 +
  6.1091 +=back
  6.1092 +
  6.1093 +Will see if it's enough ;)
  6.1094 +
  6.1095 +=head1 AUTHORS
  6.1096 +
  6.1097 + Denis Barbier <barbier@linuxfr.org>
  6.1098 + Martin Quinson (mquinson#debian.org)
  6.1099 + Jordi Vilalta <jvprat@gmail.com>
  6.1100 +
  6.1101 +=cut
  6.1102 +
  6.1103 +1;
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/tools/po4a/lib/Locale/Po4a/Xml.pm	Thu Mar 12 15:43:56 2009 +0800
     7.3 @@ -0,0 +1,1973 @@
     7.4 +#!/usr/bin/perl
     7.5 +
     7.6 +# Po4a::Xml.pm 
     7.7 +# 
     7.8 +# extract and translate translatable strings from XML documents.
     7.9 +# 
    7.10 +# This code extracts plain text from tags and attributes from generic
    7.11 +# XML documents, and it can be used as a base to build modules for
    7.12 +# XML-based documents.
    7.13 +#
    7.14 +# Copyright (c) 2004 by Jordi Vilalta  <jvprat@gmail.com>
    7.15 +# Copyright (c) 2008-2009 by Nicolas François  <nicolas.francois@centraliens.net>
    7.16 +#
    7.17 +# This program is free software; you can redistribute it and/or modify
    7.18 +# it under the terms of the GNU General Public License as published by
    7.19 +# the Free Software Foundation; either version 2 of the License, or
    7.20 +# (at your option) any later version.
    7.21 +#
    7.22 +# This program is distributed in the hope that it will be useful,
    7.23 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.24 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.25 +# GNU General Public License for more details.
    7.26 +#
    7.27 +# You should have received a copy of the GNU General Public License
    7.28 +# along with this program; if not, write to the Free Software
    7.29 +# Foundation, Inc.,
    7.30 +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    7.31 +#
    7.32 +########################################################################
    7.33 +
    7.34 +=head1 NAME
    7.35 +
    7.36 +Locale::Po4a::Xml - Convert XML documents and derivates from/to PO files
    7.37 +
    7.38 +=head1 DESCRIPTION
    7.39 +
    7.40 +The po4a (po for anything) project goal is to ease translations (and more
    7.41 +interestingly, the maintenance of translations) using gettext tools on
    7.42 +areas where they were not expected like documentation.
    7.43 +
    7.44 +Locale::Po4a::Xml is a module to help the translation of XML documents into
    7.45 +other [human] languages. It can also be used as a base to build modules for
    7.46 +XML-based documents.
    7.47 +
    7.48 +=cut
    7.49 +
    7.50 +package Locale::Po4a::Xml;
    7.51 +
    7.52 +use 5.006;
    7.53 +use strict;
    7.54 +use warnings;
    7.55 +
    7.56 +require Exporter;
    7.57 +use vars qw(@ISA @EXPORT);
    7.58 +@ISA = qw(Locale::Po4a::TransTractor);
    7.59 +@EXPORT = qw(new initialize @tag_types);
    7.60 +
    7.61 +use Locale::Po4a::TransTractor;
    7.62 +use Locale::Po4a::Common;
    7.63 +use Carp qw(croak);
    7.64 +use File::Basename;
    7.65 +use File::Spec;
    7.66 +
    7.67 +#It will mantain the path from the root tag to the current one
    7.68 +my @path;
    7.69 +
    7.70 +#It will contain a list of external entities and their attached paths
    7.71 +my %entities;
    7.72 +
    7.73 +my @comments;
    7.74 +
    7.75 +sub shiftline {
    7.76 +    my $self = shift;
    7.77 +    # call Transtractor's shiftline
    7.78 +    my ($line,$ref) = $self->SUPER::shiftline();
    7.79 +    return ($line,$ref) if (not defined $line);
    7.80 +
    7.81 +    for my $k (keys %entities) {
    7.82 +        if ($line =~ m/^(.*?)&$k;(.*)$/s) {
    7.83 +            my ($before, $after) = ($1, $2);
    7.84 +            my $linenum=0;
    7.85 +            my @textentries;
    7.86 +
    7.87 +            open (my $in, $entities{$k})
    7.88 +                or croak wrap_mod("po4a::xml",
    7.89 +                                  dgettext("po4a", "Can't read from %s: %s"),
    7.90 +                                  $entities{$k}, $!);
    7.91 +            while (defined (my $textline = <$in>)) {
    7.92 +                $linenum++;
    7.93 +                my $textref=$entities{$k}.":$linenum";
    7.94 +                push @textentries, ($textline,$textref);
    7.95 +            }
    7.96 +            close $in
    7.97 +                or croak wrap_mod("po4a::xml",
    7.98 +                          dgettext("po4a", "Can't close %s after reading: %s"),
    7.99 +                                  $entities{$k}, $!);
   7.100 +
   7.101 +            push @textentries, ($after, $ref);
   7.102 +            $line = $before.(shift @textentries);
   7.103 +            $ref .= " ".(shift @textentries);
   7.104 +            $self->unshiftline(@textentries);
   7.105 +        }
   7.106 +    }
   7.107 +
   7.108 +    return ($line,$ref);
   7.109 +}
   7.110 +
   7.111 +sub read {
   7.112 +	my ($self,$filename)=@_;
   7.113 +	push @{$self->{DOCPOD}{infile}}, $filename;
   7.114 +	$self->Locale::Po4a::TransTractor::read($filename);
   7.115 +}
   7.116 +
   7.117 +sub parse {
   7.118 +	my $self=shift;
   7.119 +	map {$self->parse_file($_)} @{$self->{DOCPOD}{infile}};
   7.120 +}
   7.121 +
   7.122 +# @save_holders is a stack of references to ('paragraph', 'translation',
   7.123 +# 'sub_translations', 'open', 'close', 'folded_attributes') hashes, where:
   7.124 +# paragraph         is a reference to an array (see paragraph in the
   7.125 +#                   treat_content() subroutine) of strings followed by
   7.126 +#                   references.  It contains the @paragraph array as it was
   7.127 +#                   before the processing was interrupted by a tag instroducing
   7.128 +#                   a placeholder.
   7.129 +# translation       is the translation of this level up to now
   7.130 +# sub_translations  is a reference to an array of strings containing the
   7.131 +#                   translations which must replace the placeholders.
   7.132 +# open              is the tag which opened the placeholder.
   7.133 +# close             is the tag which closed the placeholder.
   7.134 +# folded_attributes is an hash of tags with their attributes (<tag attrs=...>
   7.135 +#                   strings), referenced by the folded tag id, which should
   7.136 +#                   replace the <tag po4a-id=id> strings in the current
   7.137 +#                   translation.
   7.138 +#
   7.139 +# If @save_holders only has 1 holder, then we are not processing the
   7.140 +# content of an holder, we are translating the document.
   7.141 +my @save_holders;
   7.142 +
   7.143 +
   7.144 +# If we are at the bottom of the stack and there is no <placeholder ...> in
   7.145 +# the current translation, we can push the translation in the translated
   7.146 +# document.
   7.147 +# Otherwise, we keep the translation in the current holder.
   7.148 +sub pushline {
   7.149 +	my ($self, $line) = (shift, shift);
   7.150 +
   7.151 +	my $holder = $save_holders[$#save_holders];
   7.152 +	my $translation = $holder->{'translation'};
   7.153 +	$translation .= $line;
   7.154 +
   7.155 +	while (    %{$holder->{folded_attributes}}
   7.156 +	       and $translation =~ m/^(.*)<([^>]+?)\s+po4a-id=([0-9]+)>(.*)$/s) {
   7.157 +		my $begin = $1;
   7.158 +		my $tag = $2;
   7.159 +		my $id = $3;
   7.160 +		my $end = $4;
   7.161 +		if (defined $holder->{folded_attributes}->{$id}) {
   7.162 +			# TODO: check if the tag is the same
   7.163 +			$translation = $begin.$holder->{folded_attributes}->{$id}.$end;
   7.164 +			delete $holder->{folded_attributes}->{$id};
   7.165 +		} else {
   7.166 +			# TODO: It will be hard to identify the location.
   7.167 +			#       => find a way to retrieve the reference.
   7.168 +			die wrap_mod("po4a::xml", dgettext("po4a", "'po4a-id=%d' in the translation does not exist in the original string (or 'po4a-id=%d' used twice in the translation)."), $id, $id);
   7.169 +		}
   7.170 +	}
   7.171 +# TODO: check that %folded_attributes is empty at some time
   7.172 +# => in translate_paragraph?
   7.173 +
   7.174 +	if (   ($#save_holders > 0)
   7.175 +	    or ($translation =~ m/<placeholder\s+type="[^"]+"\s+id="(\d+)"\s*\/>/s)) {
   7.176 +		$holder->{'translation'} = $translation;
   7.177 +	} else {
   7.178 +		$self->SUPER::pushline($translation);
   7.179 +		$holder->{'translation'} = '';
   7.180 +	}
   7.181 +}
   7.182 +
   7.183 +=head1 TRANSLATING WITH PO4A::XML
   7.184 +
   7.185 +This module can be used directly to handle generic XML documents.  This will
   7.186 +extract all tag's content, and no attributes, since it's where the text is
   7.187 +written in most XML based documents.
   7.188 +
   7.189 +There are some options (described in the next section) that can customize
   7.190 +this behavior.  If this doesn't fit to your document format you're encouraged
   7.191 +to write your own module derived from this, to describe your format's details.
   7.192 +See the section "Writing derivate modules" below, for the process description.
   7.193 +
   7.194 +=cut
   7.195 +
   7.196 +#
   7.197 +# Parse file and translate it
   7.198 +#
   7.199 +sub parse_file {
   7.200 +	my ($self,$filename) = @_;
   7.201 +	my $eof = 0;
   7.202 +
   7.203 +	while (!$eof) {
   7.204 +		# We get all the text until the next breaking tag (not
   7.205 +		# inline) and translate it
   7.206 +		$eof = $self->treat_content;
   7.207 +		if (!$eof) {
   7.208 +			# And then we treat the following breaking tag
   7.209 +			$eof = $self->treat_tag;
   7.210 +		}
   7.211 +	}
   7.212 +}
   7.213 +
   7.214 +=head1 OPTIONS ACCEPTED BY THIS MODULE
   7.215 +
   7.216 +The global debug option causes this module to show the excluded strings, in
   7.217 +order to see if it skips something important.
   7.218 +
   7.219 +These are this module's particular options:
   7.220 +
   7.221 +=over 4
   7.222 +
   7.223 +=item B<nostrip>
   7.224 +
   7.225 +Prevents it to strip the spaces around the extracted strings.
   7.226 +
   7.227 +=item B<wrap>
   7.228 +
   7.229 +Canonizes the string to translate, considering that whitespaces are not
   7.230 +important, and wraps the translated document. This option can be overridden
   7.231 +by custom tag options. See the "tags" option below.
   7.232 +
   7.233 +=item B<caseinsensitive>
   7.234 +
   7.235 +It makes the tags and attributes searching to work in a case insensitive
   7.236 +way.  If it's defined, it will treat E<lt>BooKE<gt>laNG and E<lt>BOOKE<gt>Lang as E<lt>bookE<gt>lang.
   7.237 +
   7.238 +=item B<includeexternal>
   7.239 +
   7.240 +When defined, external entities are included in the generated (translated)
   7.241 +document, and for the extraction of strings.  If it's not defined, you
   7.242 +will have to translate external entities separately as independent
   7.243 +documents.
   7.244 +
   7.245 +=item B<ontagerror>
   7.246 +
   7.247 +This option defines the behavior of the module when it encounter a invalid
   7.248 +Xml syntax (a closing tag which does not match the last opening tag, or a
   7.249 +tag's attribute without value).
   7.250 +It can take the following values:
   7.251 +
   7.252 +=over
   7.253 +
   7.254 +=item I<fail>
   7.255 +
   7.256 +This is the default value.
   7.257 +The module will exit with an error.
   7.258 +
   7.259 +=item I<warn>
   7.260 +
   7.261 +The module will continue, and will issue a warning.
   7.262 +
   7.263 +=item I<silent>
   7.264 +
   7.265 +The module will continue without any warnings.
   7.266 +
   7.267 +=back
   7.268 +
   7.269 +Be careful when using this option.
   7.270 +It is generally recommended to fix the input file.
   7.271 +
   7.272 +=item B<tagsonly>
   7.273 +
   7.274 +Extracts only the specified tags in the "tags" option.  Otherwise, it
   7.275 +will extract all the tags except the ones specified.
   7.276 +
   7.277 +Note: This option is deprecated.
   7.278 +
   7.279 +=item B<doctype>
   7.280 +
   7.281 +String that will try to match with the first line of the document's doctype
   7.282 +(if defined). If it doesn't, a warning will indicate that the document
   7.283 +might be of a bad type.
   7.284 +
   7.285 +=item B<tags>
   7.286 +
   7.287 +Space-separated list of tags you want to translate or skip.  By default,
   7.288 +the specified tags will be excluded, but if you use the "tagsonly" option,
   7.289 +the specified tags will be the only ones included.  The tags must be in the
   7.290 +form E<lt>aaaE<gt>, but you can join some (E<lt>bbbE<gt>E<lt>aaaE<gt>) to say that the content of
   7.291 +the tag E<lt>aaaE<gt> will only be translated when it's into a E<lt>bbbE<gt> tag.
   7.292 +
   7.293 +You can also specify some tag options putting some characters in front of
   7.294 +the tag hierarchy. For example, you can put 'w' (wrap) or 'W' (don't wrap)
   7.295 +to override the default behavior specified by the global "wrap" option.
   7.296 +
   7.297 +Example: WE<lt>chapterE<gt>E<lt>titleE<gt>
   7.298 +
   7.299 +Note: This option is deprecated.
   7.300 +You should use the B<translated> and B<untranslated> options instead.
   7.301 +
   7.302 +=item B<attributes>
   7.303 +
   7.304 +Space-separated list of tag's attributes you want to translate.  You can
   7.305 +specify the attributes by their name (for example, "lang"), but you can
   7.306 +prefix it with a tag hierarchy, to specify that this attribute will only be
   7.307 +translated when it's into the specified tag. For example: E<lt>bbbE<gt>E<lt>aaaE<gt>lang
   7.308 +specifies that the lang attribute will only be translated if it's into an
   7.309 +E<lt>aaaE<gt> tag, and it's into a E<lt>bbbE<gt> tag.
   7.310 +
   7.311 +=item B<foldattributes>
   7.312 +
   7.313 +Do not translate attributes in inline tags.
   7.314 +Instead, replace all attributes of a tag by po4a-id=<id>.
   7.315 +
   7.316 +This is useful when attributes shall not be translated, as this simplifies the
   7.317 +strings for translators, and avoids typos.
   7.318 +
   7.319 +=item B<break>
   7.320 +
   7.321 +Space-separated list of tags which should break the sequence.
   7.322 +By default, all tags break the sequence.
   7.323 +
   7.324 +The tags must be in the form <aaa>, but you can join some
   7.325 +(<bbb><aaa>), if a tag (<aaa>) should only be considered 
   7.326 +when it's into another tag (<bbb>).
   7.327 +
   7.328 +=item B<inline>
   7.329 +
   7.330 +Space-separated list of tags which should be treated as inline.
   7.331 +By default, all tags break the sequence.
   7.332 +
   7.333 +The tags must be in the form <aaa>, but you can join some
   7.334 +(<bbb><aaa>), if a tag (<aaa>) should only be considered 
   7.335 +when it's into another tag (<bbb>).
   7.336 +
   7.337 +=item B<placeholder>
   7.338 +
   7.339 +Space-separated list of tags which should be treated as placeholders.
   7.340 +Placeholders do not break the sequence, but the content of placeholders is
   7.341 +translated separately.
   7.342 +
   7.343 +The location of the placeholder in its blocks will be marked with a string
   7.344 +similar to:
   7.345 +
   7.346 +  <placeholder type=\"footnote\" id=\"0\"/>
   7.347 +
   7.348 +The tags must be in the form <aaa>, but you can join some
   7.349 +(<bbb><aaa>), if a tag (<aaa>) should only be considered 
   7.350 +when it's into another tag (<bbb>).
   7.351 +
   7.352 +=item B<nodefault>
   7.353 +
   7.354 +Space separated list of tags that the module should not try to set by
   7.355 +default in any category.
   7.356 +
   7.357 +=item B<cpp>
   7.358 +
   7.359 +Support C preprocessor directives.
   7.360 +When this option is set, po4a will consider preprocessor directives as
   7.361 +paragraph separators.
   7.362 +This is important if the XML file must be preprocessed because otherwise
   7.363 +the directives may be inserted in the middle of lines if po4a consider it
   7.364 +belong to the current paragraph, and they won't be recognized by the
   7.365 +preprocessor.
   7.366 +Note: the preprocessor directives must only appear between tags
   7.367 +(they must not break a tag).
   7.368 +
   7.369 +=item B<translated>
   7.370 +
   7.371 +Space-separated list of tags you want to translate.
   7.372 +
   7.373 +The tags must be in the form <aaa>, but you can join some
   7.374 +(<bbb><aaa>), if a tag (<aaa>) should only be considered 
   7.375 +when it's into another tag (<bbb>).
   7.376 +
   7.377 +You can also specify some tag options putting some characters in front of
   7.378 +the tag hierarchy. For example, you can put 'w' (wrap) or 'W' (don't wrap)
   7.379 +to overide the default behavior specified by the global "wrap" option.
   7.380 +
   7.381 +Example: WE<lt>chapterE<gt>E<lt>titleE<gt>
   7.382 +
   7.383 +=item B<untranslated>
   7.384 +
   7.385 +Space-separated list of tags you do not want to translate.
   7.386 +
   7.387 +The tags must be in the form <aaa>, but you can join some
   7.388 +(<bbb><aaa>), if a tag (<aaa>) should only be considered 
   7.389 +when it's into another tag (<bbb>).
   7.390 +
   7.391 +=item B<defaulttranslateoption>
   7.392 +
   7.393 +The default categories for tags that are not in any of the translated,
   7.394 +untranslated, break, inline, or placeholder.
   7.395 +
   7.396 +This is a set of letters:
   7.397 +
   7.398 +=over
   7.399 +
   7.400 +=item I<w>
   7.401 +
   7.402 +Tags should be translated and content can be re-wrapped.
   7.403 +
   7.404 +=item I<W>
   7.405 +
   7.406 +Tags should be translated and content should not be re-wrapped.
   7.407 +
   7.408 +=item I<i>
   7.409 +
   7.410 +Tags should be translated inline.
   7.411 +
   7.412 +=item I<p>
   7.413 +
   7.414 +Tags should be translated as placeholders.
   7.415 +
   7.416 +=back
   7.417 +
   7.418 +=back
   7.419 +
   7.420 +=cut
   7.421 +# TODO: defaulttranslateoption
   7.422 +# w => indicate that it is only valid for translatable tags and do not
   7.423 +#      care about inline/break/placeholder?
   7.424 +# ...
   7.425 +
   7.426 +sub initialize {
   7.427 +	my $self = shift;
   7.428 +	my %options = @_;
   7.429 +
   7.430 +	# Reset the path
   7.431 +	@path = ();
   7.432 +
   7.433 +	# Initialize the stack of holders
   7.434 +	my @paragraph = ();
   7.435 +	my @sub_translations = ();
   7.436 +	my %folded_attributes;
   7.437 +	my %holder = ('paragraph' => \@paragraph,
   7.438 +	              'translation' => "",
   7.439 +	              'sub_translations' => \@sub_translations,
   7.440 +	              'folded_attributes' => \%folded_attributes);
   7.441 +	@save_holders = (\%holder);
   7.442 +
   7.443 +	$self->{options}{'nostrip'}=0;
   7.444 +	$self->{options}{'wrap'}=0;
   7.445 +	$self->{options}{'caseinsensitive'}=0;
   7.446 +	$self->{options}{'tagsonly'}=0;
   7.447 +	$self->{options}{'tags'}='';
   7.448 +	$self->{options}{'break'}='';
   7.449 +	$self->{options}{'translated'}='';
   7.450 +	$self->{options}{'untranslated'}='';
   7.451 +	$self->{options}{'defaulttranslateoption'}='';
   7.452 +	$self->{options}{'attributes'}='';
   7.453 +	$self->{options}{'foldattributes'}=0;
   7.454 +	$self->{options}{'inline'}='';
   7.455 +	$self->{options}{'placeholder'}='';
   7.456 +	$self->{options}{'doctype'}='';
   7.457 +	$self->{options}{'nodefault'}='';
   7.458 +	$self->{options}{'includeexternal'}=0;
   7.459 +	$self->{options}{'ontagerror'}="fail";
   7.460 +	$self->{options}{'cpp'}=0;
   7.461 +
   7.462 +	$self->{options}{'verbose'}='';
   7.463 +	$self->{options}{'debug'}='';
   7.464 +
   7.465 +	foreach my $opt (keys %options) {
   7.466 +		if ($options{$opt}) {
   7.467 +			die wrap_mod("po4a::xml",
   7.468 +				dgettext("po4a", "Unknown option: %s"), $opt)
   7.469 +				unless exists $self->{options}{$opt};
   7.470 +			$self->{options}{$opt} = $options{$opt};
   7.471 +		}
   7.472 +	}
   7.473 +	# Default options set by modules. Forbidden for users.
   7.474 +	$self->{options}{'_default_translated'}='';
   7.475 +	$self->{options}{'_default_untranslated'}='';
   7.476 +	$self->{options}{'_default_break'}='';
   7.477 +	$self->{options}{'_default_inline'}='';
   7.478 +	$self->{options}{'_default_placeholder'}='';
   7.479 +	$self->{options}{'_default_attributes'}='';
   7.480 +
   7.481 +	#It will maintain the list of the translatable tags
   7.482 +	$self->{tags}=();
   7.483 +	$self->{translated}=();
   7.484 +	$self->{untranslated}=();
   7.485 +	#It will maintain the list of the translatable attributes
   7.486 +	$self->{attributes}=();
   7.487 +	#It will maintain the list of the breaking tags
   7.488 +	$self->{break}=();
   7.489 +	#It will maintain the list of the inline tags
   7.490 +	$self->{inline}=();
   7.491 +	#It will maintain the list of the placeholder tags
   7.492 +	$self->{placeholder}=();
   7.493 +	#list of the tags that must not be set in the tags or inline category
   7.494 +	#by this module or sub-module (unless specified in an option)
   7.495 +	$self->{nodefault}=();
   7.496 +
   7.497 +	$self->treat_options;
   7.498 +}
   7.499 +
   7.500 +=head1 WRITING DERIVATE MODULES
   7.501 +
   7.502 +=head2 DEFINE WHAT TAGS AND ATTRIBUTES TO TRANSLATE
   7.503 +
   7.504 +The simplest customization is to define which tags and attributes you want
   7.505 +the parser to translate.  This should be done in the initialize function.
   7.506 +First you should call the main initialize, to get the command-line options,
   7.507 +and then, append your custom definitions to the options hash.  If you want
   7.508 +to treat some new options from command line, you should define them before
   7.509 +calling the main initialize:
   7.510 +
   7.511 +  $self->{options}{'new_option'}='';
   7.512 +  $self->SUPER::initialize(%options);
   7.513 +  $self->{options}{'_default_translated'}.=' <p> <head><title>';
   7.514 +  $self->{options}{'attributes'}.=' <p>lang id';
   7.515 +  $self->{options}{'_default_inline'}.=' <br>';
   7.516 +  $self->treat_options;
   7.517 +
   7.518 +You should use the B<_default_inline>, B<_default_break>,
   7.519 +B<_default_placeholder>, B<_default_translated>, B<_default_untranslated>,
   7.520 +and B<_default_attributes> options in derivated modules. This allow users
   7.521 +to override the default behavior defined in your module with command line
   7.522 +options.
   7.523 +
   7.524 +=head2 OVERRIDING THE found_string FUNCTION
   7.525 +
   7.526 +Another simple step is to override the function "found_string", which
   7.527 +receives the extracted strings from the parser, in order to translate them.
   7.528 +There you can control which strings you want to translate, and perform
   7.529 +transformations to them before or after the translation itself.
   7.530 +
   7.531 +It receives the extracted text, the reference on where it was, and a hash
   7.532 +that contains extra information to control what strings to translate, how
   7.533 +to translate them and to generate the comment.
   7.534 +
   7.535 +The content of these options depends on the kind of string it is (specified in an 
   7.536 +entry of this hash):
   7.537 +
   7.538 +=over
   7.539 +
   7.540 +=item type="tag"
   7.541 +
   7.542 +The found string is the content of a translatable tag. The entry "tag_options"
   7.543 +contains the option characters in front of the tag hierarchy in the module
   7.544 +"tags" option.
   7.545 +
   7.546 +=item type="attribute"
   7.547 +
   7.548 +Means that the found string is the value of a translatable attribute. The
   7.549 +entry "attribute" has the name of the attribute.
   7.550 +
   7.551 +=back
   7.552 +
   7.553 +It must return the text that will replace the original in the translated
   7.554 +document. Here's a basic example of this function:
   7.555 +
   7.556 +  sub found_string {
   7.557 +    my ($self,$text,$ref,$options)=@_;
   7.558 +    $text = $self->translate($text,$ref,"type ".$options->{'type'},
   7.559 +      'wrap'=>$self->{options}{'wrap'});
   7.560 +    return $text;
   7.561 +  }
   7.562 +
   7.563 +There's another simple example in the new Dia module, which only filters
   7.564 +some strings.
   7.565 +
   7.566 +=cut
   7.567 +
   7.568 +sub found_string {
   7.569 +	my ($self,$text,$ref,$options)=@_;
   7.570 +
   7.571 +	if ($text =~ m/^\s*$/s) {
   7.572 +		return $text;
   7.573 +	}
   7.574 +
   7.575 +	my $comment;
   7.576 +	my $wrap = $self->{options}{'wrap'};
   7.577 +
   7.578 +	if ($options->{'type'} eq "tag") {
   7.579 +		$comment = "Content of: ".$self->get_path;
   7.580 +
   7.581 +		if($options->{'tag_options'} =~ /w/) {
   7.582 +			$wrap = 1;
   7.583 +		}
   7.584 +		if($options->{'tag_options'} =~ /W/) {
   7.585 +			$wrap = 0;
   7.586 +		}
   7.587 +	} elsif ($options->{'type'} eq "attribute") {
   7.588 +		$comment = "Attribute '".$options->{'attribute'}."' of: ".$self->get_path;
   7.589 +	} elsif ($options->{'type'} eq "CDATA") {
   7.590 +		$comment = "CDATA";
   7.591 +		$wrap = 0;
   7.592 +	} else {
   7.593 +		die wrap_ref_mod($ref, "po4a::xml", dgettext("po4a", "Internal error: unknown type identifier '%s'."), $options->{'type'});
   7.594 +	}
   7.595 +	$text = $self->translate($text,$ref,$comment,'wrap'=>$wrap, comment => $options->{'comments'});
   7.596 +	return $text;
   7.597 +}
   7.598 +
   7.599 +=head2 MODIFYING TAG TYPES (TODO)
   7.600 +
   7.601 +This is a more complex one, but it enables a (almost) total customization.
   7.602 +It's based in a list of hashes, each one defining a tag type's behavior. The
   7.603 +list should be sorted so that the most general tags are after the most
   7.604 +concrete ones (sorted first by the beginning and then by the end keys). To
   7.605 +define a tag type you'll have to make a hash with the following keys:
   7.606 +
   7.607 +=over 4
   7.608 +
   7.609 +=item beginning
   7.610 +
   7.611 +Specifies the beginning of the tag, after the "E<lt>".
   7.612 +
   7.613 +=item end
   7.614 +
   7.615 +Specifies the end of the tag, before the "E<gt>".
   7.616 +
   7.617 +=item breaking
   7.618 +
   7.619 +It says if this is a breaking tag class.  A non-breaking (inline) tag is one
   7.620 +that can be taken as part of the content of another tag.  It can take the
   7.621 +values false (0), true (1) or undefined.  If you leave this undefined, you'll
   7.622 +have to define the f_breaking function that will say whether a concrete tag of
   7.623 +this class is a breaking tag or not.
   7.624 +
   7.625 +=item f_breaking
   7.626 +
   7.627 +It's a function that will tell if the next tag is a breaking one or not.  It
   7.628 +should be defined if the "breaking" option is not.
   7.629 +
   7.630 +=item f_extract
   7.631 +
   7.632 +If you leave this key undefined, the generic extraction function will have to
   7.633 +extract the tag itself.  It's useful for tags that can have other tags or
   7.634 +special structures in them, so that the main parser doesn't get mad.  This
   7.635 +function receives a boolean that says if the tag should be removed from the
   7.636 +input stream or not.
   7.637 +
   7.638 +=item f_translate
   7.639 +
   7.640 +This function receives the tag (in the get_string_until() format) and returns
   7.641 +the translated tag (translated attributes or all needed transformations) as a
   7.642 +single string.
   7.643 +
   7.644 +=back
   7.645 +
   7.646 +=cut
   7.647 +
   7.648 +##### Generic XML tag types #####' 
   7.649 +
   7.650 +our @tag_types = ( 
   7.651 +	{	beginning	=> "!--#",
   7.652 +		end		=> "--",
   7.653 +		breaking	=> 0,
   7.654 +		f_extract	=> \&tag_extract_comment,
   7.655 +		f_translate	=> \&tag_trans_comment},
   7.656 +	{	beginning	=> "!--",
   7.657 +		end		=> "--",
   7.658 +		breaking	=> 0,
   7.659 +		f_extract	=> \&tag_extract_comment,
   7.660 +		f_translate	=> \&tag_trans_comment},
   7.661 +	{	beginning	=> "?xml",
   7.662 +		end		=> "?",
   7.663 +		breaking	=> 1,
   7.664 +		f_translate	=> \&tag_trans_xmlhead},
   7.665 +	{	beginning	=> "?",
   7.666 +		end		=> "?",
   7.667 +		breaking	=> 1,
   7.668 +		f_translate	=> \&tag_trans_procins},
   7.669 +	{	beginning	=> "!DOCTYPE",
   7.670 +		end		=> "",
   7.671 +		breaking	=> 1,
   7.672 +		f_extract	=> \&tag_extract_doctype,
   7.673 +		f_translate	=> \&tag_trans_doctype},
   7.674 +	{	beginning	=> "![CDATA[",
   7.675 +		end		=> "",
   7.676 +		breaking	=> 1,
   7.677 +		f_extract	=> \&CDATA_extract,
   7.678 +		f_translate	=> \&CDATA_trans},
   7.679 +	{	beginning	=> "/",
   7.680 +		end		=> "",
   7.681 +		f_breaking	=> \&tag_break_close,
   7.682 +		f_translate	=> \&tag_trans_close},
   7.683 +	{	beginning	=> "",
   7.684 +		end		=> "/",
   7.685 +		f_breaking	=> \&tag_break_alone,
   7.686 +		f_translate	=> \&tag_trans_alone},
   7.687 +	{	beginning	=> "",
   7.688 +		end		=> "",
   7.689 +		f_breaking	=> \&tag_break_open,
   7.690 +		f_translate	=> \&tag_trans_open}
   7.691 +);
   7.692 +
   7.693 +sub tag_extract_comment {
   7.694 +	my ($self,$remove)=(shift,shift);
   7.695 +	my ($eof,@tag)=$self->get_string_until('-->',{include=>1,remove=>$remove});
   7.696 +	return ($eof,@tag);
   7.697 +}
   7.698 +
   7.699 +sub tag_trans_comment {
   7.700 +	my ($self,@tag)=@_;
   7.701 +	return $self->join_lines(@tag);
   7.702 +}
   7.703 +
   7.704 +sub tag_trans_xmlhead {
   7.705 +	my ($self,@tag)=@_;
   7.706 +
   7.707 +	# We don't have to translate anything from here: throw away references
   7.708 +	my $tag = $self->join_lines(@tag);
   7.709 +	$tag =~ /encoding=(("|')|)(.*?)(\s|\2)/s;
   7.710 +	my $in_charset=$3;
   7.711 +	$self->detected_charset($in_charset);
   7.712 +	my $out_charset=$self->get_out_charset;
   7.713 +
   7.714 +	if (defined $in_charset) {
   7.715 +		$tag =~ s/$in_charset/$out_charset/;
   7.716 +	} else {
   7.717 +		if ($tag =~ m/standalone/) {
   7.718 +			$tag =~ s/(standalone)/encoding="$out_charset" $1/;
   7.719 +		} else {
   7.720 +			$tag.= " encoding=\"$out_charset\"";
   7.721 +		}
   7.722 +	}
   7.723 +
   7.724 +	return $tag;
   7.725 +}
   7.726 +
   7.727 +sub tag_trans_procins {
   7.728 +	my ($self,@tag)=@_;
   7.729 +	return $self->join_lines(@tag);
   7.730 +}
   7.731 +
   7.732 +sub tag_extract_doctype {
   7.733 +	my ($self,$remove)=(shift,shift);
   7.734 +
   7.735 +	# Check if there is an internal subset (between []).
   7.736 +	my ($eof,@tag)=$self->get_string_until('>',{include=>1,unquoted=>1});
   7.737 +	my $parity = 0;
   7.738 +	my $paragraph = "";
   7.739 +	map { $parity = 1 - $parity; $paragraph.= $parity?$_:""; } @tag;
   7.740 +	my $found = 0;
   7.741 +	if ($paragraph =~ m/<.*\[.*</s) {
   7.742 +		$found = 1
   7.743 +	}
   7.744 +
   7.745 +	if (not $found) {
   7.746 +		($eof,@tag)=$self->get_string_until('>',{include=>1,remove=>$remove,unquoted=>1});
   7.747 +	} else {
   7.748 +		($eof,@tag)=$self->get_string_until(']\s*>',{include=>1,remove=>$remove,unquoted=>1,regex=>1});
   7.749 +	}
   7.750 +	return ($eof,@tag);
   7.751 +}
   7.752 +
   7.753 +sub tag_trans_doctype {
   7.754 +# This check is not really reliable.  There are system and public
   7.755 +# identifiers.  Only the public one could be checked reliably.
   7.756 +	my ($self,@tag)=@_;
   7.757 +	if (defined $self->{options}{'doctype'} ) {
   7.758 +		my $doctype = $self->{options}{'doctype'};
   7.759 +		if ( $tag[0] !~ /\Q$doctype\E/i ) {
   7.760 +			warn wrap_ref_mod($tag[1], "po4a::xml", dgettext("po4a", "Bad document type. '%s' expected. You can fix this warning with a -o doctype option, or ignore this check with -o doctype=\"\"."), $doctype);
   7.761 +		}
   7.762 +	}
   7.763 +	my $i = 0;
   7.764 +	my $basedir = $tag[1];
   7.765 +	$basedir =~ s/:[0-9]+$//;
   7.766 +	$basedir = dirname($basedir);
   7.767 +
   7.768 +	while ( $i < $#tag ) {
   7.769 +		my $t = $tag[$i];
   7.770 +		my $ref = $tag[$i+1];
   7.771 +		if ( $t =~ /^(\s*<!ENTITY\s+)(.*)$/is ) {
   7.772 +			my $part1 = $1;
   7.773 +			my $part2 = $2;
   7.774 +			my $includenow = 0;
   7.775 +			my $file = 0;
   7.776 +			my $name = "";
   7.777 +			if ($part2 =~ /^(%\s+)(.*)$/s ) {
   7.778 +				$part1.= $1;
   7.779 +				$part2 = $2;
   7.780 +				$includenow = 1;
   7.781 +			}
   7.782 +			$part2 =~ /^(\S+)(\s+)(.*)$/s;
   7.783 +			$name = $1;
   7.784 +			$part1.= $1.$2;
   7.785 +			$part2 = $3;
   7.786 +			if ( $part2 =~ /^(SYSTEM\s+)(.*)$/is ) {
   7.787 +				$part1.= $1;
   7.788 +				$part2 = $2;
   7.789 +				$file = 1;
   7.790 +				if ($self->{options}{'includeexternal'}) {
   7.791 +					$entities{$name} = $part2;
   7.792 +					$entities{$name} =~ s/^"?(.*?)".*$/$1/s;
   7.793 +					$entities{$name} = File::Spec->catfile($basedir, $entities{$name});
   7.794 +				}
   7.795 +			}
   7.796 +			if ((not $file) and (not $includenow)) {
   7.797 +			    if ($part2 =~ m/^\s*(["'])(.*)\1(\s*>.*)$/s) {
   7.798 +				my $comment = "Content of the $name entity";
   7.799 +				my $quote = $1;
   7.800 +				my $text = $2;
   7.801 +				$part2 = $3;
   7.802 +				$text = $self->translate($text,
   7.803 +				                         $ref,
   7.804 +				                         $comment,
   7.805 +				                         'wrap'=>1);
   7.806 +				$t = $part1."$quote$text$quote$part2";
   7.807 +			    }
   7.808 +			}
   7.809 +#			print $part1."\n";
   7.810 +#			print $name."\n";
   7.811 +#			print $part2."\n";
   7.812 +		}
   7.813 +		$tag[$i] = $t;
   7.814 +		$i += 2;
   7.815 +	}
   7.816 +	return $self->join_lines(@tag);
   7.817 +}
   7.818 +
   7.819 +sub tag_break_close {
   7.820 +	my ($self,@tag)=@_;
   7.821 +	my $struct = $self->get_path;
   7.822 +	my $options = $self->get_translate_options($struct);
   7.823 +	if ($options =~ m/[ip]/) {
   7.824 +		return 0;
   7.825 +	} else {
   7.826 +		return 1;
   7.827 +	}
   7.828 +}
   7.829 +
   7.830 +sub tag_trans_close {
   7.831 +	my ($self,@tag)=@_;
   7.832 +	my $name = $self->get_tag_name(@tag);
   7.833 +
   7.834 +	my $test = pop @path;
   7.835 +	if (!defined($test) || $test ne $name ) {
   7.836 +		my $ontagerror = $self->{options}{'ontagerror'};
   7.837 +		if ($ontagerror eq "warn") {
   7.838 +			warn wrap_ref_mod($tag[1], "po4a::xml", dgettext("po4a", "Unexpected closing tag </%s> found. The main document may be wrong.  Continuing..."), $name);
   7.839 +		} elsif ($ontagerror ne "silent") {
   7.840 +			die wrap_ref_mod($tag[1], "po4a::xml", dgettext("po4a", "Unexpected closing tag </%s> found. The main document may be wrong."), $name);
   7.841 +		}
   7.842 +	}
   7.843 +	return $self->join_lines(@tag);
   7.844 +}
   7.845 +
   7.846 +sub CDATA_extract {
   7.847 +	my ($self,$remove)=(shift,shift);
   7.848 +        my ($eof, @tag) = $self->get_string_until(']]>',{include=>1,unquoted=>0,remove=>$remove});
   7.849 +
   7.850 +	return ($eof, @tag);
   7.851 +}
   7.852 +
   7.853 +sub CDATA_trans {
   7.854 +	my ($self,@tag)=@_;
   7.855 +	return $self->found_string($self->join_lines(@tag),
   7.856 +	                           $tag[1],
   7.857 +	                           {'type' => "CDATA"});
   7.858 +}
   7.859 +
   7.860 +sub tag_break_alone {
   7.861 +	my ($self,@tag)=@_;
   7.862 +	my $struct = $self->get_path($self->get_tag_name(@tag));
   7.863 +	if ($self->get_translate_options($struct) =~ m/i/) {
   7.864 +		return 0;
   7.865 +	} else {
   7.866 +		return 1;
   7.867 +	}
   7.868 +}
   7.869 +
   7.870 +sub tag_trans_alone {
   7.871 +	my ($self,@tag)=@_;
   7.872 +	my $name = $self->get_tag_name(@tag);
   7.873 +	push @path, $name;
   7.874 +
   7.875 +	$name = $self->treat_attributes(@tag);
   7.876 +
   7.877 +	pop @path;
   7.878 +	return $name;
   7.879 +}
   7.880 +
   7.881 +sub tag_break_open {
   7.882 +	my ($self,@tag)=@_;
   7.883 +	my $struct = $self->get_path($self->get_tag_name(@tag));
   7.884 +	my $options = $self->get_translate_options($struct);
   7.885 +	if ($options =~ m/[ip]/) {
   7.886 +		return 0;
   7.887 +	} else {
   7.888 +		return 1;
   7.889 +	}
   7.890 +}
   7.891 +
   7.892 +sub tag_trans_open {
   7.893 +	my ($self,@tag)=@_;
   7.894 +	my $name = $self->get_tag_name(@tag);
   7.895 +	push @path, $name;
   7.896 +
   7.897 +	$name = $self->treat_attributes(@tag);
   7.898 +
   7.899 +	return $name;
   7.900 +}
   7.901 +
   7.902 +##### END of Generic XML tag types #####
   7.903 +
   7.904 +=head1 INTERNAL FUNCTIONS used to write derivated parsers
   7.905 +
   7.906 +=head2 WORKING WITH TAGS
   7.907 +
   7.908 +=over 4
   7.909 +
   7.910 +=item get_path()
   7.911 +
   7.912 +This function returns the path to the current tag from the document's root,
   7.913 +in the form E<lt>htmlE<gt>E<lt>bodyE<gt>E<lt>pE<gt>.
   7.914 +
   7.915 +An additional array of tags (without brackets) can be passed in argument.
   7.916 +These path elements are added to the end of the current path.
   7.917 +
   7.918 +=cut
   7.919 +
   7.920 +sub get_path {
   7.921 +	my $self = shift;
   7.922 +	my @add = @_;
   7.923 +	if ( @path > 0 or @add > 0 ) {
   7.924 +		return "<".join("><",@path,@add).">";
   7.925 +	} else {
   7.926 +		return "outside any tag (error?)";
   7.927 +	}
   7.928 +}
   7.929 +
   7.930 +=item tag_type()
   7.931 +
   7.932 +This function returns the index from the tag_types list that fits to the next
   7.933 +tag in the input stream, or -1 if it's at the end of the input file.
   7.934 +
   7.935 +=cut
   7.936 +
   7.937 +sub tag_type {
   7.938 +	my $self = shift;
   7.939 +	my ($line,$ref) = $self->shiftline();
   7.940 +	my ($match1,$match2);
   7.941 +	my $found = 0;
   7.942 +	my $i = 0;
   7.943 +
   7.944 +	if (!defined($line)) { return -1; }
   7.945 +
   7.946 +	$self->unshiftline($line,$ref);
   7.947 +	my ($eof,@lines) = $self->get_string_until(">",{include=>1,unquoted=>1});
   7.948 +	my $line2 = $self->join_lines(@lines);
   7.949 +	while (!$found && $i < @tag_types) {
   7.950 +		($match1,$match2) = ($tag_types[$i]->{beginning},$tag_types[$i]->{end});
   7.951 +		if ($line =~ /^<\Q$match1\E/) {
   7.952 +			if (!defined($tag_types[$i]->{f_extract})) {
   7.953 +#print substr($line2,length($line2)-1-length($match2),1+length($match2))."\n";
   7.954 +				if (defined($line2) and $line2 =~ /\Q$match2\E>$/) {
   7.955 +					$found = 1;
   7.956 +#print "YES: <".$match1." ".$match2.">\n";
   7.957 +				} else {
   7.958 +#print "NO: <".$match1." ".$match2.">\n";
   7.959 +					$i++;
   7.960 +				}
   7.961 +			} else {
   7.962 +				$found = 1;
   7.963 +			}
   7.964 +		} else {
   7.965 +			$i++;
   7.966 +		}
   7.967 +	}
   7.968 +	if (!$found) {
   7.969 +		#It should never enter here, unless you undefine the most
   7.970 +		#general tags (as <...>)
   7.971 +		die "po4a::xml: Unknown tag type: ".$line."\n";
   7.972 +	} else {
   7.973 +		return $i;
   7.974 +	}
   7.975 +}
   7.976 +
   7.977 +=item extract_tag($$)
   7.978 +
   7.979 +This function returns the next tag from the input stream without the beginning
   7.980 +and end, in an array form, to maintain the references from the input file.  It
   7.981 +has two parameters: the type of the tag (as returned by tag_type) and a
   7.982 +boolean, that indicates if it should be removed from the input stream.
   7.983 +
   7.984 +=cut
   7.985 +
   7.986 +sub extract_tag {
   7.987 +	my ($self,$type,$remove) = (shift,shift,shift);
   7.988 +	my ($match1,$match2) = ($tag_types[$type]->{beginning},$tag_types[$type]->{end});
   7.989 +	my ($eof,@tag);
   7.990 +	if (defined($tag_types[$type]->{f_extract})) {
   7.991 +		($eof,@tag) = &{$tag_types[$type]->{f_extract}}($self,$remove);
   7.992 +	} else {
   7.993 +		($eof,@tag) = $self->get_string_until($match2.">",{include=>1,remove=>$remove,unquoted=>1});
   7.994 +	}
   7.995 +	$tag[0] =~ /^<\Q$match1\E(.*)$/s;
   7.996 +	$tag[0] = $1;
   7.997 +	$tag[$#tag-1] =~ /^(.*)\Q$match2\E>$/s;
   7.998 +	$tag[$#tag-1] = $1;
   7.999 +	return ($eof,@tag);
  7.1000 +}
  7.1001 +
  7.1002 +=item get_tag_name(@)
  7.1003 +
  7.1004 +This function returns the name of the tag passed as an argument, in the array
  7.1005 +form returned by extract_tag.
  7.1006 +
  7.1007 +=cut
  7.1008 +
  7.1009 +sub get_tag_name {
  7.1010 +	my ($self,@tag)=@_;
  7.1011 +	$tag[0] =~ /^(\S*)/;
  7.1012 +	return $1;
  7.1013 +}
  7.1014 +
  7.1015 +=item breaking_tag()
  7.1016 +
  7.1017 +This function returns a boolean that says if the next tag in the input stream
  7.1018 +is a breaking tag or not (inline tag).  It leaves the input stream intact.
  7.1019 +
  7.1020 +=cut
  7.1021 +
  7.1022 +sub breaking_tag {
  7.1023 +	my $self = shift;
  7.1024 +	my $break;
  7.1025 +
  7.1026 +	my $type = $self->tag_type;
  7.1027 +	if ($type == -1) { return 0; }
  7.1028 +
  7.1029 +#print "TAG TYPE = ".$type."\n";
  7.1030 +	$break = $tag_types[$type]->{breaking};
  7.1031 +	if (!defined($break)) {
  7.1032 +		# This tag's breaking depends on its content
  7.1033 +		my ($eof,@lines) = $self->extract_tag($type,0);
  7.1034 +		$break = &{$tag_types[$type]->{f_breaking}}($self,@lines);
  7.1035 +	}
  7.1036 +#print "break = ".$break."\n";
  7.1037 +	return $break;
  7.1038 +}
  7.1039 +
  7.1040 +=item treat_tag()
  7.1041 +
  7.1042 +This function translates the next tag from the input stream.  Using each
  7.1043 +tag type's custom translation functions.
  7.1044 +
  7.1045 +=cut
  7.1046 +
  7.1047 +sub treat_tag {
  7.1048 +	my $self = shift;
  7.1049 +	my $type = $self->tag_type;
  7.1050 +
  7.1051 +	my ($match1,$match2) = ($tag_types[$type]->{beginning},$tag_types[$type]->{end});
  7.1052 +	my ($eof,@lines) = $self->extract_tag($type,1);
  7.1053 +
  7.1054 +	$lines[0] =~ /^(\s*)(.*)$/s;
  7.1055 +	my $space1 = $1;
  7.1056 +	$lines[0] = $2;
  7.1057 +	$lines[$#lines-1] =~ /^(.*?)(\s*)$/s;
  7.1058 +	my $space2 = $2;
  7.1059 +	$lines[$#lines-1] = $1;
  7.1060 +
  7.1061 +	# Calling this tag type's specific handling (translation of
  7.1062 +	# attributes...)
  7.1063 +	my $line = &{$tag_types[$type]->{f_translate}}($self,@lines);
  7.1064 +	$self->pushline("<".$match1.$space1.$line.$space2.$match2.">");
  7.1065 +	return $eof;
  7.1066 +}
  7.1067 +
  7.1068 +=item tag_in_list($@)
  7.1069 +
  7.1070 +This function returns a string value that says if the first argument (a tag
  7.1071 +hierarchy) matches any of the tags from the second argument (a list of tags
  7.1072 +or tag hierarchies). If it doesn't match, it returns 0. Else, it returns the
  7.1073 +matched tag's options (the characters in front of the tag) or 1 (if that tag
  7.1074 +doesn't have options).
  7.1075 +
  7.1076 +=back
  7.1077 +
  7.1078 +=cut
  7.1079 +sub tag_in_list ($$$) {
  7.1080 +	my ($self,$path,$list) = @_;
  7.1081 +	if ($self->{options}{'caseinsensitive'}) {
  7.1082 +		$path = lc $path;
  7.1083 +	}
  7.1084 +
  7.1085 +	while (1) {
  7.1086 +		if (defined $list->{$path}) {
  7.1087 +			if (length $list->{$path}) {
  7.1088 +				return $list->{$path};
  7.1089 +			} else {
  7.1090 +				return 1;
  7.1091 +			}
  7.1092 +		}
  7.1093 +		last unless ($path =~ m/</);
  7.1094 +		$path =~ s/^<.*?>//;
  7.1095 +	} 
  7.1096 +
  7.1097 +	return 0;
  7.1098 +}
  7.1099 +
  7.1100 +=head2 WORKING WITH ATTRIBUTES
  7.1101 +
  7.1102 +=over 4
  7.1103 +
  7.1104 +=item treat_attributes(@)
  7.1105 +
  7.1106 +This function handles the translation of the tags' attributes. It receives the tag
  7.1107 +without the beginning / end marks, and then it finds the attributes, and it
  7.1108 +translates the translatable ones (specified by the module option "attributes").
  7.1109 +This returns a plain string with the translated tag.
  7.1110 +
  7.1111 +=back
  7.1112 +
  7.1113 +=cut
  7.1114 +
  7.1115 +sub treat_attributes {
  7.1116 +	my ($self,@tag)=@_;
  7.1117 +
  7.1118 +	$tag[0] =~ /^(\S*)(.*)/s;
  7.1119 +	my $text = $1;
  7.1120 +	$tag[0] = $2;
  7.1121 +
  7.1122 +	while (@tag) {
  7.1123 +		my $complete = 1;
  7.1124 +
  7.1125 +		$text .= $self->skip_spaces(\@tag);
  7.1126 +		if (@tag) {
  7.1127 +			# Get the attribute's name
  7.1128 +			$complete = 0;
  7.1129 +
  7.1130 +			$tag[0] =~ /^([^\s=]+)(.*)/s;
  7.1131 +			my $name = $1;
  7.1132 +			my $ref = $tag[1];
  7.1133 +			$tag[0] = $2;
  7.1134 +			$text .= $name;
  7.1135 +			$text .= $self->skip_spaces(\@tag);
  7.1136 +			if (@tag) {
  7.1137 +				# Get the '='
  7.1138 +				if ($tag[0] =~ /^=(.*)/s) {
  7.1139 +					$tag[0] = $1;
  7.1140 +					$text .= "=";
  7.1141 +					$text .= $self->skip_spaces(\@tag);
  7.1142 +					if (@tag) {
  7.1143 +						# Get the value
  7.1144 +						my $value="";
  7.1145 +						$ref=$tag[1];
  7.1146 +						my $quot=substr($tag[0],0,1);
  7.1147 +						if ($quot ne "\"" and $quot ne "'") {
  7.1148 +							# Unquoted value
  7.1149 +							$quot="";
  7.1150 +							$tag[0] =~ /^(\S+)(.*)/s;
  7.1151 +							$value = $1;
  7.1152 +							$tag[0] = $2;
  7.1153 +						} else {
  7.1154 +							# Quoted value
  7.1155 +							$text .= $quot;
  7.1156 +							$tag[0] =~ /^\Q$quot\E(.*)/s;
  7.1157 +							$tag[0] = $1;
  7.1158 +							while ($tag[0] !~ /\Q$quot\E/) {
  7.1159 +								$value .= $tag[0];
  7.1160 +								shift @tag;
  7.1161 +								shift @tag;
  7.1162 +							}
  7.1163 +							$tag[0] =~ /^(.*?)\Q$quot\E(.*)/s;
  7.1164 +							$value .= $1;
  7.1165 +							$tag[0] = $2;
  7.1166 +						}
  7.1167 +						$complete = 1;
  7.1168 +						if ($self->tag_in_list($self->get_path.$name,$self->{attributes})) {
  7.1169 +							$text .= $self->found_string($value, $ref, { type=>"attribute", attribute=>$name });
  7.1170 +						} else {
  7.1171 +							print wrap_ref_mod($ref, "po4a::xml", dgettext("po4a", "Content of attribute %s excluded: %s"), $self->get_path.$name, $value)
  7.1172 +							       if $self->debug();
  7.1173 +							$text .= $self->recode_skipped_text($value);
  7.1174 +						}
  7.1175 +						$text .= $quot;
  7.1176 +					}
  7.1177 +				}
  7.1178 +			}
  7.1179 +          
  7.1180 +			unless ($complete) {
  7.1181 +				my $ontagerror = $self->{options}{'ontagerror'};
  7.1182 +				if ($ontagerror eq "warn") {
  7.1183 +					warn wrap_ref_mod($ref, "po4a::xml", dgettext ("po4a", "Bad attribute syntax.  Continuing..."));
  7.1184 +				} elsif ($ontagerror ne "silent") {
  7.1185 +					die wrap_ref_mod($ref, "po4a::xml", dgettext ("po4a", "Bad attribute syntax"));
  7.1186 +				}
  7.1187 +			}
  7.1188 +		}
  7.1189 +	}
  7.1190 +	return $text;
  7.1191 +}
  7.1192 +
  7.1193 +# Returns an empty string if the content in the $path should not be
  7.1194 +# translated.
  7.1195 +#
  7.1196 +# Otherwise, returns the set of options for translation:
  7.1197 +#   w: the content shall be re-wrapped
  7.1198 +#   W: the content shall not be re-wrapped
  7.1199 +#   i: the tag shall be inlined
  7.1200 +#   p: a placeholder shall replace the tag (and its content)
  7.1201 +#
  7.1202 +# A translatable inline tag in an untranslated tag is treated as a translatable breaking tag.
  7.1203 +my %translate_options_cache;
  7.1204 +sub get_translate_options {
  7.1205 +	my $self = shift;
  7.1206 +	my $path = shift;
  7.1207 +
  7.1208 +	if (defined $translate_options_cache{$path}) {
  7.1209 +		return $translate_options_cache{$path};
  7.1210 +	}
  7.1211 +
  7.1212 +	my $options = "";
  7.1213 +	my $translate = 0;
  7.1214 +	my $usedefault = 1;
  7.1215 +
  7.1216 +	my $inlist = 0;
  7.1217 +	my $tag = $self->get_tag_from_list($path, $self->{tags});
  7.1218 +	if (defined $tag) {
  7.1219 +		$inlist = 1;
  7.1220 +	}
  7.1221 +	if ($self->{options}{'tagsonly'} eq $inlist) {
  7.1222 +		$usedefault = 0;
  7.1223 +		if (defined $tag) {
  7.1224 +			$options = $tag;
  7.1225 +			$options =~ s/<.*$//;
  7.1226 +		} else {
  7.1227 +			if ($self->{options}{'wrap'}) {
  7.1228 +				$options = "w";
  7.1229 +			} else {
  7.1230 +				$options = "W";
  7.1231 +			}
  7.1232 +		}
  7.1233 +		$translate = 1;
  7.1234 +	}
  7.1235 +
  7.1236 +# TODO: a less precise set of tags should not override a more precise one
  7.1237 +	# The tags and tagsonly options are deprecated.
  7.1238 +	# The translated and untranslated options have an higher priority.
  7.1239 +	$tag = $self->get_tag_from_list($path, $self->{translated});
  7.1240 +	if (defined $tag) {
  7.1241 +		$usedefault = 0;
  7.1242 +		$options = $tag;
  7.1243 +		$options =~ s/<.*$//;
  7.1244 +		$translate = 1;
  7.1245 +	}
  7.1246 +
  7.1247 +	if ($translate and $options !~ m/w/i) {
  7.1248 +		$options .= ($self->{options}{'wrap'})?"w":"W";
  7.1249 +	}
  7.1250 +
  7.1251 +	if (not defined $tag) {
  7.1252 +		$tag = $self->get_tag_from_list($path, $self->{untranslated});
  7.1253 +		if (defined $tag) {
  7.1254 +			$usedefault = 0;
  7.1255 +			$options = "";
  7.1256 +			$translate = 0;
  7.1257 +		}
  7.1258 +	}
  7.1259 +
  7.1260 +	$tag = $self->get_tag_from_list($path, $self->{inline});
  7.1261 +	if (defined $tag) {
  7.1262 +		$usedefault = 0;
  7.1263 +		$options .= "i";
  7.1264 +	} else {
  7.1265 +		$tag = $self->get_tag_from_list($path, $self->{placeholder});
  7.1266 +		if (defined $tag) {
  7.1267 +			$usedefault = 0;
  7.1268 +			$options .= "p";
  7.1269 +		}
  7.1270 +	}
  7.1271 +
  7.1272 +	if ($usedefault) {
  7.1273 +		$options = $self->{options}{'defaulttranslateoption'};
  7.1274 +	}
  7.1275 +
  7.1276 +	# A translatable inline tag in an untranslated tag is treated as a
  7.1277 +	# translatable breaking tag.
  7.1278 +	if ($options =~ m/i/) {
  7.1279 +		my $ppath = $path;
  7.1280 +		$ppath =~ s/<[^>]*>$//;
  7.1281 +		my $poptions = $self->get_translate_options ($ppath);
  7.1282 +		if ($poptions eq "") {
  7.1283 +			$options =~ s/i//;
  7.1284 +		}
  7.1285 +	}
  7.1286 +
  7.1287 +	if ($options =~ m/i/ and $self->{options}{'foldattributes'}) {
  7.1288 +		$options .= "f";
  7.1289 +	}
  7.1290 +
  7.1291 +	$translate_options_cache{$path} = $options;
  7.1292 +	return $options;
  7.1293 +}
  7.1294 +
  7.1295 +
  7.1296 +# Return the tag (or biggest set of tags) of a list which matches with the
  7.1297 +# given path.
  7.1298 +#
  7.1299 +# The tag (or set of tags) is returned with its options.
  7.1300 +#
  7.1301 +# If no tags could match the path, undef is returned.
  7.1302 +sub get_tag_from_list ($$$) {
  7.1303 +	my ($self,$path,$list) = @_;
  7.1304 +	if ($self->{options}{'caseinsensitive'}) {
  7.1305 +		$path = lc $path;
  7.1306 +	}
  7.1307 +
  7.1308 +	while (1) {
  7.1309 +		if (defined $list->{$path}) {
  7.1310 +			return $list->{$path}.$path;
  7.1311 +		}
  7.1312 +		last unless ($path =~ m/</);
  7.1313 +		$path =~ s/^<.*?>//;
  7.1314 +	}
  7.1315 +
  7.1316 +	return undef;
  7.1317 +}
  7.1318 +
  7.1319 +
  7.1320 +
  7.1321 +sub treat_content {
  7.1322 +	my $self = shift;
  7.1323 +	my $blank="";
  7.1324 +	# Indicates if the paragraph will have to be translated
  7.1325 +	my $translate = "";
  7.1326 +
  7.1327 +	my ($eof,@paragraph)=$self->get_string_until('<',{remove=>1});
  7.1328 +
  7.1329 +	while (!$eof and !$self->breaking_tag) {
  7.1330 +	NEXT_TAG:
  7.1331 +		my @text;
  7.1332 +		my $type = $self->tag_type;
  7.1333 +		my $f_extract = $tag_types[$type]->{'f_extract'};
  7.1334 +		if (    defined($f_extract)
  7.1335 +		    and $f_extract eq \&tag_extract_comment) {
  7.1336 +			# Remove the content of the comments
  7.1337 +			($eof, @text) = $self->extract_tag($type,1);
  7.1338 +			$text[$#text-1] .= "\0";
  7.1339 +			if ($tag_types[$type]->{'beginning'} eq "!--#") {
  7.1340 +				$text[0] = "#".$text[0];
  7.1341 +			}
  7.1342 +			push @comments, @text;
  7.1343 +		} else {
  7.1344 +			my ($tmpeof, @tag) = $self->extract_tag($type,0);
  7.1345 +			# Append the found inline tag
  7.1346 +			($eof,@text)=$self->get_string_until('>',
  7.1347 +			                                     {include=>1,
  7.1348 +			                                      remove=>1,
  7.1349 +			                                      unquoted=>1});
  7.1350 +			# Append or remove the opening/closing tag from
  7.1351 +			# the tag path
  7.1352 +			if ($tag_types[$type]->{'end'} eq "") {
  7.1353 +				if ($tag_types[$type]->{'beginning'} eq "") {
  7.1354 +					# Opening inline tag
  7.1355 +					my $cur_tag_name = $self->get_tag_name(@tag);
  7.1356 +					my $t_opts = $self->get_translate_options($self->get_path($cur_tag_name));
  7.1357 +					if ($t_opts =~ m/p/) {
  7.1358 +						# We enter a new holder.
  7.1359 +						# Append a <placeholder ...> tag to the current
  7.1360 +						# paragraph, and save the @paragraph in the
  7.1361 +						# current holder.
  7.1362 +						my $last_holder = $save_holders[$#save_holders];
  7.1363 +						my $placeholder_str = "<placeholder type=\"".$cur_tag_name."\" id=\"".($#{$last_holder->{'sub_translations'}}+1)."\"/>";
  7.1364 +						push @paragraph, ($placeholder_str, $text[1]);
  7.1365 +						my @saved_paragraph = @paragraph;
  7.1366 +
  7.1367 +						$last_holder->{'paragraph'} = \@saved_paragraph;
  7.1368 +
  7.1369 +						# Then we must push a new holder
  7.1370 +						my @new_paragraph = ();
  7.1371 +						my @sub_translations = ();
  7.1372 +						my %folded_attributes;
  7.1373 +						my %new_holder = ('paragraph' => \@new_paragraph,
  7.1374 +						                  'open' => $text[0],
  7.1375 +						                  'translation' => "",
  7.1376 +						                  'close' => undef,
  7.1377 +						                  'sub_translations' => \@sub_translations,
  7.1378 +						                  'folded_attributes' => \%folded_attributes);
  7.1379 +						push @save_holders, \%new_holder;
  7.1380 +						@text = ();
  7.1381 +
  7.1382 +						# The current @paragraph
  7.1383 +						# (for the current holder)
  7.1384 +						# is empty.
  7.1385 +						@paragraph = ();
  7.1386 +					} elsif ($t_opts =~ m/f/) {
  7.1387 +						my $tag_full = $self->join_lines(@text);
  7.1388 +						my $tag_ref = $text[1];
  7.1389 +						if ($tag_full =~ m/^<\s*\S+\s+\S.*>$/s) {
  7.1390 +							my $holder = $save_holders[$#save_holders];
  7.1391 +							my $id = 0;
  7.1392 +							foreach (keys %{$holder->{folded_attributes}}) {
  7.1393 +								$id = $_ + 1 if ($_ >= $id);
  7.1394 +							}
  7.1395 +							$holder->{folded_attributes}->{$id} = $tag_full;
  7.1396 +
  7.1397 +							@text = ("<$cur_tag_name po4a-id=$id>", $tag_ref);
  7.1398 +						}
  7.1399 +					}
  7.1400 +					push @path, $cur_tag_name;
  7.1401 +				} elsif ($tag_types[$type]->{'beginning'} eq "/") {
  7.1402 +					# Closing inline tag
  7.1403 +
  7.1404 +					# Check if this is closing the
  7.1405 +					# last opening tag we detected.
  7.1406 +					my $test = pop @path;
  7.1407 +					my $name = $self->get_tag_name(@tag);
  7.1408 +					if (!defined($test) ||
  7.1409 +					    $test ne $name ) {
  7.1410 +						my $ontagerror = $self->{options}{'ontagerror'};
  7.1411 +						if ($ontagerror eq "warn") {
  7.1412 +							warn wrap_ref_mod($tag[1], "po4a::xml", dgettext("po4a", "Unexpected closing tag </%s> found. The main document may be wrong.  Continuing..."), $name);
  7.1413 +						} elsif ($ontagerror ne "silent") {
  7.1414 +							die wrap_ref_mod($tag[1], "po4a::xml", dgettext("po4a", "Unexpected closing tag </%s> found. The main document may be wrong."), $name);
  7.1415 +						}
  7.1416 +					}
  7.1417 +
  7.1418 +					if ($self->get_translate_options($self->get_path($self->get_tag_name(@tag))) =~ m/p/) {
  7.1419 +						# This closes the current holder.
  7.1420 +
  7.1421 +						push @path, $self->get_tag_name(@tag);
  7.1422 +						# Now translate this paragraph if needed.
  7.1423 +						# This will call pushline and append the
  7.1424 +						# translation to the current holder's translation.
  7.1425 +						$self->translate_paragraph(@paragraph);
  7.1426 +						pop @path;
  7.1427 +
  7.1428 +						# Now that this holder is closed, we can remove
  7.1429 +						# the holder from the stack.
  7.1430 +						my $holder = pop @save_holders;
  7.1431 +						# We need to keep the translation of this holder
  7.1432 +						my $translation = $holder->{'open'}.$holder->{'translation'}.$text[0];
  7.1433 +						# FIXME: @text could be multilines.
  7.1434 +
  7.1435 +						@text = ();
  7.1436 +
  7.1437 +						# Then we store the translation in the previous
  7.1438 +						# holder's sub_translations array
  7.1439 +						my $previous_holder = $save_holders[$#save_holders];
  7.1440 +						push @{$previous_holder->{'sub_translations'}}, $translation;
  7.1441 +						# We also need to restore the @paragraph array, as
  7.1442 +						# it was before we encountered the holder.
  7.1443 +						@paragraph = @{$previous_holder->{'paragraph'}};
  7.1444 +					}
  7.1445 +				}
  7.1446 +			}
  7.1447 +			push @paragraph, @text;
  7.1448 +		}
  7.1449 +
  7.1450 +		# Next tag
  7.1451 +		($eof,@text)=$self->get_string_until('<',{remove=>1});
  7.1452 +		if ($#text > 0) {
  7.1453 +			# Check if text (extracted after the inline tag)
  7.1454 +			# has to be translated
  7.1455 +			push @paragraph, @text;
  7.1456 +		}
  7.1457 +	}
  7.1458 +
  7.1459 +	# This strips the extracted strings
  7.1460 +	# (only if you don't specify the 'nostrip' option, and if the
  7.1461 +	# paragraph can be re-wrapped)
  7.1462 +	$translate = $self->get_translate_options($self->get_path);
  7.1463 +	if (!$self->{options}{'nostrip'} and $translate !~ m/W/) {
  7.1464 +		my $clean = 0;
  7.1465 +		# Clean the beginning
  7.1466 +		while (!$clean and $#paragraph > 0) {
  7.1467 +			$paragraph[0] =~ /^(\s*)(.*)/s;
  7.1468 +			my $match = $1;
  7.1469 +			if ($paragraph[0] eq $match) {
  7.1470 +				if ($match ne "") {
  7.1471 +					$self->pushline($match);
  7.1472 +				}
  7.1473 +				shift @paragraph;
  7.1474 +				shift @paragraph;
  7.1475 +			} else {
  7.1476 +				$paragraph[0] = $2;
  7.1477 +				if ($match ne "") {
  7.1478 +					$self->pushline($match);
  7.1479 +				}
  7.1480 +				$clean = 1;
  7.1481 +			}
  7.1482 +		}
  7.1483 +		$clean = 0;
  7.1484 +		# Clean the end
  7.1485 +		while (!$clean and $#paragraph > 0) {
  7.1486 +			$paragraph[$#paragraph-1] =~ /^(.*?)(\s*)$/s;
  7.1487 +			my $match = $2;
  7.1488 +			if ($paragraph[$#paragraph-1] eq $match) {
  7.1489 +				if ($match ne "") {
  7.1490 +					$blank = $match.$blank;
  7.1491 +				}
  7.1492 +				pop @paragraph;
  7.1493 +				pop @paragraph;
  7.1494 +			} else {
  7.1495 +				$paragraph[$#paragraph-1] = $1;
  7.1496 +				if ($match ne "") {
  7.1497 +					$blank = $match.$blank;
  7.1498 +				}
  7.1499 +				$clean = 1;
  7.1500 +			}
  7.1501 +		}
  7.1502 +	}
  7.1503 +
  7.1504 +	# Translate the string when needed
  7.1505 +	# This will either push the translation in the translated document or
  7.1506 +	# in the current holder translation.
  7.1507 +	$self->translate_paragraph(@paragraph);
  7.1508 +
  7.1509 +	# Push the trailing blanks
  7.1510 +	if ($blank ne "") {
  7.1511 +		$self->pushline($blank);
  7.1512 +	}
  7.1513 +	return $eof;
  7.1514 +}
  7.1515 +
  7.1516 +# Translate a @paragraph array of (string, reference).
  7.1517 +# The $translate argument indicates if the strings must be translated or
  7.1518 +# just pushed
  7.1519 +sub translate_paragraph {
  7.1520 +	my $self = shift;
  7.1521 +	my @paragraph = @_;
  7.1522 +	my $translate = $self->get_translate_options($self->get_path);
  7.1523 +
  7.1524 +	while (    (scalar @paragraph)
  7.1525 +	       and ($paragraph[0] =~ m/^\s*\n/s)) {
  7.1526 +		$self->pushline($paragraph[0]);
  7.1527 +		shift @paragraph;
  7.1528 +		shift @paragraph;
  7.1529 +	}
  7.1530 +
  7.1531 +	my $comments;
  7.1532 +	while (@comments) {
  7.1533 +		my ($comment,$eoc);
  7.1534 +		do {
  7.1535 +			my ($t,$l) = (shift @comments, shift @comments);
  7.1536 +			$t =~ s/\n?(\0)?$//;
  7.1537 +			$eoc = $1;
  7.1538 +			$comment .= "\n" if defined $comment;
  7.1539 +			$comment .= $t;
  7.1540 +		} until ($eoc);
  7.1541 +		$comments .= "\n" if defined $comments;
  7.1542 +		$comments .= $comment;
  7.1543 +		$self->pushline("<!--".$comment."-->\n") if defined $comment;
  7.1544 +	}
  7.1545 +	@comments = ();
  7.1546 +
  7.1547 +	if ($self->{options}{'cpp'}) {
  7.1548 +		my @tmp = @paragraph;
  7.1549 +		@paragraph = ();
  7.1550 +		while (@tmp) {
  7.1551 +			my ($t,$l) = (shift @tmp, shift @tmp);
  7.1552 +			# #include can be followed by a filename between
  7.1553 +			# <> brackets. In that case, the argument won't be
  7.1554 +			# handled in the same call to translate_paragraph.
  7.1555 +			# Thus do not try to match "include ".
  7.1556 +			if ($t =~ m/^#[ \t]*(if |endif|undef |include|else|ifdef |ifndef |define )/si) {
  7.1557 +				if (@paragraph) {
  7.1558 +					$self->translate_paragraph(@paragraph);
  7.1559 +					@paragraph = ();
  7.1560 +					$self->pushline("\n");
  7.1561 +				}
  7.1562 +				$self->pushline($t);
  7.1563 +			} else {
  7.1564 +				push @paragraph, ($t,$l);
  7.1565 +			}
  7.1566 +		}
  7.1567 +	}
  7.1568 +
  7.1569 +	my $para = $self->join_lines(@paragraph);
  7.1570 +	if ( length($para) > 0 ) {
  7.1571 +		if ($translate ne "") {
  7.1572 +			# This tag should be translated
  7.1573 +			$self->pushline($self->found_string(
  7.1574 +				$para,
  7.1575 +				$paragraph[1], {
  7.1576 +					type=>"tag",
  7.1577 +					tag_options=>$translate,
  7.1578 +					comments=>$comments
  7.1579 +				}));
  7.1580 +		} else {
  7.1581 +			# Inform that this tag isn't translated in debug mode
  7.1582 +			print wrap_ref_mod($paragraph[1], "po4a::xml", dgettext ("po4a", "Content of tag %s excluded: %s"), $self->get_path, $para)
  7.1583 +			       if $self->debug();
  7.1584 +			$self->pushline($self->recode_skipped_text($para));
  7.1585 +		}
  7.1586 +	}
  7.1587 +	# Now the paragraph is fully translated.
  7.1588 +	# If we have all the holders' translation, we can replace the
  7.1589 +	# placeholders by their translations.
  7.1590 +	# We must wait to have all the translations because the holders are
  7.1591 +	# numbered.
  7.1592 +	{
  7.1593 +		my $holder = $save_holders[$#save_holders];
  7.1594 +		my $translation = $holder->{'translation'};
  7.1595 +
  7.1596 +		# Count the number of <placeholder ...> in $translation
  7.1597 +		my $count = 0;
  7.1598 +		my $str = $translation;
  7.1599 +		while (    (defined $str)
  7.1600 +		       and ($str =~ m/^.*?<placeholder\s+type="[^"]+"\s+id="(\d+)"\s*\/>(.*)$/s)) {
  7.1601 +			$count += 1;
  7.1602 +			$str = $2;
  7.1603 +			if ($holder->{'sub_translations'}->[$1] =~ m/<placeholder\s+type="[^"]+"\s+id="(\d+)"\s*\/>/s) {
  7.1604 +				$count = -1;
  7.1605 +				last;
  7.1606 +			}
  7.1607 +		}
  7.1608 +
  7.1609 +		if (    (defined $translation)
  7.1610 +		    and (scalar(@{$holder->{'sub_translations'}}) == $count)) {
  7.1611 +			# OK, all the holders of the current paragraph are
  7.1612 +			# closed (and translated).
  7.1613 +			# Replace them by their translation.
  7.1614 +			while ($translation =~ m/^(.*?)<placeholder\s+type="[^"]+"\s+id="(\d+)"\s*\/>(.*)$/s) {
  7.1615 +				# FIXME: we could also check that
  7.1616 +				#          * the holder exists
  7.1617 +				#          * all the holders are used
  7.1618 +				$translation = $1.$holder->{'sub_translations'}->[$2].$3;
  7.1619 +			}
  7.1620 +			# We have our translation
  7.1621 +			$holder->{'translation'} = $translation;
  7.1622 +			# And there is no need for any holder in it.
  7.1623 +			my @sub_translations = ();
  7.1624 +			$holder->{'sub_translations'} = \@sub_translations;
  7.1625 +		}
  7.1626 +	}
  7.1627 +
  7.1628 +}
  7.1629 +
  7.1630 +
  7.1631 +
  7.1632 +=head2 WORKING WITH THE MODULE OPTIONS
  7.1633 +
  7.1634 +=over 4
  7.1635 +
  7.1636 +=item treat_options()
  7.1637 +
  7.1638 +This function fills the internal structures that contain the tags, attributes
  7.1639 +and inline data with the options of the module (specified in the command-line
  7.1640 +or in the initialize function).
  7.1641 +
  7.1642 +=back
  7.1643 +
  7.1644 +=cut
  7.1645 +
  7.1646 +sub treat_options {
  7.1647 +	my $self = shift;
  7.1648 +
  7.1649 +	if ($self->{options}{'caseinsensitive'}) {
  7.1650 +		$self->{options}{'nodefault'}             = lc $self->{options}{'nodefault'};
  7.1651 +		$self->{options}{'tags'}                  = lc $self->{options}{'tags'};
  7.1652 +		$self->{options}{'break'}                 = lc $self->{options}{'break'};
  7.1653 +		$self->{options}{'_default_break'}        = lc $self->{options}{'_default_break'};
  7.1654 +		$self->{options}{'translated'}            = lc $self->{options}{'translated'};
  7.1655 +		$self->{options}{'_default_translated'}   = lc $self->{options}{'_default_translated'};
  7.1656 +		$self->{options}{'untranslated'}          = lc $self->{options}{'untranslated'};
  7.1657 +		$self->{options}{'_default_untranslated'} = lc $self->{options}{'_default_untranslated'};
  7.1658 +		$self->{options}{'attributes'}            = lc $self->{options}{'attributes'};
  7.1659 +		$self->{options}{'_default_attributes'}   = lc $self->{options}{'_default_attributes'};
  7.1660 +		$self->{options}{'inline'}                = lc $self->{options}{'inline'};
  7.1661 +		$self->{options}{'_default_inline'}       = lc $self->{options}{'_default_inline'};
  7.1662 +		$self->{options}{'placeholder'}           = lc $self->{options}{'placeholder'};
  7.1663 +		$self->{options}{'_default_placeholder'}  = lc $self->{options}{'_default_placeholder'};
  7.1664 +	}
  7.1665 +
  7.1666 +	$self->{options}{'nodefault'} =~ /^\s*(.*)\s*$/s;
  7.1667 +	my %list_nodefault;
  7.1668 +	foreach (split(/\s+/s,$1)) {
  7.1669 +		$list_nodefault{$_} = 1;
  7.1670 +	}
  7.1671 +	$self->{nodefault} = \%list_nodefault;
  7.1672 +
  7.1673 +	$self->{options}{'tags'} =~ /^\s*(.*)\s*$/s;
  7.1674 +	if (length $self->{options}{'tags'}) {
  7.1675 +		warn wrap_mod("po4a::xml",
  7.1676 +		             dgettext("po4a",
  7.1677 +		                      "The '%s' option is deprecated. Please use the translated/untranslated and/or break/inline/placeholder categories."), "tags");
  7.1678 +	}
  7.1679 +	foreach (split(/\s+/s,$1)) {
  7.1680 +		$_ =~ m/^(.*?)(<.*)$/;
  7.1681 +		$self->{tags}->{$2} = $1 || "";
  7.1682 +	}
  7.1683 +
  7.1684 +	if ($self->{options}{'tagsonly'}) {
  7.1685 +		warn wrap_mod("po4a::xml",
  7.1686 +		             dgettext("po4a",
  7.1687 +		                      "The '%s' option is deprecated. Please use the translated/untranslated and/or break/inline/placeholder categories."), "tagsonly");
  7.1688 +	}
  7.1689 +
  7.1690 +	$self->{options}{'break'} =~ /^\s*(.*)\s*$/s;
  7.1691 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1692 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1693 +		$self->{break}->{$2} = $1 || "";
  7.1694 +	}
  7.1695 +	$self->{options}{'_default_break'} =~ /^\s*(.*)\s*$/s;
  7.1696 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1697 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1698 +		$self->{break}->{$2} = $1 || ""
  7.1699 +			unless    $list_nodefault{$2}
  7.1700 +			       or defined $self->{break}->{$2};
  7.1701 +	}
  7.1702 +
  7.1703 +	$self->{options}{'translated'} =~ /^\s*(.*)\s*$/s;
  7.1704 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1705 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1706 +		$self->{translated}->{$2} = $1 || "";
  7.1707 +	}
  7.1708 +	$self->{options}{'_default_translated'} =~ /^\s*(.*)\s*$/s;
  7.1709 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1710 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1711 +		$self->{translated}->{$2} = $1 || ""
  7.1712 +			unless    $list_nodefault{$2}
  7.1713 +			       or defined $self->{translated}->{$2};
  7.1714 +	}
  7.1715 +
  7.1716 +	$self->{options}{'untranslated'} =~ /^\s*(.*)\s*$/s;
  7.1717 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1718 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1719 +		$self->{untranslated}->{$2} = $1 || "";
  7.1720 +	}
  7.1721 +	$self->{options}{'_default_untranslated'} =~ /^\s*(.*)\s*$/s;
  7.1722 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1723 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1724 +		$self->{untranslated}->{$2} = $1 || ""
  7.1725 +			unless    $list_nodefault{$2}
  7.1726 +			       or defined $self->{untranslated}->{$2};
  7.1727 +	}
  7.1728 +
  7.1729 +	$self->{options}{'attributes'} =~ /^\s*(.*)\s*$/s;
  7.1730 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1731 +		if ($tag =~ m/^(.*?)(<.*)$/) {
  7.1732 +			$self->{attributes}->{$2} = $1 || "";
  7.1733 +		} else {
  7.1734 +			$self->{attributes}->{$tag} = "";
  7.1735 +		}
  7.1736 +	}
  7.1737 +	$self->{options}{'_default_attributes'} =~ /^\s*(.*)\s*$/s;
  7.1738 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1739 +		if ($tag =~ m/^(.*?)(<.*)$/) {
  7.1740 +			$self->{attributes}->{$2} = $1 || ""
  7.1741 +				unless    $list_nodefault{$2}
  7.1742 +				       or defined $self->{attributes}->{$2};
  7.1743 +		} else {
  7.1744 +			$self->{attributes}->{$tag} = ""
  7.1745 +				unless    $list_nodefault{$tag}
  7.1746 +				       or defined $self->{attributes}->{$tag};
  7.1747 +		}
  7.1748 +	}
  7.1749 +
  7.1750 +	my @list_inline;
  7.1751 +	$self->{options}{'inline'} =~ /^\s*(.*)\s*$/s;
  7.1752 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1753 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1754 +		$self->{inline}->{$2} = $1 || "";
  7.1755 +	}
  7.1756 +	$self->{options}{'_default_inline'} =~ /^\s*(.*)\s*$/s;
  7.1757 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1758 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1759 +		$self->{inline}->{$2} = $1 || ""
  7.1760 +			unless    $list_nodefault{$2}
  7.1761 +			       or defined $self->{inline}->{$2};
  7.1762 +	}
  7.1763 +
  7.1764 +	$self->{options}{'placeholder'} =~ /^\s*(.*)\s*$/s;
  7.1765 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1766 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1767 +		$self->{placeholder}->{$2} = $1 || "";
  7.1768 +	}
  7.1769 +	$self->{options}{'_default_placeholder'} =~ /^\s*(.*)\s*$/s;
  7.1770 +	foreach my $tag (split(/\s+/s,$1)) {
  7.1771 +		$tag =~ m/^(.*?)(<.*)$/;
  7.1772 +		$self->{placeholder}->{$2} = $1 || ""
  7.1773 +			unless    $list_nodefault{$2}
  7.1774 +			       or defined $self->{placeholder}->{$2};
  7.1775 +	}
  7.1776 +
  7.1777 +	# There should be no translated and untranslated tags
  7.1778 +	foreach my $tag (keys %{$self->{translated}}) {
  7.1779 +		die wrap_mod("po4a::xml",
  7.1780 +		             dgettext("po4a",
  7.1781 +		                      "Tag '%s' both in the %s and %s categories."), $tag, "translated", "untranslated")
  7.1782 +			if defined $self->{untranslated}->{$tag};
  7.1783 +	}
  7.1784 +	# There should be no inline, break, and placeholder tags
  7.1785 +	foreach my $tag (keys %{$self->{inline}}) {
  7.1786 +		die wrap_mod("po4a::xml",
  7.1787 +		             dgettext("po4a",
  7.1788 +		                      "Tag '%s' both in the %s and %s categories."), $tag, "inline", "break")
  7.1789 +			if defined $self->{break}->{$tag};
  7.1790 +		die wrap_mod("po4a::xml",
  7.1791 +		             dgettext("po4a",
  7.1792 +		                      "Tag '%s' both in the %s and %s categories."), $tag, "inline", "placeholder")
  7.1793 +			if defined $self->{placeholder}->{$tag};
  7.1794 +	}
  7.1795 +	foreach my $tag (keys %{$self->{break}}) {
  7.1796 +		die wrap_mod("po4a::xml",
  7.1797 +		             dgettext("po4a",
  7.1798 +		                      "Tag '%s' both in the %s and %s categories."), $tag, "break", "placeholder")
  7.1799 +			if defined $self->{placeholder}->{$tag};
  7.1800 +	}
  7.1801 +}
  7.1802 +
  7.1803 +=head2 GETTING TEXT FROM THE INPUT DOCUMENT
  7.1804 +
  7.1805 +=over
  7.1806 +
  7.1807 +=item get_string_until($%)
  7.1808 +
  7.1809 +This function returns an array with the lines (and references) from the input
  7.1810 +document until it finds the first argument.  The second argument is an options
  7.1811 +hash. Value 0 means disabled (the default) and 1, enabled.
  7.1812 +
  7.1813 +The valid options are:
  7.1814 +
  7.1815 +=over 4
  7.1816 +
  7.1817 +=item include
  7.1818 +
  7.1819 +This makes the returned array to contain the searched text
  7.1820 +
  7.1821 +=item remove
  7.1822 +
  7.1823 +This removes the returned stream from the input
  7.1824 +
  7.1825 +=item unquoted
  7.1826 +
  7.1827 +This ensures that the searched text is outside any quotes
  7.1828 +
  7.1829 +=back
  7.1830 +
  7.1831 +=cut
  7.1832 +
  7.1833 +sub get_string_until {
  7.1834 +	my ($self,$search) = (shift,shift);
  7.1835 +	my $options = shift;
  7.1836 +	my ($include,$remove,$unquoted, $regex) = (0,0,0,0);
  7.1837 +
  7.1838 +	if (defined($options->{include})) { $include = $options->{include}; }
  7.1839 +	if (defined($options->{remove})) { $remove = $options->{remove}; }
  7.1840 +	if (defined($options->{unquoted})) { $unquoted = $options->{unquoted}; }
  7.1841 +	if (defined($options->{regex})) { $regex = $options->{regex}; }
  7.1842 +
  7.1843 +	my ($line,$ref) = $self->shiftline();
  7.1844 +	my (@text,$paragraph);
  7.1845 +	my ($eof,$found) = (0,0);
  7.1846 +
  7.1847 +	$search = "\Q$search\E" unless $regex;
  7.1848 +	while (defined($line) and !$found) {
  7.1849 +		push @text, ($line,$ref);
  7.1850 +		$paragraph .= $line;
  7.1851 +		if ($unquoted) {
  7.1852 +			if ( $paragraph =~ /^((\".*?\")|(\'.*?\')|[^\"\'])*$search/s ) {
  7.1853 +				$found = 1;
  7.1854 +			}
  7.1855 +		} else {
  7.1856 +			if ( $paragraph =~ /$search/s ) {
  7.1857 +				$found = 1;
  7.1858 +			}
  7.1859 +		}
  7.1860 +		if (!$found) {
  7.1861 +			($line,$ref)=$self->shiftline();
  7.1862 +		}
  7.1863 +	}
  7.1864 +
  7.1865 +	if (!defined($line)) { $eof = 1; }
  7.1866 +
  7.1867 +	if ( $found ) {
  7.1868 +		$line = "";
  7.1869 +		if($unquoted) {
  7.1870 +			$paragraph =~ /^(?:(?:\".*?\")|(?:\'.*?\')|[^\"\'])*?$search(.*)$/s;
  7.1871 +			$line = $1;
  7.1872 +			$text[$#text-1] =~ s/\Q$line\E$//s;
  7.1873 +		} else {
  7.1874 +			$paragraph =~ /$search(.*)$/s;
  7.1875 +			$line = $1;
  7.1876 +			$text[$#text-1] =~ s/\Q$line\E$//s;
  7.1877 +		}
  7.1878 +		if(!$include) {
  7.1879 +			$text[$#text-1] =~ /^(.*)($search.*)$/s;
  7.1880 +			$text[$#text-1] = $1;
  7.1881 +			$line = $2.$line;
  7.1882 +		}
  7.1883 +		if (defined($line) and ($line ne "")) {
  7.1884 +			$self->unshiftline ($line,$text[$#text]);
  7.1885 +		}
  7.1886 +	}
  7.1887 +	if (!$remove) {
  7.1888 +		$self->unshiftline (@text);
  7.1889 +	}
  7.1890 +
  7.1891 +	#If we get to the end of the file, we return the whole paragraph
  7.1892 +	return ($eof,@text);
  7.1893 +}
  7.1894 +
  7.1895 +=item skip_spaces(\@)
  7.1896 +
  7.1897 +This function receives as argument the reference to a paragraph (in the format
  7.1898 +returned by get_string_until), skips his heading spaces and returns them as
  7.1899 +a simple string.
  7.1900 +
  7.1901 +=cut
  7.1902 +
  7.1903 +sub skip_spaces {
  7.1904 +	my ($self,$pstring)=@_;
  7.1905 +	my $space="";
  7.1906 +
  7.1907 +	while (@$pstring and (@$pstring[0] =~ /^(\s+)(.*)$/s or @$pstring[0] eq "")) {
  7.1908 +		if (@$pstring[0] ne "") {
  7.1909 +			$space .= $1;
  7.1910 +			@$pstring[0] = $2;
  7.1911 +		}
  7.1912 +
  7.1913 +		if (@$pstring[0] eq "") {
  7.1914 +			shift @$pstring;
  7.1915 +			shift @$pstring;
  7.1916 +		}
  7.1917 +	}
  7.1918 +	return $space;
  7.1919 +}
  7.1920 +
  7.1921 +=item join_lines(@)
  7.1922 +
  7.1923 +This function returns a simple string with the text from the argument array
  7.1924 +(discarding the references).
  7.1925 +
  7.1926 +=cut
  7.1927 +
  7.1928 +sub join_lines {
  7.1929 +	my ($self,@lines)=@_;
  7.1930 +	my ($line,$ref);
  7.1931 +	my $text = "";
  7.1932 +	while ($#lines > 0) {
  7.1933 +		($line,$ref) = (shift @lines,shift @lines);
  7.1934 +		$text .= $line;
  7.1935 +	}
  7.1936 +	return $text;
  7.1937 +}
  7.1938 +
  7.1939 +=back
  7.1940 +
  7.1941 +=head1 STATUS OF THIS MODULE
  7.1942 +
  7.1943 +This module can translate tags and attributes.
  7.1944 +
  7.1945 +=head1 TODO LIST
  7.1946 +
  7.1947 +DOCTYPE (ENTITIES)
  7.1948 +
  7.1949 +There is a minimal support for the translation of entities. They are
  7.1950 +translated as a whole, and tags are not taken into account. Multilines
  7.1951 +entities are not supported and entities are always rewrapped during the
  7.1952 +translation.
  7.1953 +
  7.1954 +MODIFY TAG TYPES FROM INHERITED MODULES
  7.1955 +(move the tag_types structure inside the $self hash?)
  7.1956 +
  7.1957 +=head1 SEE ALSO
  7.1958 +
  7.1959 +L<po4a(7)|po4a.7>, L<Locale::Po4a::TransTractor(3pm)|Locale::Po4a::TransTractor>.
  7.1960 +
  7.1961 +=head1 AUTHORS
  7.1962 +
  7.1963 + Jordi Vilalta <jvprat@gmail.com>
  7.1964 + Nicolas François <nicolas.francois@centraliens.net>
  7.1965 +
  7.1966 +=head1 COPYRIGHT AND LICENSE
  7.1967 +
  7.1968 + Copyright (c) 2004 by Jordi Vilalta  <jvprat@gmail.com>
  7.1969 + Copyright (c) 2008-2009 by Nicolas François <nicolas.francois@centraliens.net>
  7.1970 +
  7.1971 +This program is free software; you may redistribute it and/or modify it
  7.1972 +under the terms of GPL (see the COPYING file).
  7.1973 +
  7.1974 +=cut
  7.1975 +
  7.1976 +1;
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/tools/po4a/po4a-translate	Thu Mar 12 15:43:56 2009 +0800
     8.3 @@ -0,0 +1,257 @@
     8.4 +#! /usr/bin/env perl
     8.5 +eval 'exec perl -S $0 ${1+"$@"}'
     8.6 +    if $running_under_some_shell;
     8.7 +
     8.8 +# po4a-translate -- translate doc files using a message catalog(ie, po file)
     8.9 +# $Id: po4a-translate,v 1.41 2009-03-07 12:33:10 nekral-guest Exp $
    8.10 +#
    8.11 +# Copyright 2002, 2003, 2004 by Martin Quinson (mquinson#debian.org)
    8.12 +#
    8.13 +# This program is free software; you can redistribute it and/or modify it
    8.14 +# under the terms of GPL (see COPYING).
    8.15 +
    8.16 +=head1 NAME
    8.17 +
    8.18 +po4a-translate - convert a po file back to documentation format
    8.19 +
    8.20 +=head1 SYNOPSIS
    8.21 +
    8.22 +po4a-translate -f E<lt>fmtE<gt> -m E<lt>master.docE<gt> -p E<lt>XX.poE<gt> -l E<lt>XX.docE<gt>
    8.23 +
    8.24 +(XX.doc is the output, all others are inputs)
    8.25 +
    8.26 +=head1 DESCRIPTION
    8.27 +
    8.28 +The po4a (po for anything) project goal is to ease translations (and more
    8.29 +interestingly, the maintenance of translations) using gettext tools on
    8.30 +areas where they were not expected like documentation.
    8.31 +
    8.32 +The C<po4a-translate> script is in charge of converting the translation
    8.33 +(which was done in a po file) under the documentation format back. The
    8.34 +provided C<po> file should be the translation of the C<pot> file which were
    8.35 +produced by po4a-gettextize(1).
    8.36 +
    8.37 +=head1 OPTIONS
    8.38 +
    8.39 +=over 4
    8.40 +
    8.41 +=item -f, --format
    8.42 +
    8.43 +Format of the documentation you want to handle. Use the --help-format
    8.44 +option to see the list of available formats.
    8.45 +
    8.46 +=item -a, --addendum
    8.47 +
    8.48 +Add a file to the resulting file (to put translator's name or a section
    8.49 +"About this translation", for example). The first line of the file to insert
    8.50 +should be a PO4A header indicating where it should be added (see section
    8.51 +I<HOWTO add extra text to translations> in po4a(7)).
    8.52 +
    8.53 +=item -A, --addendum-charset
    8.54 +
    8.55 +Charset of the addenda. Note that all the addenda should be in the same
    8.56 +charset.
    8.57 +
    8.58 +=item -m, --master
    8.59 +
    8.60 +File containing the master document to translate.
    8.61 +
    8.62 +=item -M, --master-charset
    8.63 +
    8.64 +Charset of the file containing the document to translate.
    8.65 +
    8.66 +=item -l, --localized
    8.67 +
    8.68 +File where the localized (translated) document should be written.
    8.69 +
    8.70 +=item -L, --localized-charset
    8.71 +
    8.72 +Charset of the file containing the localized document.
    8.73 +
    8.74 +=item -p, --po
    8.75 +
    8.76 +File from which the message catalog should be read.
    8.77 +
    8.78 +=item -o, --option
    8.79 +
    8.80 +Extra option(s) to pass to the format plugin. Specify each option in the
    8.81 +'name=value' format. See the documentation of each plugin for more
    8.82 +information about the valid options and their meanings.
    8.83 +
    8.84 +=item -k, --keep
    8.85 +
    8.86 +Minimal threshold for translation percentage to keep (ie, write) the
    8.87 +resulting file (default: 80). Ie, by default, files have to be translated
    8.88 +at at least 80% to get written.
    8.89 +
    8.90 +=item -w, --width
    8.91 +
    8.92 +Column at which we should wrap the resulting file.
    8.93 +
    8.94 +=item -h, --help
    8.95 +
    8.96 +Show a short help message.
    8.97 +
    8.98 +=item --help-format
    8.99 +
   8.100 +List the documentation format understood by po4a.
   8.101 +
   8.102 +=item -V, --version
   8.103 +
   8.104 +Display the version of the script and exit.
   8.105 +
   8.106 +=item -v, --verbose
   8.107 +
   8.108 +Increase the verbosity of the program.
   8.109 +
   8.110 +=item -d, --debug
   8.111 +
   8.112 +Output some debugging information.
   8.113 +
   8.114 +=back
   8.115 +
   8.116 +=head1 Adding content (beside translations) to generated files
   8.117 +
   8.118 +To add some extra content to the generated document beside what you
   8.119 +translated (like the name of the translator, or a "about this translation"
   8.120 +section), you should use the C<--addendum> option. 
   8.121 +
   8.122 +The first line of the addendum must be a header indicating where to put
   8.123 +it in the document (it can be before or after a given part of the
   8.124 +document).  The rest of the file will be added verbatim to the resulting
   8.125 +file without further processing.
   8.126 +
   8.127 +Note that if po4a-translate fails to add one of the given files, it discards
   8.128 +the whole translation (because the missing file could be the one indicating
   8.129 +the author, what would prevent the users to contact him to report bugs in
   8.130 +the translation).
   8.131 +
   8.132 +The header has a pretty rigid syntax. For more information on how to use
   8.133 +this feature and how it works, please refer to the po4a(7) man page.
   8.134 +
   8.135 +=head1 SEE ALSO
   8.136 +
   8.137 +L<po4a(7)>, L<po4a-gettextize(1)>, L<po4a-updatepo(1)>, L<po4a-normalize(1)>.
   8.138 +
   8.139 +
   8.140 +=head1 AUTHORS
   8.141 +
   8.142 + Denis Barbier <barbier@linuxfr.org>
   8.143 + Martin Quinson (mquinson#debian.org)
   8.144 +
   8.145 +=head1 COPYRIGHT AND LICENSE
   8.146 +
   8.147 +Copyright 2002, 2003, 2004 by SPI, inc.
   8.148 +
   8.149 +This program is free software; you may redistribute it and/or modify it
   8.150 +under the terms of GPL (see the COPYING file).
   8.151 +
   8.152 +=cut
   8.153 +
   8.154 +use 5.006;
   8.155 +use strict;
   8.156 +use warnings;
   8.157 +
   8.158 +use Locale::Po4a::Chooser;
   8.159 +use Locale::Po4a::TransTractor;
   8.160 +use Locale::Po4a::Common;
   8.161 +
   8.162 +use Pod::Usage qw(pod2usage);
   8.163 +use Getopt::Long qw(GetOptions);
   8.164 +
   8.165 +Locale::Po4a::Common::textdomain("po4a");
   8.166 +
   8.167 +sub show_version {
   8.168 +    Locale::Po4a::Common::show_version("po4a-translate");
   8.169 +    exit 0;
   8.170 +}
   8.171 +
   8.172 +
   8.173 +Getopt::Long::Configure('no_auto_abbrev','no_ignore_case');
   8.174 +my ($outfile,$width,$threshold)=('-',80,80);
   8.175 +my ($help,$help_fmt,@verbose,$debug,@addfiles,$format,@options);
   8.176 +my ($master_filename,$po_filename);
   8.177 +my ($mastchar,$locchar,$addchar);
   8.178 +GetOptions(
   8.179 +	'help|h'        => \$help,
   8.180 +	'help-format'   => \$help_fmt,
   8.181 +
   8.182 +	'master|m=s'    => \$master_filename,
   8.183 +	'localized|l=s' => \$outfile,
   8.184 +	'po|p=s'        => \$po_filename,
   8.185 +	'addendum|a=s'  => \@addfiles,
   8.186 +	'format|f=s'    => \$format,
   8.187 +
   8.188 +	'master-charset|M=s'    => \$mastchar,
   8.189 +	'localized-charset|L=s' => \$locchar,
   8.190 +	'addendum-charset|A=s' => \$addchar,
   8.191 +
   8.192 +	'option|o=s'    => \@options,
   8.193 +
   8.194 +	'width|w=s'     => \$width,
   8.195 +	'verbose|v'     => \@verbose,
   8.196 +	'debug|d'       => \$debug,
   8.197 +	'keep|k=s'      => \$threshold,
   8.198 +
   8.199 +	'version|V'     => \&show_version
   8.200 +) or pod2usage();
   8.201 +
   8.202 +$help && pod2usage(-verbose => 1, -exitval => 0);
   8.203 +$help_fmt && Locale::Po4a::Chooser::list(0);
   8.204 +
   8.205 +(defined($master_filename) && length($master_filename))||pod2usage();
   8.206 +(defined($po_filename)     && length($po_filename))    ||pod2usage();
   8.207 +-e $master_filename || die wrap_msg(gettext("File %s does not exist."), $master_filename);
   8.208 +-e $po_filename || die wrap_msg(gettext("File %s does not exist."), $po_filename);
   8.209 +
   8.210 +my (@pos,@masters);
   8.211 +push @pos,$po_filename;
   8.212 +push @masters,$master_filename;
   8.213 +
   8.214 +my %options = (
   8.215 +    "verbose" => scalar @verbose,
   8.216 +    "debug" => $debug);
   8.217 +
   8.218 +foreach (@options) {
   8.219 +    if (m/^([^=]*)=(.*)$/) {
   8.220 +	$options{$1}="$2";
   8.221 +    } else {
   8.222 +	$options{$_}=1;
   8.223 +    }
   8.224 +}
   8.225 +# parser
   8.226 +my $doc=Locale::Po4a::Chooser::new($format,%options);
   8.227 +
   8.228 +
   8.229 +# Prepare the document to be used as translator, but not parser
   8.230 +$doc->process('po_in_name'       => \@pos,
   8.231 +	      'file_in_name'     => \@masters,
   8.232 +	      'file_in_charset'  => $mastchar,
   8.233 +	      'file_out_charset' => $locchar,
   8.234 +	      'addendum_charset' => $addchar);
   8.235 +
   8.236 +my ($percent,$hit,$queries) = $doc->stats();
   8.237 +my $error=0;
   8.238 +
   8.239 +print STDERR wrap_msg(gettext("%s is %s%% translated (%s of %s strings)."),
   8.240 +    $master_filename, $percent, $hit, $queries)
   8.241 +  if (scalar @verbose) && ($percent>=$threshold);
   8.242 +
   8.243 +
   8.244 +if ($percent<$threshold)  {
   8.245 +    print STDERR wrap_msg(gettext("Discard the translation of %s (only %s%% translated; need %s%%)."),
   8.246 +	$master_filename, $percent, $threshold);
   8.247 +    unlink($outfile) if (-e $outfile);
   8.248 +} else {
   8.249 +    foreach my $add (@addfiles) {
   8.250 +	unless ($doc->addendum($add)) {
   8.251 +	    unlink($outfile) if (-e $outfile);
   8.252 +	    die wrap_msg(gettext("Discard the translation of %s (addendum %s does not apply)."),
   8.253 +		$master_filename, $add);
   8.254 +	}
   8.255 +    }
   8.256 +    $doc->write($outfile);
   8.257 +}
   8.258 +
   8.259 +1;
   8.260 +
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/tools/po4a/po4a-updatepo	Thu Mar 12 15:43:56 2009 +0800
     9.3 @@ -0,0 +1,235 @@
     9.4 +#! /usr/bin/env perl
     9.5 +eval 'exec perl -S $0 ${1+"$@"}'
     9.6 +    if $running_under_some_shell;
     9.7 +
     9.8 +# pod-updatepo -- Update the po translation of POD data.
     9.9 +# $Id: po4a-updatepo,v 1.44 2009-03-07 12:33:10 nekral-guest Exp $
    9.10 +#
    9.11 +# Copyright 2002, 2003, 2004 by Martin Quinson (mquinson#debian.org)
    9.12 +#
    9.13 +# This program is free software; you can redistribute it and/or modify it
    9.14 +# under the terms of GPL (see COPYING).
    9.15 +
    9.16 +=head1 NAME
    9.17 +
    9.18 +po4a-updatepo - update the translation (in po format) of documentation
    9.19 +
    9.20 +=head1 SYNOPSIS
    9.21 +
    9.22 +po4a-updatepo -f E<lt>fmtE<gt> (-m E<lt>master.docE<gt>)+ (-p E<lt>XX.poE<gt>)+
    9.23 +
    9.24 +(XX.po are the outputs, all others are inputs)
    9.25 +
    9.26 +=head1 DESCRIPTION
    9.27 +
    9.28 +The po4a (po for anything) project goal is to ease translations (and more
    9.29 +interestingly, the maintenance of translations) using gettext tools on
    9.30 +areas where they were not expected like documentation.
    9.31 +
    9.32 +The C<po4a-updatepo> script is in charge of updating po files to make
    9.33 +them reflect the changes made to the original documentation file. For that,
    9.34 +it converts the documentation file to a pot file, and call L<msgmerge(1)>
    9.35 +on this new pot and on the provided po files.
    9.36 +
    9.37 +It is possible to give more than one po file (if you want to update several
    9.38 +languages at once), and several documentation files (if you want to store
    9.39 +the translations of several documents in the same po file).
    9.40 +
    9.41 +If the master document has non-ascii characters, it will convert the po files
    9.42 +to utf-8 (if they weren't already), in order to allow non-standard characters
    9.43 +in a culture independent way.
    9.44 +
    9.45 +=head1 COMMAND-LINE OPTIONS
    9.46 +
    9.47 +=over 4
    9.48 +
    9.49 +=item -f, --format
    9.50 +
    9.51 +Format of the documentation you want to handle. Use the --help-format
    9.52 +option to see the list of available formats.
    9.53 +
    9.54 +=item -m, --master
    9.55 +
    9.56 +File(s) containing the master document to translate.
    9.57 +
    9.58 +=item -M, --master-charset
    9.59 +
    9.60 +Charset of the files containing the document to translate. Note that all
    9.61 +files must have the same charset.
    9.62 +
    9.63 +=item -p, --po
    9.64 +
    9.65 +Po file(s) to update. If these files do not exist, they are created by
    9.66 +C<po4a-updatepo>.
    9.67 +
    9.68 +=item -o, --option
    9.69 +
    9.70 +Extra option(s) to pass to the format plugin and other po4a internal module.
    9.71 +Specify each option in the 'name=value' format. See the documentation of
    9.72 +each plugin for more information about the valid options and their meanings.
    9.73 +
    9.74 +=item --previous
    9.75 +
    9.76 +This option adds '--previous' to the options passed to msgmerge.
    9.77 +It requires gettext 0.16 or later.
    9.78 +
    9.79 +=item --msgmerge-opt options
    9.80 +
    9.81 +Extra options for msgmerge.
    9.82 +
    9.83 +=item -h, --help
    9.84 +
    9.85 +Show a short help message.
    9.86 +
    9.87 +=item --help-format
    9.88 +
    9.89 +List the documentation format handled by po4a.
    9.90 +
    9.91 +=item -V, --version
    9.92 +
    9.93 +Display the version of the script and exit.
    9.94 +
    9.95 +=item -v, --verbose
    9.96 +
    9.97 +Increase the verbosity of the program.
    9.98 +
    9.99 +=item -d, --debug
   9.100 +
   9.101 +Output some debugging information.
   9.102 +
   9.103 +=back
   9.104 +
   9.105 +=head1 SEE ALSO
   9.106 +
   9.107 +L<po4a(7)>, L<po4a-gettextize(1)>, L<po4a-translate(1)>, L<po4a-normalize(1)>.
   9.108 +
   9.109 +=head1 AUTHORS
   9.110 +
   9.111 + Denis Barbier <barbier@linuxfr.org>
   9.112 + Martin Quinson (mquinson#debian.org)
   9.113 +
   9.114 +=head1 COPYRIGHT AND LICENSE
   9.115 +
   9.116 +Copyright 2002, 2003, 2004, 2005 by SPI, inc.
   9.117 +
   9.118 +This program is free software; you may redistribute it and/or modify it
   9.119 +under the terms of GPL (see the COPYING file).
   9.120 +
   9.121 +=cut
   9.122 +
   9.123 +use 5.006;
   9.124 +use strict;
   9.125 +use warnings;
   9.126 +
   9.127 +use Getopt::Long qw(GetOptions);
   9.128 +use Locale::Po4a::Po;
   9.129 +
   9.130 +use Locale::Po4a::Chooser;
   9.131 +use Locale::Po4a::TransTractor;
   9.132 +use Locale::Po4a::Common;
   9.133 +
   9.134 +use Pod::Usage qw(pod2usage);
   9.135 +
   9.136 +use File::Temp;
   9.137 +
   9.138 +Locale::Po4a::Common::textdomain('po4a');
   9.139 +
   9.140 +sub show_version {
   9.141 +    Locale::Po4a::Common::show_version("po4a-updatepo");
   9.142 +    exit 0;
   9.143 +}
   9.144 +
   9.145 +
   9.146 +# init commandline parser
   9.147 +Getopt::Long::config('bundling', 'no_getopt_compat', 'no_auto_abbrev');
   9.148 +
   9.149 +# Parse our options
   9.150 +my (@masterfiles,@pofiles);
   9.151 +my ($help,$help_fmt,$verbose,$debug,$format,@options);
   9.152 +my $mastchar;
   9.153 +my $previous;
   9.154 +my $msgmerge_opt = "";
   9.155 +GetOptions('help|h'      => \$help,
   9.156 +	   'help-format' => \$help_fmt,
   9.157 +
   9.158 +	   'master|m=s'  => \@masterfiles,
   9.159 +	   'po|p=s'      => \@pofiles,
   9.160 +	   'format|f=s'  => \$format,
   9.161 +
   9.162 +	   'master-charset|M=s' => \$mastchar,
   9.163 +
   9.164 +	   'option|o=s'  => \@options,
   9.165 +
   9.166 +	   'previous'    => \$previous,
   9.167 +	   'msgmerge-opt=s' => \$msgmerge_opt,
   9.168 +    
   9.169 +	   'verbose|v'   => \$verbose,
   9.170 +	   'debug|d'     => \$debug,
   9.171 +	   'version|V'   => \&show_version)
   9.172 +    or pod2usage();
   9.173 +
   9.174 +$help && pod2usage (-verbose => 1, -exitval => 0);
   9.175 +$help_fmt && Locale::Po4a::Chooser::list(0);
   9.176 +pod2usage () if scalar @masterfiles < 1 || scalar @pofiles < 1;
   9.177 +
   9.178 +$msgmerge_opt .= " --previous" if $previous;
   9.179 +
   9.180 +my %options = (
   9.181 +    "verbose" => $verbose,
   9.182 +    "debug" => $debug);
   9.183 +
   9.184 +foreach (@options) {
   9.185 +    if (m/^([^=]*)=(.*)$/) {
   9.186 +	$options{$1}="$2";
   9.187 +    } else {
   9.188 +	$options{$_}=1;
   9.189 +    }
   9.190 +}
   9.191 +
   9.192 +# parser
   9.193 +my ($doc)=Locale::Po4a::Chooser::new($format,%options);
   9.194 +
   9.195 +map { -e $_ || die wrap_msg(gettext("File %s does not exist."), $_) } @masterfiles;
   9.196 +map { die wrap_msg(gettext("po4a-updatepo can't take the input po from stdin."))
   9.197 +	if $_ eq '-'  && !-e '-'} @pofiles;
   9.198 +
   9.199 +my ($pot_filename);
   9.200 +(undef,$pot_filename)=File::Temp->tempfile("po4a-updatepoXXXX",
   9.201 +					   DIR    => "/tmp",
   9.202 +					   SUFFIX => ".pot",
   9.203 +					   OPEN   => 0,
   9.204 +					   UNLINK => 0)
   9.205 +    or die wrap_msg(gettext("Can't create a temporary pot file: %s"), $!);
   9.206 +
   9.207 +
   9.208 +print STDERR wrap_msg(gettext("Parse input files... ")) if $verbose;
   9.209 +
   9.210 +$doc->{TT}{utf_mode} = 1;
   9.211 +
   9.212 +$doc->process('file_in_name'    => \@masterfiles,
   9.213 +	      'file_in_charset' => $mastchar,
   9.214 +	      'po_out_name'     => $pot_filename,
   9.215 +	      'debug'           => $debug,
   9.216 +	      'verbose'         => $verbose);
   9.217 +
   9.218 +print STDERR wrap_msg(gettext("done.")) if $verbose;
   9.219 +
   9.220 +
   9.221 +while (my $po_filename=shift @pofiles) {
   9.222 +    if (-e $po_filename) {
   9.223 +	print STDERR wrap_msg(gettext("Updating %s:"), $po_filename)
   9.224 +	    if $verbose;
   9.225 +	my $cmd = "msgmerge $msgmerge_opt -U $po_filename $pot_filename";
   9.226 +	system ($cmd) == 0
   9.227 +	    or die wrap_msg(gettext("Error while running msgmerge: %s"), $!);
   9.228 +	system "msgfmt --statistics -v -o /dev/null $po_filename"
   9.229 +	  if $verbose;
   9.230 +    } else {
   9.231 +	print STDERR wrap_msg(gettext("Creating %s:"), $po_filename)
   9.232 +	    if $verbose;
   9.233 +	system ("cp",$pot_filename,$po_filename) == 0
   9.234 +	    or die wrap_msg(gettext("Error while copying the po file: %s"), $!);
   9.235 +    }
   9.236 +}
   9.237 +
   9.238 +unlink($pot_filename);