Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[Changes for 0.77 - 2008-12-29]

 * Locale::Maketext::Extract::Plugin::FormFu
    - Rewrote the FormFu plugin completely to use the YAML.pm parser.
      Previously, this plugin was completely broken.
    - Resolves bugs:
       - http://rt.cpan.org/Ticket/Display.html?id=42000
         # Locale::Maketext::Extract::Plugin::FormFu broken
       - http://rt.cpan.org/Ticket/Display.html?id=39292
         # Parsing formfu config

 * Locale::Maketext::Extract::Plugin::YAML
    - Improved the accuracy of the line numbers.
    - Added documentation and tests for using folded and block scalars.

 * Locale::Maketext::Extract::Plugin::PPI
    - Added a PPI based Perl plugin - more accurate than the Perl plugin,
      but much slower.
    - Not enabled by default
    - moved the Perl extraction tests into t/51-perlextract.t
    - Resolves bug: http://rt.cpan.org/Ticket/Display.html?id=14701
      # xgettext doesn't handle interpolating functions

 Also, corrected the POD to use the correct names for the plugins.
  • Loading branch information...
commit 175685b0a813c9980f4b73ef1eb1b3af2098c032 1 parent b98cd6c
@clintongormley clintongormley authored
View
27 Changes
@@ -1,3 +1,28 @@
+[Changes for 0.77 - 2008-12-29]
+
+ * Locale::Maketext::Extract::Plugin::FormFu
+ - Rewrote the FormFu plugin completely to use the YAML.pm parser.
+ Previously, this plugin was completely broken.
+ - Resolves bugs:
+ - http://rt.cpan.org/Ticket/Display.html?id=42000
+ # Locale::Maketext::Extract::Plugin::FormFu broken
+ - http://rt.cpan.org/Ticket/Display.html?id=39292
+ # Parsing formfu config
+
+ * Locale::Maketext::Extract::Plugin::YAML
+ - Improved the accuracy of the line numbers.
+ - Added documentation and tests for using folded and block scalars.
+
+ * Locale::Maketext::Extract::Plugin::PPI
+ - Added a PPI based Perl plugin - more accurate than the Perl plugin,
+ but much slower.
+ - Not enabled by default
+ - moved the Perl extraction tests into t/51-perlextract.t
+ - Resolves bug: http://rt.cpan.org/Ticket/Display.html?id=14701
+ # xgettext doesn't handle interpolating functions
+
+ Also, corrected the POD to use the correct names for the plugins.
+
[Changes for 0.76 - 2008-12-14]
* Locale::Maketext::Extract / Locale::Maketext::Lexicon::Gettext
@@ -10,7 +35,7 @@
# relative paths to lang files fail
- added tests to prove it
- * Locale::Maketext::Extract::Plugin::TT
+ * Locale::Maketext::Extract::Plugin::TT2
- changed the line string to contain only the start line, and not the range
as this was not consistent with the gettext spec
View
2  MANIFEST
@@ -22,6 +22,7 @@ lib/Locale/Maketext/Extract/Plugin/FormFu.pm
lib/Locale/Maketext/Extract/Plugin/Generic.pm
lib/Locale/Maketext/Extract/Plugin/Mason.pm
lib/Locale/Maketext/Extract/Plugin/Perl.pm
+lib/Locale/Maketext/Extract/Plugin/PPI.pm
lib/Locale/Maketext/Extract/Plugin/TextTemplate.pm
lib/Locale/Maketext/Extract/Plugin/TT2.pm
lib/Locale/Maketext/Extract/Plugin/YAML.pm
@@ -43,6 +44,7 @@ t/2-lmg.t
t/3-big-endian.t
t/4-encodings.t
t/5-extract.t
+t/51-perlextract.t
t/55-runextract.t
t/6-gettext.t
t/7-roundtrip.t
View
1  MANIFEST.SKIP
@@ -7,3 +7,4 @@
blib
pm_to_blib
^Makefile$
+.project
View
3  META.yml
@@ -18,6 +18,7 @@ no_index:
recommends:
HTML::Parser: 3.56
Lingua::EN::Sentence: 0.25
+ PPI: 1.203
Template: 2.20
Template::Constants: 2.75
YAML: 0.66
@@ -25,4 +26,4 @@ recommends:
requires:
Locale::Maketext: 0.01
perl: 5.005
-version: 0.76
+version: 0.77
View
36 Makefile.PL
@@ -3,24 +3,26 @@
use 5.005;
use inc::Module::Install;
-author 'Audrey Tang <cpan@audreyt.org>';
-abstract 'Use other catalog formats in Maketext';
-name 'Locale-Maketext-Lexicon';
-license 'MIT';
-perl_version '5.005';
-all_from 'lib/Locale/Maketext/Lexicon.pm';
-install_script 'script/xgettext.pl';
-requires 'Locale::Maketext' => '0.01';
-recommends 'Template' => '2.20';
-recommends 'Template::Constants' => '2.75';
-recommends 'YAML' => '0.66';
-recommends 'YAML::Loader' => '0.66';
-recommends 'HTML::Parser' => '3.56';
-recommends 'Lingua::EN::Sentence' => '0.25';
-test_requires 'Test::More' => '0';
+author 'Audrey Tang <cpan@audreyt.org>';
+abstract 'Use other catalog formats in Maketext';
+name 'Locale-Maketext-Lexicon';
+license 'MIT';
+perl_version '5.005';
+all_from 'lib/Locale/Maketext/Lexicon.pm';
+install_script 'script/xgettext.pl';
+requires 'Locale::Maketext' => '0.01';
+recommends 'Template' => '2.20';
+recommends 'Template::Constants' => '2.75';
+recommends 'YAML' => '0.66';
+recommends 'YAML::Loader' => '0.66';
+recommends 'HTML::Parser' => '3.56';
+recommends 'Lingua::EN::Sentence' => '0.25';
+recommends 'PPI' => '1.203';
+
+test_requires 'Test::More' => '0';
if ( $^O eq 'MSWin32' ) {
- recommends 'Win32::Console' => '0.09';
+ recommends 'Win32::Console' => '0.09';
}
-include_deps 'Test::More';
+include_deps 'Test::More';
WriteAll;
View
26 lib/Locale/Maketext/Extract.pm
@@ -1,5 +1,5 @@
package Locale::Maketext::Extract;
-$Locale::Maketext::Extract::VERSION = '0.34';
+$Locale::Maketext::Extract::VERSION = '0.35';
use strict;
use Locale::Maketext::Lexicon();
@@ -80,6 +80,10 @@ Following formats of input files are supported:
Valid localization function names are: C<translate>, C<maketext>,
C<gettext>, C<loc>, C<x>, C<_> and C<__>.
+For a slightly more accurate, but much slower Perl parser, you can use the PPI
+plugin. This does not have a short name (like C<perl>), but must be specified
+in full.
+
=item HTML::Mason (plugin: mason)
Strings inside C<E<lt>&|/lE<gt>I<...>E<lt>/&E<gt>> and
@@ -195,23 +199,27 @@ plugins, and overrides the file types specified in each plugin
=over 4
-=item C<perl> : L<Locale::Maketext::Plugin::Perl>
+=item C<perl> : L<Locale::Maketext::Extract::Plugin::Perl>
+
+For a slightly more accurate but much slower Perl parser, you can use
+the PPI plugin. This does not have a short name, but must be specified in
+full, ie: L<Locale::Maketext::Extract::Plugin::PPI>
-=item C<tt2> : L<Locale::Maketext::Plugin::TT2>
+=item C<tt2> : L<Locale::Maketext::Extract::Plugin::TT2>
-=item C<yaml> : L<Locale::Maketext::Plugin::YAML>
+=item C<yaml> : L<Locale::Maketext::Extract::Plugin::YAML>
-=item C<formfu> : L<Locale::Maketext::Plugin::FormFu>
+=item C<formfu> : L<Locale::Maketext::Extract::Plugin::FormFu>
-=item C<mason> : L<Locale::Maketext::Plugin::Mason>
+=item C<mason> : L<Locale::Maketext::Extract::Plugin::Mason>
-=item C<text> : L<Locale::Maketext::Plugin::TextTemplate>
+=item C<text> : L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item C<generic> : L<Locale::Maketext::Plugin::Generic>
+=item C<generic> : L<Locale::Maketext::Extract::Plugin::Generic>
=back
-Also, see L<Locale::Maketext::Plugin::Base> for details of how to
+Also, see L<Locale::Maketext::Extract::Plugin::Base> for details of how to
write your own plugin.
=head2 Warnings
View
16 lib/Locale/Maketext/Extract/Plugin/Base.pm
@@ -227,19 +227,21 @@ systems and perl source files.
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::PPI>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
+
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
View
189 lib/Locale/Maketext/Extract/Plugin/FormFu.pm
@@ -27,7 +27,7 @@ for localizing errors, labels etc.
=head1 VALID FORMATS
-We extract the text after _loc:
+We extract the text after any key which ends in C<_loc>:
content_loc: this is the string
@@ -35,28 +35,176 @@ We extract the text after _loc:
=over 4
-=item All file types
+=item .yaml
+
+=item .yml
+
+=item .conf
=back
+=head1 REQUIRES
+
+L<YAML>
+
+=head1 NOTES
+
+The docs for the YAML module describes it as alpha code. It is not as tolerant
+of errors as L<YAML::Syck>. However, because it is pure Perl, it is easy
+to hook into.
+
+I have seen it enter endless loops, so if xgettext.pl hangs, try running it
+again with C<--verbose --verbose> (twice) enabled, so that you can see if
+the fault lies with YAML. If it does, either correct the YAML source file,
+or use the file_types to exclude that file.
+
=cut
sub file_types {
- return qw( * );
+ return qw( yaml yml conf );
}
sub extract {
my $self = shift;
- local $_ = shift;
- my$line = 1;
- while (m/\G(.*?_loc:\s+(.*))/sg) {
- my ($str) = $2;
- $line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
- $self->add_entry($str, $line );
+ my $data = shift;
+
+ my $y = Locale::Maketext::Extract::Plugin::FormFu::Extractor->new();
+ $y->load($data);
+
+ foreach my $entry ( @{ $y->found } ) {
+ $self->add_entry(@$entry);
}
}
+package Locale::Maketext::Extract::Plugin::FormFu::Extractor;
+
+use base qw(YAML::Loader);
+
+#===================================
+sub new {
+#===================================
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{found} = [];
+ return $self;
+}
+
+#===================================
+sub _check_key {
+#===================================
+ my $self = shift;
+ my ( $key, $value, $line ) = @_;
+ if ( $key && $key =~ /_loc$/ && defined $value && !ref $value ) {
+ push @{ $self->{found} }, [ $value, $line ];
+ }
+
+ return;
+}
+
+#===================================
+sub _parse_mapping {
+#===================================
+ my $self = shift;
+ my ($anchor) = @_;
+ my $mapping = {};
+ $self->anchor2node->{$anchor} = $mapping;
+ my $key;
+ while ( not $self->done
+ and $self->indent == $self->offset->[ $self->level ] )
+ {
+
+ # If structured key:
+ if ( $self->{content} =~ s/^\?\s*// ) {
+ $self->preface( $self->content );
+ $self->_parse_next_line(YAML::Loader::COLLECTION);
+ $key = $self->_parse_node();
+ $key = "$key";
+ }
+
+ # If "default" key (equals sign)
+ elsif ( $self->{content} =~ s/^\=\s*// ) {
+ $key = YAML::Loader::VALUE;
+ }
+
+ # If "comment" key (slash slash)
+ elsif ( $self->{content} =~ s/^\=\s*// ) {
+ $key = YAML::Loader::COMMENT;
+ }
+
+ # Regular scalar key:
+ else {
+ $self->inline( $self->content );
+ $key = $self->_parse_inline();
+ $key = "$key";
+ $self->content( $self->inline );
+ $self->inline('');
+ }
+
+ unless ( $self->{content} =~ s/^:\s*// ) {
+ $self->die('YAML_LOAD_ERR_BAD_MAP_ELEMENT');
+ }
+ $self->preface( $self->content );
+ my $line = $self->line;
+ $self->_parse_next_line(YAML::Loader::COLLECTION);
+ my $value = $self->_parse_node();
+ if ( exists $mapping->{$key} ) {
+ $self->warn('YAML_LOAD_WARN_DUPLICATE_KEY');
+ }
+ else {
+ $mapping->{$key} = $value;
+ $self->_check_key( $key, $value, $line );
+ }
+ }
+ return $mapping;
+}
+
+#===================================
+sub _parse_inline_mapping {
+#===================================
+ my $self = shift;
+ my ($anchor) = @_;
+ my $node = {};
+ my $start_line = $self->{_start_line};
+
+ $self->anchor2node->{$anchor} = $node;
+
+ $self->die('YAML_PARSE_ERR_INLINE_MAP')
+ unless $self->{inline} =~ s/^\{\s*//;
+ while ( not $self->{inline} =~ s/^\s*\}// ) {
+ my $key = $self->_parse_inline();
+ $self->die('YAML_PARSE_ERR_INLINE_MAP')
+ unless $self->{inline} =~ s/^\: \s*//;
+ my $value = $self->_parse_inline();
+ if ( exists $node->{$key} ) {
+ $self->warn('YAML_LOAD_WARN_DUPLICATE_KEY');
+ }
+ else {
+ $node->{$key} = $value;
+ $self->_check_key( $key, $value, $start_line );
+ }
+ next if $self->inline =~ /^\s*\}/;
+ $self->die('YAML_PARSE_ERR_INLINE_MAP')
+ unless $self->{inline} =~ s/^\,\s*//;
+ }
+ return $node;
+}
+
+#===================================
+sub _parse_next_line {
+#===================================
+ my $self = shift;
+ $self->{_start_line} = $self->line;
+ $self->SUPER::_parse_next_line(@_);
+}
+
+#===================================
+sub found {
+#===================================
+ my $self = shift;
+ return $self->{found};
+}
+
=head1 SEE ALSO
=over 4
@@ -66,27 +214,31 @@ sub extract {
for extracting translatable strings from common template
systems and perl source files.
+=item L<YAML>
+
+=item L<HTML::FormFu>
+
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Extract::Plugin::Base>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
-Audrey Tang E<lt>cpan@audreyt.orgE<gt>
+Clinton Gormley E<lt>clint@traveljury.comE<gt>
=head1 COPYRIGHT
@@ -116,5 +268,4 @@ DEALINGS IN THE SOFTWARE.
=cut
-
-1;
+1;
View
14 lib/Locale/Maketext/Extract/Plugin/Generic.pm
@@ -91,19 +91,19 @@ systems and perl source files.
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Extract::Plugin::Base>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=back
View
14 lib/Locale/Maketext/Extract/Plugin/Mason.pm
@@ -70,19 +70,19 @@ systems and perl source files.
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Extract::Plugin::Base>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
View
224 lib/Locale/Maketext/Extract/Plugin/PPI.pm
@@ -0,0 +1,224 @@
+package Locale::Maketext::Extract::Plugin::PPI;
+
+use strict;
+use base qw(Locale::Maketext::Extract::Plugin::Base);
+use PPI();
+
+=head1 NAME
+
+Locale::Maketext::Extract::Plugin::PPI - Perl format parser
+
+=head1 SYNOPSIS
+
+ $plugin = Locale::Maketext::Extract::Plugin::PPI->new(
+ $lexicon # A Locale::Maketext::Extract object
+ @file_types # Optionally specify a list of recognised file types
+ )
+
+ $plugin->extract($filename,$filecontents);
+
+=head1 DESCRIPTION
+
+Does exactly the same thing as the L<Locale::Maketext::Extract::Plugin::Perl>
+parser, but more accurately, and more slowly. Considerably more slowly! For this
+reason it isn't a built-in plugin.
+
+
+=head1 SHORT PLUGIN NAME
+
+ none - the module must be specified in full
+
+=head1 VALID FORMATS
+
+Valid localization function names are:
+
+=over 4
+
+=item translate
+
+=item maketext
+
+=item gettext
+
+=item loc
+
+=item x
+
+=item _
+
+=item __
+
+=back
+
+=head1 KNOWN FILE TYPES
+
+=over 4
+
+=item .pm
+
+=item .pl
+
+=item .cgi
+
+=back
+
+=cut
+
+sub file_types {
+ return qw( pm pl cgi );
+}
+
+my %subnames = map { $_ => 1 } qw (translate maketext gettext loc x __);
+
+#===================================
+sub extract {
+#===================================
+ my $self = shift;
+ my $text = shift;
+
+ my $doc = PPI::Document->new( \$text, index_locations => 1 );
+
+ foreach my $statement ( @{ $doc->find('PPI::Statement') } ) {
+ my @children = $statement->schildren;
+
+ while ( my $child = shift @children ) {
+ next
+ unless @children
+ && ( $child->isa('PPI::Token::Word')
+ && $subnames{ $child->content }
+ || $child->isa('PPI::Token::Magic')
+ && $child->content eq '_' );
+
+ my $list = shift @children;
+ next
+ unless $list->isa('PPI::Structure::List')
+ && $list->schildren;
+
+ $self->_check_arg_list($list);
+ }
+ }
+}
+
+#===================================
+sub _check_arg_list {
+#===================================
+ my $self = shift;
+ my $list = shift;
+ my @args = ( $list->schildren )[0]->schildren;
+
+ my $final_string = '';
+ my ( $line, $mode );
+
+ while ( my $string_el = shift @args ) {
+ return
+ unless $string_el->isa('PPI::Token::Quote')
+ || $string_el->isa('PPI::Token::HereDoc');
+ $line ||= $string_el->location->[0];
+ my $string;
+ if ( $string_el->isa('PPI::Token::HereDoc') ) {
+ $string = join( '', $string_el->heredoc );
+ $mode
+ = $string_el->{_mode} eq 'interpolate'
+ ? 'double'
+ : 'literal';
+ }
+ else {
+ $string = $string_el->string;
+ $mode
+ = $string_el->isa('PPI::Token::Quote::Literal') ? 'literal'
+ : ( $string_el->isa('PPI::Token::Quote::Double')
+ || $string_el->isa('PPI::Token::Quote::Interpolate') )
+ ? 'double'
+ : 'single';
+ }
+
+ if ( $mode eq 'double' ) {
+ return
+ if !!( $string =~ /(?<!\\)(?:\\\\)*[\$\@]/ );
+ $string = eval qq("$string");
+ }
+ elsif ( $mode eq 'single' ) {
+ $string =~ s/\\'/'/g;
+ }
+
+ # $string =~ s/(?<!\\)\\//g;
+ $string =~ s/\\\\/\\/g;
+
+ # unless $mode eq 'literal';
+
+ $final_string .= $string;
+
+ my $next_op = shift @args;
+ last
+ unless $next_op
+ && $next_op->isa('PPI::Token::Operator')
+ && $next_op->content eq '.';
+ }
+ return unless $final_string;
+
+ my $vars = join( '', map { $_->content } @args );
+ $self->add_entry( $final_string, $line, $vars );
+}
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<xgettext.pl>
+
+for extracting translatable strings from common template
+systems and perl source files.
+
+=item L<Locale::Maketext::Lexicon>
+
+=item L<Locale::Maketext::Extract::Plugin::Base>
+
+=item L<Locale::Maketext::Extract::Plugin::Perl>
+
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
+
+=item L<Locale::Maketext::Extract::Plugin::Perl>
+
+=item L<Locale::Maketext::Extract::Plugin::TT2>
+
+=item L<Locale::Maketext::Extract::Plugin::YAML>
+
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
+
+=item L<Locale::Maketext::Extract::Plugin::Generic>
+
+=back
+
+=head1 AUTHORS
+
+Audrey Tang E<lt>cpan@audreyt.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
+
+This software is released under the MIT license cited below.
+
+=head2 The "MIT" License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+=cut
+
+1;
View
20 lib/Locale/Maketext/Extract/Plugin/Perl.pm
@@ -22,6 +22,10 @@ Locale::Maketext::Extract::Plugin::Perl - Perl format parser
Extracts strings to localise (including HEREDOCS and
concatenated strings) from Perl code.
+This Perl parser is very fast and very good, but not perfect - it does make
+mistakes. The PPI parser (L<Locale::Maketext::Extract::Plugin::PPI>) is more
+accurate, but a lot slower, and so is not enabled by default.
+
=head1 SHORT PLUGIN NAME
perl
@@ -212,19 +216,21 @@ systems and perl source files.
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Extract::Plugin::Base>
+
+=item L<Locale::Maketext::Extract::Plugin::PPI>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
View
14 lib/Locale/Maketext/Extract/Plugin/TT2.pm
@@ -392,19 +392,19 @@ systems and perl source files.
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Extract::Plugin::Base>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=item L<Template::Toolkit>
View
14 lib/Locale/Maketext/Extract/Plugin/TextTemplate.pm
@@ -90,19 +90,19 @@ systems and perl source files.
=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Extract::Plugin::Base>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
View
73 lib/Locale/Maketext/Extract/Plugin/YAML.pm
@@ -30,15 +30,39 @@ Valid formats are:
=over 4
-=item key: _"string"
+=item *
-=item key: _'string'
+ key: _"string"
-=item key: _'string with embedded 'quotes''
+=item *
-=back
+ key: _'string'
+
+=item *
+
+ key: _'string with embedded 'quotes''
+
+=item *
+
+ key: |-
+ _'my folded
+ string
+ to translate'
+
+Note, the left hand side of the folded string must line up with the C<_>,
+otherwise YAML adds spaces at the beginning of each line.
+
+=item *
+
+ key: |-
+ _'my block
+ string
+ to translate
+ '
+Note, you must use the trailing C<-> so that YAMl doesn't add a carriage
+return after your final quote.
-You cannot use block or folded strings with this plugin.
+=back
=head1 KNOWN FILE TYPES
@@ -63,7 +87,7 @@ of errors as L<YAML::Syck>. However, because it is pure Perl, it is easy
to hook into.
I have seen it enter endless loops, so if xgettext.pl hangs, try running it
-again with --verbose --verbose (twice) enabled, so that you can see if
+again with C<--verbose --verbose> (twice) enabled, so that you can see if
the fault lies with YAML. If it does, either correct the YAML source file,
or use the file_types to exclude that file.
@@ -116,30 +140,43 @@ sub check_scalar {
sub _parse_node {
my $self = shift;
+ my $line = $self->{_start_line}||=length($self->preface) ? $self->line - 1 : $self->line;
my $node = $self->SUPER::_parse_node(@_);
- return $self->check_scalar($node,$self->line-1);
+ $self->{start_line} = 0;
+ return $self->check_scalar($node,$line);
}
sub _parse_inline_seq {
my $self = shift;
+ my $line = $self->{_start_line}||=$self->line;
my $node = $self->SUPER::_parse_inline_seq(@_);
- my $line = $self->line;
foreach (@$node) {
$self->check_scalar( $_, $line );
}
+ $self->{start_line} = 0;
return $node;
}
sub _parse_inline_mapping {
my $self = shift;
+ my $line = $self->{_start_line}||=$self->line;
my $node = $self->SUPER::_parse_inline_mapping(@_);
- my $line = $self->line;
foreach ( values %$node ) {
$self->check_scalar( $_, $line );
}
+ $self->{start_line} = 0;
return $node;
}
+#===================================
+sub _parse_next_line {
+#===================================
+ my $self = shift;
+ $self->{_start_line} = $self->line
+ if $_[0] == YAML::Loader::COLLECTION;
+ $self->SUPER::_parse_next_line(@_);
+}
+
sub found {
my $self = shift;
return $self->{found};
@@ -154,23 +191,23 @@ sub found {
for extracting translatable strings from common template
systems and perl source files.
-=item L<Locale::Maketext::Lexicon>
+=item L<YAML>
-=item L<Locale::Maketext::Plugin::Base>
+=item L<Locale::Maketext::Lexicon>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::Base>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item L<Template::Alloy>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
View
2  lib/Locale/Maketext/Extract/Run.pm
@@ -1,5 +1,5 @@
package Locale::Maketext::Extract::Run;
-$Locale::Maketext::Lexicon::Extract::Run::VERSION = '0.33';
+$Locale::Maketext::Lexicon::Extract::Run::VERSION = '0.34';
use strict;
use vars qw( @ISA @EXPORT_OK );
View
10 lib/Locale/Maketext/Lexicon.pm
@@ -1,5 +1,5 @@
package Locale::Maketext::Lexicon;
-$Locale::Maketext::Lexicon::VERSION = '0.76';
+$Locale::Maketext::Lexicon::VERSION = '0.77';
use 5.004;
use strict;
@@ -10,8 +10,8 @@ Locale::Maketext::Lexicon - Use other catalog formats in Maketext
=head1 VERSION
-This document describes version 0.71 of Locale::Maketext::Lexicon,
-released October 5, 2008.
+This document describes version 0.77 of Locale::Maketext::Lexicon,
+released December 29, 2008.
=head1 SYNOPSIS
@@ -74,8 +74,8 @@ This module provides lexicon-handling modules to read from other
localization formats, such as I<Gettext>, I<Msgcat>, and so on.
If you are unfamiliar with the concept of lexicon modules, please
-consult L<Locale::Maketext> and L<http://www.autrijus.org/webl10n/>
-first.
+consult L<Locale::Maketext> and the C<webl10n> HTML files in the C<docs/>
+directory of this module.
A command-line utility L<xgettext.pl> is also installed with this
module, for extracting translatable strings from source files.
View
47 script/xgettext.pl
@@ -127,7 +127,34 @@ =head2 Plugins:
=back
-Multiple plugins can be specified on the command line
+Multiple plugins can be specified on the command line.
+
+=head3 Available plugins:
+
+
+=over 4
+
+=item C<perl> : L<Locale::Maketext::Extract::Plugin::Perl>
+
+For a slightly more accurate but much slower Perl parser, you can use
+the PPI plugin. This does not have a short name, but must be specified in
+full, eg:
+
+ xgettext.pl -P Locale::Maketext::Extract::Plugin::PPI
+
+=item C<tt2> : L<Locale::Maketext::Extract::Plugin::TT2>
+
+=item C<yaml> : L<Locale::Maketext::Extract::Plugin::YAML>
+
+=item C<formfu> : L<Locale::Maketext::Extract::Plugin::FormFu>
+
+=item C<mason> : L<Locale::Maketext::Extract::Plugin::Mason>
+
+=item C<text> : L<Locale::Maketext::Extract::Plugin::TextTemplate>
+
+=item C<generic> : L<Locale::Maketext::Extract::Plugin::Generic>
+
+=back
=head2 Warnings:
@@ -169,8 +196,6 @@ =head2 Verbose:
=back
-=cut
-
=head1 SEE ALSO
=over 4
@@ -181,19 +206,21 @@ =head1 SEE ALSO
=item L<Locale::Maketext>
-=item L<Locale::Maketext::Plugin::Perl>
+=item L<Locale::Maketext::Extract::Plugin::Perl>
+
+=item L<Locale::Maketext::Extract::Plugin::PPI>
-=item L<Locale::Maketext::Plugin::TT2>
+=item L<Locale::Maketext::Extract::Plugin::TT2>
-=item L<Locale::Maketext::Plugin::YAML>
+=item L<Locale::Maketext::Extract::Plugin::YAML>
-=item L<Locale::Maketext::Plugin::FormFu>
+=item L<Locale::Maketext::Extract::Plugin::FormFu>
-=item L<Locale::Maketext::Plugin::Mason>
+=item L<Locale::Maketext::Extract::Plugin::Mason>
-=item L<Locale::Maketext::Plugin::TextTemplate>
+=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
-=item L<Locale::Maketext::Plugin::Generic>
+=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
View
547 t/5-extract.t
@@ -1,178 +1,265 @@
#! /usr/bin/perl -w
use lib '../lib';
use strict;
-use Test::More tests => 100;
+use Test::More tests => 64;
use_ok('Locale::Maketext::Extract');
my $Ext = Locale::Maketext::Extract->new();
-isa_ok($Ext => 'Locale::Maketext::Extract');
-extract_ok('_("123")' => 123, 'Simple extraction');
+isa_ok( $Ext => 'Locale::Maketext::Extract' );
-extract_ok('_("[_1] is happy")' => '%1 is happy', '[_1] to %1');
-extract_ok('_("%1 is happy")' => '%1 is happy', '%1 verbatim', 1);
+#### BEGIN WRAPPING TESTS ############
-extract_ok('_("[*,_1] counts")' => '%*(%1) counts', '[*,_1] to %*(%1)');
-extract_ok('_("%*(%1) counts")' => '%*(%1) counts', '%*(%1) verbatim', 1);
+write_po_ok( <<'__EXAMPLE__' => <<'__EXPECTED__', "wrap off" );
+_('string');
+_('string');
+__EXAMPLE__
+#: :1 :2
+msgid "string"
+msgstr ""
+__EXPECTED__
-extract_ok('_("[*,_1,_2] counts")' => '%*(%1,%2) counts',
-'[*,_1,_2] to %*(%1,%2)');
-extract_ok('_("[*,_1,_2] counts")' => '[*,_1,_2] counts',
-'[*,_1,_2] verbatim', 1);
+$Ext->{wrap} = 1;
+write_po_ok( <<'__EXAMPLE__' => <<'__EXPECTED__', "wrap on" );
+_('string');
+_('string');
+__EXAMPLE__
+#: :1
+#: :2
+msgid "string"
+msgstr ""
+__EXPECTED__
-extract_ok(q(_('foo\$bar')) => 'foo\\$bar', 'Escaped \$ in q');
-extract_ok(q(_("foo\$bar")) => 'foo$bar', 'Normalized \$ in qq');
+#### END WRAPPING TESTS ############
+$Ext->{wrap} = 0;
-extract_ok(q(_('foo\x20bar')) => 'foo\\x20bar', 'Escaped \x in q');
-extract_ok(q(_("foo\x20bar")) => 'foo bar', 'Normalized \x in qq');
+#### BEGIN FORMFU TESTS ############
+SKIP: {
+ skip( 'YAML.pm unavailable', 5 ) unless eval { require YAML };
+
+ extract_ok( " content_loc: foo bar\n" => "foo bar", "html-formfu 1" );
+ write_po_ok( <<"__YAML__", <<"__PO__", 'html-formfu 2' );
+---
+ content_loc: foo bar
+ name: something else
+ value_loc: something else as well
+__YAML__
+#: :2
+msgid "foo bar"
+msgstr ""
-extract_ok(q(_('foo\nbar')) => 'foo\\nbar', 'Escaped \n in qq');
-extract_ok(q(_("foo\nbar")) => "foo\nbar", 'Normalized \n in qq');
-extract_ok(qq(_("foo\nbar")) => "foo\nbar", 'Normalized literal \n in qq');
+#: :4
+msgid "something else as well"
+msgstr ""
+__PO__
-extract_ok(q(_("foo\nbar")) => "foo\nbar", 'Trailing \n in qq');
-extract_ok(qq(_("foobar\n")) => "foobar\n", 'Trailing literal \n in qq');
+ write_po_ok( <<"__YAML__" => <<"__PO__", 'html-formfu 3' );
+---
+ content_loc: foo bar
+ name: something else
+---
+ value_loc: something else as well
+__YAML__
+#: :2
+msgid "foo bar"
+msgstr ""
-extract_ok(q(_('foo\bar')) => 'foo\\bar', 'Escaped \ in q');
-extract_ok(q(_('foo\\\\bar')) => 'foo\\bar', 'Normalized \\\\ in q');
-extract_ok(q(_("foo\bar")) => "foo\bar", 'Interpolated \b in qq');
+#: :5
+msgid "something else as well"
+msgstr ""
+__PO__
+ write_po_ok( <<"__YAML__" => <<"__PO__", 'html-formfu 4' );
+---
+ name: {content_loc: foo, other: bar, value_loc: baz }
+ value_loc: other
-extract_ok(q([% loc( 'foo "bar" baz' ) %]) => 'foo "bar" baz', 'Escaped double quote in text');
+__YAML__
+#: :2
+msgid "baz"
+msgstr ""
-extract_ok(q( _(q{foo bar})) => "foo bar", 'No escapes');
-extract_ok(q(_(q{foo\bar})) => 'foo\\bar', 'Escaped \ in q');
-extract_ok(q(_(q{foo\\\\bar})) => 'foo\\bar', 'Normalized \\\\ in q');
-extract_ok(q(_(qq{foo\bar})) => "foo\bar", 'Interpolated \b in qq');
+#: :2
+msgid "foo"
+msgstr ""
-# HTML::FormFu test
-extract_ok(' content_loc: foo bar' => "foo bar", "html-formfu extraction");
+#: :3
+msgid "other"
+msgstr ""
+__PO__
+
+ write_po_ok( <<"__YAML__" => <<"__PO__", 'html-formfu 5' );
+---
+ name: {content_loc: foo, other: bar, value_loc: baz }
+ list:
+ - { content_loc: hash1 }
+ - more: { content_loc: hash2 }
+ - and_more:
+ - content_loc: nest_1
+ - { value_loc: nest_2 }
+ - content_loc:
+ - foo
+ - bar
+ value_loc: other
+
+__YAML__
+#: :2
+msgid "baz"
+msgstr ""
-extract_ok(
- q(my $x = loc('I "think" you\'re a cow.') . "\n";) => 'I "think" you\'re a cow.',
- "Handle escaped single quotes"
-);
+#: :2
+msgid "foo"
+msgstr ""
-extract_ok(
- q(my $x = loc("I'll poke you like a \"cow\" man.") . "\n";)
- => 'I\'ll poke you like a "cow" man.',
- "Handle escaped double quotes"
-);
+#: :4
+msgid "hash1"
+msgstr ""
-extract_ok(q(_("","car")) => '', 'ignore empty string');
-extract_ok(q(_("0")) => '', 'ignore zero');
+#: :5
+msgid "hash2"
+msgstr ""
+#: :7
+msgid "nest_1"
+msgstr ""
+
+#: :8
+msgid "nest_2"
+msgstr ""
+
+#: :12
+msgid "other"
+msgstr ""
+__PO__
+
+}
+
+#### END FORMFU TESTS ############
#### BEGIN TT TESTS ############
-SKIP: { skip('Template.pm unavailable', 46) unless eval { require Template };
+SKIP: {
+ skip( 'Template.pm unavailable', 46 ) unless eval { require Template };
-extract_ok(<<'__EXAMPLE__' => 'foo bar baz', 'trim the string (tt)');
+ extract_ok( <<'__EXAMPLE__' => 'foo bar baz', 'trim the string (tt)' );
[% |loc -%]
foo bar baz
[%- END %]
__EXAMPLE__
-write_po_ok(q([% l(string) %]) => '', 'TT l function - no string');
+ write_po_ok( q([% l(string) %]) => '', 'TT l function - no string' );
-write_po_ok(q([% l('string') %]) => <<'__EXAMPLE__', 'TT l function - no arg');
+ write_po_ok(
+ q([% l('string') %]) => <<'__EXAMPLE__', 'TT l function - no arg' );
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% l('string',arg) %]) => <<'__EXAMPLE__', 'TT l function - variable arg');
+ write_po_ok( q([% l('string',arg) %]) =>
+ <<'__EXAMPLE__', 'TT l function - variable arg' );
#. (arg)
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% l('string','arg') %]) => <<'__EXAMPLE__', 'TT l function - literal arg');
+ write_po_ok( q([% l('string','arg') %]) =>
+ <<'__EXAMPLE__', 'TT l function - literal arg' );
#. ("arg")
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% string | l %]) => '', 'TT l inline filter - no string');
+ write_po_ok( q([% string | l %]) => '',
+ 'TT l inline filter - no string' );
-write_po_ok(q([% 'string' | l %]) => <<'__EXAMPLE__', 'TT l inline filter - no arg');
+ write_po_ok( q([% 'string' | l %]) =>
+ <<'__EXAMPLE__', 'TT l inline filter - no arg' );
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% 'string' | l('arg') %]) => <<'__EXAMPLE__', 'TT l inline filter - literal arg');
+ write_po_ok( q([% 'string' | l('arg') %]) =>
+ <<'__EXAMPLE__', 'TT l inline filter - literal arg' );
#. ("arg")
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% 'string' | l(arg) %]) => <<'__EXAMPLE__', 'TT l inline filter - variable arg');
+ write_po_ok( q([% 'string' | l(arg) %]) =>
+ <<'__EXAMPLE__', 'TT l inline filter - variable arg' );
#. (arg)
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% |l %][% string %][% END %]) => '', 'TT l block filter - no string');
+ write_po_ok( q([% |l %][% string %][% END %]) => '',
+ 'TT l block filter - no string' );
SKIP: {
- skip "Can't handle directive embedded in text blocks",1;
+ skip "Can't handle directive embedded in text blocks", 1;
- write_po_ok(q([% |l %] string [% var %][% END %]) => '', 'TT l block filter - embedded directive');
-}
+ write_po_ok( q([% |l %] string [% var %][% END %]) => '',
+ 'TT l block filter - embedded directive' );
+ }
-write_po_ok(q([% |l %]string[% END %]) => <<'__EXAMPLE__', 'TT l block filter - no arg');
+ write_po_ok( q([% |l %]string[% END %]) =>
+ <<'__EXAMPLE__', 'TT l block filter - no arg' );
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% |l('arg') %]string[% END %]) => <<'__EXAMPLE__', 'TT l block filter - literal arg');
+ write_po_ok( q([% |l('arg') %]string[% END %]) =>
+ <<'__EXAMPLE__', 'TT l block filter - literal arg' );
#. ("arg")
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% |l(arg) %]string[% END %]) => <<'__EXAMPLE__', 'TT l block filter - variable arg');
+ write_po_ok( q([% |l(arg) %]string[% END %]) =>
+ <<'__EXAMPLE__', 'TT l block filter - variable arg' );
#. (arg)
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-
-write_po_ok(q([% FILTER l(arg) %]string[% END %]) => <<'__EXAMPLE__', 'TT block FILTER - variable arg');
+ write_po_ok( q([% FILTER l(arg) %]string[% END %]) =>
+ <<'__EXAMPLE__', 'TT block FILTER - variable arg' );
#. (arg)
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-
# Use just the TT2 parser, otherwise loc() throws false positives in the Perl plugin
-my $Old_Ext = $Ext;
-$Ext = Locale::Maketext::Extract->new(plugins=>{tt2 => '*'});
+ my $Old_Ext = $Ext;
+ $Ext = Locale::Maketext::Extract->new( plugins => { tt2 => '*' } );
-write_po_ok(q([% loc('string',arg) %]) => <<'__EXAMPLE__', 'TT loc function - variable arg');
+ write_po_ok( q([% loc('string',arg) %]) =>
+ <<'__EXAMPLE__', 'TT loc function - variable arg' );
#. (arg)
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(q([% 'string' | loc('arg') %]) => <<'__EXAMPLE__', 'TT loc inline filter - literal arg');
+ write_po_ok( q([% 'string' | loc('arg') %]) =>
+ <<'__EXAMPLE__', 'TT loc inline filter - literal arg' );
#. ("arg")
#: :1
msgid "string"
msgstr ""
__EXAMPLE__
-$Ext = $Old_Ext;
+ $Ext = $Old_Ext;
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT multiline filter');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT multiline filter' );
[% | l(arg1,arg2) %]
my string
[% END %]
@@ -185,7 +272,8 @@ msgid ""
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT multiline filter with chomp');
+ write_po_ok(
+ <<'__TT__' => <<'__EXAMPLE__', 'TT multiline filter with chomp' );
[%- | l(arg1,arg2) -%]
my string
[%- END -%]
@@ -196,15 +284,19 @@ msgid "my string"
msgstr ""
__EXAMPLE__
-extract_ok(q([% l('catted ' _ 'string') %]) => "catted string", "TT catted string");
-extract_ok(q([% l('catted ' _ string) %]) => "", "TT catted dir 1");
-extract_ok(q([% l('catted ' _ string) %]) => "", "TT catted dir 2");
+ extract_ok( q([% l('catted ' _ 'string') %]) => "catted string",
+ "TT catted string" );
+ extract_ok( q([% l('catted ' _ string) %]) => "", "TT catted dir 1" );
+ extract_ok( q([% l('catted ' _ string) %]) => "", "TT catted dir 2" );
-extract_ok(q([% l("embedded ${string}") %]) => "", "TT embedded string 1");
-extract_ok(q([% l("embedded \${string}") %]) => 'embedded ${string}', "TT embedded string 2");
-extract_ok(q([% l('embedded ${string}') %]) => 'embedded ${string}', "TT embedded string 3");
+ extract_ok( q([% l("embedded ${string}") %]) => "",
+ "TT embedded string 1" );
+ extract_ok( q([% l("embedded \${string}") %]) => 'embedded ${string}',
+ "TT embedded string 2" );
+ extract_ok( q([% l('embedded ${string}') %]) => 'embedded ${string}',
+ "TT embedded string 3" );
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 1');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 1' );
[% l('my \ string', 'my \ string') %]
[% l('my \\ string', 'my \\ string') %]
[% l("my \\ string", "my \\ string") %]
@@ -215,7 +307,7 @@ msgid "my \\ string"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 2');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 2' );
[% l('my str\'ing','my str\'ing') %]
__TT__
#. ("my str'ing")
@@ -224,7 +316,7 @@ msgid "my str'ing"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 3');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 3' );
[% l('my string"','my string"') %]
__TT__
#. ("my string\"")
@@ -233,7 +325,7 @@ msgid "my string\""
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 4');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 4' );
[% l("my string'","my string'") %]
__TT__
#. ("my string'")
@@ -242,7 +334,7 @@ msgid "my string'"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 5');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 5' );
[% l("my \nstring","my \nstring") %]
__TT__
#. ("my \nstring")
@@ -253,7 +345,7 @@ msgid ""
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 6');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 6' );
[% l('my \nstring','my \nstring') %]
__TT__
#. ("my \\nstring")
@@ -262,7 +354,7 @@ msgid "my \\nstring"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 7');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 7' );
[% 'my \ string' | l('my \ string') %]
[% 'my \\ string' | l('my \\ string') %]
[% "my \\ string" | l("my \\ string") %]
@@ -273,7 +365,7 @@ msgid "my \\ string"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 8');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 8' );
[% 'my str\'ing' | l('my str\'ing') %]
__TT__
#. ("my str'ing")
@@ -282,7 +374,7 @@ msgid "my str'ing"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 9');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 9' );
[% 'my string"' | l('my string"') %]
__TT__
#. ("my string\"")
@@ -291,7 +383,7 @@ msgid "my string\""
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 10');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 10' );
[% "my string'" |l("my string'") %]
__TT__
#. ("my string'")
@@ -300,7 +392,7 @@ msgid "my string'"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 11');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 11' );
[% "my \nstring" |l("my \nstring") %]
__TT__
#. ("my \nstring")
@@ -311,7 +403,7 @@ msgid ""
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 12');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 12' );
[% 'my \nstring' |l('my \nstring') %]
__TT__
#. ("my \\nstring")
@@ -320,7 +412,7 @@ msgid "my \\nstring"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 13');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 13' );
[% | l('my \ string') %]my \ string[% END %]
[% | l('my \\ string') %]my \\ string[% END %]
__TT__
@@ -335,7 +427,7 @@ msgid "my \\\\ string"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 14');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 14' );
[% | l('my str\'ing') %]my str'ing[% END %]
__TT__
#. ("my str'ing")
@@ -344,7 +436,7 @@ msgid "my str'ing"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 15');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 15' );
[% | l('my str\'ing') %]my str\'ing[% END %]
__TT__
#. ("my str'ing")
@@ -353,8 +445,7 @@ msgid "my str\\'ing"
msgstr ""
__EXAMPLE__
-
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 16');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 16' );
[% | l("my str\"ing") %]my str"ing[% END %]
__TT__
#. ("my str\"ing")
@@ -363,7 +454,7 @@ msgid "my str\"ing"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 17');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 17' );
[% | l("my str\"ing") %]my str\"ing[% END %]
__TT__
#. ("my str\"ing")
@@ -372,7 +463,7 @@ msgid "my str\\\"ing"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 18');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 18' );
[% |l("my \nstring") %]my
string[% END %]
__TT__
@@ -384,7 +475,7 @@ msgid ""
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 19');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT quoted - 19' );
[% |l('my \nstring') %]my \nstring[% END %]
__TT__
#. ("my \\nstring")
@@ -393,8 +484,7 @@ msgid "my \\nstring"
msgstr ""
__EXAMPLE__
-
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT key values');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT key values' );
[% l('string', key1=>'value',key2=>value, key3 => value.method) %]
__TT__
#. ({ 'key1' => 'value', 'key2' => value, 'key3' => value.method })
@@ -403,7 +493,7 @@ msgid "string"
msgstr ""
__EXAMPLE__
-write_po_ok(<<'__TT__' => <<'__EXAMPLE__', 'TT complex args');
+ write_po_ok( <<'__TT__' => <<'__EXAMPLE__', 'TT complex args' );
[% l('string',b.method.$var(arg),c('arg').method.5) %]
__TT__
#. (b.method.$var(arg), c("arg").method.5)
@@ -412,246 +502,139 @@ msgid "string"
msgstr ""
__EXAMPLE__
-#### END TT TESTS ############
+ #### END TT TESTS ############
}
#### BEGIN YAML TESTS ############
-SKIP: { skip('YAML.pm unavailable', 7) unless eval { require YAML };
-
-extract_ok(qq(key: _"string"\n) => "string", "YAML double quotes");
-extract_ok(qq(key: _'string'\n) => "string", "YAML single quotes");
-extract_ok(qq(key: _"str"ing"\n) => 'str"ing', "YAML embedded double quote");
-
-extract_ok(qq( key: { s1: _"string_1", s2: _'string_2', s3: _'string'3'}\n)
- => q(string_1string'3string_2), 'YAML inline hash');
-
-
-extract_ok(qq( - _"string_1"\n - _'string_2'\n - _'string'3'\n)
- => q(string_1string'3string_2), 'YAML array');
-
-extract_ok(qq(key: [ _"string_1", _'string_2', _'string'3' ]\n)
- => q(string_1string'3string_2), 'YAML Inline arrays' );
-
-write_po_ok(qq(---\nkey: _"string"\n---\nkey2: _"string2"\n) => <<'__EXAMPLE__', 'YAML multiple docs');
+SKIP: {
+ skip( 'YAML.pm unavailable', 9 ) unless eval { require YAML };
+
+ extract_ok( qq(key: _"string"\n) => "string", "YAML double quotes" );
+ extract_ok( qq(key: _'string'\n) => "string", "YAML single quotes" );
+ extract_ok( qq(key: _"str"ing"\n) => 'str"ing',
+ "YAML embedded double quote" );
+
+ extract_ok(
+ qq( key: { s1: _"string_1", s2: _'string_2', s3: _'string'3'}\n) =>
+ q(string_1string'3string_2),
+ 'YAML inline hash'
+ );
+
+ extract_ok( qq( - _"string_1"\n - _'string_2'\n - _'string'3'\n) =>
+ q(string_1string'3string_2),
+ 'YAML array'
+ );
+
+ extract_ok( qq(key: [ _"string_1", _'string_2', _'string'3' ]\n) =>
+ q(string_1string'3string_2),
+ 'YAML Inline arrays'
+ );
+
+ write_po_ok( qq(---\nkey: _"string"\n---\nkey2: _"string2"\n\n\n\n) =>
+ <<'__EXAMPLE__', 'YAML multiple docs' );
#: :2
msgid "string"
msgstr ""
-#: :3
+#: :4
msgid "string2"
msgstr ""
__EXAMPLE__
-}
-
-#### END YAML TESTS ############
-
-
-extract_ok(<<'__EXAMPLE__' => "123\n", "Simple extraction (heredoc)");
-_(<<__LOC__);
-123
-__LOC__
-__EXAMPLE__
-
-extract_ok(<<'__EXAMPLE__' => "foo\\\$bar\\\'baz\n", "No escaped of \$ and \' in singlequoted terminator (heredoc)");
-_(<<'__LOC__');
-foo\$bar\'baz
-__LOC__
-__EXAMPLE__
-
-extract_ok(<<'__EXAMPLE__' => "foo\$bar\n", "Normalized \$ in doublequoted terminator (heredoc)");
-_(<<"__LOC__");
-foo\$bar
-__LOC__
-__EXAMPLE__
-
-extract_ok(<<'__EXAMPLE__' => "foo\nbar\n", "multilines (heredoc)");
-_(<<__LOC__);
-foo
-bar
-__LOC__
-__EXAMPLE__
-
-extract_ok(<<'__EXAMPLE__' => "example\n", "null identifier (heredoc)");
-_(<<"");
-example
-
-__EXAMPLE__
-
-extract_ok(<<'__EXAMPLE__' => "example\n", "end() after the heredoc (heredoc)");
-_(<<__LOC__
-example
-__LOC__
-);
-__EXAMPLE__
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "null identifier with end after the heredoc (heredoc)");
-_(<<""
-example
-
-);
-__EXAMPLE__
-#: :1
-msgid "example\n"
-msgstr ""
-__EXPECTED__
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "q with multilines with args");
-_(q{example %1
-with multilines
-},20);
-__EXAMPLE__
-#. (20)
-#: :1
+ write_po_ok( <<__YAML__ => <<'__EXAMPLE__', 'YAML folded/block scalars' );
+---
+key: >
+ _'My folded
+ scalar'
+key2: |-
+ _'My block
+ scalar
+ '
+__YAML__
+#: :5
msgid ""
-"example %1\n"
-"with multilines\n"
+"My block\n"
+"scalar\n"
msgstr ""
-__EXPECTED__
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "null terminator with multilines with args (heredoc)");
-_(<<"", 15)
-example %1
-with multilines
-__EXAMPLE__
-#. (15)
-#: :1
-msgid ""
-"example %1\n"
-"with multilines\n"
+#: :2
+msgid "My folded scalar"
msgstr ""
-__EXPECTED__
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "null terminator with end after the heredoc with args (heredoc)");
-_(<<"", 10)
-example %1
-
__EXAMPLE__
-#. (10)
-#: :1
-msgid "example %1\n"
-msgstr ""
-__EXPECTED__
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "two _() calls (heredoc)");
-_(<<"", 10)
-example1 %1
+ write_po_ok( <<__YAML__ => <<'__EXAMPLE__', 'YAML nested' );
+---
+foo:
+ bar:
+ - _'first'
+ - baz: >
+ _'second'
+ boo: |-
+ _'My block
+ scalar
+ '
+ bla: [ _'inline_seq' , _'inline_seq2' ]
-_(<<"", 5)
-example2 %1
-__EXAMPLE__
-#. (10)
-#: :1
-msgid "example1 %1\n"
+__YAML__
+#: :7
+msgid ""
+"My block\n"
+"scalar\n"
msgstr ""
-#. (5)
#: :4
-msgid "example2 %1\n"
+msgid "first"
msgstr ""
-__EXPECTED__
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "concat (heredoc)");
-_('exam'.<<"", 10)
-ple1 %1
-
-__EXAMPLE__
-#. (10)
-#: :1
-msgid "example1 %1\n"
+#: :11
+msgid "inline_seq"
msgstr ""
-__EXPECTED__
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "two _() calls with concat over multiline (heredoc)");
-_('example' .
-<<"", 10)
-1 %1
-_(<<"", 5)
-example2 %1
-
-__EXAMPLE__
-#. (10)
-#: :1
-msgid "example1 %1\n"
+#: :11
+msgid "inline_seq2"
msgstr ""
-#. (5)
#: :5
-msgid "example2 %1\n"
+msgid "second"
msgstr ""
-__EXPECTED__
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "i can concat the world!");
-_(
-'\$foo'
-."\$bar"
-.<<''
-\$baz
-
-)
-__EXAMPLE__
-#: :2
-msgid "\\$foo$bar\\$baz\n"
-msgstr ""
-__EXPECTED__
-
-## Wrapping
-
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "wrap off");
-_('string');
-_('string');
__EXAMPLE__
-#: :1 :2
-msgid "string"
-msgstr ""
-__EXPECTED__
-$Ext->{wrap} = 1;
-write_po_ok(<<'__EXAMPLE__' => <<'__EXPECTED__', "wrap on");
-_('string');
-_('string');
-__EXAMPLE__
-#: :1
-#: :2
-msgid "string"
-msgstr ""
-__EXPECTED__
+}
+#### END YAML TESTS ############
sub extract_ok {
- my ($text, $expected, $info, $verbatim) = @_;
- $Ext->extract('' => $text);
+ my ( $text, $expected, $info, $verbatim ) = @_;
+ $Ext->extract( '' => $text );
$Ext->compile($verbatim);
- my $result = join('', %{$Ext->lexicon});
- is($result, $expected, $info );
+ my $result = join( '', %{ $Ext->lexicon } );
+ is( $result, $expected, $info );
$Ext->clear;
}
sub write_po_ok {
- my ($text, $expected, $info, $verbatim) = @_;
+ my ( $text, $expected, $info, $verbatim ) = @_;
my $po_file = 't/5-extract.po';
# create .po
- $Ext->extract('' => $text);
+ $Ext->extract( '' => $text );
$Ext->compile($verbatim);
$Ext->write_po($po_file);
# read .po
- open(my $po_handle,'<',$po_file) or die("Cannot open $po_file: $!");
+ open( my $po_handle, '<', $po_file ) or die("Cannot open $po_file: $!");
local $/ = undef;
my $result = <$po_handle>;
close($po_handle);
unlink($po_file) or die("Cannot unlink $po_file: $!");
# cut the header from result
- my $start_expected = length($Ext->header);
- $start_expected++ if( $start_expected < length($result) );
+ my $start_expected = length( $Ext->header );
+ $start_expected++ if ( $start_expected < length($result) );
# check result vs expected
- is(substr($result, $start_expected), $expected, $info );
+ is( substr( $result, $start_expected ), $expected, $info );
$Ext->clear;
}
View
290 t/51-perlextract.t
@@ -0,0 +1,290 @@
+#! /usr/bin/perl -w
+use lib '../lib';
+use strict;
+use Test::More tests => 87;
+
+use_ok('Locale::Maketext::Extract');
+my $Ext = Locale::Maketext::Extract->new();
+
+# Standard Perl parser
+run_tests('perl - ');
+
+SKIP: {
+ # PPI parser
+ skip( 'PPI unavailable', 43 ) unless eval { require PPI };
+ $Ext = Locale::Maketext::Extract->new(
+ plugins => { 'Locale::Maketext::Extract::Plugin::PPI' => '*' } );
+ run_tests('ppi - ');
+}
+
+sub run_tests {
+ my $prefix = shift;
+ isa_ok( $Ext => 'Locale::Maketext::Extract' );
+
+ #### BEGIN PERL TESTS ############
+ extract_ok( '_("123")' => 123, $prefix . 'Simple extraction' );
+
+ extract_ok( '_("[_1] is happy")' => '%1 is happy',
+ $prefix . '[_1] to %1' );
+ extract_ok( '_("%1 is happy")' => '%1 is happy',
+ $prefix . '%1 verbatim', 1 );
+
+ extract_ok( '_("[*,_1] counts")' => '%*(%1) counts',
+ $prefix . '[*,_1] to %*(%1)' );
+ extract_ok( '_("%*(%1) counts")' => '%*(%1) counts',
+ $prefix . '%*(%1) verbatim', 1 );
+
+ extract_ok( '_("[*,_1,_2] counts")' => '%*(%1,%2) counts',
+ $prefix . '[*,_1,_2] to %*(%1,%2)' );
+ extract_ok( '_("[*,_1,_2] counts")' => '[*,_1,_2] counts',
+ $prefix . '[*,_1,_2] verbatim', 1 );
+ extract_ok( q(_('foo\$bar')) => 'foo\\$bar',
+ $prefix . 'Escaped \$ in q' );
+ extract_ok( q(_("foo\$bar")) => 'foo$bar',
+ $prefix . 'Normalized \$ in qq' );
+
+ extract_ok( q(_('foo\x20bar')) => 'foo\\x20bar',
+ $prefix . 'Escaped \x in q' );
+ extract_ok( q(_("foo\x20bar")) => 'foo bar',
+ $prefix . 'Normalized \x in qq' );
+
+ extract_ok( q(_('foo\nbar')) => 'foo\\nbar',
+ $prefix . 'Escaped \n in qq' );
+ extract_ok( q(_("foo\nbar")) => "foo\nbar",
+ $prefix . 'Normalized \n in qq' );
+ extract_ok( qq(_("foo\nbar")) => "foo\nbar",
+ $prefix . 'Normalized literal \n in qq' );
+
+ extract_ok( q(_("foo\nbar")) => "foo\nbar",
+ $prefix . 'Trailing \n in qq' );
+ extract_ok( qq(_("foobar\n")) => "foobar\n",
+ $prefix . 'Trailing literal \n in qq' );
+
+ extract_ok( q(_('foo\bar')) => 'foo\\bar', $prefix . 'Escaped \ in q' );
+ extract_ok( q(_('foo\\\\bar')) => 'foo\\bar',
+ $prefix . 'Normalized \\\\ in q' );
+ extract_ok( q(_("foo\bar")) => "foo\bar",
+ $prefix . 'Interpolated \b in qq' );
+
+ extract_ok( q([% loc( 'foo "bar" baz' ) %]) => 'foo "bar" baz',
+ $prefix . 'Escaped double quote in text' );
+
+ extract_ok( q( _(q{foo bar})) => "foo bar", $prefix . 'No escapes' );
+ extract_ok( q(_(q{foo\bar})) => 'foo\\bar', $prefix . 'Escaped \ in q' );
+ extract_ok( q(_(q{foo\\\\bar})) => 'foo\\bar',
+ $prefix . 'Normalized \\\\ in q' );
+ extract_ok( q(_(qq{foo\bar})) => "foo\bar",
+ $prefix . 'Interpolated \b in qq' );
+
+ extract_ok( q(my $x = loc('I "think" you\'re a cow.') . "\n";) =>
+ 'I "think" you\'re a cow.',
+ $prefix . "Handle escaped single quotes"
+ );
+
+ extract_ok( q(my $x = loc("I'll poke you like a \"cow\" man.") . "\n";) =>
+ 'I\'ll poke you like a "cow" man.',
+ $prefix . "Handle escaped double quotes"
+ );
+
+ extract_ok( q(_("","car")) => '', $prefix . 'ignore empty string' );
+ extract_ok( q(_("0")) => '', $prefix . 'ignore zero' );
+
+ extract_ok( <<'__EXAMPLE__' => "123\n", "Simple extraction (heredoc)" );
+_(<<__LOC__);
+123
+__LOC__
+__EXAMPLE__
+
+ extract_ok(
+ <<'__EXAMPLE__' => "foo\\\$bar\\\'baz\n", "No escaped of \$ and \' in singlequoted terminator (heredoc)" );
+_(<<'__LOC__');
+foo\$bar\'baz
+__LOC__
+__EXAMPLE__
+
+ extract_ok(
+ <<'__EXAMPLE__' => "foo\$bar\n", "Normalized \$ in doublequoted terminator (heredoc)" );
+_(<<"__LOC__");
+foo\$bar
+__LOC__
+__EXAMPLE__
+
+ extract_ok( <<'__EXAMPLE__' => "foo\nbar\n", "multilines (heredoc)" );
+_(<<__LOC__);
+foo
+bar
+__LOC__
+__EXAMPLE__
+
+ extract_ok( <<'__EXAMPLE__' => "example\n", "null identifier (heredoc)" );
+_(<<"");
+example
+
+__EXAMPLE__
+
+ extract_ok(
+ <<'__EXAMPLE__' => "example\n", "end() after the heredoc (heredoc)" );
+_(<<__LOC__
+example
+__LOC__
+);
+__EXAMPLE__
+
+ write_po_ok(
+ <<'__EXAMPLE__' => <<'__EXPECTED__', "null identifier with end after the heredoc (heredoc)" );
+_(<<""
+example
+
+);
+__EXAMPLE__
+#: :1
+msgid "example\n"
+msgstr ""
+__EXPECTED__
+
+ write_po_ok(
+ <<'__EXAMPLE__' => <<'__EXPECTED__', "q with multilines with args" );
+_(q{example %1
+with multilines
+},20);
+__EXAMPLE__
+#. (20)
+#: :1
+msgid ""
+"example %1\n"
+"with multilines\n"
+msgstr ""
+__EXPECTED__
+
+ write_po_ok(
+ <<'__EXAMPLE__' => <<'__EXPECTED__', "null terminator with multilines with args (heredoc)" );
+_(<<"", 15)
+example %1
+with multilines
+
+__EXAMPLE__
+#. (15)
+#: :1
+msgid ""
+"example %1\n"
+"with multilines\n"
+msgstr ""
+__EXPECTED__
+
+ write_po_ok(
+ <<'__EXAMPLE__' => <<'__EXPECTED__', "null terminator with end after the heredoc with args (heredoc)" );
+_(<<"", 10)
+example %1
+
+__EXAMPLE__
+#. (10)
+#: :1
+msgid "example %1\n"
+msgstr ""
+__EXPECTED__
+
+ write_po_ok(