[svnbook commit] r3464 - Import po4a(cut version) cvs trunk(2009-03-16) for PO based translation

codesite-noreply at google.com codesite-noreply at google.com
Thu Apr 2 00:56:17 CDT 2009


Author: dongsheng.song at gmail.com
Date: Wed Apr  1 22:49:13 2009
New Revision: 3464

Added:
    trunk/src/tools/po4a/
    trunk/src/tools/po4a/lib/
    trunk/src/tools/po4a/lib/Locale/
    trunk/src/tools/po4a/lib/Locale/Po4a/
    trunk/src/tools/po4a/lib/Locale/Po4a/Chooser.pm
    trunk/src/tools/po4a/lib/Locale/Po4a/Common.pm
    trunk/src/tools/po4a/lib/Locale/Po4a/Docbook.pm
    trunk/src/tools/po4a/lib/Locale/Po4a/Po.pm
    trunk/src/tools/po4a/lib/Locale/Po4a/TransTractor.pm
    trunk/src/tools/po4a/lib/Locale/Po4a/Xml.pm
    trunk/src/tools/po4a/po4a-translate   (contents, props changed)
    trunk/src/tools/po4a/po4a-updatepo   (contents, props changed)

Log:
Import po4a(cut version) cvs trunk(2009-03-16) for PO based translation


Added: trunk/src/tools/po4a/lib/Locale/Po4a/Chooser.pm
==============================================================================
--- (empty file)
+++ trunk/src/tools/po4a/lib/Locale/Po4a/Chooser.pm	Wed Apr  1 22:49:13 2009
@@ -0,0 +1,148 @@
+# Locale::Po4a::Pod -- Convert POD data to PO file, for translation.
+# $Id: Chooser.pm,v 1.41 2008-07-20 16:31:55 nekral-guest Exp $
+#
+# This program is free software; you may redistribute it and/or modify it
+# under the terms of GPL (see COPYING).
+#
+# This module converts POD to PO file, so that it becomes possible to
+# translate POD formatted documentation. See gettext documentation for
+# more info about PO files.
+
+############################################################################
+# Modules and declarations
+############################################################################
+
+
+package Locale::Po4a::Chooser;
+
+use 5.006;
+use strict;
+use warnings;
+use Locale::Po4a::Common;
+
+sub new {
+    my ($module)=shift;
+    my (%options)=@_;
+
+    die wrap_mod("po4a::chooser", gettext("Need to provide a module name"))
+      unless defined $module;
+
+    my $modname;
+    if ($module eq 'kernelhelp') {
+        $modname = 'KernelHelp';
+    } elsif ($module eq 'newsdebian') {
+        $modname = 'NewsDebian';
+    } elsif ($module eq 'latex') {
+        $modname = 'LaTeX';
+    } elsif ($module eq 'bibtex') {
+        $modname = 'BibTex';
+    } elsif ($module eq 'tex') {
+        $modname = 'TeX';
+    } else {
+        $modname = ucfirst($module);
+    }
+    if (! UNIVERSAL::can("Locale::Po4a::$modname", 'new')) {
+        eval qq{use Locale::Po4a::$modname};
+        if ($@) {
+            my $error=$@;
+            warn wrap_msg(gettext("Unknown format type: %s."), $module);
+	    warn wrap_mod("po4a::chooser",
+		gettext("Module loading error: %s"), $error)
+	      if defined $options{'verbose'} && $options{'verbose'} > 0;
+            list(1);
+        }
+    }
+    return "Locale::Po4a::$modname"->new(%options);
+}
+
+sub list {
+    warn wrap_msg(gettext("List of valid formats:")
+#	."\n  - ".gettext("bibtex: BibTex bibliography format.")
+	."\n  - ".gettext("dia: uncompressed Dia diagrams.")
+	."\n  - ".gettext("docbook: Docbook XML.")
+	."\n  - ".gettext("guide: Gentoo Linux's xml documentation format.")
+#	."\n  - ".gettext("html: HTML documents (EXPERIMENTAL).")
+	."\n  - ".gettext("ini: .INI format.")
+	."\n  - ".gettext("kernelhelp: Help messages of each kernel compilation  
option.")
+	."\n  - ".gettext("latex: LaTeX format.")
+	."\n  - ".gettext("man: Good old manual page format.")
+	."\n  - ".gettext("pod: Perl Online Documentation format.")
+	."\n  - ".gettext("sgml: either debiandoc or docbook DTD.")
+	."\n  - ".gettext("texinfo: The info page format.")
+	."\n  - ".gettext("tex: generic TeX documents (see also latex).")
+	."\n  - ".gettext("text: simple text document.")
+	."\n  - ".gettext("wml: WML documents.")
+	."\n  - ".gettext("xhtml: XHTML documents.")
+	."\n  - ".gettext("xml: generic XML documents (see also docbook).")
+    );
+    exit shift;
+}
+##############################################################################
+# Module return value and documentation
+##############################################################################
+
+1;
+__END__
+
+=head1 NAME
+
+Locale::Po4a::Chooser - Manage po4a modules
+
+=head1 DESCRIPTION
+
+Locale::Po4a::Chooser is a module to manage po4a modules. Before, all po4a
+binaries used to know all po4a modules (pod, man, sgml, etc). This made the
+add of a new module boring, to make sure the documentation is synchronized
+in all modules, and that each of them can access the new module.
+
+Now, you just have to call the Locale::Po4a::Chooser::new() function,
+passing the name of module as argument.
+
+You also have the Locale::Po4a::Chooser::list() function which lists the
+available format and exits on the value passed as argument.
+
+=head1 SEE ALSO
+
+=over 4
+
+=item About po4a:
+
+L<po4a(7)|po4a.7>,
+L<Locale::Po4a::TransTractor(3pm)>,
+L<Locale::Po4a::Po(3pm)>
+
+=item About modules:
+
+L<Locale::Po4a::Dia(3pm)>,
+L<Locale::Po4a::Docbook(3pm)>,
+L<Locale::Po4a::Guide(3pm)>,
+L<Locale::Po4a::Halibut(3pm)>,
+L<Locale::Po4a::Ini(3pm)>,
+L<Locale::Po4a::KernelHelp(3pm)>,
+L<Locale::Po4a::LaTeX(3pm)>,
+L<Locale::Po4a::Man(3pm)>,
+L<Locale::Po4a::Pod(3pm)>,
+L<Locale::Po4a::Sgml(3pm)>,
+L<Locale::Po4a::TeX(3pm)>,
+L<Locale::Po4a::Texinfo(3pm)>,
+L<Locale::Po4a::Text(3pm)>,
+L<Locale::Po4a::Wml(3pm)>.
+L<Locale::Po4a::Xhtml(3pm)>,
+L<Locale::Po4a::Xml(3pm)>,
+L<Locale::Po4a::Wml(3pm)>.
+
+=back
+
+=head1 AUTHORS
+
+ Denis Barbier <barbier at linuxfr.org>
+ Martin Quinson (mquinson#debian.org)
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2002,2003,2004,2005 by SPI, inc.
+
+This program is free software; you may redistribute it and/or modify it
+under the terms of GPL (see the COPYING file).
+
+=cut

Added: trunk/src/tools/po4a/lib/Locale/Po4a/Common.pm
==============================================================================
--- (empty file)
+++ trunk/src/tools/po4a/lib/Locale/Po4a/Common.pm	Wed Apr  1 22:49:13 2009
@@ -0,0 +1,246 @@
+# Locale::Po4a::Common -- Common parts of the po4a scripts and utils
+# $Id: Common.pm,v 1.20 2009-02-13 23:16:44 nekral-guest Exp $
+#
+# Copyright 2005 by Jordi Vilalta <jvprat at gmail.com>
+#
+# This program is free software; you may redistribute it and/or modify it
+# under the terms of GPL (see COPYING).
+#
+# This module has common utilities for the various scripts of po4a
+
+=head1 NAME
+
+Locale::Po4a::Common - Common parts of the po4a scripts and utils
+
+=head1 DESCRIPTION
+
+Locale::Po4a::Common contains common parts of the po4a scripts and some  
useful
+functions used along the other modules.
+
+In order to use Locale::Po4a programatically, one may want to disable
+the use of Text::WrapI18N, by writing e.g.
+
+    use Locale::Po4a::Common qw(nowrapi18n);
+    use Locale::Po4a::Text;
+
+instead of:
+
+    use Locale::Po4a::Text;
+
+Ordering is important here: as most Locale::Po4a modules themselves
+load Locale::Po4a::Common, the first time this module is loaded
+determines whether Text::WrapI18N is used.
+
+=cut
+
+package Locale::Po4a::Common;
+
+require Exporter;
+use vars qw(@ISA @EXPORT);
+ at ISA = qw(Exporter);
+ at EXPORT = qw(wrap_msg wrap_mod wrap_ref_mod textdomain gettext dgettext);
+
+use 5.006;
+use strict;
+use warnings;
+
+sub import {
+    my $class=shift;
+
+    my $wrapi18n=1;
+    if (exists $_[0] && defined $_[0] && $_[0] eq 'nowrapi18n') {
+        shift;
+        $wrapi18n=0;
+    }
+    $class->export_to_level(1, $class, @_);
+
+    return if defined &wrapi18n;
+
+    if ($wrapi18n && -t STDERR && -t STDOUT && eval { require  
Text::WrapI18N }) {
+
+        # Don't bother determining the wrap column if we cannot wrap.
+        my $col=$ENV{COLUMNS};
+        if (!defined $col) {
+            my @term=eval "use Term::ReadKey;  
Term::ReadKey::GetTerminalSize()";
+            $col=$term[0] if (!$@);
+            # If GetTerminalSize() failed we will fallback to a safe  
default.
+            # This can happen if Term::ReadKey is not available
+            # or this is a terminal-less build or such strange condition.
+        }
+        $col=76 if (!defined $col);
+
+        eval ' use Text::WrapI18N qw($columns);
+               $columns = $col;
+             ';
+
+        eval ' sub wrapi18n($$$) { Text::WrapI18N::wrap($_[0],$_[1],$_[2])  
} '
+    } else {
+
+        # If we cannot wrap, well, that's too bad. Survive anyway.
+        eval ' sub wrapi18n($$$) { $_[0].$_[2] } '
+    }
+}
+
+sub min($$) {
+    return $_[0] < $_[1] ? $_[0] : $_[1];
+}
+
+=head1 FUNCTIONS
+
+=head2 Showing output messages
+
+=over
+
+=item
+
+show_version($)
+
+Shows the current version of the script, and a short copyright message. It
+takes the name of the script as an argument.
+
+=cut
+
+sub show_version {
+    my $name = shift;
+
+    print sprintf(gettext(
+	"%s version %s.\n".
+	"written by Martin Quinson and Denis Barbier.\n\n".
+	"Copyright (C) 2002, 2003, 2004 Software of Public Interest, Inc.\n".
+	"This is free software; see source code for copying\n".
+	"conditions. There is NO warranty; not even for\n".
+	"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+	), $name, $Locale::Po4a::TransTractor::VERSION)."\n";
+}
+
+=item
+
+wrap_msg($@)
+
+This function displays a message the same way than sprintf() does, but  
wraps
+the result so that they look nice on the terminal.
+
+=cut
+
+sub wrap_msg($@) {
+    my $msg = shift;
+    my @args = @_;
+
+    return wrapi18n("", "", sprintf($msg, @args))."\n";
+}
+
+=item
+
+wrap_mod($$@)
+
+This function works like wrap_msg(), but it takes a module name as the  
first
+argument, and leaves a space at the left of the message.
+
+=cut
+
+sub wrap_mod($$@) {
+    my ($mod, $msg) = (shift, shift);
+    my @args = @_;
+
+    $mod .= ": ";
+    my $spaces = " " x min(length($mod), 15);
+    return wrapi18n($mod, $spaces, sprintf($msg, @args))."\n";
+}
+
+=item
+
+wrap_ref_mod($$$@)
+
+This function works like wrap_msg(), but it takes a file:line reference as  
the
+first argument, a module name as the second one, and leaves a space at the  
left
+of the message.
+
+=back
+
+=cut
+
+sub wrap_ref_mod($$$@) {
+    my ($ref, $mod, $msg) = (shift, shift, shift);
+    my @args = @_;
+
+    if (!$mod) {
+	# If we don't get a module name, show the message like wrap_mod does
+	return wrap_mod($ref, $msg, @args);
+    } else {
+	$ref .= ": ";
+	my $spaces = " " x min(length($ref), 15);
+	$msg = "$ref($mod)\n$msg";
+	return wrapi18n("", $spaces, sprintf($msg, @args))."\n";
+    }
+}
+
+=head2 Wrappers for other modules
+
+=over
+
+=item
+
+Locale::Gettext
+
+When the Locale::Gettext module cannot be loaded, this module provide dummy
+(empty) implementation of the following functions. In that case, po4a
+messages won't get translated but the program will continue to work.
+
+If Locale::gettext is present, this wrapper also calls
+setlocale(LC_MESSAGES, "") so callers don't depend on the POSIX module
+either.
+
+=over
+
+=item
+
+bindtextdomain($$)
+
+=item
+
+textdomain($)
+
+=item
+
+gettext($)
+
+=item
+
+dgettext($$)
+
+=back
+
+=back
+
+=cut
+
+BEGIN {
+    if (eval { require Locale::gettext }) {
+       import Locale::gettext;
+       require POSIX;
+       POSIX::setlocale(&POSIX::LC_MESSAGES, '');
+    } else {
+       eval '
+           sub bindtextdomain($$) { }
+           sub textdomain($) { }
+           sub gettext($) { shift }
+           sub dgettext($$) { return $_[1] }
+       '
+    }
+}
+
+1;
+__END__
+
+=head1 AUTHORS
+
+ Jordi Vilalta <jvprat at gmail.com>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2005 by SPI, inc.
+
+This program is free software; you may redistribute it and/or modify it
+under the terms of GPL (see the COPYING file).
+
+=cut

Added: trunk/src/tools/po4a/lib/Locale/Po4a/Docbook.pm
==============================================================================
--- (empty file)
+++ trunk/src/tools/po4a/lib/Locale/Po4a/Docbook.pm	Wed Apr  1 22:49:13 2009
@@ -0,0 +1,2040 @@
+#!/usr/bin/perl
+# aptitude: cmdsynopsis => missing removal of leading spaces
+
+# Po4a::Docbook.pm
+#
+# extract and translate translatable strings from Docbook XML documents.
+#
+# This code extracts plain text from tags and attributes on Docbook XML
+# documents.
+#
+# Copyright (c) 2004 by Jordi Vilalta  <jvprat at gmail.com>
+# Copyright (c) 2007-2009 by Nicolas François  
<nicolas.francois at centraliens.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+########################################################################
+
+=head1 NAME
+
+Locale::Po4a::Docbook - Convert Docbook XML documents from/to PO files
+
+=head1 DESCRIPTION
+
+The po4a (po for anything) project goal is to ease translations (and more
+interestingly, the maintenance of translations) using gettext tools on
+areas where they were not expected like documentation.
+
+Locale::Po4a::Docbook is a module to help the translation of DocBook XML
+documents into other [human] languages.
+
+Please note that this module is still under heavy development, and not
+distributed in official po4a release since we don't feel it to be mature
+enough. If you insist on trying, check the CVS out.
+
+=head1 STATUS OF THIS MODULE
+
+This module is fully functional, as it relies in the L<Locale::Po4a::Xml>
+module. This only defines the translatable tags and attributes.
+
+The only known issue is that it doesn't handle entities yet, and this  
includes
+the file inclusion entities, but you can translate most of those files  
alone
+(except the typical entities files), and it's usually better to maintain  
them
+separated.
+
+=head1 SEE ALSO
+
+L<po4a(7)|po4a.7>, L<Locale::Po4a::TransTractor(3pm)>,  
L<Locale::Po4a::Xml(3pm)>.
+
+=head1 AUTHORS
+
+ Jordi Vilalta <jvprat at gmail.com>
+
+=head1 COPYRIGHT AND LICENSE
+
+ Copyright (c) 2004 by Jordi Vilalta  <jvprat at gmail.com>
+ Copyright (c) 2007-2009 by Nicolas François  
<nicolas.francois at centraliens.net>
+
+This program is free software; you may redistribute it and/or modify it
+under the terms of GPL (see the COPYING file).
+
+=cut
+
+package Locale::Po4a::Docbook;
+
+use 5.006;
+use strict;
+use warnings;
+
+use Locale::Po4a::Xml;
+
+use vars qw(@ISA);
+ at ISA = qw(Locale::Po4a::Xml);
+
+sub initialize {
+	my $self = shift;
+	my %options = @_;
+
+	$self->SUPER::initialize(%options);
+	$self->{options}{'wrap'}=1;
+	$self->{options}{'doctype'}=$self->{options}{'doctype'} || 'docbook xml';
+
+#  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+
+	# abbrev; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <abbrev>";
+	$self->{options}{'_default_inline'} .= " <abbrev>";
+
+	# abstract; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <abstract>";
+	$self->{options}{'_default_break'} .= " <abstract>";
+
+	# accel; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <accel>";
+	$self->{options}{'_default_inline'} .= " <accel>";
+
+	# ackno; does not contain text; Formatted as a displayed block
+	# Replaced by acknowledgements in Docbook v5.0
+	$self->{options}{'_default_untranslated'} .= " <ackno>";
+	$self->{options}{'_default_break'} .= " <ackno>";
+	# acknowledgements; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <acknowledgements>";
+	$self->{options}{'_default_break'} .= " <acknowledgements>";
+
+	# acronym; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <acronym>";
+	$self->{options}{'_default_inline'} .= " <acronym>";
+
+	# action; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <action>";
+	$self->{options}{'_default_inline'} .= " <action>";
+
+	# address; contains text; Formatted as a displayed block; verbatim
+	$self->{options}{'_default_translated'} .= " W<address>";
+	$self->{options}{'_default_placeholder'} .= " <address>";
+
+	# affiliation; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_untranslated'} .= " <affiliation>";
+	$self->{options}{'_default_inline'} .= " <affiliation>";
+
+	# alt; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <alt>";
+	$self->{options}{'_default_inline'} .= " <alt>";
+
+	# anchor; does not contain text; Produces no output
+	$self->{options}{'_default_untranslated'} .= " <anchor>";
+	$self->{options}{'_default_inline'} .= " <anchor>";
+
+	# annotation; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <annotation>";
+	$self->{options}{'_default_placeholder'} .= " <annotation>";
+
+	# answer; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <answer>";
+	$self->{options}{'_default_break'} .= " <answer>";
+
+	# appendix; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <appendix>";
+	$self->{options}{'_default_break'} .= " <appendix>";
+
+	# appendixinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <appendixinfo>";
+	$self->{options}{'_default_placeholder'} .= " <appendixinfo>";
+
+	# application; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <application>";
+	$self->{options}{'_default_inline'} .= " <application>";
+
+	# arc; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <arc>";
+	$self->{options}{'_default_inline'} .= " <arc>";
+
+	# area; does not contain text;
+	# NOTE: the area is not translatable as is, but the coords
+	# attribute might be.
+	$self->{options}{'_default_untranslated'} .= " <area>";
+	$self->{options}{'_default_inline'} .= " <area>";
+
+	# areaset; does not contain text;
+	# NOTE: the areaset is not translatable as is. depending on the
+	# language there might be more or less area tags inside.
+	$self->{options}{'_default_untranslated'} .= " <areaset>";
+	$self->{options}{'_default_inline'} .= " <areaset>";
+
+	# areaspec; does not contain text;
+	# NOTE: see area and areaset
+	$self->{options}{'_default_translated'} .= " <areaspec>";
+	$self->{options}{'_default_break'} .= " <areaspec>";
+
+	# arg; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <arg>";
+	$self->{options}{'_default_inline'} .= " <arg>";
+
+	# artheader; does not contain text; renamed to articleinfo in v4.0
+	$self->{options}{'_default_untranslated'} .= " <artheader>";
+	$self->{options}{'_default_placeholder'} .= " <artheader>";
+
+	# article; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <article>";
+	$self->{options}{'_default_break'} .= " <article>";
+
+	# articleinfo; does not contain text; v4 only
+	$self->{options}{'_default_untranslated'} .= " <articleinfo>";
+	$self->{options}{'_default_placeholder'} .= " <articleinfo>";
+
+	# artpagenums; contains text; Formatted inline
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <artpagenums>";
+	$self->{options}{'_default_inline'} .= " <artpagenums>";
+
+	# attribution; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <attribution>";
+	$self->{options}{'_default_inline'} .= " <attribution>";
+
+	# audiodata; does not contain text;
+	# NOTE: the attributes might be translated
+	$self->{options}{'_default_translated'} .= " <audiodata>";
+	$self->{options}{'_default_placeholder'} .= " <audiodata>";
+	$self->{options}{'_default_attributes'}.=' <audiodata>fileref';
+
+	# audioobject; does not contain text;
+	# NOTE: might be contaioned in a inlinemediaobject
+	$self->{options}{'_default_translated'} .= " <audioobject>";
+	$self->{options}{'_default_placeholder'} .= " <audioobject>";
+
+	# author; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_untranslated'} .= " <author>";
+	$self->{options}{'_default_inline'} .= " <author>";
+
+	# authorblurb; does not contain text; Formatted as a displayed block.
+	# v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <authorblurb>";
+	$self->{options}{'_default_placeholder'} .= " <authorblurb>";
+
+	# authorgroup; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	# NOTE: given the possible parents, it is probably very rarely
+	#       inlined
+	$self->{options}{'_default_untranslated'} .= " <authorgroup>";
+	$self->{options}{'_default_break'} .= " <authorgroup>";
+
+	# authorinitials; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <authorinitials>";
+	$self->{options}{'_default_inline'} .= " <authorinitials>";
+
+#  
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+
+	# beginpage; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <beginpage>";
+	$self->{options}{'_default_break'} .= " <beginpage>";
+
+	# bibliocoverage; contains text; Formatted inline
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <bibliocoverage>";
+	$self->{options}{'_default_inline'} .= " <bibliocoverage>";
+
+	# bibliodiv; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <bibliodiv>";
+	$self->{options}{'_default_break'} .= " <bibliodiv>";
+
+	# biblioentry; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <biblioentry>";
+	$self->{options}{'_default_break'} .= " <biblioentry>";
+
+	# bibliography; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <bibliography>";
+	$self->{options}{'_default_break'} .= " <bibliography>";
+
+	# bibliographyinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <bibliographyinfo>";
+	$self->{options}{'_default_placeholder'} .= " <bibliographyinfo>";
+
+	# biblioid; contains text; Formatted inline
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <biblioid>";
+	$self->{options}{'_default_inline'} .= " <biblioid>";
+
+	# bibliolist; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <bibliolist>";
+	$self->{options}{'_default_break'} .= " <bibliolist>";
+
+	# bibliomisc; contains text; Formatted inline
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <bibliomisc>";
+	$self->{options}{'_default_inline'} .= " <bibliomisc>";
+
+	# bibliomixed; contains text; Formatted as a displayed block
+	$self->{options}{'_default_translated'} .= " <bibliomixed>";
+	$self->{options}{'_default_placeholder'} .= " <bibliomixed>";
+
+	# bibliomset; contains text; Formatted as a displayed block
+	# NOTE: content might need to be inlined, e.g. <bibliomset><title>
+	$self->{options}{'_default_translated'} .= " <bibliomset>";
+	$self->{options}{'_default_placeholder'} .= " <bibliomset>";
+
+	# biblioref; does not contain text; Formatted inline
+	$self->{options}{'_default_untranslated'} .= " <biblioref>";
+	$self->{options}{'_default_inline'} .= " <biblioref>";
+
+	# bibliorelation; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <bibliorelation>";
+	$self->{options}{'_default_inline'} .= " <bibliorelation>";
+
+	# biblioset; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <biblioset>";
+	$self->{options}{'_default_break'} .= " <biblioset>";
+
+	# bibliosource; contains text; Formatted inline
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <bibliosource>";
+	$self->{options}{'_default_inline'} .= " <bibliosource>";
+
+	# blockinfo; does not contain text; v4.2, not in v5
+	$self->{options}{'_default_untranslated'} .= " <blockinfo>";
+	$self->{options}{'_default_placeholder'} .= " <blockinfo>";
+
+	# blockquote; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <blockquote>";
+	$self->{options}{'_default_break'} .= " <blockquote>";
+
+	# book; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <book>";
+	$self->{options}{'_default_break'} .= " <book>";
+
+	# bookbiblio; does not contain text; Formatted as a displayed block
+	# Removed in v4.0
+	$self->{options}{'_default_untranslated'} .= " <bookbiblio>";
+	$self->{options}{'_default_break'} .= " <bookbiblio>";
+
+	# bookinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <bookinfo>";
+	$self->{options}{'_default_placeholder'} .= " <bookinfo>";
+
+	# bridgehead; contains text; Formatted as a displayed block
+	$self->{options}{'_default_translated'} .= " <bridgehead>";
+	$self->{options}{'_default_break'} .= " <bridgehead>";
+
+#  
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+
+	# callout; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <callout>";
+	$self->{options}{'_default_break'} .= " <callout>";
+
+	# calloutlist; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <calloutlist>";
+	$self->{options}{'_default_break'} .= " <calloutlist>";
+
+	# caption; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <caption>";
+	$self->{options}{'_default_break'} .= " <caption>";
+
+	# caption (db.html.caption); contains text; Formatted as a displayed block
+	# TODO: Check if this works
+	$self->{options}{'_default_translated'} .= " <table><caption>";
+	$self->{options}{'_default_break'} .= " <table><caption>";
+
+	# caution; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <caution>";
+	$self->{options}{'_default_break'} .= " <caution>";
+
+	# chapter; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <chapter>";
+	$self->{options}{'_default_break'} .= " <chapter>";
+
+	# chapterinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <chapterinfo>";
+	$self->{options}{'_default_placeholder'} .= " <chapterinfo>";
+
+	# citation; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <citation>";
+	$self->{options}{'_default_inline'} .= " <citation>";
+
+	# citebiblioid; contains text; Formatted inline
+	# NOTE: maybe untranslated?
+	$self->{options}{'_default_translated'} .= " <citebiblioid>";
+	$self->{options}{'_default_inline'} .= " <citebiblioid>";
+
+	# citerefentry; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <citerefentry>";
+	$self->{options}{'_default_inline'} .= " <citerefentry>";
+
+	# citetitle; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <citetitle>";
+	$self->{options}{'_default_inline'} .= " <citetitle>";
+
+	# city; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <city>";
+	$self->{options}{'_default_inline'} .= " <city>";
+
+	# classname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <classname>";
+	$self->{options}{'_default_inline'} .= " <classname>";
+
+	# classsynopsis; does not contain text; may be in a para
+	# NOTE: It may contain a classsynopsisinfo, which should be
+	#       verbatim
+	# XXX: since it is in untranslated class, does the W flag takes
+	#      effect?
+	$self->{options}{'_default_untranslated'} .= " W<classsynopsis>";
+	$self->{options}{'_default_placeholder'} .= " <classsynopsis>";
+
+	# classsynopsisinfo; contains text;
+	# NOTE: see above
+	$self->{options}{'_default_translated'} .= " W<classsynopsisinfo>";
+	$self->{options}{'_default_inline'} .= " <classsynopsisinfo>";
+
+	# cmdsynopsis; does not contain text; may be in a para
+	# NOTE: It may be clearer as a verbatim block
+	# XXX: since it is in untranslated class, does the W flag takes
+	#      effect? => not completely. Rewrap afterward?
+	$self->{options}{'_default_untranslated'} .= " W<cmdsynopsis>";
+	$self->{options}{'_default_placeholder'} .= " <cmdsynopsis>";
+
+	# co; does not contain text; Formatted inline
+	# XXX: tranlsated or not? (label attribute)
+	$self->{options}{'_default_translated'} .= " <co>";
+	$self->{options}{'_default_inline'} .= " <co>";
+
+	# code; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <code>";
+	$self->{options}{'_default_inline'} .= " <code>";
+
+	# col; does not contain text;
+	# NOTE: could be translated to change the layout in a translation
+	#       To be done on colgroup in that case.
+	$self->{options}{'_default_untranslated'} .= " <col>";
+	$self->{options}{'_default_break'} .= " <col>";
+
+	# colgroup; does not contain text;
+	# NOTE: could be translated to change the layout in a translation
+	$self->{options}{'_default_untranslated'} .= " <colgroup>";
+	$self->{options}{'_default_break'} .= " <colgroup>";
+
+	# collab; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	# NOTE: could be in the break class
+	$self->{options}{'_default_untranslated'} .= " <collab>";
+	$self->{options}{'_default_inline'} .= " <collab>";
+
+	# collabname; contains text; Formatted inline or as a
+	# displayed block depending on context; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <collabname>";
+	$self->{options}{'_default_inline'} .= " <collabname>";
+
+	# colophon; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <colophon>";
+	$self->{options}{'_default_break'} .= " <colophon>";
+
+	# colspec; does not contain text;
+	# NOTE: could be translated to change the layout in a translation
+	$self->{options}{'_default_untranslated'} .= " <colspec>";
+	$self->{options}{'_default_break'} .= " <colspec>";
+
+	# command; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <command>";
+	$self->{options}{'_default_inline'} .= " <command>";
+
+	# comment; contains text; Formatted inline or as a displayed block
+	# Renamed to remark in v4.0
+	$self->{options}{'_default_translated'} .= " <comment>";
+	$self->{options}{'_default_inline'} .= " <comment>";
+
+	# computeroutput; contains text; Formatted inline
+	# NOTE: "is not a verbatim environment, but an inline."
+	$self->{options}{'_default_translated'} .= " <computeroutput>";
+	$self->{options}{'_default_inline'} .= " <computeroutput>";
+
+	# confdates; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <confdates>";
+	$self->{options}{'_default_inline'} .= " <confdates>";
+
+	# confgroup; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	# NOTE: could be in the break class
+	$self->{options}{'_default_untranslated'} .= " <confgroup>";
+	$self->{options}{'_default_inline'} .= " <confgroup>";
+
+	# confnum; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <confnum>";
+	$self->{options}{'_default_inline'} .= " <confnum>";
+
+	# confsponsor; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <confsponsor>";
+	$self->{options}{'_default_inline'} .= " <confsponsor>";
+
+	# conftitle; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <conftitle>";
+	$self->{options}{'_default_inline'} .= " <conftitle>";
+
+	# constant; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <constant>";
+	$self->{options}{'_default_inline'} .= " <constant>";
+
+	# constraint; does not contain text;
+	# NOTE: it might be better to have the production as verbatim
+	#       Keeping the constrainst inline to have it close to the
+	#       lhs or rhs.
+	#       The attribute is translatable
+	$self->{options}{'_default_untranslated'} .= " <constraint>";
+	$self->{options}{'_default_break'} .= " <constraint>";
+
+	# constraintdef; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <constraintdef>";
+	$self->{options}{'_default_break'} .= " <constraintdef>";
+
+	# constructorsynopsis; does not contain text; may be in a para
+	# NOTE: It may be clearer as a verbatim block
+	# XXX: since it is in untranslated class, does the W flag takes
+	#      effect?
+	$self->{options}{'_default_untranslated'} .= " W<constructorsynopsis>";
+	$self->{options}{'_default_placeholder'} .= " <constructorsynopsis>";
+
+	# contractnum; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <contractnum>";
+	$self->{options}{'_default_inline'} .= " <contractnum>";
+
+	# contractsponsor; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <contractsponsor>";
+	$self->{options}{'_default_inline'} .= " <contractsponsor>";
+
+	# contrib; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_translated'} .= " <contrib>";
+	$self->{options}{'_default_inline'} .= " <contrib>";
+
+	# copyright; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <copyright>";
+	$self->{options}{'_default_inline'} .= " <copyright>";
+
+	# coref; does not contain text; Formatted inline
+	# XXX: tranlsated or not? (label attribute)
+	$self->{options}{'_default_translated'} .= " <coref>";
+	$self->{options}{'_default_inline'} .= " <coref>";
+
+	# corpauthor; contains text; Formatted inline or as a
+	# displayed block depending on context; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <corpauthor>";
+	$self->{options}{'_default_inline'} .= " <corpauthor>";
+
+	# corpcredit; contains text; Formatted inline or as a
+	# displayed block depending on context; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <corpcredit>";
+	$self->{options}{'_default_inline'} .= " <corpcredit>";
+
+	# corpname; contains text; Formatted inline or as a
+	# displayed block depending on context; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <corpname>";
+	$self->{options}{'_default_inline'} .= " <corpname>";
+
+	# country; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <country>";
+	$self->{options}{'_default_inline'} .= " <country>";
+
+	# cover; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <cover>";
+	$self->{options}{'_default_break'} .= " <cover>";
+
+#  
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+
+	# database; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <database>";
+	$self->{options}{'_default_inline'} .= " <database>";
+
+	# date; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <date>";
+	$self->{options}{'_default_inline'} .= " <date>";
+
+	# dedication; contains text; Formatted as a displayed block
+	$self->{options}{'_default_translated'} .= " <dedication>";
+	$self->{options}{'_default_break'} .= " <dedication>";
+
+	# destructorsynopsis; does not contain text; may be in a para
+	# NOTE: It may be clearer as a verbatim block
+	# XXX: since it is in untranslated class, does the W flag takes
+	#      effect?
+	$self->{options}{'_default_untranslated'} .= " W<destructorsynopsis>";
+	$self->{options}{'_default_placeholder'} .= " <destructorsynopsis>";
+
+	# docinfo; does not contain text; removed in v4.0
+	$self->{options}{'_default_untranslated'} .= " <docinfo>";
+	$self->{options}{'_default_placeholder'} .= " <docinfo>";
+
+#  
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+
+	# edition; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <edition>";
+	$self->{options}{'_default_inline'} .= " <edition>";
+
+	# editor; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_untranslated'} .= " <editor>";
+	$self->{options}{'_default_inline'} .= " <editor>";
+
+	# email; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <email>";
+	$self->{options}{'_default_inline'} .= " <email>";
+
+	# emphasis; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <emphasis>";
+	$self->{options}{'_default_inline'} .= " <emphasis>";
+
+	# entry; contains text;
+	$self->{options}{'_default_translated'} .= " <entry>";
+	$self->{options}{'_default_break'} .= " <entry>";
+
+	# entrytbl; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <entrytbl>";
+	$self->{options}{'_default_break'} .= " <entrytbl>";
+
+	# envar; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <envar>";
+	$self->{options}{'_default_inline'} .= " <envar>";
+
+	# epigraph; contains text; Formatted as a displayed block.
+	# NOTE: maybe contained in a para
+	$self->{options}{'_default_translated'} .= " <epigraph>";
+	$self->{options}{'_default_placeholder'} .= " <epigraph>";
+
+	# equation; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <equation>";
+	$self->{options}{'_default_break'} .= " <equation>";
+
+	# errorcode; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <errorcode>";
+	$self->{options}{'_default_inline'} .= " <errorcode>";
+
+	# errorname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <errorname>";
+	$self->{options}{'_default_inline'} .= " <errorname>";
+
+	# errortext; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <errortext>";
+	$self->{options}{'_default_inline'} .= " <errortext>";
+
+	# errortype; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <errortype>";
+	$self->{options}{'_default_inline'} .= " <errortype>";
+
+	# example; does not contain text; Formatted as a displayed block.
+	# NOTE: maybe contained in a para
+	$self->{options}{'_default_untranslated'} .= " <example>";
+	$self->{options}{'_default_placeholder'} .= " <example>";
+
+	# exceptionname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <exceptionname>";
+	$self->{options}{'_default_inline'} .= " <exceptionname>";
+
+	# extendedlink; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <extendedlink>";
+	$self->{options}{'_default_inline'} .= " <extendedlink>";
+
+#  
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+
+	# fax; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <fax>";
+	$self->{options}{'_default_inline'} .= " <fax>";
+
+	# fieldsynopsis; does not contain text; may be in a para
+	$self->{options}{'_default_untranslated'} .= " <fieldsynopsis>";
+	$self->{options}{'_default_inline'} .= " <fieldsynopsis>";
+
+	# figure; does not contain text; Formatted as a displayed block.
+	# NOTE: maybe contained in a para
+	$self->{options}{'_default_untranslated'} .= " <figure>";
+	$self->{options}{'_default_placeholder'} .= " <figure>";
+
+	# filename; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <filename>";
+	$self->{options}{'_default_inline'} .= " <filename>";
+
+	# firstname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <firstname>";
+	$self->{options}{'_default_inline'} .= " <firstname>";
+
+	# firstterm; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <firstterm>";
+	$self->{options}{'_default_inline'} .= " <firstterm>";
+
+	# footnote; contains text;
+	$self->{options}{'_default_translated'} .= " <footnote>";
+	$self->{options}{'_default_placeholder'} .= " <footnote>";
+
+	# footnoteref; contains text;
+	$self->{options}{'_default_translated'} .= " <footnoteref>";
+	$self->{options}{'_default_inline'} .= " <footnoteref>";
+
+	# foreignphrase; contains text;
+	$self->{options}{'_default_translated'} .= " <foreignphrase>";
+	$self->{options}{'_default_inline'} .= " <foreignphrase>";
+
+	# formalpara; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <formalpara>";
+	$self->{options}{'_default_break'} .= " <formalpara>";
+
+	# funcdef; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <funcdef>";
+	$self->{options}{'_default_inline'} .= " <funcdef>";
+
+	# funcparams; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <funcparams>";
+	$self->{options}{'_default_inline'} .= " <funcparams>";
+
+	# funcprototype; does not contain text;
+	# NOTE: maybe contained in a funcsynopsis, contained in a para
+	$self->{options}{'_default_untranslated'} .= " <funcprototype>";
+	$self->{options}{'_default_placeholder'} .= " <funcprototype>";
+
+	# funcsynopsis; does not contain text;
+	# NOTE: maybe contained in a para
+	$self->{options}{'_default_untranslated'} .= " <funcsynopsis>";
+	$self->{options}{'_default_placeholder'} .= " <funcsynopsis>";
+
+	# funcsynopsisinfo; contains text; verbatim
+	# NOTE: maybe contained in a funcsynopsis, contained in a para
+	$self->{options}{'_default_translated'} .= " W<funcsynopsisinfo>";
+	$self->{options}{'_default_placeholder'} .= " <funcsynopsisinfo>";
+
+	# function; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <function>";
+	$self->{options}{'_default_inline'} .= " <function>";
+
+#  
GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+
+	# glossary; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <glossary>";
+	$self->{options}{'_default_break'} .= " <glossary>";
+
+	# glossaryinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <glossaryinfo>";
+	$self->{options}{'_default_placeholder'} .= " <glossaryinfo>";
+
+	# glossdef; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <glossdef>";
+	$self->{options}{'_default_break'} .= " <glossdef>";
+
+	# glossdiv; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <glossdiv>";
+	$self->{options}{'_default_break'} .= " <glossdiv>";
+
+	# glossentry; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <glossentry>";
+	$self->{options}{'_default_break'} .= " <glossentry>";
+
+	# glosslist; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <glosslist>";
+	$self->{options}{'_default_break'} .= " <glosslist>";
+
+	# glosssee; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <glosssee>";
+	$self->{options}{'_default_break'} .= " <glosssee>";
+
+	# glossseealso; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <glossseealso>";
+	$self->{options}{'_default_break'} .= " <glossseealso>";
+
+	# glossterm; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <glossterm>";
+	$self->{options}{'_default_inline'} .= " <glossterm>";
+
+	# graphic; does not contain text; Formatted as a displayed block
+	# v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <graphic>";
+	$self->{options}{'_default_inline'} .= " <graphic>";
+	$self->{options}{'_default_attributes'}.=' <graphic>fileref';
+
+	# graphicco; does not contain text; Formatted as a displayed block.
+	# v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <graphicco>";
+	$self->{options}{'_default_placeholder'} .= " <graphicco>";
+
+	# group; does not contain text; Formatted inline
+	$self->{options}{'_default_untranslated'} .= " W<group>";
+	$self->{options}{'_default_inline'} .= " <group>";
+
+	# guibutton; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <guibutton>";
+	$self->{options}{'_default_inline'} .= " <guibutton>";
+
+	# guiicon; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <guiicon>";
+	$self->{options}{'_default_inline'} .= " <guiicon>";
+
+	# guilabel; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <guilabel>";
+	$self->{options}{'_default_inline'} .= " <guilabel>";
+
+	# guimenu; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <guimenu>";
+	$self->{options}{'_default_inline'} .= " <guimenu>";
+
+	# guimenuitem; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <guimenuitem>";
+	$self->{options}{'_default_inline'} .= " <guimenuitem>";
+
+	# guisubmenu; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <guisubmenu>";
+	$self->{options}{'_default_inline'} .= " <guisubmenu>";
+
+#  
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+
+	# hardware; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <hardware>";
+	$self->{options}{'_default_inline'} .= " <hardware>";
+
+	# highlights; does not contain text; Formatted inline
+	# v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <highlights>";
+	$self->{options}{'_default_break'} .= " <highlights>";
+
+	# holder; contains text;
+	# NOTE: may depend on the copyright container
+	$self->{options}{'_default_translated'} .= " <holder>";
+	$self->{options}{'_default_inline'} .= " <holder>";
+
+	# honorific; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <honorific>";
+	$self->{options}{'_default_inline'} .= " <honorific>";
+
+	# html:button; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <html:button>";
+	$self->{options}{'_default_inline'} .= " <html:button>";
+
+	# html:fieldset; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <html:fieldset>";
+	$self->{options}{'_default_inline'} .= " <html:fieldset>";
+
+	# html:form; does not contain text;
+	$self->{options}{'_default_translated'} .= " <html:form>";
+	$self->{options}{'_default_inline'} .= " <html:form>";
+
+	# html:input; does not contain text; Formatted inline
+	# NOTE: attributes are translatable
+	$self->{options}{'_default_translated'} .= " <html:input>";
+	$self->{options}{'_default_inline'} .= " <html:input>";
+
+	# html:label; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <html:label>";
+	$self->{options}{'_default_inline'} .= " <html:label>";
+
+	# html:legend; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <html:legend>";
+	$self->{options}{'_default_inline'} .= " <html:legend>";
+
+	# html:option; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <html:option>";
+	$self->{options}{'_default_inline'} .= " <html:option>";
+
+	# html:select; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <html:select>";
+	$self->{options}{'_default_inline'} .= " <html:select>";
+
+	# html:textarea; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <html:textarea>";
+	$self->{options}{'_default_placeholder'} .= " <html:textarea>";
+
+	# imagedata; does not contain text; May be formatted inline or
+	# as a displayed block, depending on context
+	$self->{options}{'_default_translated'} .= " <imagedata>";
+	$self->{options}{'_default_inline'} .= " <imagedata>";
+	$self->{options}{'_default_attributes'}.=' <imagedata>fileref';
+
+	# imageobject; does not contain text; May be formatted inline or
+	# as a displayed block, depending on context
+	$self->{options}{'_default_untranslated'} .= " <imageobject>";
+	$self->{options}{'_default_inline'} .= " <imageobject>";
+
+	# imageobjectco; does not contain text; Formatted as a displayed block
+	# NOTE: may be in a inlinemediaobject
+	# TODO: check if this works when the inlinemediaobject is defined
+	# as inline
+	$self->{options}{'_default_untranslated'} .= " <imageobjectco>";
+	$self->{options}{'_default_break'} .= " <imageobjectco>";
+
+	# important; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <important>";
+	$self->{options}{'_default_break'} .= " <important>";
+
+	# index; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <index>";
+	$self->{options}{'_default_break'} .= " <index>";
+
+	# indexdiv; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <indexdiv>";
+	$self->{options}{'_default_break'} .= " <indexdiv>";
+
+	# indexentry; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <indexentry>";
+	$self->{options}{'_default_break'} .= " <indexentry>";
+
+	# indexinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <indexinfo>";
+	$self->{options}{'_default_placeholder'} .= " <indexinfo>";
+
+	# indexterm; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <indexterm>";
+	$self->{options}{'_default_placeholder'} .= " <indexterm>";
+
+	# info; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <info>";
+	$self->{options}{'_default_placeholder'} .= " <info>";
+
+	# informalequation; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <informalequation>";
+	$self->{options}{'_default_placeholder'} .= " <informalequation>";
+
+	# informalexample; does not contain text; Formatted as a displayed block.
+	# NOTE: can be in a para
+	$self->{options}{'_default_untranslated'} .= " <informalexample>";
+	$self->{options}{'_default_break'} .= " <informalexample>";
+
+	# informalfigure; does not contain text; Formatted as a displayed block.
+	# NOTE: can be in a para
+	$self->{options}{'_default_untranslated'} .= " <informalfigure>";
+	$self->{options}{'_default_break'} .= " <informalfigure>";
+
+	# informaltable; does not contain text; Formatted as a displayed block.
+	# NOTE: can be in a para
+	$self->{options}{'_default_untranslated'} .= " <informaltable>";
+	$self->{options}{'_default_break'} .= " <informaltable>";
+
+	# initializer; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <initializer>";
+	$self->{options}{'_default_inline'} .= " <initializer>";
+
+	# inlineequation; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " W<inlineequation>";
+	$self->{options}{'_default_placeholder'} .= " <inlineequation>";
+
+	# inlinegraphic; does not contain text; Formatted inline
+	# empty; v4, not in v5
+	$self->{options}{'_default_translated'} .= " W<inlinegraphic>";
+	$self->{options}{'_default_inline'} .= " <inlinegraphic>";
+
+	# inlinemediaobject; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <inlinemediaobject>";
+	$self->{options}{'_default_placeholder'} .= " <inlinemediaobject>";
+
+	# interface; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <interface>";
+	$self->{options}{'_default_inline'} .= " <interface>";
+
+	# interfacedefinition; contains text; Formatted inline
+	# Removed in v4.0
+	$self->{options}{'_default_translated'} .= " <interfacedefinition>";
+	$self->{options}{'_default_inline'} .= " <interfacedefinition>";
+
+	# interfacename; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <interfacename>";
+	$self->{options}{'_default_inline'} .= " <interfacename>";
+
+	# invpartnumber; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <invpartnumber>";
+	$self->{options}{'_default_inline'} .= " <invpartnumber>";
+
+	# isbn; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <isbn>";
+	$self->{options}{'_default_inline'} .= " <isbn>";
+
+	# issn; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <issn>";
+	$self->{options}{'_default_inline'} .= " <issn>";
+
+	# issuenum; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <issuenum>";
+	$self->{options}{'_default_inline'} .= " <issuenum>";
+
+	# itemizedlist; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <itemizedlist>";
+	$self->{options}{'_default_break'} .= " <itemizedlist>";
+
+	# itermset; does not contain text;
+	# FIXME
+	$self->{options}{'_default_untranslated'} .= " <itermset>";
+	$self->{options}{'_default_inline'} .= " <itermset>";
+
+#  
JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+
+	# jobtitle; contains text; Formatted inline or as a displayed block
+	# NOTE: can be in a para
+	$self->{options}{'_default_translated'} .= " <jobtitle>";
+	$self->{options}{'_default_inline'} .= " <jobtitle>";
+
+#  
KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+
+	# keycap; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <keycap>";
+	$self->{options}{'_default_inline'} .= " <keycap>";
+
+	# keycode; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <keycode>";
+	$self->{options}{'_default_inline'} .= " <keycode>";
+
+	# keycombo; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <keycombo>";
+	$self->{options}{'_default_inline'} .= " <keycombo>";
+
+	# keysym; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <keysym>";
+	$self->{options}{'_default_inline'} .= " <keysym>";
+
+	# keyword; contains text;
+	# NOTE: could be inline
+	$self->{options}{'_default_translated'} .= " <keyword>";
+	$self->{options}{'_default_break'} .= " <keyword>";
+
+	# keywordset; contains text; Formatted inline or as a displayed block
+	# NOTE: could be placeholder/break
+	$self->{options}{'_default_translated'} .= " <keywordset>";
+	$self->{options}{'_default_break'} .= " <keywordset>";
+
+#  
LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+
+	# label; contains text; Formatted as a displayed block
+	$self->{options}{'_default_translated'} .= " <label>";
+	$self->{options}{'_default_break'} .= " <label>";
+
+	# legalnotice; contains text; Formatted as a displayed block
+	$self->{options}{'_default_translated'} .= " <legalnotice>";
+	$self->{options}{'_default_break'} .= " <legalnotice>";
+
+	# lhs; contains text; Formatted as a displayed block.
+	# NOTE: it might be better to have the production as verbatim
+	#       Keeping the constrainst inline to have it close to the
+	#       lhs or rhs.
+	$self->{options}{'_default_translated'} .= " <lhs>";
+	$self->{options}{'_default_break'} .= " <lhs>";
+
+	# lineage; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <lineage>";
+	$self->{options}{'_default_inline'} .= " <lineage>";
+
+	# lineannotation; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <lineannotation>";
+	$self->{options}{'_default_inline'} .= " <lineannotation>";
+
+	# link; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <link>";
+	$self->{options}{'_default_inline'} .= " <link>";
+
+	# listitem; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <listitem>";
+	$self->{options}{'_default_break'} .= " <listitem>";
+
+	# literal; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <literal>";
+	$self->{options}{'_default_inline'} .= " <literal>";
+
+	# literallayout; contains text; verbatim
+	$self->{options}{'_default_translated'} .= " W<literallayout>";
+	$self->{options}{'_default_placeholder'} .= " <literallayout>";
+
+	# locator; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <locator>";
+	$self->{options}{'_default_inline'} .= " <locator>";
+
+	# lot; does not contain text; Formatted as a displayed block.
+	# v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <lot>";
+	$self->{options}{'_default_break'} .= " <lot>";
+
+	# lotentry; contains text; Formatted as a displayed block.
+	# v4, not in v5
+	$self->{options}{'_default_translated'} .= " <lotentry>";
+	$self->{options}{'_default_break'} .= " <lotentry>";
+
+#  
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+
+	# manvolnum; contains text;
+	$self->{options}{'_default_translated'} .= " <manvolnum>";
+	$self->{options}{'_default_inline'} .= " <manvolnum>";
+
+	# markup; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <markup>";
+	$self->{options}{'_default_inline'} .= " <markup>";
+
+	# mathphrase; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <mathphrase>";
+	$self->{options}{'_default_inline'} .= " <mathphrase>";
+
+	# medialabel; contains text; Formatted inline
+	# v4, not in v5
+	$self->{options}{'_default_translated'} .= " <medialabel>";
+	$self->{options}{'_default_inline'} .= " <medialabel>";
+
+	# mediaobject; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <mediaobject>";
+	$self->{options}{'_default_placeholder'} .= " <mediaobject>";
+
+	# mediaobjectco; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <mediaobjectco>";
+	$self->{options}{'_default_placeholder'} .= " <mediaobjectco>";
+
+	# member; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <member>";
+	$self->{options}{'_default_inline'} .= " <member>";
+
+	# menuchoice; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <menuchoice>";
+	$self->{options}{'_default_inline'} .= " <menuchoice>";
+
+	# methodname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <methodname>";
+	$self->{options}{'_default_inline'} .= " <methodname>";
+
+	# methodparam; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <methodparam>";
+	$self->{options}{'_default_inline'} .= " <methodparam>";
+
+	# methodsynopsis; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <methodsynopsis>";
+	$self->{options}{'_default_inline'} .= " <methodsynopsis>";
+
+	# modifier; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <modifier>";
+	$self->{options}{'_default_inline'} .= " <modifier>";
+
+	# mousebutton; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <mousebutton>";
+	$self->{options}{'_default_inline'} .= " <mousebutton>";
+
+	# msg; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msg>";
+	$self->{options}{'_default_break'} .= " <msg>";
+
+	# msgaud; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <msgaud>";
+	$self->{options}{'_default_break'} .= " <msgaud>";
+
+	# msgentry; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgentry>";
+	$self->{options}{'_default_break'} .= " <msgentry>";
+
+	# msgexplan; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgexplan>";
+	$self->{options}{'_default_break'} .= " <msgexplan>";
+
+	# msginfo; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msginfo>";
+	$self->{options}{'_default_break'} .= " <msginfo>";
+
+	# msglevel; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <msglevel>";
+	$self->{options}{'_default_break'} .= " <msglevel>";
+
+	# msgmain; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgmain>";
+	$self->{options}{'_default_break'} .= " <msgmain>";
+
+	# msgorig; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <msgorig>";
+	$self->{options}{'_default_break'} .= " <msgorig>";
+
+	# msgrel; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgrel>";
+	$self->{options}{'_default_break'} .= " <msgrel>";
+
+	# msgset; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgset>";
+	$self->{options}{'_default_placeholder'} .= " <msgset>";
+
+	# msgsub; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgsub>";
+	$self->{options}{'_default_break'} .= " <msgsub>";
+
+	# msgtext; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <msgtext>";
+	$self->{options}{'_default_break'} .= " <msgtext>";
+
+#  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+
+	# nonterminal; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <nonterminal>";
+	$self->{options}{'_default_inline'} .= " <nonterminal>";
+
+	# note; does not contain text; Formatted inline
+	# NOTE: can be in a para
+	$self->{options}{'_default_untranslated'} .= " <note>";
+	$self->{options}{'_default_inline'} .= " <note>";
+
+#  
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+
+	# objectinfo; does not contain text; v3.1 -> v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <objectinfo>";
+	$self->{options}{'_default_placeholder'} .= " <objectinfo>";
+
+	# olink; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <olink>";
+	$self->{options}{'_default_inline'} .= " <olink>";
+
+	# ooclass; does not contain text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <ooclass>";
+	$self->{options}{'_default_inline'} .= " <ooclass>";
+
+	# ooexception; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <ooexception>";
+	$self->{options}{'_default_inline'} .= " <ooexception>";
+
+	# oointerface; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <oointerface>";
+	$self->{options}{'_default_inline'} .= " <oointerface>";
+
+	# option; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <option>";
+	$self->{options}{'_default_inline'} .= " <option>";
+
+	# optional; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <optional>";
+	$self->{options}{'_default_inline'} .= " <optional>";
+
+	# orderedlist; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <orderedlist>";
+	$self->{options}{'_default_placeholder'} .= " <orderedlist>";
+
+	# org; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_untranslated'} .= " <org>";
+	$self->{options}{'_default_inline'} .= " <org>";
+
+	# orgdiv; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <orgdiv>";
+	$self->{options}{'_default_inline'} .= " <orgdiv>";
+
+	# orgname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <orgname>";
+	$self->{options}{'_default_inline'} .= " <orgname>";
+
+	# otheraddr; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <otheraddr>";
+	$self->{options}{'_default_inline'} .= " <otheraddr>";
+
+	# othercredit; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_untranslated'} .= " <othercredit>";
+	$self->{options}{'_default_inline'} .= " <othercredit>";
+
+	# othername; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <othername>";
+	$self->{options}{'_default_inline'} .= " <othername>";
+
+#  
PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
+
+	# package; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <package>";
+	$self->{options}{'_default_inline'} .= " <package>";
+
+	# pagenums; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <pagenums>";
+	$self->{options}{'_default_inline'} .= " <pagenums>";
+
+	# para; contains text; Formatted as a displayed block
+	$self->{options}{'_default_translated'} .= " <para>";
+	$self->{options}{'_default_break'} .= " <para>";
+
+	# paramdef; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <paramdef>";
+	$self->{options}{'_default_inline'} .= " <paramdef>";
+
+	# parameter; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <parameter>";
+	$self->{options}{'_default_inline'} .= " <parameter>";
+
+	# part; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <part>";
+	$self->{options}{'_default_break'} .= " <part>";
+
+	# partinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <partinfo>";
+	$self->{options}{'_default_placeholder'} .= " <partinfo>";
+
+	# partintro; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <partintro>";
+	$self->{options}{'_default_break'} .= " <partintro>";
+
+	# person; does not contain text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_untranslated'} .= " <person>";
+	$self->{options}{'_default_inline'} .= " <person>";
+
+	# personblurb; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <personblurb>";
+	$self->{options}{'_default_placeholder'} .= " <personblurb>";
+
+	# personname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <personname>";
+	$self->{options}{'_default_inline'} .= " <personname>";
+
+	# phone; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <phone>";
+	$self->{options}{'_default_inline'} .= " <phone>";
+
+	# phrase; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <phrase>";
+	$self->{options}{'_default_inline'} .= " <phrase>";
+
+	# pob; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <pob>";
+	$self->{options}{'_default_inline'} .= " <pob>";
+
+	# postcode; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <postcode>";
+	$self->{options}{'_default_inline'} .= " <postcode>";
+
+	# preface; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <preface>";
+	$self->{options}{'_default_break'} .= " <preface>";
+
+	# prefaceinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <prefaceinfo>";
+	$self->{options}{'_default_placeholder'} .= " <prefaceinfo>";
+
+	# primary; contains text;
+	$self->{options}{'_default_translated'} .= " <primary>";
+	$self->{options}{'_default_break'} .= " <primary>";
+
+	# primaryie; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <primaryie>";
+	$self->{options}{'_default_break'} .= " <primaryie>";
+
+	# printhistory; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <printhistory>";
+	$self->{options}{'_default_break'} .= " <printhistory>";
+
+	# procedure; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <procedure>";
+	$self->{options}{'_default_placeholder'} .= " <procedure>";
+
+	# production; doesnot contain text;
+	# NOTE: it might be better to have the production as verbatim
+	#       Keeping the constrainst inline to have it close to the
+	#       lhs or rhs.
+	$self->{options}{'_default_untranslated'} .= " <production>";
+	$self->{options}{'_default_break'} .= " <production>";
+
+	# productionrecap; does not contain text; like production
+	$self->{options}{'_default_untranslated'} .= " <productionrecap>";
+	$self->{options}{'_default_break'} .= " <productionrecap>";
+
+	# productionset; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <productionset>";
+	$self->{options}{'_default_placeholder'} .= " <productionset>";
+
+	# productname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <productname>";
+	$self->{options}{'_default_inline'} .= " <productname>";
+
+	# productnumber; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <productnumber>";
+	$self->{options}{'_default_inline'} .= " <productnumber>";
+
+	# programlisting; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " W<programlisting>";
+	$self->{options}{'_default_placeholder'} .= " <programlisting>";
+
+	# programlistingco; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <programlistingco>";
+	$self->{options}{'_default_placeholder'} .= " <programlistingco>";
+
+	# prompt; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <prompt>";
+	$self->{options}{'_default_inline'} .= " <prompt>";
+
+	# property; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <property>";
+	$self->{options}{'_default_inline'} .= " <property>";
+
+	# pubdate; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <pubdate>";
+	$self->{options}{'_default_inline'} .= " <pubdate>";
+
+	# publisher; does not contain text; Formatted inline or as a displayed  
block
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <publisher>";
+	$self->{options}{'_default_inline'} .= " <publisher>";
+
+	# publishername; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_translated'} .= " <publishername>";
+	$self->{options}{'_default_inline'} .= " <publishername>";
+
+#  
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
+
+	# qandadiv; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <qandadiv>";
+	$self->{options}{'_default_break'} .= " <qandadiv>";
+
+	# qandaentry; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <qandaentry>";
+	$self->{options}{'_default_break'} .= " <qandaentry>";
+
+	# qandaset; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <qandaset>";
+	$self->{options}{'_default_break'} .= " <qandaset>";
+
+	# question; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <question>";
+	$self->{options}{'_default_break'} .= " <question>";
+
+	# quote; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <quote>";
+	$self->{options}{'_default_inline'} .= " <quote>";
+
+#  
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
+
+	# refclass; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <refclass>";
+	$self->{options}{'_default_break'} .= " <refclass>";
+
+	# refdescriptor; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <refdescriptor>";
+	$self->{options}{'_default_break'} .= " <refdescriptor>";
+
+	# refentry; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refentry>";
+	$self->{options}{'_default_break'} .= " <refentry>";
+
+	# refentryinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <refentryinfo>";
+	$self->{options}{'_default_placeholder'} .= " <refentryinfo>";
+
+	# refentrytitle; contains text; Formatted as a displayed block
+# FIXME: do not seems to be a block
+	$self->{options}{'_default_translated'} .= " <refentrytitle>";
+	$self->{options}{'_default_inline'} .= " <refentrytitle>";
+
+	# reference; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <reference>";
+	$self->{options}{'_default_break'} .= " <reference>";
+
+	# referenceinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <referenceinfo>";
+	$self->{options}{'_default_placeholder'} .= " <referenceinfo>";
+
+	# refmeta; does not contains text;
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_untranslated'} .= " <refmeta>";
+	$self->{options}{'_default_break'} .= " <refmeta>";
+
+	# refmiscinfo; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <refmiscinfo>";
+	$self->{options}{'_default_break'} .= " <refmiscinfo>";
+
+	# refname; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <refname>";
+	$self->{options}{'_default_break'} .= " <refname>";
+
+	# refnamediv; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refnamediv>";
+	$self->{options}{'_default_break'} .= " <refnamediv>";
+
+	# refpurpose; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <refpurpose>";
+	$self->{options}{'_default_inline'} .= " <refpurpose>";
+
+	# refsect1; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refsect1>";
+	$self->{options}{'_default_break'} .= " <refsect1>";
+
+	# refsect1info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <refsect1info>";
+	$self->{options}{'_default_placeholder'} .= " <refsect1info>";
+
+	# refsect2; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refsect2>";
+	$self->{options}{'_default_break'} .= " <refsect2>";
+
+	# refsect2info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <refsect2info>";
+	$self->{options}{'_default_placeholder'} .= " <refsect2info>";
+
+	# refsect3; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refsect3>";
+	$self->{options}{'_default_break'} .= " <refsect3>";
+
+	# refsect3info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <refsect3info>";
+	$self->{options}{'_default_placeholder'} .= " <refsect3info>";
+
+	# refsection; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refsection>";
+	$self->{options}{'_default_break'} .= " <refsection>";
+
+	# refsectioninfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <refsectioninfo>";
+	$self->{options}{'_default_placeholder'} .= " <refsectioninfo>";
+
+	# refsynopsisdiv; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <refsynopsisdiv>";
+	$self->{options}{'_default_break'} .= " <refsynopsisdiv>";
+
+	# refsynopsisdivinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <refsynopsisdivinfo>";
+	$self->{options}{'_default_placeholder'} .= " <refsynopsisdivinfo>";
+
+	# releaseinfo; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <releaseinfo>";
+	$self->{options}{'_default_break'} .= " <releaseinfo>";
+
+	# remark; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_translated'} .= " <remark>";
+	$self->{options}{'_default_inline'} .= " <remark>";
+
+	# replaceable; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <replaceable>";
+	$self->{options}{'_default_inline'} .= " <replaceable>";
+
+	# returnvalue; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <returnvalue>";
+	$self->{options}{'_default_inline'} .= " <returnvalue>";
+
+	# revdescription; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_translated'} .= " <revdescription>";
+	$self->{options}{'_default_break'} .= " <revdescription>";
+
+	# revhistory; does not contain text; Formatted as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <revhistory>";
+	$self->{options}{'_default_break'} .= " <revhistory>";
+
+	# revision; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <revision>";
+	$self->{options}{'_default_break'} .= " <revision>";
+
+	# revnumber; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <revnumber>";
+	$self->{options}{'_default_inline'} .= " <revnumber>";
+
+	# revremark; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_translated'} .= " <revremark>";
+	$self->{options}{'_default_break'} .= " <revremark>";
+
+	# rhs; contains text; Formatted as a displayed block.
+	# NOTE: it might be better to have the production as verbatim
+	#       Keeping the constrainst inline to have it close to the
+	#       lhs or rhs.
+	$self->{options}{'_default_translated'} .= " <rhs>";
+	$self->{options}{'_default_break'} .= " <rhs>";
+
+	# row; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <row>";
+	$self->{options}{'_default_break'} .= " <row>";
+
+#  
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
+
+	# sbr; does not contain text; line break
+	$self->{options}{'_default_untranslated'} .= " <sbr>";
+	$self->{options}{'_default_break'} .= " <sbr>";
+
+	# screen; contains text; verbatim
+	$self->{options}{'_default_translated'} .= " W<screen>";
+	$self->{options}{'_default_placeholder'} .= " <screen>";
+
+	# screenco; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <screenco>";
+	$self->{options}{'_default_placeholder'} .= " <screenco>";
+
+	# screeninfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <screeninfo>";
+	$self->{options}{'_default_placeholder'} .= " <screeninfo>";
+
+	# screenshot; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <screenshot>";
+	$self->{options}{'_default_placeholder'} .= " <screenshot>";
+
+	# secondary; contains text;
+	$self->{options}{'_default_translated'} .= " <secondary>";
+	$self->{options}{'_default_break'} .= " <secondary>";
+
+	# secondaryie; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <secondaryie>";
+	$self->{options}{'_default_break'} .= " <secondaryie>";
+
+	# sect1; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <sect1>";
+	$self->{options}{'_default_break'} .= " <sect1>";
+
+	# sect1info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sect1info>";
+	$self->{options}{'_default_placeholder'} .= " <sect1info>";
+
+	# sect2; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <sect2>";
+	$self->{options}{'_default_break'} .= " <sect2>";
+
+	# sect2info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sect2info>";
+	$self->{options}{'_default_placeholder'} .= " <sect2info>";
+
+	# sect3; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <sect3>";
+	$self->{options}{'_default_break'} .= " <sect3>";
+
+	# sect3info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sect3info>";
+	$self->{options}{'_default_placeholder'} .= " <sect3info>";
+
+	# sect4; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <sect4>";
+	$self->{options}{'_default_break'} .= " <sect4>";
+
+	# sect4info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sect4info>";
+	$self->{options}{'_default_placeholder'} .= " <sect4info>";
+
+	# sect5; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <sect5>";
+	$self->{options}{'_default_break'} .= " <sect5>";
+
+	# sect5info; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sect5info>";
+	$self->{options}{'_default_placeholder'} .= " <sect5info>";
+
+	# section; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <section>";
+	$self->{options}{'_default_break'} .= " <section>";
+
+	# sectioninfo; does not contain text; v3.1 -> v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sectioninfo>";
+	$self->{options}{'_default_placeholder'} .= " <sectioninfo>";
+
+	# see; contains text;
+	$self->{options}{'_default_translated'} .= " <see>";
+	$self->{options}{'_default_break'} .= " <see>";
+
+	# seealso; contains text;
+	$self->{options}{'_default_translated'} .= " <seealso>";
+	$self->{options}{'_default_break'} .= " <seealso>";
+
+	# seealsoie; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <seealsoie>";
+	$self->{options}{'_default_break'} .= " <seealsoie>";
+
+	# seeie; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <seeie>";
+	$self->{options}{'_default_break'} .= " <seeie>";
+
+	# seg; contains text;
+	$self->{options}{'_default_translated'} .= " <seg>";
+	$self->{options}{'_default_break'} .= " <seg>";
+
+	# seglistitem; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <seglistitem>";
+	$self->{options}{'_default_break'} .= " <seglistitem>";
+
+	# segmentedlist; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <segmentedlist>";
+	$self->{options}{'_default_break'} .= " <segmentedlist>";
+
+	# segtitle; contains text;
+	$self->{options}{'_default_translated'} .= " <segtitle>";
+	$self->{options}{'_default_break'} .= " <segtitle>";
+
+	# seriesinfo; does not contain text;
+	# Removed in v4.0
+	$self->{options}{'_default_untranslated'} .= " <seriesinfo>";
+	$self->{options}{'_default_placeholder'} .= " <seriesinfo>";
+
+	# seriesvolnums; contains text; Formatted inline
+	# NOTE: could be in the break class
+	$self->{options}{'_default_translated'} .= " <seriesvolnums>";
+	$self->{options}{'_default_inline'} .= " <seriesvolnums>";
+
+	# set; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <set>";
+	$self->{options}{'_default_break'} .= " <set>";
+
+	# setindex; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <setindex>";
+	$self->{options}{'_default_break'} .= " <setindex>";
+
+	# setindexinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <setindexinfo>";
+	$self->{options}{'_default_placeholder'} .= " <setindexinfo>";
+
+	# setinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <setinfo>";
+	$self->{options}{'_default_placeholder'} .= " <setinfo>";
+
+	# sgmltag; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <sgmltag>";
+	$self->{options}{'_default_inline'} .= " <sgmltag>";
+
+	# shortaffil; contains text; Formatted inline or as a
+	# displayed block depending on context
+	$self->{options}{'_default_translated'} .= " <shortaffil>";
+	$self->{options}{'_default_inline'} .= " <shortaffil>";
+
+	# shortcut; does not contain text; Formatted inline
+	$self->{options}{'_default_untranslated'} .= " <shortcut>";
+	$self->{options}{'_default_inline'} .= " <shortcut>";
+
+	# sidebar; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <sidebar>";
+	$self->{options}{'_default_break'} .= " <sidebar>";
+
+	# sidebarinfo; does not contain text; v4, not in v5
+	$self->{options}{'_default_untranslated'} .= " <sidebarinfo>";
+	$self->{options}{'_default_placeholder'} .= " <sidebarinfo>";
+
+	# simpara; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <simpara>";
+	$self->{options}{'_default_break'} .= " <simpara>";
+
+	# simplelist; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <simplelist>";
+	$self->{options}{'_default_inline'} .= " <simplelist>";
+
+	# simplemsgentry; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <simplemsgentry>";
+	$self->{options}{'_default_break'} .= " <simplemsgentry>";
+
+	# simplesect; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <simplesect>";
+	$self->{options}{'_default_break'} .= " <simplesect>";
+
+	# spanspec; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <spanspec>";
+	$self->{options}{'_default_break'} .= " <spanspec>";
+
+	# state; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <state>";
+	$self->{options}{'_default_inline'} .= " <state>";
+
+	# step; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <step>";
+	$self->{options}{'_default_break'} .= " <step>";
+
+	# stepalternatives; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <stepalternatives>";
+	$self->{options}{'_default_break'} .= " <stepalternatives>";
+
+	# street; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <street>";
+	$self->{options}{'_default_inline'} .= " <street>";
+
+	# structfield; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <structfield>";
+	$self->{options}{'_default_inline'} .= " <structfield>";
+
+	# structname; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <structname>";
+	$self->{options}{'_default_inline'} .= " <structname>";
+
+	# subject; does not contain text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_untranslated'} .= " <subject>";
+	$self->{options}{'_default_break'} .= " <subject>";
+
+	# subjectset; does not contain text; Formatted inline or as a displayed  
block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_untranslated'} .= " <subjectset>";
+	$self->{options}{'_default_break'} .= " <subjectset>";
+
+	# subjectterm; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <subjectterm>";
+	$self->{options}{'_default_break'} .= " <subjectterm>";
+
+	# subscript; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <subscript>";
+	$self->{options}{'_default_inline'} .= " <subscript>";
+
+	# substeps; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <substeps>";
+	$self->{options}{'_default_break'} .= " <substeps>";
+
+	# subtitle; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <subtitle>";
+	$self->{options}{'_default_break'} .= " <subtitle>";
+
+	# superscript; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <superscript>";
+	$self->{options}{'_default_inline'} .= " <superscript>";
+
+	# surname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <surname>";
+	$self->{options}{'_default_inline'} .= " <surname>";
+
+#svg:svg
+
+	# symbol; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <symbol>";
+	$self->{options}{'_default_inline'} .= " <symbol>";
+
+	# synopfragment; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <synopfragment>";
+	$self->{options}{'_default_placeholder'} .= " <synopfragment>";
+
+	# synopfragmentref; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <synopfragmentref>";
+	$self->{options}{'_default_inline'} .= " <synopfragmentref>";
+
+	# synopsis; contains text; verbatim
+	$self->{options}{'_default_translated'} .= " W<synopsis>";
+	$self->{options}{'_default_placeholder'} .= " <synopsis>";
+
+	# systemitem; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <systemitem>";
+	$self->{options}{'_default_inline'} .= " <systemitem>";
+
+#  
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
+
+	# table; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <table>";
+	$self->{options}{'_default_placeholder'} .= " <table>";
+
+	# tag; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <tag>";
+	$self->{options}{'_default_inline'} .= " <tag>";
+
+	# task; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <task>";
+	$self->{options}{'_default_placeholder'} .= " <task>";
+
+	# taskprerequisites; does not contain text; Formatted as a displayed  
block.
+	$self->{options}{'_default_untranslated'} .= " <taskprerequisites>";
+	$self->{options}{'_default_break'} .= " <taskprerequisites>";
+
+	# taskrelated; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <taskrelated>";
+	$self->{options}{'_default_break'} .= " <taskrelated>";
+
+	# tasksummary; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <tasksummary>";
+	$self->{options}{'_default_break'} .= " <tasksummary>";
+
+	# tbody; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <tbody>";
+	$self->{options}{'_default_break'} .= " <tbody>";
+
+	# td; contains text;
+	$self->{options}{'_default_translated'} .= " <td>";
+	$self->{options}{'_default_break'} .= " <td>";
+
+	# term; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <term>";
+	$self->{options}{'_default_break'} .= " <term>";
+
+	# termdef; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <termdef>";
+	$self->{options}{'_default_inline'} .= " <termdef>";
+
+	# tertiary; contains text; Suppressed
+	$self->{options}{'_default_translated'} .= " <tertiary>";
+	$self->{options}{'_default_placeholder'} .= " <tertiary>";
+
+	# tertiaryie; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <tertiaryie>";
+	$self->{options}{'_default_break'} .= " <tertiaryie>";
+
+	# textdata; does not contain text; Formatted inline or as a displayed  
block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_untranslated'} .= " <textdata>";
+	$self->{options}{'_default_break'} .= " <textdata>";
+	$self->{options}{'_default_attributes'}.=' <textdata>fileref';
+
+	# textobject; does not contain text; Formatted inline or as a displayed  
block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_untranslated'} .= " <textobject>";
+	$self->{options}{'_default_break'} .= " <textobject>";
+
+	# tfoot; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <tfoot>";
+	$self->{options}{'_default_break'} .= " <tfoot>";
+
+	# tgroup; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <tgroup>";
+	$self->{options}{'_default_break'} .= " <tgroup>";
+
+	# th; contains text;
+	$self->{options}{'_default_translated'} .= " <th>";
+	$self->{options}{'_default_break'} .= " <th>";
+
+	# thead; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <thead>";
+	$self->{options}{'_default_break'} .= " <thead>";
+
+	# tip; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <tip>";
+	$self->{options}{'_default_break'} .= " <tip>";
+
+	# title; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <title>";
+	$self->{options}{'_default_break'} .= " <title>";
+
+	# titleabbrev; contains text; Formatted inline or as a displayed block
+	# NOTE: could be in the inline class
+	$self->{options}{'_default_translated'} .= " <titleabbrev>";
+	$self->{options}{'_default_break'} .= " <titleabbrev>";
+
+	# toc; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <toc>";
+	$self->{options}{'_default_break'} .= " <toc>";
+
+	# tocback; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <tocback>";
+	$self->{options}{'_default_break'} .= " <tocback>";
+
+	# tocchap; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <tocchap>";
+	$self->{options}{'_default_break'} .= " <tocchap>";
+
+	# tocdiv; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <tocdiv>";
+	$self->{options}{'_default_break'} .= " <tocdiv>";
+
+	# tocentry; contains text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <tocentry>";
+	$self->{options}{'_default_break'} .= " <tocentry>";
+
+	# tocfront; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_translated'} .= " <tocfront>";
+	$self->{options}{'_default_break'} .= " <tocfront>";
+
+	# toclevel1; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <toclevel1>";
+	$self->{options}{'_default_break'} .= " <toclevel1>";
+
+	# toclevel2; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <toclevel2>";
+	$self->{options}{'_default_break'} .= " <toclevel2>";
+
+	# toclevel3; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <toclevel3>";
+	$self->{options}{'_default_break'} .= " <toclevel3>";
+
+	# toclevel4; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <toclevel4>";
+	$self->{options}{'_default_break'} .= " <toclevel4>";
+
+	# toclevel5; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <toclevel5>";
+	$self->{options}{'_default_break'} .= " <toclevel5>";
+
+	# tocpart; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <tocpart>";
+	$self->{options}{'_default_break'} .= " <tocpart>";
+
+	# token; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <token>";
+	$self->{options}{'_default_inline'} .= " <token>";
+
+	# tr; does not contain text;
+	$self->{options}{'_default_untranslated'} .= " <tr>";
+	$self->{options}{'_default_break'} .= " <tr>";
+
+	# trademark; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <trademark>";
+	$self->{options}{'_default_inline'} .= " <trademark>";
+
+	# type; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <type>";
+	$self->{options}{'_default_inline'} .= " <type>";
+
+#  
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
+
+	# ulink; contains text; Formatted inline; v4, not in v5
+	$self->{options}{'_default_translated'} .= " <ulink>";
+	$self->{options}{'_default_inline'} .= " <ulink>";
+
+	# uri; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <uri>";
+	$self->{options}{'_default_inline'} .= " <uri>";
+
+	# userinput; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <userinput>";
+	$self->{options}{'_default_inline'} .= " <userinput>";
+
+#  
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+
+	# varargs; empty element;
+	$self->{options}{'_default_untranslated'} .= " <varargs>";
+	$self->{options}{'_default_inline'} .= " <varargs>";
+
+	# variablelist; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <variablelist>";
+	$self->{options}{'_default_placeholder'} .= " <variablelist>";
+
+	# varlistentry; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <varlistentry>";
+	$self->{options}{'_default_break'} .= " <varlistentry>";
+
+	# varname; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <varname>";
+	$self->{options}{'_default_inline'} .= " <varname>";
+
+	# videodata; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <videodata>";
+	$self->{options}{'_default_break'} .= " <videodata>";
+	$self->{options}{'_default_attributes'}.=' <videodata>fileref';
+
+	# videoobject; contains text; Formatted inline or as a displayed block
+	$self->{options}{'_default_untranslated'} .= " <videoobject>";
+	$self->{options}{'_default_break'} .= " <videoobject>";
+
+	# void; empty element;
+	$self->{options}{'_default_untranslated'} .= " <void>";
+	$self->{options}{'_default_inline'} .= " <void>";
+
+	# volumenum; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <volumenum>";
+	$self->{options}{'_default_inline'} .= " <volumenum>";
+
+#  
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
+
+	# warning; does not contain text; Formatted as a displayed block.
+	$self->{options}{'_default_untranslated'} .= " <warning>";
+	$self->{options}{'_default_break'} .= " <warning>";
+
+	# wordasword; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <wordasword>";
+	$self->{options}{'_default_inline'} .= " <wordasword>";
+
+#  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+	# xref; empty element;
+	$self->{options}{'_default_untranslated'} .= " <xref>";
+	$self->{options}{'_default_inline'} .= " <xref>";
+
+#  
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
+
+	# year; contains text; Formatted inline
+	$self->{options}{'_default_translated'} .= " <year>";
+	$self->{options}{'_default_inline'} .= " <year>";
+
+#  
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
+
+	$self->{options}{'_default_attributes'}.='
+		lang
+		xml:lang';
+
+	$self->treat_options;
+}

Added: trunk/src/tools/po4a/lib/Locale/Po4a/Po.pm
==============================================================================
--- (empty file)
+++ trunk/src/tools/po4a/lib/Locale/Po4a/Po.pm	Wed Apr  1 22:49:13 2009
@@ -0,0 +1,1579 @@
+# Locale::Po4a::Po -- manipulation of po files
+# $Id: Po.pm,v 1.95 2009-02-28 22:18:39 nekral-guest Exp $
+#
+# This program is free software; you may redistribute it and/or modify it
+# under the terms of GPL (see COPYING).
+
+############################################################################
+# Modules and declarations
+############################################################################
+
+=head1 NAME
+
+Locale::Po4a::Po - po file manipulation module
+
+=head1 SYNOPSIS
+
+    use Locale::Po4a::Po;
+    my $pofile=Locale::Po4a::Po->new();
+
+    # Read po file
+    $pofile->read('file.po');
+
+    # Add an entry
+    $pofile->push('msgid' => 'Hello', 'msgstr' => 'bonjour',
+                  'flags' => "wrap", 'reference'=>'file.c:46');
+
+    # Extract a translation
+    $pofile->gettext("Hello"); # returns 'bonjour'
+
+    # Write back to a file
+    $pofile->write('otherfile.po');
+
+=head1 DESCRIPTION
+
+Locale::Po4a::Po is a module that allows you to manipulate message
+catalogs. You can load and write from/to a file (which extension is often
+I<po>), you can build new entries on the fly or request for the translation
+of a string.
+
+For a more complete description of message catalogs in the po format and
+their use, please refer to the documentation of the gettext program.
+
+This module is part of the PO4A project, which objective is to use po files
+(designed at origin to ease the translation of program messages) to
+translate everything, including documentation (man page, info manual),
+package description, debconf templates, and everything which may benefit
+from this.
+
+=head1 OPTIONS ACCEPTED BY THIS MODULE
+
+=over 4
+
+=item porefs
+
+This specifies the reference format. It can be one of 'none' to not produce
+any reference, 'noline' to not specify the line number, and 'full' to
+include complete references.
+
+=back
+
+=cut
+
+use IO::File;
+
+
+require Exporter;
+
+package Locale::Po4a::Po;
+use DynaLoader;
+
+use Locale::Po4a::Common qw(wrap_msg wrap_mod wrap_ref_mod dgettext);
+
+use subs qw(makespace);
+use vars qw(@ISA @EXPORT_OK);
+ at ISA = qw(Exporter DynaLoader);
+ at EXPORT = qw(%debug);
+ at EXPORT_OK = qw(&move_po_if_needed);
+
+use Locale::Po4a::TransTractor;
+# Try to use a C extension if present.
+eval("bootstrap Locale::Po4a::Po $Locale::Po4a::TransTractor::VERSION");
+
+use 5.006;
+use strict;
+use warnings;
+
+use Carp qw(croak);
+use File::Path; # mkdir before write
+use File::Copy; # move
+use POSIX qw(strftime floor);
+use Time::Local;
+
+use Encode;
+
+my @known_flags=qw(wrap no-wrap c-format fuzzy);
+
+our %debug=('canonize'  => 0,
+            'quote'     => 0,
+            'escape'    => 0,
+            'encoding'  => 0,
+            'filter'    => 0);
+
+=head1 Functions about whole message catalogs
+
+=over 4
+
+=item new()
+
+Creates a new message catalog. If an argument is provided, it's the name of
+a po file we should load.
+
+=cut
+
+sub new {
+    my ($this, $options) = (shift, shift);
+    my $class = ref($this) || $this;
+    my $self = {};
+    bless $self, $class;
+    $self->initialize($options);
+
+    my $filename = shift;
+    $self->read($filename) if defined($filename) && length($filename);
+    return $self;
+}
+
+# Return the numerical timezone (e.g. +0200)
+# Neither the %z nor the %s formats of strftime are portable:
+# '%s' is not supported on Solaris and '%z' indicates
+# "2006-10-25 19:36E. Europe Standard Time" on MS Windows.
+sub timezone {
+    my @g = gmtime();
+    my @l = localtime();
+
+    my $diff;
+    $diff  = floor(timelocal(@l)/60 +0.5);
+    $diff -= floor(timelocal(@g)/60 +0.5);
+
+    my $h = floor($diff / 60) + $l[8]; # $l[8] indicates if we are  
currently
+                                       # in a daylight saving time zone
+    my $m = $diff%60;
+
+    return sprintf "%+03d%02d\n", $h, $m;
+}
+
+sub initialize {
+    my ($self, $options) = (shift, shift);
+    my $date = strftime("%Y-%m-%d %H:%M", localtime).timezone();
+    chomp $date;
+#    $options = ref($options) || $options;
+
+    $self->{options}{'porefs'}= 'full';
+    $self->{options}{'msgid-bugs-address'}= undef;
+    $self->{options}{'copyright-holder'}= "Free Software Foundation, Inc.";
+    $self->{options}{'package-name'}= "PACKAGE";
+    $self->{options}{'package-version'}= "VERSION";
+    foreach my $opt (keys %$options) {
+        if ($options->{$opt}) {
+            die wrap_mod("po4a::po",
+                         dgettext ("po4a", "Unknown option: %s"), $opt)
+                unless exists $self->{options}{$opt};
+            $self->{options}{$opt} = $options->{$opt};
+        }
+    }
+    $self->{options}{'porefs'} =~ /^(full|noline|none)$/ ||
+        die wrap_mod("po4a::po",
+                     dgettext ("po4a",
+                               "Invalid value for option 'porefs' ('%s'  
is ".
+                               "not one of 'full', 'noline' or 'none')"),
+                     $self->{options}{'porefs'});
+
+    $self->{po}=();
+    $self->{count}=0;  # number of msgids in the PO
+    # count_doc: number of strings in the document
+    # (duplicate strings counted multiple times)
+    $self->{count_doc}=0;
+    $self->{header_comment}=
+                     " SOME DESCRIPTIVE TITLE\n"
+                    ." Copyright (C) YEAR ".
+                     $self->{options}{'copyright-holder'}."\n"
+                    ." This file is distributed under the same license ".
+                     "as the ".$self->{options}{'package-name'}."  
package.\n"
+                    ." FIRST AUTHOR <EMAIL\@ADDRESS>, YEAR.\n"
+                    ."\n"
+                    .", fuzzy";
+#    $self->header_tag="fuzzy";
+    $self->{header}=escape_text("Project-Id-Version: ".
+                                $self->{options}{'package-name'}." ".
+                                $self->{options}{'package-version'}."\n".
+                        ((defined $self->{options}{'msgid-bugs-address'})?
+        "Report-Msgid-Bugs-To: ".$self->{options}{'msgid-bugs-address'}."\n":
+                                "").
+                                "POT-Creation-Date: $date\n".
+                                "PO-Revision-Date: YEAR-MO-DA  
HO:MI+ZONE\n".
+                                "Last-Translator: FULL NAME  
<EMAIL\@ADDRESS>\n".
+                                "Language-Team: LANGUAGE <LL\@li.org>\n".
+                                "MIME-Version: 1.0\n".
+                                "Content-Type: text/plain;  
charset=CHARSET\n".
+                                "Content-Transfer-Encoding: ENCODING");
+
+    $self->{encoder}=find_encoding("ascii");
+
+    # To make stats about gettext hits
+    $self->stats_clear();
+}
+
+=item read($)
+
+Reads a po file (which name is given as argument).  Previously existing
+entries in self are not removed, the new ones are added to the end of the
+catalog.
+
+=cut
+
+sub read {
+    my $self=shift;
+    my $filename=shift
+        or croak wrap_mod("po4a::po",
+                          dgettext("po4a",
+                                   "Please provide a non-null filename"));
+
+    my $fh;
+    if ($filename eq '-') {
+        $fh=*STDIN;
+    } else {
+        open $fh,"<$filename"
+            or croak wrap_mod("po4a::po",
+                              dgettext("po4a", "Can't read from %s: %s"),
+                              $filename, $!);
+    }
+
+    ## Read paragraphs line-by-line
+    my $pofile="";
+    my $textline;
+    while (defined ($textline = <$fh>)) {
+        $pofile .= $textline;
+    }
+#    close INPUT
+#        or croak (sprintf(dgettext("po4a",
+#                                   "Can't close %s after reading: %s"),
+#                          $filename,$!)."\n");
+
+    my $linenum=0;
+
+    foreach my $msg (split (/\n\n/,$pofile)) {
+        my ($msgid,$msgstr,$comment,$automatic,$reference,$flags,$buffer);
+        my ($msgid_plural, $msgstr_plural);
+        foreach my $line (split (/\n/,$msg)) {
+            $linenum++;
+            if ($line =~ /^#\. ?(.*)$/) {  # Automatic comment
+                $automatic .= (defined($automatic) ? "\n" : "").$1;
+
+            } elsif ($line =~ /^#: ?(.*)$/) { # reference
+                $reference .= (defined($reference) ? "\n" : "").$1;
+
+            } elsif ($line =~ /^#, ?(.*)$/) { # flags
+                $flags .= (defined($flags) ? "\n" : "").$1;
+
+            } elsif ($line =~ /^#(.*)$/) {  # Translator comments
+                $comment .= (defined($comment) ? "\n" : "").($1||"");
+
+            } elsif ($line =~ /^msgid (".*")$/) { # begin of msgid
+                $buffer = $1;
+
+            } elsif ($line =~ /^msgid_plural (".*")$/) {
+                # begin of msgid_plural, end of msgid
+
+                $msgid = $buffer;
+                $buffer = $1;
+
+            } elsif ($line =~ /^msgstr (".*")$/) {
+                # begin of msgstr, end of msgid
+
+                $msgid = $buffer;
+                $buffer = "$1";
+
+            } elsif ($line =~ /^msgstr\[([0-9]+)\] (".*")$/) {
+                # begin of msgstr[x], end of msgid_plural or msgstr[x-1]
+
+                # Note: po4a cannot uses plural forms
+                # (no integer to use the plural form)
+                #   * drop the msgstr[x] where x >= 2
+                #   * use msgstr[0] as the translation of msgid
+                #   * use msgstr[1] as the translation of msgid_plural
+
+                if ($1 eq "0") {
+                    $msgid_plural = $buffer;
+                    $buffer = "$2";
+                } elsif ($1 eq "1") {
+                    $msgstr = $buffer;
+                    $buffer = "$2";
+                } elsif ($1 eq "2") {
+                    $msgstr_plural = $buffer;
+                    warn wrap_ref_mod("$filename:$linenum",
+                                      "po4a::po",
+                                      dgettext("po4a", "Messages with more  
than 2 plural forms are not supported."));
+                }
+            } elsif ($line =~ /^(".*")$/) {
+                # continuation of a line
+                $buffer .= "\n$1";
+
+            } else {
+                warn wrap_ref_mod("$filename:$linenum",
+                                  "po4a::po",
+                                  dgettext("po4a", "Strange line:  
-->%s<--"),
+                                  $line);
+            }
+        }
+        $linenum++;
+        if (defined $msgid_plural) {
+            $msgstr_plural=$buffer;
+
+            $msgid = unquote_text($msgid) if (defined($msgid));
+            $msgstr = unquote_text($msgstr) if (defined($msgstr));
+
+            $self->push_raw ('msgid'     => $msgid,
+                             'msgstr'    => $msgstr,
+                             'reference' => $reference,
+                             'flags'     => $flags,
+                             'comment'   => $comment,
+                             'automatic' => $automatic,
+                             'plural'    => 0);
+
+            $msgid_plural = unquote_text($msgid_plural)
+                if (defined($msgid_plural));
+            $msgstr_plural = unquote_text($msgstr_plural)
+                if (defined($msgstr_plural));
+
+            $self->push_raw ('msgid'     => $msgid_plural,
+                             'msgstr'    => $msgstr_plural,
+                             'reference' => $reference,
+                             'flags'     => $flags,
+                             'comment'   => $comment,
+                             'automatic' => $automatic,
+                             'plural'    => 1);
+        } else {
+            $msgstr=$buffer;
+
+            $msgid = unquote_text($msgid) if (defined($msgid));
+            $msgstr = unquote_text($msgstr) if (defined($msgstr));
+
+            $self->push_raw ('msgid'     => $msgid,
+                             'msgstr'    => $msgstr,
+                             'reference' => $reference,
+                             'flags'     => $flags,
+                             'comment'   => $comment,
+                             'automatic' => $automatic);
+        }
+    }
+}
+
+=item write($)
+
+Writes the current catalog to the given file.
+
+=cut
+
+sub write{
+    my $self=shift;
+    my $filename=shift
+        or croak dgettext("po4a","Can't write to a file without  
filename")."\n";
+
+    my $fh;
+    if ($filename eq '-') {
+        $fh=\*STDOUT;
+    } else {
+        # make sure the directory in which we should write the localized
+        # file exists
+        my $dir = $filename;
+        if ($dir =~ m|/|) {
+            $dir =~ s|/[^/]*$||;
+
+            File::Path::mkpath($dir, 0, 0755) # Croaks on error
+                if (length ($dir) && ! -e $dir);
+        }
+        open $fh,">$filename"
+            or croak wrap_mod("po4a::po",
+                              dgettext("po4a", "Can't write to %s: %s"),
+                              $filename, $!);
+    }
+
+    print $fh "".format_comment($self->{header_comment},"")
+        if defined($self->{header_comment}) &&  
length($self->{header_comment});
+
+    print $fh "msgid \"\"\n";
+    print $fh "msgstr ".quote_text($self->{header})."\n\n";
+
+
+    my $buf_msgstr_plural; # USed to keep the first msgstr of plural forms
+    my $first=1;
+    foreach my $msgid ( sort { ($self->{po}{"$a"}{'pos'}) <=>
+                               ($self->{po}{"$b"}{'pos'})
+                             }  keys %{$self->{po}}) {
+        my $output="";
+
+        if ($first) {
+            $first=0;
+        } else {
+            $output .= "\n";
+        }
+
+        $output .= format_comment($self->{po}{$msgid}{'comment'},"")
+            if    defined($self->{po}{$msgid}{'comment'})
+               && length ($self->{po}{$msgid}{'comment'});
+        if (   defined($self->{po}{$msgid}{'automatic'})
+            && length ($self->{po}{$msgid}{'automatic'})) {
+            foreach my $comment  
(split(/\\n/,$self->{po}{$msgid}{'automatic'}))
+            {
+                $output .= format_comment($comment, ". ")
+            }
+        }
+        $output .= format_comment($self->{po}{$msgid}{'type'},". type: ")
+            if    defined($self->{po}{$msgid}{'type'})
+               && length ($self->{po}{$msgid}{'type'});
+        $output .= format_comment($self->{po}{$msgid}{'reference'},": ")
+            if    defined($self->{po}{$msgid}{'reference'})
+               && length ($self->{po}{$msgid}{'reference'});
+        $output .= "#, ". join(", ", sort  
split(/\s+/,$self->{po}{$msgid}{'flags'}))."\n"
+            if    defined($self->{po}{$msgid}{'flags'})
+               && length ($self->{po}{$msgid}{'flags'});
+
+        if (exists $self->{po}{$msgid}{'plural'}) {
+            if ($self->{po}{$msgid}{'plural'} == 0) {
+                if ($self->get_charset =~ /^utf-8$/i) {
+                    my $msgstr =  
Encode::decode_utf8($self->{po}{$msgid}{'msgstr'});
+                    $msgid = Encode::decode_utf8($msgid);
+                    $output .=  
Encode::encode_utf8("msgid ".quote_text($msgid)."\n");
+                    $buf_msgstr_plural =  
Encode::encode_utf8("msgstr[0] ".quote_text($msgstr)."\n");
+                } else {
+                    $output = "msgid ".quote_text($msgid)."\n";
+                    $buf_msgstr_plural  
= "msgstr[0] ".quote_text($self->{po}{$msgid}{'msgstr'})."\n";
+                }
+            } elsif ($self->{po}{$msgid}{'plural'} == 1) {
+# TODO: there may be only one plural form
+                if ($self->get_charset =~ /^utf-8$/i) {
+                    my $msgstr =  
Encode::decode_utf8($self->{po}{$msgid}{'msgstr'});
+                    $msgid = Encode::decode_utf8($msgid);
+                    $output =  
Encode::encode_utf8("msgid_plural ".quote_text($msgid)."\n");
+                    $output .= $buf_msgstr_plural;
+                    $output .=  
Encode::encode_utf8("msgstr[1] ".quote_text($msgstr)."\n");
+                    $buf_msgstr_plural = "";
+                } else {
+                    $output = "msgid_plural ".quote_text($msgid)."\n";
+                    $output .= $buf_msgstr_plural;
+                     
$output .= "msgstr[1] ".quote_text($self->{po}{$msgid}{'msgstr'})."\n";
+                }
+            } else {
+                die wrap_msg(dgettext("po4a","Can't write PO files with  
more than two plural forms."));
+            }
+        } else {
+            if ($self->get_charset =~ /^utf-8$/i) {
+                my $msgstr =  
Encode::decode_utf8($self->{po}{$msgid}{'msgstr'});
+                $msgid = Encode::decode_utf8($msgid);
+                $output .=  
Encode::encode_utf8("msgid ".quote_text($msgid)."\n");
+                $output .=  
Encode::encode_utf8("msgstr ".quote_text($msgstr)."\n");
+            } else {
+                $output .= "msgid ".quote_text($msgid)."\n";
+                 
$output .= "msgstr ".quote_text($self->{po}{$msgid}{'msgstr'})."\n";
+            }
+        }
+
+        print $fh $output;
+    }
+#    print STDERR "$fh";
+#    if ($filename ne '-') {
+#        close $fh
+#            or croak (sprintf(dgettext("po4a",
+#                                       "Can't close %s after  
writing: %s\n"),
+#                              $filename,$!));
+#    }
+}
+
+=item write_if_needed($$)
+
+Like write, but if the PO or POT file already exists, the object will be
+written in a temporary file which will be compared with the existing file
+to check that the update is needed (this avoids to change a POT just to
+update a line reference or the POT-Creation-Date field).
+
+=cut
+
+sub move_po_if_needed {
+    my ($new_po, $old_po, $backup) = (shift, shift, shift);
+    my $diff;
+
+    if (-e $old_po) {
+        my $diff_ignore = "-I'^#:' "
+                         ."-I'^\"POT-Creation-Date:' "
+                         ."-I'^\"PO-Revision-Date:'";
+        $diff = qx(diff -q $diff_ignore $old_po $new_po);
+        if ( $diff eq "" ) {
+            unlink $new_po
+                or die wrap_msg(dgettext("po4a","Can't unlink %s: %s."),
+                                $new_po, $!);
+            # touch the old PO
+            my ($atime, $mtime) = (time,time);
+            utime $atime, $mtime, $old_po;
+        } else {
+            if ($backup) {
+                copy $old_po, $old_po."~"
+                    or die wrap_msg(dgettext("po4a","Can't copy %s  
to %s: %s."),
+                                    $old_po, $old_po."~", $!);
+            } else {
+            }
+            move $new_po, $old_po
+                or die wrap_msg(dgettext("po4a","Can't move %s  
to %s: %s."),
+                                $new_po, $old_po, $!);
+        }
+    } else {
+        move $new_po, $old_po
+            or die wrap_msg(dgettext("po4a","Can't move %s to %s: %s."),
+                            $new_po, $old_po, $!);
+    }
+}
+
+sub write_if_needed {
+    my $self=shift;
+    my $filename=shift
+        or croak dgettext("po4a","Can't write to a file without  
filename")."\n";
+
+    if (-e $filename) {
+        my ($tmp_filename);
+        (undef,$tmp_filename)=File::Temp->tempfile($filename."XXXX",
+                                                   OPEN   => 0,
+                                                   UNLINK => 0);
+        $self->write($tmp_filename);
+        move_po_if_needed($tmp_filename, $filename);
+    } else {
+        $self->write($filename);
+    }
+}
+
+=item gettextize($$)
+
+This function produces one translated message catalog from two catalogs, an
+original and a translation. This process is described in L<po4a(7)|po4a.7>,
+section I<Gettextization: how does it work?>.
+
+=cut
+
+sub gettextize {
+    my $this = shift;
+    my $class = ref($this) || $this;
+    my ($poorig,$potrans)=(shift,shift);
+
+    my $pores=Locale::Po4a::Po->new();
+
+    my $please_fail = 0;
+    my $toobad = dgettext("po4a",
+        "\nThe gettextization failed (once again). Don't give up, ".
+        "gettextizing is a subtle art, but this is only needed once ".
+        "to convert a project to the gorgeous luxus offered by po4a ".
+        "to translators.".
+        "\nPlease refer to the po4a(7) documentation, the section ".
+        "\"HOWTO convert a pre-existing translation to po4a?\" ".
+        "contains several hints to help you in your task");
+
+    # Don't fail right now when the entry count does not match. Instead,  
give
+    # it a try so that the user can see where we fail (which is probably  
where
+    # the problem is).
+    if ($poorig->count_entries_doc() > $potrans->count_entries_doc()) {
+        warn wrap_mod("po4a gettextize", dgettext("po4a",
+            "Original has more strings than the translation (%d>%d). ".
+            "Please fix it by editing the translated version to add ".
+            "some dummy entry."),
+                      $poorig->count_entries_doc(),
+                      $potrans->count_entries_doc());
+        $please_fail = 1;
+    } elsif ($poorig->count_entries_doc() < $potrans->count_entries_doc())  
{
+        warn wrap_mod("po4a gettextize", dgettext("po4a",
+            "Original has less strings than the translation (%d<%d). ".
+            "Please fix it by removing the extra entry from the ".
+            "translated file. You may need an addendum (cf po4a(7)) ".
+            "to reput the chunk in place after gettextization. A ".
+            "possible cause is that a text duplicated in the original ".
+            "is not translated the same way each time. Remove one of ".
+            "the translations, and you're fine."),
+                      $poorig->count_entries_doc(),
+                      $potrans->count_entries_doc());
+        $please_fail = 1;
+    }
+
+    if ( $poorig->get_charset =~ /^utf-8$/i ) {
+        $potrans->to_utf8;
+        $pores->set_charset("utf-8");
+    } else {
+        if ($potrans->get_charset eq "CHARSET") {
+            $pores->set_charset("ascii");
+        } else {
+            $pores->set_charset($potrans->get_charset);
+        }
+    }
+    print "Po character sets:\n".
+        "  original=".$poorig->get_charset."\n".
+        "  translated=".$potrans->get_charset."\n".
+        "  result=".$pores->get_charset."\n"
+            if $debug{'encoding'};
+
+    for (my ($o,$t)=(0,0) ;
+         $o<$poorig->count_entries_doc() &&  
$t<$potrans->count_entries_doc();
+         $o++,$t++) {
+        #
+        # Extract some informations
+
+        my ($orig,$trans)=($poorig->msgid_doc($o),$potrans->msgid_doc($t));
+#       print STDERR "Matches [[$orig]]<<$trans>>\n";
+
+        my ($reforig,$reftrans)=($poorig->{po}{$orig}{'reference'},
+                                 $potrans->{po}{$trans}{'reference'});
+        my ($typeorig,$typetrans)=($poorig->{po}{$orig}{'type'},
+                                   $potrans->{po}{$trans}{'type'});
+
+        #
+        # Make sure the type of both string exist
+        #
+        die wrap_mod("po4a gettextize",
+                     "Internal error: type of original string number %s ".
+                     "isn't provided", $o)
+            if ($typeorig eq '');
+
+        die wrap_mod("po4a gettextize",
+                     "Internal error: type of translated string  
number %s ".
+                     "isn't provided", $o)
+            if ($typetrans eq '');
+
+        #
+        # Make sure both type are the same
+        #
+        if ($typeorig ne $typetrans){
+            $pores->write("gettextization.failed.po");
+            die wrap_msg(dgettext("po4a",
+                         "po4a gettextization: Structure disparity  
between ".
+                         "original and translated files:\n".
+                         "msgid (at %s) is of type '%s' while\n".
+                         "msgstr (at %s) is of type '%s'.\n".
+                         "Original text: %s\n".
+                         "Translated text: %s\n".
+                         "(result so far dumped to  
gettextization.failed.po)").
+                         "%s",
+                         $reforig, $typeorig,
+                         $reftrans, $typetrans,
+                         $orig,
+                         $trans,
+                         $toobad);
+        }
+
+        #
+        # Push the entry
+        #
+        my $flags;
+        if (defined $poorig->{po}{$orig}{'flags'}) {
+            $flags = $poorig->{po}{$orig}{'flags'}." fuzzy";
+        } else {
+            $flags = "fuzzy";
+        }
+        $pores->push_raw('msgid'     => $orig,
+                         'msgstr'    => $trans,
+                         'flags'     => $flags,
+                         'type'      => $typeorig,
+                         'reference' => $reforig,
+                         'conflict'  => 1,
+                         'transref'  =>  
$potrans->{po}{$trans}{'reference'})
+            unless (defined($pores->{po}{$orig})
+                    and ($pores->{po}{$orig}{'msgstr'} eq $trans))
+        # FIXME: maybe we should be smarter about what reference should be
+        #        sent to push_raw.
+    }
+
+    # make sure we return a useful error message when entry count differ
+    die "$toobad\n" if $please_fail;
+
+    return $pores;
+}
+
+=item filter($)
+
+This function extracts a catalog from an existing one. Only the entries  
having
+a reference in the given file will be placed in the resulting catalog.
+
+This function parses its argument, converts it to a perl function  
definition,
+eval this definition and filter the fields for which this function returns
+true.
+
+I love perl sometimes ;)
+
+=cut
+
+sub filter {
+    my $self=shift;
+    our $filter=shift;
+
+    my $res;
+    $res = Locale::Po4a::Po->new();
+
+    # Parse the filter
+    our $code="sub apply { return ";
+    our $pos=0;
+    our $length = length $filter;
+
+    # explode chars to parts. How to subscript a string in Perl?
+    our @filter = split(//,$filter);
+
+    sub gloups {
+        my $fmt=shift;
+        my $space = "";
+        for (1..$pos){
+            $space .= ' ';
+        }
+        die wrap_msg("$fmt\n$filter\n$space^ HERE");
+    }
+    sub showmethecode {
+        return unless $debug{'filter'};
+        my $fmt=shift;
+        my $space="";
+        for (1..$pos){
+            $space .= ' ';
+        }
+        print STDERR "$filter\n$space^ $fmt\n";#"$code\n";
+    }
+
+    # I dream of a lex in perl :-/
+    sub parse_expression {
+        showmethecode("Begin expression")
+            if $debug{'filter'};
+
+        gloups("Begin of expression expected, got '%s'",$filter[$pos])
+            unless ($filter[$pos] eq '(');
+        $pos ++; # pass the '('
+        if ($filter[$pos] eq '&') {
+            # AND
+            $pos++;
+            showmethecode("Begin of AND")
+                if $debug{'filter'};
+            $code .= "(";
+            while (1) {
+                gloups ("Unfinished AND statement.")
+                    if ($pos == $length);
+                parse_expression();
+                if ($filter[$pos] eq '(') {
+                    $code .= " && ";
+                } elsif ($filter[$pos] eq ')') {
+                    last; # do not eat that char
+                } else {
+                    gloups("End of AND or begin of sub-expression  
expected, got '%s'", $filter[$pos]);
+                }
+            }
+            $code .= ")";
+        } elsif ($filter[$pos] eq '|') {
+            # OR
+            $pos++;
+            $code .= "(";
+            while (1) {
+                gloups("Unfinished OR statement.")
+                    if ($pos == $length);
+                parse_expression();
+                if ($filter[$pos] eq '(') {
+                    $code .= " || ";
+                } elsif ($filter[$pos] eq ')') {
+                    last; # do not eat that char
+                } else {
+                    gloups("End of OR or begin of sub-expression expected,  
got '%s'",$filter[$pos]);
+                }
+            }
+            $code .= ")";
+        } elsif ($filter[$pos] eq '!') {
+            # NOT
+            $pos++;
+            $code .= "(!";
+            gloups("Missing sub-expression in NOT statement.")
+                if ($pos == $length);
+            parse_expression();
+            $code .= ")";
+        } else {
+            # must be an equal. Let's get field and argument
+            my ($field,$arg,$done);
+            $field = substr($filter,$pos);
+            gloups("EQ statement contains no '=' or invalid field name")
+                unless ($field =~ /([a-z]*)=/i);
+            $field = lc($1);
+            $pos += (length $field) + 1;
+
+            # check that we've got a valid field name,
+            # and the number it referes to
+            # DO NOT CHANGE THE ORDER
+            my @names=qw(msgid msgstr reference flags comment automatic);
+            my $fieldpos;
+            for ($fieldpos = 0;
+                 $fieldpos < scalar @names && $field ne $names[$fieldpos];
+                 $fieldpos++) {}
+            gloups("Invalid field name: %s",$field)
+                if $fieldpos == scalar @names; # not found
+
+            # Now, get the argument value. It has to be between quotes,
+            # which can be escaped
+            # We point right on the first char of the argument
+            # (first quote already eaten)
+            my $escaped = 0;
+            my $quoted = 0;
+            if ($filter[$pos] eq '"') {
+                $pos++;
+                $quoted = 1;
+            }
+            showmethecode(($quoted?"Quoted":"Unquoted")." argument of  
field '$field'")
+                if $debug{'filter'};
+
+            while (!$done) {
+                gloups("Unfinished EQ argument.")
+                    if ($pos == $length);
+
+                if ($quoted) {
+                    if ($filter[$pos] eq '\\') {
+                        if ($escaped) {
+                            $arg .= '\\';
+                            $escaped = 0;
+                        } else {
+                            $escaped = 1;
+                        }
+                    } elsif ($escaped) {
+                        if ($filter[$pos] eq '"') {
+                            $arg .= '"';
+                            $escaped = 0;
+                        } else {
+                            gloups("Invalid escape sequence in  
argument: '\\%s'",$filter[$pos]);
+                        }
+                    } else {
+                        if ($filter[$pos] eq '"') {
+                            $done = 1;
+                        } else {
+                            $arg .= $filter[$pos];
+                        }
+                    }
+                } else {
+                    if ($filter[$pos] eq ')') {
+                        # counter the next ++ since we don't want to eat
+                        # this char
+                        $pos--;
+                        $done = 1;
+                    } else {
+                        $arg .= $filter[$pos];
+                    }
+                }
+                $pos++;
+            }
+            # and now, add the code to check this equality
+            $code .= "(\$_[$fieldpos] =~ m/$arg/)";
+
+        }
+        showmethecode("End of expression")
+            if $debug{'filter'};
+        gloups("Unfinished statement.")
+            if ($pos == $length);
+        gloups("End of expression expected, got '%s'",$filter[$pos])
+            unless ($filter[$pos] eq ')');
+        $pos++;
+    }
+    # And now, launch the beast, finish the function and use eval
+    # to construct this function.
+    # Ok, the lack of lexer is a fair price for the eval ;)
+    parse_expression();
+    gloups("Garbage at the end of the expression")
+        if ($pos != $length);
+    $code .= "; }";
+    print STDERR "CODE = $code\n"
+        if $debug{'filter'};
+    eval $code;
+    die wrap_mod("po4a::po", dgettext("po4a", "Eval failure: %s"), $@)
+        if $@;
+
+    for (my $cpt=(0) ;
+         $cpt<$self->count_entries();
+         $cpt++) {
+
+        my ($msgid,$ref,$msgstr,$flags,$type,$comment,$automatic);
+
+        $msgid = $self->msgid($cpt);
+        $ref=$self->{po}{$msgid}{'reference'};
+
+        $msgstr= $self->{po}{$msgid}{'msgstr'};
+        $flags =  $self->{po}{$msgid}{'flags'};
+        $type = $self->{po}{$msgid}{'type'};
+        $comment = $self->{po}{$msgid}{'comment'};
+        $automatic = $self->{po}{$msgid}{'automatic'};
+
+        # DO NOT CHANGE THE ORDER
+        $res->push_raw('msgid' => $msgid,
+                       'msgstr' => $msgstr,
+                       'flags' => $flags,
+                       'type'  => $type,
+                       'reference' => $ref,
+                       'comment' => $comment,
+                       'automatic' => $automatic)
+               if (apply($msgid,$msgstr,$ref,$flags,$comment,$automatic));
+    }
+    # delete the apply subroutine
+    # otherwise it will be redefined.
+    undef &apply;
+    return $res;
+}
+
+=item to_utf8()
+
+Recodes to utf-8 the po's msgstrs. Does nothing if the charset is not
+specified in the po file ("CHARSET" value), or if it's already utf-8 or
+ascii.
+
+=cut
+
+sub to_utf8 {
+    my $this = shift;
+    my $charset = $this->get_charset();
+
+    unless ($charset eq "CHARSET" or
+            $charset =~ /^ascii$/i or
+            $charset =~ /^utf-8$/i) {
+        foreach my $msgid ( keys %{$this->{po}} ) {
+            Encode::from_to($this->{po}{$msgid}{'msgstr'},  
$charset, "utf-8");
+        }
+        $this->set_charset("utf-8");
+    }
+}
+
+=back
+
+=head1 Functions to use a message catalog for translations
+
+=over 4
+
+=item gettext($%)
+
+Request the translation of the string given as argument in the current  
catalog.
+The function returns the original (untranslated) string if the string was  
not
+found.
+
+After the string to translate, you can pass a hash of extra
+arguments. Here are the valid entries:
+
+=over
+
+=item wrap
+
+boolean indicating whether we can consider that whitespaces in string are
+not important. If yes, the function canonizes the string before looking for
+a translation, and wraps the result.
+
+=item wrapcol
+
+The column at which we should wrap (default: 76).
+
+=back
+
+=cut
+
+sub gettext {
+    my $self=shift;
+    my $text=shift;
+    my (%opt)=@_;
+    my $res;
+
+    return "" unless defined($text) && length($text); # Avoid returning  
the header.
+    my $validoption="reference wrap wrapcol";
+    my %validoption;
+
+    map { $validoption{$_}=1 } (split(/ /,$validoption));
+    foreach (keys %opt) {
+        Carp::confess "internal error:  unknown arg $_.\n".
+                      "Here are the valid options: $validoption.\n"
+            unless $validoption{$_};
+    }
+
+    $text=canonize($text)
+        if ($opt{'wrap'});
+
+    my $esc_text=escape_text($text);
+
+    $self->{gettextqueries}++;
+
+    if (    defined $self->{po}{$esc_text}
+        and defined $self->{po}{$esc_text}{'msgstr'}
+        and length $self->{po}{$esc_text}{'msgstr'}
+        and (   not defined $self->{po}{$esc_text}{'flags'}
+             or $self->{po}{$esc_text}{'flags'} !~ /fuzzy/)) {
+
+        $self->{gettexthits}++;
+        $res = unescape_text($self->{po}{$esc_text}{'msgstr'});
+        if (defined $self->{po}{$esc_text}{'plural'}) {
+            if ($self->{po}{$esc_text}{'plural'} eq "0") {
+                warn wrap_mod("po4a gettextize", dgettext("po4a",
+                              "'%s' is the singular form of a message, ".
+                              "po4a will use the msgstr[0] translation  
(%s)."),
+                              $esc_text, $res);
+            } else {
+                warn wrap_mod("po4a gettextize", dgettext("po4a",
+                              "'%s' is the plural form of a message, ".
+                              "po4a will use the msgstr[1] translation  
(%s)."),
+                              $esc_text, $res);
+            }
+        }
+    } else {
+        $res = $text;
+    }
+
+    if ($opt{'wrap'}) {
+        if ($self->get_charset =~ /^utf-8$/i) {
+            $res=Encode::decode_utf8($res);
+            $res=wrap ($res, $opt{'wrapcol'} || 76);
+            $res=Encode::encode_utf8($res);
+        } else {
+            $res=wrap ($res, $opt{'wrapcol'} || 76);
+        }
+    }
+#    print STDERR "Gettext >>>$text<<<(escaped=$esc_text)=[[[$res]]]\n\n";
+    return $res;
+}
+
+=item stats_get()
+
+Returns statistics about the hit ratio of gettext since the last time that
+stats_clear() was called. Please note that it's not the same
+statistics than the one printed by msgfmt --statistic. Here, it's  
statistics
+about recent usage of the po file, while msgfmt reports the status of the
+file.  Example of use:
+
+    [some use of the po file to translate stuff]
+
+    ($percent,$hit,$queries) = $pofile->stats_get();
+    print "So far, we found translations for $percent\%  ($hit of  
$queries) of strings.\n";
+
+=cut
+
+sub stats_get() {
+    my $self=shift;
+    my ($h,$q)=($self->{gettexthits},$self->{gettextqueries});
+    my $p = ($q == 0 ? 100 : int($h/$q*10000)/100);
+
+#    $p =~ s/\.00//;
+#    $p =~ s/(\..)0/$1/;
+
+    return ( $p,$h,$q );
+}
+
+=item stats_clear()
+
+Clears the statistics about gettext hits.
+
+=cut
+
+sub stats_clear {
+    my $self = shift;
+    $self->{gettextqueries} = 0;
+    $self->{gettexthits} = 0;
+}
+
+=back
+
+=head1 Functions to build a message catalog
+
+=over 4
+
+=item push(%)
+
+Push a new entry at the end of the current catalog. The arguments should
+form a hash table. The valid keys are:
+
+=over 4
+
+=item msgid
+
+the string in original language.
+
+=item msgstr
+
+the translation.
+
+=item reference
+
+an indication of where this string was found. Example: file.c:46 (meaning
+in 'file.c' at line 46). It can be a space-separated list in case of
+multiple occurrences.
+
+=item comment
+
+a comment added here manually (by the translators). The format here is  
free.
+
+=item automatic
+
+a comment which was automatically added by the string extraction
+program. See the I<--add-comments> option of the B<xgettext> program for
+more information.
+
+=item flags
+
+space-separated list of all defined flags for this entry.
+
+Valid flags are: c-text, python-text, lisp-text, elisp-text, librep-text,
+smalltalk-text, java-text, awk-text, object-pascal-text, ycp-text,
+tcl-text, wrap, no-wrap and fuzzy.
+
+See the gettext documentation for their meaning.
+
+=item type
+
+This is mostly an internal argument: it is used while gettextizing
+documents. The idea here is to parse both the original and the translation
+into a po object, and merge them, using one's msgid as msgid and the
+other's msgid as msgstr. To make sure that things get ok, each msgid in po
+objects are given a type, based on their structure (like "chapt", "sect1",
+"p" and so on in docbook). If the types of strings are not the same, that
+means that both files do not share the same structure, and the process
+reports an error.
+
+This information is written as automatic comment in the po file since this
+gives to translators some context about the strings to translate.
+
+=item wrap
+
+boolean indicating whether whitespaces can be mangled in cosmetic
+reformattings. If true, the string is canonized before use.
+
+This information is written to the po file using the 'wrap' or 'no-wrap'  
flag.
+
+=item wrapcol
+
+The column at which we should wrap (default: 76).
+
+This information is not written to the po file.
+
+=back
+
+=cut
+
+sub push {
+    my $self=shift;
+    my %entry=@_;
+
+    my $validoption="wrap wrapcol type msgid msgstr automatic flags  
reference";
+    my %validoption;
+
+    map { $validoption{$_}=1 } (split(/ /,$validoption));
+    foreach (keys %entry) {
+        Carp::confess "internal error:  unknown arg $_.\n".
+                      "Here are the valid options: $validoption.\n"
+            unless $validoption{$_};
+    }
+
+    unless ($entry{'wrap'}) {
+        $entry{'flags'} .= " no-wrap";
+    }
+    if (defined ($entry{'msgid'})) {
+        $entry{'msgid'} = canonize($entry{'msgid'})
+            if ($entry{'wrap'});
+
+        $entry{'msgid'} = escape_text($entry{'msgid'});
+    }
+    if (defined ($entry{'msgstr'})) {
+        $entry{'msgstr'} = canonize($entry{'msgstr'})
+            if ($entry{'wrap'});
+
+        $entry{'msgstr'} = escape_text($entry{'msgstr'});
+    }
+
+    $self->push_raw(%entry);
+}
+
+# The same as push(), but assuming that msgid and msgstr are already  
escaped
+sub push_raw {
+    my $self=shift;
+    my %entry=@_;
+    my  
($msgid,$msgstr,$reference,$comment,$automatic,$flags,$type,$transref)=
+        ($entry{'msgid'},$entry{'msgstr'},
+         $entry{'reference'},$entry{'comment'},$entry{'automatic'},
+         $entry{'flags'},$entry{'type'},$entry{'transref'});
+    my $keep_conflict = $entry{'conflict'};
+
+#    print STDERR "Push_raw\n";
+#    print STDERR " msgid=>>>$msgid<<<\n" if $msgid;
+#    print STDERR " msgstr=[[[$msgstr]]]\n" if $msgstr;
+#    Carp::cluck " flags=$flags\n" if $flags;
+
+    return unless defined($entry{'msgid'});
+
+    #no msgid => header definition
+    unless (length($entry{'msgid'})) {
+#       if (defined($self->{header}) && $self->{header} =~ /\S/) {
+#           warn dgettext("po4a","Redefinition of the header. ".
+#                                "The old one will be discarded\n");
+#       } FIXME: do that iff the header isn't the default one.
+        $self->{header}=$msgstr;
+        $self->{header_comment}=$comment;
+        my $charset = $self->get_charset;
+        if ($charset ne "CHARSET") {
+            $self->{encoder}=find_encoding($charset);
+        } else {
+            $self->{encoder}=find_encoding("ascii");
+        }
+        return;
+    }
+
+    if ($self->{options}{'porefs'} eq "none") {
+        $reference = "";
+    } elsif ($self->{options}{'porefs'} eq "noline") {
+        $reference =~ s/:[0-9]*/:1/g;
+    }
+
+    if (defined($self->{po}{$msgid})) {
+        warn wrap_mod("po4a::po",
+                      dgettext("po4a","msgid defined twice: %s"),
+                      $msgid)
+            if (0); # FIXME: put a verbose stuff
+        if (    defined $msgstr
+            and defined $self->{po}{$msgid}{'msgstr'}
+            and $self->{po}{$msgid}{'msgstr'} ne $msgstr) {
+            my $txt=quote_text($msgid);
+            my ($first,$second)=
+                (format_comment(". ",$self->{po}{$msgid}{'reference'}).
+                 quote_text($self->{po}{$msgid}{'msgstr'}),
+
+                 format_comment(". ",$reference).
+                 quote_text($msgstr));
+
+            if ($keep_conflict) {
+                if ($self->{po}{$msgid}{'msgstr'} =~ m/^#-#-#-#-#  .*   
#-#-#-#-#\\n/s) {
+                    $msgstr = $self->{po}{$msgid}{'msgstr'}.
+                              "\\n#-#-#-#-#  $transref  #-#-#-#-#\\n".
+                              $msgstr;
+                } else {
+                    $msgstr = "#-#-#-#-#  ".
+                              $self->{po}{$msgid}{'transref'}.
+                              "  #-#-#-#-#\\n".
+                              $self->{po}{$msgid}{'msgstr'}."\\n".
+                              "#-#-#-#-#  $transref  #-#-#-#-#\\n".
+                              $msgstr;
+                }
+                # Every msgid will have the same list of references.
+                # Only keep the last list.
+                $self->{po}{$msgid}{'reference'} = "";
+            } else {
+            warn wrap_msg(dgettext("po4a",
+                                   "Translations don't match for:\n".
+                                   "%s\n".
+                                   "-->First translation:\n".
+                                   "%s\n".
+                                   " Second translation:\n".
+                                   "%s\n".
+                                   " Old translation discarded."),
+                          $txt,$first,$second);
+            }
+        }
+    }
+    if (defined $transref) {
+        $self->{po}{$msgid}{'transref'} = $transref;
+    }
+    if (defined $reference) {
+        if (defined $self->{po}{$msgid}{'reference'}) {
+            $self->{po}{$msgid}{'reference'} .= " ".$reference;
+        } else {
+            $self->{po}{$msgid}{'reference'} = $reference;
+        }
+    }
+    $self->{po}{$msgid}{'msgstr'} = $msgstr;
+    $self->{po}{$msgid}{'comment'} = $comment;
+    $self->{po}{$msgid}{'automatic'} = $automatic;
+    if (defined($self->{po}{$msgid}{'pos_doc'})) {
+        $self->{po}{$msgid}{'pos_doc'} .= " ".$self->{count_doc}++;
+    } else {
+        $self->{po}{$msgid}{'pos_doc'}  = $self->{count_doc}++;
+    }
+    unless (defined($self->{po}{$msgid}{'pos'})) {
+        $self->{po}{$msgid}{'pos'} = $self->{count}++;
+    }
+    $self->{po}{$msgid}{'type'} = $type;
+    $self->{po}{$msgid}{'plural'} = $entry{'plural'}
+        if defined $entry{'plural'};
+
+    if (defined($flags)) {
+        $flags = " $flags ";
+        $flags =~ s/,/ /g;
+        foreach my $flag (@known_flags) {
+            if ($flags =~ /\s$flag\s/) { # if flag to be set
+                unless (   defined($self->{po}{$msgid}{'flags'})
+                        && $self->{po}{$msgid}{'flags'} =~ /\b$flag\b/) {
+                    # flag not already set
+                    if (defined $self->{po}{$msgid}{'flags'}) {
+                        $self->{po}{$msgid}{'flags'} .= " ".$flag;
+                    } else {
+                        $self->{po}{$msgid}{'flags'} = $flag;
+                    }
+                }
+            }
+        }
+    }
+#    print STDERR "stored  
((($msgid)))=>(((".$self->{po}{$msgid}{'msgstr'}.")))\n\n";
+
+}
+
+=back
+
+=head1 Miscellaneous functions
+
+=over 4
+
+=item count_entries()
+
+Returns the number of entries in the catalog (without the header).
+
+=cut
+
+sub count_entries($) {
+    my $self=shift;
+    return $self->{count};
+}
+
+=item count_entries_doc()
+
+Returns the number of entries in document. If a string appears multiple  
times
+in the document, it will be counted multiple times
+
+=cut
+
+sub count_entries_doc($) {
+    my $self=shift;
+    return $self->{count_doc};
+}
+
+=item msgid($)
+
+Returns the msgid of the given number.
+
+=cut
+
+sub msgid($$) {
+    my $self=shift;
+    my $num=shift;
+
+    foreach my $msgid ( keys %{$self->{po}} ) {
+        return $msgid if ($self->{po}{$msgid}{'pos'} eq $num);
+    }
+    return undef;
+}
+
+=item msgid_doc($)
+
+Returns the msgid with the given position in the document.
+
+=cut
+
+sub msgid_doc($$) {
+    my $self=shift;
+    my $num=shift;
+
+    foreach my $msgid ( keys %{$self->{po}} ) {
+        foreach my $pos (split / /, $self->{po}{$msgid}{'pos_doc'}) {
+            return $msgid if ($pos eq $num);
+        }
+    }
+    return undef;
+}
+
+=item get_charset()
+
+Returns the character set specified in the po header. If it hasn't been
+set, it will return "CHARSET".
+
+=cut
+
+sub get_charset() {
+    my $self=shift;
+
+    $self->{header} =~ /charset=(.*?)[\s\\]/;
+
+    if (defined $1) {
+        return $1;
+    } else {
+        return "CHARSET";
+    }
+}
+
+=item set_charset($)
+
+This sets the character set of the po header to the value specified in its
+first argument. If you never call this function (and no file with a  
specified
+character set is read), the default value is left to "CHARSET". This value
+doesn't change the behavior of this module, it's just used to fill that  
field
+in the header, and to return it in get_charset().
+
+=cut
+
+sub set_charset() {
+    my $self=shift;
+
+    my ($newchar,$oldchar);
+    $newchar = shift;
+    $oldchar = $self->get_charset();
+
+    $self->{header} =~ s/$oldchar/$newchar/;
+    $self->{encoder}=find_encoding($newchar);
+}
+
+#----[ helper functions  
]---------------------------------------------------
+
+# transforme the string from its po file representation to the form which
+#   should be used to print it
+sub unescape_text {
+    my $text = shift;
+
+    print STDERR "\nunescape [$text]====" if $debug{'escape'};
+    $text = join("",split(/\n/,$text));
+    $text =~ s/\\"/"/g;
+    # unescape newlines
+    #   NOTE on \G:
+    #   The following regular expression introduce newlines.
+    #   Thus, ^ doesn't match all beginnings of lines.
+    #   \G is a zero-width assertion that matches the position
+    #   of the previous substitution with s///g. As every
+    #   substitution ends by a newline, it always matches a
+    #   position just after a newline.
+    $text =~ s/(           # $1:
+                (\G|[^\\]) #    beginning of the line or any char
+                           #    different from '\'
+                (\\\\)*    #    followed by any even number of '\'
+               )\\n        # and followed by an escaped newline
+              /$1\n/sgx;   # single string, match globally, allow comments
+    # unescape tabulations
+    $text =~ s/(          # $1:
+                (\G|[^\\])#    beginning of the line or any char
+                          #    different from '\'
+                (\\\\)*   #    followed by any even number of '\'
+               )\\t       # and followed by an escaped tabulation
+              /$1\t/mgx;  # multilines string, match globally, allow  
comments
+    # and unescape the escape character
+    $text =~ s/\\\\/\\/g;
+    print STDERR ">$text<\n" if $debug{'escape'};
+
+    return $text;
+}
+
+# transform the string to its representation as it should be written in po
+# files
+sub escape_text {
+    my $text = shift;
+
+    print STDERR "\nescape [$text]====" if $debug{'escape'};
+    $text =~ s/\\/\\\\/g;
+    $text =~ s/"/\\"/g;
+    $text =~ s/\n/\\n/g;
+    $text =~ s/\t/\\t/g;
+    print STDERR ">$text<\n" if $debug{'escape'};
+
+    return $text;
+}
+
+# put quotes around the string on each lines (without escaping it)
+# It does also normalize the text (ie, make sure its representation is  
wraped
+#   on the 80th char, but without changing the meaning of the string)
+sub quote_text {
+    my $string = shift;
+
+    return '""' unless defined($string) && length($string);
+
+    print STDERR "\nquote [$string]====" if $debug{'quote'};
+    # break lines on newlines, if any
+    # see unescape_text for an explanation on \G
+    $string =~ s/(           # $1:
+                  (\G|[^\\]) #    beginning of the line or any char
+                             #    different from '\'
+                  (\\\\)*    #    followed by any even number of '\'
+                 \\n)        # and followed by an escaped newline
+                /$1\n/sgx;   # single string, match globally, allow  
comments
+    $string = wrap($string);
+    my @string = split(/\n/,$string);
+    $string = join ("\"\n\"", at string);
+    $string = "\"$string\"";
+    if (scalar @string > 1 && $string[0] ne '') {
+        $string = "\"\"\n".$string;
+    }
+
+    print STDERR ">$string<\n" if $debug{'quote'};
+    return $string;
+}
+
+# undo the work of the quote_text function
+sub unquote_text {
+    my $string = shift;
+    print STDERR "\nunquote [$string]====" if $debug{'quote'};
+    $string =~ s/^""\\n//s;
+    $string =~ s/^"(.*)"$/$1/s;
+    $string =~ s/"\n"//gm;
+    # Note: an even number of '\' could precede \\n, but I could not build  
a
+    # document to test this
+    $string =~ s/([^\\])\\n\n/$1!!DUMMYPOPM!!/gm;
+    $string =~ s|!!DUMMYPOPM!!|\\n|gm;
+    print STDERR ">$string<\n" if $debug{'quote'};
+    return $string;
+}
+
+# canonize the string: write it on only one line, changing consecutive
+# whitespace to only one space.
+# Warning, it changes the string and should only be called if the string is
+# plain text
+sub canonize {
+    my $text=shift;
+    print STDERR "\ncanonize [$text]====" if $debug{'canonize'};
+    $text =~ s/^ *//s;
+    $text =~ s/^[ \t]+/  /gm;
+    # if ($text eq "\n"), it messed up the first string (header)
+    $text =~ s/\n/  /gm if ($text ne "\n");
+    $text =~ s/([.)])  +/$1  /gm;
+    $text =~ s/([^.)])  */$1 /gm;
+    $text =~ s/ *$//s;
+    print STDERR ">$text<\n" if $debug{'canonize'};
+    return $text;
+}
+
+# wraps the string. We don't use Text::Wrap since it mangles whitespace at
+# the end of splited line
+sub wrap {
+    my $text=shift;
+    return "0" if ($text eq '0');
+    my $col=shift || 76;
+    my @lines=split(/\n/,"$text");
+    my $res="";
+    my $first=1;
+    while (defined(my $line=shift @lines)) {
+        if ($first && length($line) > $col - 10) {
+            unshift @lines,$line;
+            $first=0;
+            next;
+        }
+        if (length($line) > $col) {
+            my $pos=rindex($line," ",$col);
+            while (substr($line,$pos-1,1) eq '.' && $pos != -1) {
+                $pos=rindex($line," ",$pos-1);
+            }
+            if ($pos == -1) {
+                # There are no spaces in the first $col chars, pick-up the
+                # first space
+                $pos = index($line," ");
+            }
+            if ($pos != -1) {
+                my $end=substr($line,$pos+1);
+                $line=substr($line,0,$pos+1);
+                if ($end =~ s/^( +)//) {
+                    $line .= $1;
+                }
+                unshift @lines,$end;
+            }
+        }
+        $first=0;
+        $res.="$line\n";
+    }
+    # Restore the original trailing spaces
+    $res =~ s/\s+$//s;
+    if ($text =~ m/(\s+)$/s) {
+        $res .= $1;
+    }
+    return $res;
+}
+
+# outputs properly a '# ... ' line to be put in the po file
+sub format_comment {
+    my $comment=shift;
+    my $char=shift;
+    my $result = "#". $char . $comment;
+    $result =~ s/\n/\n#$char/gs;
+    $result =~ s/^#$char$/#/gm;
+    $result .= "\n";
+    return $result;
+}
+
+
+1;
+__END__
+
+=back
+
+=head1 AUTHORS
+
+ Denis Barbier <barbier at linuxfr.org>
+ Martin Quinson (mquinson#debian.org)
+
+=cut

Added: trunk/src/tools/po4a/lib/Locale/Po4a/TransTractor.pm
==============================================================================
--- (empty file)
+++ trunk/src/tools/po4a/lib/Locale/Po4a/TransTractor.pm	Wed Apr  1  
22:49:13 2009
@@ -0,0 +1,1100 @@
+#!/usr/bin/perl -w
+
+require Exporter;
+
+package Locale::Po4a::TransTractor;
+use DynaLoader;
+
+use 5.006;
+use strict;
+use warnings;
+
+use subs qw(makespace);
+use vars qw($VERSION @ISA @EXPORT);
+$VERSION="0.36";
+ at ISA = qw(DynaLoader);
+ at EXPORT = qw(new process translate
+             read write readpo writepo
+             getpoout setpoout);
+
+# Try to use a C extension if present.
+eval("bootstrap Locale::Po4a::TransTractor $VERSION");
+
+use Carp qw(croak);
+use Locale::Po4a::Po;
+use Locale::Po4a::Common;
+
+use File::Path; # mkdir before write
+
+use Encode;
+use Encode::Guess;
+
+=head1 NAME
+
+Locale::Po4a::TransTractor - Generic trans(lator ex)tractor.
+
+=head1 DESCRIPTION
+
+The po4a (po for anything) project goal is to ease translations (and more
+interestingly, the maintenance of translations) using gettext tools on
+areas where they were not expected like documentation.
+
+This class is the ancestor of every po4a parsers used to parse a document  
to
+search translatable strings, extract them to a po file and replace them by
+their translation in the output document.
+
+More formally, it takes the following arguments as input:
+
+=over 2
+
+=item -
+
+a document to translate ;
+
+=item -
+
+a po file containing the translations to use.
+
+=back
+
+As output, it produces:
+
+=over 2
+
+=item -
+
+another po file, resulting of the extraction of translatable strings from
+the input document ;
+
+=item -
+
+a translated document, with the same structure than the one in input, but
+with all translatable strings replaced with the translations found in the
+po file provided in input.
+
+=back
+
+Here is a graphical representation of this:
+
+   Input document --\                             /---> Output document
+                     \                           /       (translated)
+                      +-> parse() function -----+
+                     /                           \
+   Input po --------/                             \---> Output po
+                                                         (extracted)
+
+=head1 FUNCTIONS YOUR PARSER SHOULD OVERRIDE
+
+=over 4
+
+=item parse()
+
+This is where all the work takes place: the parsing of input documents, the
+generation of output, and the extraction of the translatable strings. This
+is pretty simple using the provided functions presented in the section
+"INTERNAL FUNCTIONS" below. See also the synopsis, which present an
+example.
+
+This function is called by the process() function bellow, but if you choose
+to use the new() function, and to add content manually to your document,
+you will have to call this function yourself.
+
+=item docheader()
+
+This function returns the header we should add to the produced document,
+quoted properly to be a comment in the target language.  See the section
+"Educating developers about translations", from L<po4a(7)|po4a.7>, for what
+it is good for.
+
+=back
+
+=cut
+
+sub docheader {}
+
+sub parse {}
+
+=head1 SYNOPSIS
+
+The following example parses a list of paragraphs beginning with "<p>".  
For the sake
+of simplicity, we assume that the document is well formatted, i.e.  
that '<p>'
+tags are the only tags present, and that this tag is at the very beginning
+of each paragraph.
+
+ sub parse {
+   my $self = shift;
+
+   PARAGRAPH: while (1) {
+       my ($paragraph,$pararef)=("","");
+       my $first=1;
+       my ($line,$lref)=$self->shiftline();
+       while (defined($line)) {
+	   if ($line =~ m/<p>/ && !$first--; ) {
+	       # Not the first time we see <p>.
+	       # Reput the current line in input,
+	       #  and put the built paragraph to output
+	       $self->unshiftline($line,$lref);
+	
+	       # Now that the document is formed, translate it:
+	       #   - Remove the leading tag
+	       $paragraph =~ s/^<p>//s;
+
+	       #   - push to output the leading tag (untranslated) and the
+	       #     rest of the paragraph (translated)
+	       $self->pushline(  "<p>"
+                               . $document->translate($paragraph,$pararef)
+                               );
+
+ 	       next PARAGRAPH;
+	   } else {
+	       # Append to the paragraph
+	       $paragraph .= $line;
+	       $pararef = $lref unless(length($pararef));
+	   }
+
+           # Reinit the loop
+           ($line,$lref)=$self->shiftline();
+       }
+       # Did not get a defined line? End of input file.
+       return;
+   }
+ }
+
+Once you've implemented the parse function, you can use your document
+class, using the public interface presented in the next section.
+
+=head1 PUBLIC INTERFACE for scripts using your parser
+
+=head2 Constructor
+
+=over 4
+
+=item process(%)
+
+This function can do all you need to do with a po4a document in one
+invocation. Its arguments must be packed as a hash. ACTIONS:
+
+=over 3
+
+=item a.
+
+Reads all the po files specified in po_in_name
+
+=item b.
+
+Reads all original documents specified in file_in_name
+
+=item c.
+
+Parses the document
+
+=item d.
+
+Reads and applies all the addenda specified
+
+=item e.
+
+Writes the translated document to file_out_name (if given)
+
+=item f.
+
+Writes the extracted po file to po_out_name (if given)
+
+=back
+
+ARGUMENTS, beside the ones accepted by new() (with expected type):
+
+=over 4
+
+=item file_in_name (@)
+
+List of filenames where we should read the input document.
+
+=item file_in_charset ($)
+
+Charset used in the input document (if it isn't specified, it will try
+to detect it from the input document).
+
+=item file_out_name ($)
+
+Filename where we should write the output document.
+
+=item file_out_charset ($)
+
+Charset used in the output document (if it isn't specified, it will use
+the po file charset).
+
+=item po_in_name (@)
+
+List of filenames where we should read the input po files from, containing
+the translation which will be used to translate the document.
+
+=item po_out_name ($)
+
+Filename where we should write the output po file, containing the strings
+extracted from the input document.
+
+=item addendum (@)
+
+List of filenames where we should read the addenda from.
+
+=item addendum_charset ($)
+
+Charset for the addenda.
+
+=back
+
+=item new(%)
+
+Create a new Po4a document. Accepted options (but be in a hash):
+
+=over 4
+
+=item verbose ($)
+
+Sets the verbosity.
+
+=item debug ($)
+
+Sets the debugging.
+
+=back
+
+=cut
+
+sub process {
+    ## Determine if we were called via an object-ref or a classname
+    my $self = shift;
+
+    ## Any remaining arguments are treated as initial values for the
+    ## hash that is used to represent this object.
+    my %params = @_;
+
+    # Build the args for new()
+    my %newparams = ();
+    foreach (keys %params) {
+	next if ($_ eq 'po_in_name' ||
+		 $_ eq 'po_out_name' ||
+		 $_ eq 'file_in_name' ||
+		 $_ eq 'file_in_charset' ||
+		 $_ eq 'file_out_name' ||
+		 $_ eq 'file_out_charset' ||
+		 $_ eq 'addendum' ||
+		 $_ eq 'addendum_charset');
+	$newparams{$_}=$params{$_};
+    }
+
+    $self->detected_charset($params{'file_in_charset'});
+    $self->{TT}{'file_out_charset'}=$params{'file_out_charset'};
+    if (defined($self->{TT}{'file_out_charset'}) and
+	length($self->{TT}{'file_out_charset'})) {
+	$self->{TT}{'file_out_encoder'} =  
find_encoding($self->{TT}{'file_out_charset'});
+    }
+    $self->{TT}{'addendum_charset'}=$params{'addendum_charset'};
+
+    foreach my $file (@{$params{'po_in_name'}}) {
+	print STDERR "readpo($file)... " if $self->debug();
+	$self->readpo($file);
+	print STDERR "done.\n" if $self->debug()
+    }
+    foreach my $file (@{$params{'file_in_name'}}) {
+	print STDERR "read($file)..." if $self->debug();
+	$self->read($file);
+	print STDERR "done.\n"  if $self->debug();
+    }
+    print STDERR "parse..." if $self->debug();
+    $self->parse();
+    print STDERR "done.\n" if $self->debug();
+    foreach my $file (@{$params{'addendum'}}) {
+	print STDERR "addendum($file)..." if $self->debug();
+	$self->addendum($file) || die "An addendum failed\n";
+	print STDERR "done.\n" if $self->debug();
+    }
+    if (defined $params{'file_out_name'}) {
+	print STDERR "write(".$params{'file_out_name'}.")... "
+	    if $self->debug();
+	$self->write($params{'file_out_name'});
+	print STDERR "done.\n" if $self->debug();
+    }
+    if (defined $params{'po_out_name'}) {
+	print STDERR "writepo(".$params{'po_out_name'}.")... "
+	     if $self->debug();
+	$self->writepo($params{'po_out_name'});
+	print STDERR "done.\n" if $self->debug();
+    }
+    return $self;
+}
+
+sub new {
+    ## Determine if we were called via an object-ref or a classname
+    my $this = shift;
+    my $class = ref($this) || $this;
+    my $self = { };
+    my %options=@_;
+    ## Bless ourselves into the desired class and perform any  
initialization
+    bless $self, $class;
+
+    ## initialize the plugin
+    # prevent the plugin from croaking on the options intended for Po.pm
+    $self->{options}{'porefs'} = '';
+    # let the plugin parse the options and such
+    $self->initialize(%options);
+
+    ## Create our private data
+    my %po_options;
+    $po_options{'porefs'} = $self->{options}{'porefs'};
+
+    # private data
+    $self->{TT}=();
+    $self->{TT}{po_in}=Locale::Po4a::Po->new();
+    $self->{TT}{po_out}=Locale::Po4a::Po->new(\%po_options);
+    # Warning, this is an array of array:
+    #  The document is splited on lines, and for each
+    #  [0] is the line content, [1] is the reference [2] the type
+    $self->{TT}{doc_in}=();
+    $self->{TT}{doc_out}=();
+    if (defined $options{'verbose'}) {
+	$self->{TT}{verbose}  =  $options{'verbose'};
+    }
+    if (defined $options{'debug'}) {
+	$self->{TT}{debug}  =  $options{'debug'};
+    }
+    # Input document is in ascii until we prove the opposite (in read())
+    $self->{TT}{ascii_input}=1;
+    # We try not to use utf unless it's forced from the outside (in case  
the
+    # document isn't in ascii)
+    $self->{TT}{utf_mode}=0;
+
+
+    return $self;
+}
+
+=back
+
+=head2 Manipulating document files
+
+=over 4
+
+=item read($)
+
+Add another input document at the end of the existing one. The argument is
+the filename to read.
+
+Please note that it does not parse anything. You should use the parse()
+function when you're done with packing input files into the document.
+
+=cut
+
+#'
+sub read() {
+    my $self=shift;
+    my $filename=shift
+	or croak wrap_msg(dgettext("po4a", "Can't read from file without having a  
filename"));
+    my $linenum=0;
+
+    open INPUT,"<$filename"
+	or croak wrap_msg(dgettext("po4a", "Can't read from %s: %s"), $filename,  
$!);
+    while (defined (my $textline = <INPUT>)) {
+	$linenum++;
+	my $ref="$filename:$linenum";
+	my @entry=($textline,$ref);
+	push @{$self->{TT}{doc_in}}, @entry;
+
+	if (!defined($self->{TT}{'file_in_charset'})) {
+	    # Detect if this file has non-ascii characters
+	    if($self->{TT}{ascii_input}) {
+		my $decoder = guess_encoding($textline);
+		if (!ref($decoder) or $decoder !~ /Encode::XS=/) {
+		    # We have detected a non-ascii line
+		    $self->{TT}{ascii_input} = 0;
+		    # Save the reference for future error message
+		    $self->{TT}{non_ascii_ref} ||= $ref;
+		}
+	    }
+	}
+    }
+    close INPUT
+	or croak wrap_msg(dgettext("po4a", "Can't close %s after reading: %s"),  
$filename, $!);
+
+}
+
+=item write($)
+
+Write the translated document to the given filename.
+
+=cut
+
+sub write {
+    my $self=shift;
+    my $filename=shift
+	or croak wrap_msg(dgettext("po4a", "Can't write to a file without  
filename"));
+
+    my $fh;
+    if ($filename eq '-') {
+	$fh=\*STDOUT;
+    } else {
+	# make sure the directory in which we should write the localized file  
exists
+	my $dir = $filename;
+	if ($dir =~ m|/|) {
+	    $dir =~ s|/[^/]*$||;
+	
+	    File::Path::mkpath($dir, 0, 0755) # Croaks on error
+	      if (length ($dir) && ! -e $dir);
+	}
+	open $fh,">$filename"
+	    or croak wrap_msg(dgettext("po4a", "Can't write to %s: %s"),  
$filename, $!);
+    }
+
+    map { print $fh $_ } $self->docheader();
+    map { print $fh $_ } @{$self->{TT}{doc_out}};
+
+    if ($filename ne '-') {
+	close $fh or croak wrap_msg(dgettext("po4a", "Can't close %s after  
writing: %s"), $filename, $!);
+    }
+
+}
+
+=back
+
+=head2 Manipulating po files
+
+=over 4
+
+=item readpo($)
+
+Add the content of a file (which name is passed in argument) to the
+existing input po. The old content is not discarded.
+
+=item writepo($)
+
+Write the extracted po file to the given filename.
+
+=item stats()
+
+Returns some statistics about the translation done so far. Please note that
+it's not the same statistics than the one printed by msgfmt
+--statistic. Here, it's stats about recent usage of the po file, while
+msgfmt reports the status of the file. It is a wrapper to the
+Locale::Po4a::Po::stats_get function applied to the input po file. Example
+of use:
+
+    [normal use of the po4a document...]
+
+    ($percent,$hit,$queries) = $document->stats();
+    print "We found translations for $percent\%  ($hit from $queries) of  
strings.\n";
+
+=back
+
+=cut
+
+sub getpoout {
+    return $_[0]->{TT}{po_out};
+}
+sub setpoout {
+    $_[0]->{TT}{po_out} = $_[1];
+}
+sub readpo  {
+    $_[0]->{TT}{po_in}->read($_[1]);
+}
+sub writepo {
+    $_[0]->{TT}{po_out}->write( $_[1] );
+}
+sub stats   {
+    return $_[0]->{TT}{po_in}->stats_get();
+}
+
+=head2 Manipulating addenda
+
+=over 4
+
+=item addendum($)
+
+Please refer to L<po4a(7)|po4a.7> for more information on what addenda are,
+and how translators should write them. To apply an addendum to the  
translated
+document, simply pass its filename to this function and you are done ;)
+
+This function returns a non-null integer on error.
+
+=cut
+
+# Internal function to read the header.
+sub addendum_parse {
+    my ($filename,$header)=shift;
+
+    my ($errcode,$mode,$position,$boundary,$bmode,$content)=
+	(1,"","","","","");
+
+    unless (open (INS, "<$filename")) {
+	warn wrap_msg(dgettext("po4a", "Can't read from %s: %s"), $filename, $!);
+	goto END_PARSE_ADDFILE;
+    }
+
+    unless (defined ($header=<INS>) && $header)  {
+	warn wrap_msg(dgettext("po4a", "Can't read Po4a header from %s."),  
$filename);
+	goto END_PARSE_ADDFILE;
+    }
+
+    unless ($header =~ s/PO4A-HEADER://i) {
+	warn wrap_msg(dgettext("po4a", "First line of %s does not look like a  
Po4a header."), $filename);
+	goto END_PARSE_ADDFILE;
+    }
+    foreach my $part (split(/;/,$header)) {
+	unless ($part =~ m/^\s*([^=]*)=(.*)$/) {
+	    warn wrap_msg(dgettext("po4a", "Syntax error in Po4a header of %s,  
near \"%s\""), $filename, $part);
+	    goto END_PARSE_ADDFILE;
+	}
+	my ($key,$value)=($1,$2);
+	$key=lc($key);
+  	     if ($key eq 'mode')     {  $mode=lc($value);
+	} elsif ($key eq 'position') {  $position=$value;
+	} elsif ($key eq 'endboundary') {
+	    $boundary=$value;
+	    $bmode='after';
+	} elsif ($key eq 'beginboundary') {
+	    $boundary=$value;
+	    $bmode='before';
+	} else {
+	    warn wrap_msg(dgettext("po4a", "Invalid argument in the Po4a header  
of %s: %s"), $filename, $key);
+	    goto END_PARSE_ADDFILE;
+	}
+    }
+
+    unless (length($mode)) {
+	warn wrap_msg(dgettext("po4a", "The Po4a header of %s does not define the  
mode."), $filename);
+	goto END_PARSE_ADDFILE;
+    }
+    unless ($mode eq "before" || $mode eq "after") {
+	warn wrap_msg(dgettext("po4a", "Mode invalid in the Po4a header of %s:  
should be 'before' or 'after' not %s."), $filename, $mode);
+	goto END_PARSE_ADDFILE;
+    }
+
+    unless (length($position)) {
+	warn wrap_msg(dgettext("po4a", "The Po4a header of %s does not define the  
position."), $filename);
+	goto END_PARSE_ADDFILE;
+    }
+    unless ($mode eq "before" || length($boundary)) {
+    	warn wrap_msg(dgettext("po4a", "No ending boundary given in the Po4a  
header, but mode=after."));
+	goto END_PARSE_ADDFILE;
+    }
+
+    while (defined(my $line = <INS>)) {
+	$content .= $line;
+    }
+    close INS;
+
+    $errcode=0;
+  END_PARSE_ADDFILE:
+      return ($errcode,$mode,$position,$boundary,$bmode,$content);
+}
+
+sub mychomp {
+    my ($str) = shift;
+    chomp($str);
+    return $str;
+}
+
+sub addendum {
+    my ($self,$filename) = @_;
+
+    print STDERR "Apply addendum $filename..." if $self->debug();
+    unless ($filename) {
+	warn wrap_msg(dgettext("po4a",
+	    "Can't apply addendum when not given the filename"));
+	return 0;
+    }
+    die wrap_msg(dgettext("po4a", "Addendum %s does not exist."),  
$filename)
+      unless -e $filename;
+
+    my ($errcode,$mode,$position,$boundary,$bmode,$content)=
+	addendum_parse($filename);
+    return 0 if ($errcode);
+
+    print  
STDERR "mode=$mode;pos=$position;bound=$boundary;bmode=$bmode;ctn=$content\n"
+      if $self->debug();
+
+    # We only recode the addendum if an origin charset is specified, else  
we
+    # suppose it's already in the output document's charset
+    if (defined($self->{TT}{'addendum_charset'}) &&
+        length($self->{TT}{'addendum_charset'})) {
+	Encode::from_to($content,$self->{TT}{'addendum_charset'},
+	    $self->get_out_charset);
+    }
+
+    my $found = scalar grep { /$position/ } @{$self->{TT}{doc_out}};
+    if ($found == 0) {
+	warn wrap_msg(dgettext("po4a",
+	    "No candidate position for the addendum %s."), $filename);
+	return 0;
+    }
+    if ($found > 1) {
+	warn wrap_msg(dgettext("po4a",
+	    "More than one candidate position found for the addendum %s."),  
$filename);
+	return 0;
+    }
+
+    if ($mode eq "before") {
+	if ($self->verbose() > 1 || $self->debug() ) {
+	    map { print STDERR wrap_msg(dgettext("po4a", "Addendum '%s' applied  
before this line: %s"), $filename, $_) if (/$position/);
+ 	        } @{$self->{TT}{doc_out}};
+	}
+	@{$self->{TT}{doc_out}} = map { /$position/ ? ($content,$_) : $_
+                                        }  @{$self->{TT}{doc_out}};
+    } else {
+	my @newres=();
+
+	do {
+	    # make sure it doesnt whine on empty document
+	    my $line = scalar @{$self->{TT}{doc_out}} ? shift  
@{$self->{TT}{doc_out}} : "";
+	    push @newres,$line;
+	    my $outline=mychomp($line);
+	    $outline =~ s/^[ \t]*//;
+	
+	    if ($line =~ m/$position/) {
+		while ($line=shift @{$self->{TT}{doc_out}}) {
+		    last if ($line=~/$boundary/);
+		    push @newres,$line;
+		}
+		if (defined $line) {
+		    if ($bmode eq 'before') {
+			print wrap_msg(dgettext("po4a",
+			    "Addendum '%s' applied before this line: %s"),
+			    $filename, $outline)
+			  if ($self->verbose() > 1 || $self->debug());
+			push @newres,$content;
+			push @newres,$line;
+		    } else {
+			print wrap_msg(dgettext("po4a",
+			    "Addendum '%s' applied after the line: %s."),
+			    $filename, $outline)
+			  if ($self->verbose() > 1 || $self->debug());
+			push @newres,$line;
+			push @newres,$content;
+		    }
+		} else {
+		    print wrap_msg(dgettext("po4a", "Addendum '%s' applied at the end of  
the file."), $filename)
+		      if ($self->verbose() > 1 || $self->debug());
+		    push @newres,$content;
+		}
+	    }
+	} while (scalar @{$self->{TT}{doc_out}});
+	@{$self->{TT}{doc_out}} = @newres;
+    }
+    print STDERR "done.\n" if $self->debug();
+    return 1;
+}
+
+=back
+
+=head1 INTERNAL FUNCTIONS used to write derivated parsers
+
+=head2 Getting input, providing output
+
+Four functions are provided to get input and return output. They are very
+similar to shift/unshift and push/pop. The first pair is about input, while
+the second is about output. Mnemonic: in input, you are interested in the
+first line, what shift gives, and in output you want to add your result at
+the end, like push does.
+
+=over 4
+
+=item shiftline()
+
+This function returns the next line of the doc_in to be parsed and its
+reference (packed as an array).
+
+=item unshiftline($$)
+
+Unshifts a line of the input document and its reference.
+
+=item pushline($)
+
+Push a new line to the doc_out.
+
+=item popline()
+
+Pop the last pushed line from the doc_out.
+
+=back
+
+=cut
+
+sub shiftline   {
+    my ($line,$ref)=(shift @{$_[0]->{TT}{doc_in}},
+		     shift @{$_[0]->{TT}{doc_in}});
+    return ($line,$ref);
+}
+sub unshiftline {
+	my $self = shift;
+	unshift @{$self->{TT}{doc_in}}, at _;
+}
+
+sub pushline    {  push @{$_[0]->{TT}{doc_out}}, $_[1] if defined $_[1]; }
+sub popline     {  return pop @{$_[0]->{TT}{doc_out}};            }
+
+=head2 Marking strings as translatable
+
+One function is provided to handle the text which should be translated.
+
+=over 4
+
+=item translate($$$)
+
+Mandatory arguments:
+
+=over 2
+
+=item -
+
+A string to translate
+
+=item -
+
+The reference of this string (ie, position in inputfile)
+
+=item -
+
+The type of this string (ie, the textual description of its structural role
+; used in Locale::Po4a::Po::gettextization() ; see also L<po4a(7)|po4a.7>,
+section I<Gettextization: how does it work?>)
+
+=back
+
+This function can also take some extra arguments. They must be organized as
+a hash. For example:
+
+  $self->translate("string","ref","type",
+		   'wrap' => 1);
+
+=over
+
+=item wrap
+
+boolean indicating whether we can consider that whitespaces in string are
+not important. If yes, the function canonizes the string before looking for
+a translation or extracting it, and wraps the translation.
+
+=item wrapcol
+
+The column at which we should wrap (default: 76).
+
+=item comment
+
+An extra comment to add to the entry.
+
+=back
+
+Actions:
+
+=over 2
+
+=item -
+
+Pushes the string, reference and type to po_out.
+
+=item -
+
+Returns the translation of the string (as found in po_in) so that the
+parser can build the doc_out.
+
+=item -
+
+Handles the charsets to recode the strings before sending them to
+po_out and before returning the translations.
+
+=back
+
+=back
+
+=cut
+
+sub translate {
+    my $self=shift;
+    my ($string,$ref,$type)=(shift,shift,shift);
+    my (%options)=@_;
+
+    # my $validoption="wrap wrapcol";
+    # my %validoption;
+
+    return "" unless defined($string) && length($string);
+
+    # map { $validoption{$_}=1 } (split(/ /,$validoption));
+    # foreach (keys %options) {
+    #	Carp::confess "internal error: translate() called with unknown arg  
$_. Valid options: $validoption"
+    #	    unless $validoption{$_};
+    # }
+
+    my $in_charset;
+    if ($self->{TT}{ascii_input}) {
+	$in_charset = "ascii";
+    } else {
+	if (defined($self->{TT}{'file_in_charset'}) and
+	    length($self->{TT}{'file_in_charset'}) and
+	    $self->{TT}{'file_in_charset'} !~ m/ascii/i) {
+	    $in_charset=$self->{TT}{'file_in_charset'};
+	} else {
+	    # FYI, the document charset have to be determined *before* we see the  
first
+	    # string to recode.
+	    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})
+	}
+    }
+
+    if ($self->{TT}{po_in}->get_charset ne "CHARSET") {
+	$string = encode_from_to($string,
+	                         $self->{TT}{'file_in_encoder'},
+	                         $self->{TT}{po_in}{encoder});
+    }
+
+    if (defined $options{'wrapcol'} && $options{'wrapcol'} < 0) {
+# FIXME: should be the parameter given with --width
+        $options{'wrapcol'} = 76 + $options{'wrapcol'};
+    }
+    my $transstring = $self->{TT}{po_in}->gettext($string,
+					'wrap'      => $options{'wrap'}||0,
+					'wrapcol'   => $options{'wrapcol'});
+
+    if ($self->{TT}{po_in}->get_charset ne "CHARSET") {
+	my $out_encoder = $self->{TT}{'file_out_encoder'};
+	unless (defined $out_encoder) {
+	    $out_encoder = find_encoding($self->get_out_charset)
+	}
+	$transstring = encode_from_to($transstring,
+	                              $self->{TT}{po_in}{encoder},
+	                              $out_encoder);
+    }
+
+    # If the input document isn't completely in ascii, we should see what  
to
+    # do with the current string
+    unless ($self->{TT}{ascii_input}) {
+        my $out_charset = $self->{TT}{po_out}->get_charset;
+	# We set the output po charset
+        if ($out_charset eq "CHARSET") {
+	    if ($self->{TT}{utf_mode}) {
+		$out_charset="utf-8";
+	    } else {
+		$out_charset=$in_charset;
+	    }
+	    $self->{TT}{po_out}->set_charset($out_charset);
+	}
+	if ( $in_charset !~ /^$out_charset$/i ) {
+	    Encode::from_to($string,$in_charset,$out_charset);
+	    if (defined($options{'comment'}) and length($options{'comment'})) {
+		Encode::from_to($options{'comment'},$in_charset,$out_charset);
+	    }
+	}
+    }
+
+    # the comments provided by the modules are automatic comments from the  
PO point of view
+    $self->{TT}{po_out}->push('msgid'     => $string,
+			      'reference' => $ref,
+			      'type'      => $type,
+	                      'automatic' => $options{'comment'},
+			      'wrap'      => $options{'wrap'}||0,
+			      'wrapcol'   => $options{'wrapcol'});
+
+#    if ($self->{TT}{po_in}->get_charset ne "CHARSET") {
+#	Encode::from_to($transstring,$self->{TT}{po_in}->get_charset,
+#	    $self->get_out_charset);
+#    }
+
+    if ($options{'wrap'}||0) {
+        $transstring =~ s/( *)$//s;
+        my $trailing_spaces = $1||"";
+        $transstring =~ s/ *$//gm;
+        $transstring .= $trailing_spaces;
+    }
+
+    return $transstring;
+}
+
+=head2 Misc functions
+
+=over 4
+
+=item verbose()
+
+Returns if the verbose option was passed during the creation of the
+TransTractor.
+
+=cut
+
+sub verbose {
+    if (defined $_[1]) {
+	$_[0]->{TT}{verbose} = $_[1];
+    } else {
+	return $_[0]->{TT}{verbose} || 0; # undef and 0 have the same meaning,  
but one generates warnings
+    }
+}
+
+=item debug()
+
+Returns if the debug option was passed during the creation of the
+TransTractor.
+
+=cut
+
+sub debug {
+    return $_[0]->{TT}{debug};
+}
+
+=item detected_charset($)
+
+This tells TransTractor that a new charset (the first argument) has been
+detected from the input document. It can usually be read from the document
+header. Only the first charset will remain, coming either from the
+process() arguments or detected from the document.
+
+=cut
+
+sub detected_charset {
+    my ($self,$charset)=(shift,shift);
+    unless (defined($self->{TT}{'file_in_charset'}) and
+            length($self->{TT}{'file_in_charset'}) ) {
+        $self->{TT}{'file_in_charset'}=$charset;
+        if (defined $charset) {
+            $self->{TT}{'file_in_encoder'}=find_encoding($charset);
+        }
+    }
+
+    if (defined $self->{TT}{'file_in_charset'} and
+        length $self->{TT}{'file_in_charset'} and
+        $self->{TT}{'file_in_charset'} !~ m/ascii/i) {
+	$self->{TT}{ascii_input}=0;
+    }
+}
+
+=item get_out_charset()
+
+This function will return the charset that should be used in the output
+document (usually useful to substitute the input document's detected  
charset
+where it has been found).
+
+It will use the output charset specified in the command line. If it wasn't
+specified, it will use the input po's charset, and if the input po has the
+default "CHARSET", it will return the input document's charset, so that no
+encoding is performed.
+
+=cut
+
+sub get_out_charset {
+    my $self=shift;
+    my $charset;
+
+    # Use the value specified at the command line
+    if (defined($self->{TT}{'file_out_charset'}) and
+	length($self->{TT}{'file_out_charset'})) {
+	$charset=$self->{TT}{'file_out_charset'};
+    } else {
+	if ($self->{TT}{utf_mode} && $self->{TT}{ascii_input}) {
+	    $charset="utf-8";
+	} else {
+	    $charset=$self->{TT}{po_in}->get_charset;
+	    $charset=$self->{TT}{'file_in_charset'}
+		if $charset eq "CHARSET" and
+		    defined($self->{TT}{'file_in_charset'}) and
+		    length($self->{TT}{'file_in_charset'});
+	    $charset="ascii"
+		if $charset eq "CHARSET";
+	}
+    }
+    return $charset;
+}
+
+=item recode_skipped_text($)
+
+This function returns the recoded text passed as argument, from the input
+document's charset to the output document's one. This isn't needed when
+translating a string (translate() recodes everything itself), but it is  
when
+you skip a string from the input document and you want the output document  
to
+be consistent with the global encoding.
+
+=cut
+
+sub recode_skipped_text {
+    my ($self,$text)=(shift,shift);
+    unless ($self->{TT}{'ascii_input'}) {
+	if(defined($self->{TT}{'file_in_charset'}) and
+	    length($self->{TT}{'file_in_charset'}) ) {
+	    $text = encode_from_to($text,
+	                           $self->{TT}{'file_in_encoder'},
+	                           find_encoding($self->get_out_charset));
+	} else {
+	    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})
+	}
+    }
+    return $text;
+}
+
+
+# encode_from_to($,$,$)
+#
+# Encode the given text from one encoding to another one.
+# It differs from Encode::from_to because it does not take the name of the
+# encoding in argument, but the encoders (as returned by the
+# Encode::find_encoding(<name>) method). Thus it permits to save a bunch
+# of call to find_encoding.
+#
+# If the "from" encoding is undefined, it is considered as UTF-8 (or
+# ascii).
+# If the "to" encoding is undefined, it is considered as UTF-8.
+#
+sub encode_from_to {
+    my ($text,$from,$to) = (shift,shift,shift);
+
+    if (not defined $from) {
+        # for ascii and UTF-8, no conversion needed to get an utf-8
+        # string.
+    } else {
+        $text = $from->decode($text, 0);
+    }
+
+    if (not defined $to) {
+        # Already in UTF-8, no conversion needed
+    } else {
+        $text = $to->encode($text, 0);
+    }
+
+    return $text;
+}
+
+=back
+
+=head1 FUTURE DIRECTIONS
+
+One shortcoming of the current TransTractor is that it can't handle
+translated document containing all languages, like debconf templates, or
+.desktop files.
+
+To address this problem, the only interface changes needed are:
+
+=over 2
+
+=item -
+
+take a hash as po_in_name (a list per language)
+
+=item -
+
+add an argument to translate to indicate the target language
+
+=item -
+
+make a pushline_all function, which would make pushline of its content for
+all language, using a map-like syntax:
+
+    $self->pushline_all({ "Description[".$langcode."]=".
+			  $self->translate($line,$ref,$langcode)
+		        });
+
+=back
+
+Will see if it's enough ;)
+
+=head1 AUTHORS
+
+ Denis Barbier <barbier at linuxfr.org>
+ Martin Quinson (mquinson#debian.org)
+ Jordi Vilalta <jvprat at gmail.com>
+
+=cut
+
+1;

Added: trunk/src/tools/po4a/lib/Locale/Po4a/Xml.pm
==============================================================================
--- (empty file)
+++ trunk/src/tools/po4a/lib/Locale/Po4a/Xml.pm	Wed Apr  1 22:49:13 2009
@@ -0,0 +1,1973 @@
+#!/usr/bin/perl
+
+# Po4a::Xml.pm
+#
+# extract and translate translatable strings from XML documents.
+#
+# This code extracts plain text from tags and attributes from generic
+# XML documents, and it can be used as a base to build modules for
+# XML-based documents.
+#
+# Copyright (c) 2004 by Jordi Vilalta  <jvprat at gmail.com>
+# Copyright (c) 2008-2009 by Nicolas François   
<nicolas.francois at centraliens.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+########################################################################
+
+=head1 NAME
+
+Locale::Po4a::Xml - Convert XML documents and derivates from/to PO files
+
+=head1 DESCRIPTION
+
+The po4a (po for anything) project goal is to ease translations (and more
+interestingly, the maintenance of translations) using gettext tools on
+areas where they were not expected like documentation.
+
+Locale::Po4a::Xml is a module to help the translation of XML documents into
+other [human] languages. It can also be used as a base to build modules for
+XML-based documents.
+
+=cut
+
+package Locale::Po4a::Xml;
+
+use 5.006;
+use strict;
+use warnings;
+
+require Exporter;
+use vars qw(@ISA @EXPORT);
+ at ISA = qw(Locale::Po4a::TransTractor);
+ at EXPORT = qw(new initialize @tag_types);
+
+use Locale::Po4a::TransTractor;
+use Locale::Po4a::Common;
+use Carp qw(croak);
+use File::Basename;
+use File::Spec;
+
+#It will mantain the path from the root tag to the current one
+my @path;
+
+#It will contain a list of external entities and their attached paths
+my %entities;
+
+my @comments;
+
+sub shiftline {
+    my $self = shift;
+    # call Transtractor's shiftline
+    my ($line,$ref) = $self->SUPER::shiftline();
+    return ($line,$ref) if (not defined $line);
+
+    for my $k (keys %entities) {
+        if ($line =~ m/^(.*?)&$k;(.*)$/s) {
+            my ($before, $after) = ($1, $2);
+            my $linenum=0;
+            my @textentries;
+
+            open (my $in, $entities{$k})
+                or croak wrap_mod("po4a::xml",
+                                  dgettext("po4a", "Can't read  
from %s: %s"),
+                                  $entities{$k}, $!);
+            while (defined (my $textline = <$in>)) {
+                $linenum++;
+                my $textref=$entities{$k}.":$linenum";
+                push @textentries, ($textline,$textref);
+            }
+            close $in
+                or croak wrap_mod("po4a::xml",
+                          dgettext("po4a", "Can't close %s after  
reading: %s"),
+                                  $entities{$k}, $!);
+
+            push @textentries, ($after, $ref);
+            $line = $before.(shift @textentries);
+            $ref .= " ".(shift @textentries);
+            $self->unshiftline(@textentries);
+        }
+    }
+
+    return ($line,$ref);
+}
+
+sub read {
+	my ($self,$filename)=@_;
+	push @{$self->{DOCPOD}{infile}}, $filename;
+	$self->Locale::Po4a::TransTractor::read($filename);
+}
+
+sub parse {
+	my $self=shift;
+	map {$self->parse_file($_)} @{$self->{DOCPOD}{infile}};
+}
+
+# @save_holders is a stack of references to ('paragraph', 'translation',
+# 'sub_translations', 'open', 'close', 'folded_attributes') hashes, where:
+# paragraph         is a reference to an array (see paragraph in the
+#                   treat_content() subroutine) of strings followed by
+#                   references.  It contains the @paragraph array as it was
+#                   before the processing was interrupted by a tag  
instroducing
+#                   a placeholder.
+# translation       is the translation of this level up to now
+# sub_translations  is a reference to an array of strings containing the
+#                   translations which must replace the placeholders.
+# open              is the tag which opened the placeholder.
+# close             is the tag which closed the placeholder.
+# folded_attributes is an hash of tags with their attributes (<tag  
attrs=...>
+#                   strings), referenced by the folded tag id, which should
+#                   replace the <tag po4a-id=id> strings in the current
+#                   translation.
+#
+# If @save_holders only has 1 holder, then we are not processing the
+# content of an holder, we are translating the document.
+my @save_holders;
+
+
+# If we are at the bottom of the stack and there is no <placeholder ...> in
+# the current translation, we can push the translation in the translated
+# document.
+# Otherwise, we keep the translation in the current holder.
+sub pushline {
+	my ($self, $line) = (shift, shift);
+
+	my $holder = $save_holders[$#save_holders];
+	my $translation = $holder->{'translation'};
+	$translation .= $line;
+
+	while (    %{$holder->{folded_attributes}}
+	       and $translation =~ m/^(.*)<([^>]+?)\s+po4a-id=([0-9]+)>(.*)$/s) {
+		my $begin = $1;
+		my $tag = $2;
+		my $id = $3;
+		my $end = $4;
+		if (defined $holder->{folded_attributes}->{$id}) {
+			# TODO: check if the tag is the same
+			$translation = $begin.$holder->{folded_attributes}->{$id}.$end;
+			delete $holder->{folded_attributes}->{$id};
+		} else {
+			# TODO: It will be hard to identify the location.
+			#       => find a way to retrieve the reference.
+			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);
+		}
+	}
+# TODO: check that %folded_attributes is empty at some time
+# => in translate_paragraph?
+
+	if (   ($#save_holders > 0)
+	    or ($translation =~  
m/<placeholder\s+type="[^"]+"\s+id="(\d+)"\s*\/>/s)) {
+		$holder->{'translation'} = $translation;
+	} else {
+		$self->SUPER::pushline($translation);
+		$holder->{'translation'} = '';
+	}
+}
+
+=head1 TRANSLATING WITH PO4A::XML
+
+This module can be used directly to handle generic XML documents.  This  
will
+extract all tag's content, and no attributes, since it's where the text is
+written in most XML based documents.
+
+There are some options (described in the next section) that can customize
+this behavior.  If this doesn't fit to your document format you're  
encouraged
+to write your own module derived from this, to describe your format's  
details.
+See the section "Writing derivate modules" below, for the process  
description.
+
+=cut
+
+#
+# Parse file and translate it
+#
+sub parse_file {
+	my ($self,$filename) = @_;
+	my $eof = 0;
+
+	while (!$eof) {
+		# We get all the text until the next breaking tag (not
+		# inline) and translate it
+		$eof = $self->treat_content;
+		if (!$eof) {
+			# And then we treat the following breaking tag
+			$eof = $self->treat_tag;
+		}
+	}
+}
+
+=head1 OPTIONS ACCEPTED BY THIS MODULE
+
+The global debug option causes this module to show the excluded strings, in
+order to see if it skips something important.
+
+These are this module's particular options:
+
+=over 4
+
+=item B<nostrip>
+
+Prevents it to strip the spaces around the extracted strings.
+
+=item B<wrap>
+
+Canonizes the string to translate, considering that whitespaces are not
+important, and wraps the translated document. This option can be overridden
+by custom tag options. See the "tags" option below.
+
+=item B<caseinsensitive>
+
+It makes the tags and attributes searching to work in a case insensitive
+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.
+
+=item B<includeexternal>
+
+When defined, external entities are included in the generated (translated)
+document, and for the extraction of strings.  If it's not defined, you
+will have to translate external entities separately as independent
+documents.
+
+=item B<ontagerror>
+
+This option defines the behavior of the module when it encounter a invalid
+Xml syntax (a closing tag which does not match the last opening tag, or a
+tag's attribute without value).
+It can take the following values:
+
+=over
+
+=item I<fail>
+
+This is the default value.
+The module will exit with an error.
+
+=item I<warn>
+
+The module will continue, and will issue a warning.
+
+=item I<silent>
+
+The module will continue without any warnings.
+
+=back
+
+Be careful when using this option.
+It is generally recommended to fix the input file.
+
+=item B<tagsonly>
+
+Extracts only the specified tags in the "tags" option.  Otherwise, it
+will extract all the tags except the ones specified.
+
+Note: This option is deprecated.
+
+=item B<doctype>
+
+String that will try to match with the first line of the document's doctype
+(if defined). If it doesn't, a warning will indicate that the document
+might be of a bad type.
+
+=item B<tags>
+
+Space-separated list of tags you want to translate or skip.  By default,
+the specified tags will be excluded, but if you use the "tagsonly" option,
+the specified tags will be the only ones included.  The tags must be in the
+form E<lt>aaaE<gt>, but you can join some (E<lt>bbbE<gt>E<lt>aaaE<gt>) to  
say that the content of
+the tag E<lt>aaaE<gt> will only be translated when it's into a  
E<lt>bbbE<gt> tag.
+
+You can also specify some tag options putting some characters in front of
+the tag hierarchy. For example, you can put 'w' (wrap) or 'W' (don't wrap)
+to override the default behavior specified by the global "wrap" option.
+
+Example: WE<lt>chapterE<gt>E<lt>titleE<gt>
+
+Note: This option is deprecated.
+You should use the B<translated> and B<untranslated> options instead.
+
+=item B<attributes>
+
+Space-separated list of tag's attributes you want to translate.  You can
+specify the attributes by their name (for example, "lang"), but you can
+prefix it with a tag hierarchy, to specify that this attribute will only be
+translated when it's into the specified tag. For example:  
E<lt>bbbE<gt>E<lt>aaaE<gt>lang
+specifies that the lang attribute will only be translated if it's into an
+E<lt>aaaE<gt> tag, and it's into a E<lt>bbbE<gt> tag.
+
+=item B<foldattributes>
+
+Do not translate attributes in inline tags.
+Instead, replace all attributes of a tag by po4a-id=<id>.
+
+This is useful when attributes shall not be translated, as this simplifies  
the
+strings for translators, and avoids typos.
+
+=item B<break>
+
+Space-separated list of tags which should break the sequence.
+By default, all tags break the sequence.
+
+The tags must be in the form <aaa>, but you can join some
+(<bbb><aaa>), if a tag (<aaa>) should only be considered
+when it's into another tag (<bbb>).
+
+=item B<inline>
+
+Space-separated list of tags which should be treated as inline.
+By default, all tags break the sequence.
+
+The tags must be in the form <aaa>, but you can join some
+(<bbb><aaa>), if a tag (<aaa>) should only be considered
+when it's into another tag (<bbb>).
+
+=item B<placeholder>
+
+Space-separated list of tags which should be treated as placeholders.
+Placeholders do not break the sequence, but the content of placeholders is
+translated separately.
+
+The location of the placeholder in its block will be marked with a string
+similar to:
+
+  <placeholder type=\"footnote\" id=\"0\"/>
+
+The tags must be in the form <aaa>, but you can join some
+(<bbb><aaa>), if a tag (<aaa>) should only be considered
+when it's into another tag (<bbb>).
+
+=item B<nodefault>
+
+Space separated list of tags that the module should not try to set by
+default in any category.
+
+=item B<cpp>
+
+Support C preprocessor directives.
+When this option is set, po4a will consider preprocessor directives as
+paragraph separators.
+This is important if the XML file must be preprocessed because otherwise
+the directives may be inserted in the middle of lines if po4a consider it
+belong to the current paragraph, and they won't be recognized by the
+preprocessor.
+Note: the preprocessor directives must only appear between tags
+(they must not break a tag).
+
+=item B<translated>
+
+Space-separated list of tags you want to translate.
+
+The tags must be in the form <aaa>, but you can join some
+(<bbb><aaa>), if a tag (<aaa>) should only be considered
+when it's into another tag (<bbb>).
+
+You can also specify some tag options putting some characters in front of
+the tag hierarchy. For example, you can put 'w' (wrap) or 'W' (don't wrap)
+to overide the default behavior specified by the global "wrap" option.
+
+Example: WE<lt>chapterE<gt>E<lt>titleE<gt>
+
+=item B<untranslated>
+
+Space-separated list of tags you do not want to translate.
+
+The tags must be in the form <aaa>, but you can join some
+(<bbb><aaa>), if a tag (<aaa>) should only be considered
+when it's into another tag (<bbb>).
+
+=item B<defaulttranslateoption>
+
+The default categories for tags that are not in any of the translated,
+untranslated, break, inline, or placeholder.
+
+This is a set of letters:
+
+=over
+
+=item I<w>
+
+Tags should be translated and content can be re-wrapped.
+
+=item I<W>
+
+Tags should be translated and content should not be re-wrapped.
+
+=item I<i>
+
+Tags should be translated inline.
+
+=item I<p>
+
+Tags should be translated as placeholders.
+
+=back
+
+=back
+
+=cut
+# TODO: defaulttranslateoption
+# w => indicate that it is only valid for translatable tags and do not
+#      care about inline/break/placeholder?
+# ...
+
+sub initialize {
+	my $self = shift;
+	my %options = @_;
+
+	# Reset the path
+	@path = ();
+
+	# Initialize the stack of holders
+	my @paragraph = ();
+	my @sub_translations = ();
+	my %folded_attributes;
+	my %holder = ('paragraph' => \@paragraph,
+	              'translation' => "",
+	              'sub_translations' => \@sub_translations,
+	              'folded_attributes' => \%folded_attributes);
+	@save_holders = (\%holder);
+
+	$self->{options}{'nostrip'}=0;
+	$self->{options}{'wrap'}=0;
+	$self->{options}{'caseinsensitive'}=0;
+	$self->{options}{'tagsonly'}=0;
+	$self->{options}{'tags'}='';
+	$self->{options}{'break'}='';
+	$self->{options}{'translated'}='';
+	$self->{options}{'untranslated'}='';
+	$self->{options}{'defaulttranslateoption'}='';
+	$self->{options}{'attributes'}='';
+	$self->{options}{'foldattributes'}=0;
+	$self->{options}{'inline'}='';
+	$self->{options}{'placeholder'}='';
+	$self->{options}{'doctype'}='';
+	$self->{options}{'nodefault'}='';
+	$self->{options}{'includeexternal'}=0;
+	$self->{options}{'ontagerror'}="fail";
+	$self->{options}{'cpp'}=0;
+
+	$self->{options}{'verbose'}='';
+	$self->{options}{'debug'}='';
+
+	foreach my $opt (keys %options) {
+		if ($options{$opt}) {
+			die wrap_mod("po4a::xml",
+				dgettext("po4a", "Unknown option: %s"), $opt)
+				unless exists $self->{options}{$opt};
+			$self->{options}{$opt} = $options{$opt};
+		}
+	}
+	# Default options set by modules. Forbidden for users.
+	$self->{options}{'_default_translated'}='';
+	$self->{options}{'_default_untranslated'}='';
+	$self->{options}{'_default_break'}='';
+	$self->{options}{'_default_inline'}='';
+	$self->{options}{'_default_placeholder'}='';
+	$self->{options}{'_default_attributes'}='';
+
+	#It will maintain the list of the translatable tags
+	$self->{tags}=();
+	$self->{translated}=();
+	$self->{untranslated}=();
+	#It will maintain the list of the translatable attributes
+	$self->{attributes}=();
+	#It will maintain the list of the breaking tags
+	$self->{break}=();
+	#It will maintain the list of the inline tags
+	$self->{inline}=();
+	#It will maintain the list of the placeholder tags
+	$self->{placeholder}=();
+	#list of the tags that must not be set in the tags or inline category
+	#by this module or sub-module (unless specified in an option)
+	$self->{nodefault}=();
+
+	$self->treat_options;
+}
+
+=head1 WRITING DERIVATE MODULES
+
+=head2 DEFINE WHAT TAGS AND ATTRIBUTES TO TRANSLATE
+
+The simplest customization is to define which tags and attributes you want
+the parser to translate.  This should be done in the initialize function.
+First you should call the main initialize, to get the command-line options,
+and then, append your custom definitions to the options hash.  If you want
+to treat some new options from command line, you should define them before
+calling the main initialize:
+
+  $self->{options}{'new_option'}='';
+  $self->SUPER::initialize(%options);
+  $self->{options}{'_default_translated'}.=' <p> <head><title>';
+  $self->{options}{'attributes'}.=' <p>lang id';
+  $self->{options}{'_default_inline'}.=' <br>';
+  $self->treat_options;
+
+You should use the B<_default_inline>, B<_default_break>,
+B<_default_placeholder>, B<_default_translated>, B<_default_untranslated>,
+and B<_default_attributes> options in derivated modules. This allow users
+to override the default behavior defined in your module with command line
+options.
+
+=head2 OVERRIDING THE found_string FUNCTION
+
+Another simple step is to override the function "found_string", which
+receives the extracted strings from the parser, in order to translate them.
+There you can control which strings you want to translate, and perform
+transformations to them before or after the translation itself.
+
+It receives the extracted text, the reference on where it was, and a hash
+that contains extra information to control what strings to translate, how
+to translate them and to generate the comment.
+
+The content of these options depends on the kind of string it is  
(specified in an
+entry of this hash):
+
+=over
+
+=item type="tag"
+
+The found string is the content of a translatable tag. The  
entry "tag_options"
+contains the option characters in front of the tag hierarchy in the module
+"tags" option.
+
+=item type="attribute"
+
+Means that the found string is the value of a translatable attribute. The
+entry "attribute" has the name of the attribute.
+
+=back
+
+It must return the text that will replace the original in the translated
+document. Here's a basic example of this function:
+
+  sub found_string {
+    my ($self,$text,$ref,$options)=@_;
+    $text = $self->translate($text,$ref,"type ".$options->{'type'},
+      'wrap'=>$self->{options}{'wrap'});
+    return $text;
+  }
+
+There's another simple example in the new Dia module, which only filters
+some strings.
+
+=cut
+
+sub found_string {
+	my ($self,$text,$ref,$options)=@_;
+
+	if ($text =~ m/^\s*$/s) {
+		return $text;
+	}
+
+	my $comment;
+	my $wrap = $self->{options}{'wrap'};
+
+	if ($options->{'type'} eq "tag") {
+		$comment = "Content of: ".$self->get_path;
+
+		if($options->{'tag_options'} =~ /w/) {
+			$wrap = 1;
+		}
+		if($options->{'tag_options'} =~ /W/) {
+			$wrap = 0;
+		}
+	} elsif ($options->{'type'} eq "attribute") {
+		$comment = "Attribute '".$options->{'attribute'}."'  
of: ".$self->get_path;
+	} elsif ($options->{'type'} eq "CDATA") {
+		$comment =

==============================================================================
Diff truncated at 200k characters


More information about the svnbook-dev mailing list