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