From: Jos I. Boumans Date: Sun, 13 Aug 2006 15:51:58 +0000 (+0200) Subject: Re: [PATCH] Add Locale::Maketext::Simple to the core X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=c9d0c046ab7aa1e87edc8cd6fbfa8dc66f709875;p=p5sagit%2Fp5-mst-13.2.git Re: [PATCH] Add Locale::Maketext::Simple to the core Message-ID: <24406.80.127.35.68.1155477118.squirrel@webmail.xs4all.nl> Actually added 0.18, rather than 0.17 which this PATCH contained, for an updated licence statement. p4raw-id: //depot/perl@28809 --- diff --git a/MANIFEST b/MANIFEST index 7d131fe..cdbb260 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1806,6 +1806,9 @@ lib/Locale/Maketext/Guts.pm Locale::Maketext lib/Locale/Maketext.pm Locale::Maketext lib/Locale/Maketext.pod Locale::Maketext documentation lib/Locale/Maketext/README Locale::Maketext +lib/Locale/Maketext/Simple.pm Locale::Simple +lib/Locale/Maketext/Simple/t/0-signature.t Locale::Simple tests +lib/Locale/Maketext/Simple/t/1-basic.t Locale::Simple tests lib/Locale/Maketext/t/01_about_verbose.t See if Locale::Maketext works lib/Locale/Maketext/t/10_make.t See if Locale::Maketext works lib/Locale/Maketext/t/20_get.t See if Locale::Maketext works diff --git a/lib/Locale/Maketext/Simple.pm b/lib/Locale/Maketext/Simple.pm new file mode 100644 index 0000000..ddc1c65 --- /dev/null +++ b/lib/Locale/Maketext/Simple.pm @@ -0,0 +1,338 @@ +package Locale::Maketext::Simple; +$Locale::Maketext::Simple::VERSION = '0.18'; + +use strict; +use 5.004; + +=head1 NAME + +Locale::Maketext::Simple - Simple interface to Locale::Maketext::Lexicon + +=head1 VERSION + +This document describes version 0.18 of Locale::Maketext::Simple, +released Septermber 8, 2006. + +=head1 SYNOPSIS + +Minimal setup (looks for F and F): + + package Foo; + use Locale::Maketext::Simple; # exports 'loc' + loc_lang('fr'); # set language to French + sub hello { + print loc("Hello, [_1]!", "World"); + } + +More sophisticated example: + + package Foo::Bar; + use Locale::Maketext::Simple ( + Class => 'Foo', # search in auto/Foo/ + Style => 'gettext', # %1 instead of [_1] + Export => 'maketext', # maketext() instead of loc() + Subclass => 'L10N', # Foo::L10N instead of Foo::I18N + Decode => 1, # decode entries to unicode-strings + Encoding => 'locale', # but encode lexicons in current locale + # (needs Locale::Maketext::Lexicon 0.36) + ); + sub japh { + print maketext("Just another %1 hacker", "Perl"); + } + +=head1 DESCRIPTION + +This module is a simple wrapper around B, +designed to alleviate the need of creating I for +module authors. + +If B is not present, it implements a +minimal localization function by simply interpolating C<[_1]> with +the first argument, C<[_2]> with the second, etc. Interpolated +function like C<[quant,_1]> are treated as C<[_1]>, with the sole +exception of C<[tense,_1,X]>, which will append C to C<_1> when +X is C, or appending C to <_1> otherwise. + +=head1 OPTIONS + +All options are passed either via the C statement, or via an +explicit C. + +=head2 Class + +By default, B draws its source from the +calling package's F directory; you can override this behaviour +by explicitly specifying another package as C. + +=head2 Path + +If your PO and MO files are under a path elsewhere than C, +you may specify it using the C option. + +=head2 Style + +By default, this module uses the C style of C<[_1]> and +C<[quant,_1]> for interpolation. Alternatively, you can specify the +C style, which uses C<%1> and C<%quant(%1)> for interpolation. + +This option is case-insensitive. + +=head2 Export + +By default, this module exports a single function, C, into its +caller's namespace. You can set it to another name, or set it to +an empty string to disable exporting. + +=head2 Subclass + +By default, this module creates an C<::I18N> subclass under the +caller's package (or the package specified by C), and stores +lexicon data in its subclasses. You can assign a name other than +C via this option. + +=head2 Decode + +If set to a true value, source entries will be converted into +utf8-strings (available in Perl 5.6.1 or later). This feature +needs the B or B module. + +=head2 Encoding + +Specifies an encoding to store lexicon entries, instead of +utf8-strings. If set to C, the encoding from the current +locale setting is used. Implies a true value for C. + +=cut + +sub import { + my ($class, %args) = @_; + + $args{Class} ||= caller; + $args{Style} ||= 'maketext'; + $args{Export} ||= 'loc'; + $args{Subclass} ||= 'I18N'; + + my ($loc, $loc_lang) = $class->load_loc(%args); + $loc ||= $class->default_loc(%args); + + no strict 'refs'; + *{caller(0) . "::$args{Export}"} = $loc if $args{Export}; + *{caller(0) . "::$args{Export}_lang"} = $loc_lang || sub { 1 }; +} + +my %Loc; + +sub reload_loc { %Loc = () } + +sub load_loc { + my ($class, %args) = @_; + + my $pkg = join('::', grep { defined and length } $args{Class}, $args{Subclass}); + return $Loc{$pkg} if exists $Loc{$pkg}; + + eval { require Locale::Maketext::Lexicon; 1 } or return; + $Locale::Maketext::Lexicon::VERSION > 0.20 or return; + eval { require File::Spec; 1 } or return; + + my $path = $args{Path} || $class->auto_path($args{Class}) or return; + my $pattern = File::Spec->catfile($path, '*.[pm]o'); + my $decode = $args{Decode} || 0; + my $encoding = $args{Encoding} || undef; + + $decode = 1 if $encoding; + + $pattern =~ s{\\}{/}g; # to counter win32 paths + + eval " + package $pkg; + use base 'Locale::Maketext'; + %${pkg}::Lexicon = ( '_AUTO' => 1 ); + Locale::Maketext::Lexicon->import({ + 'i-default' => [ 'Auto' ], + '*' => [ Gettext => \$pattern ], + _decode => \$decode, + _encoding => \$encoding, + }); + *tense = sub { \$_[1] . ((\$_[2] eq 'present') ? 'ing' : 'ed') } + unless defined &tense; + + 1; + " or die $@; + + my $lh = eval { $pkg->get_handle } or return; + my $style = lc($args{Style}); + if ($style eq 'maketext') { + $Loc{$pkg} = sub { + $lh->maketext(@_) + }; + } + elsif ($style eq 'gettext') { + $Loc{$pkg} = sub { + my $str = shift; + $str =~ s{([\~\[\]])}{~$1}g; + $str =~ s{ + ([%\\]%) # 1 - escaped sequence + | + % (?: + ([A-Za-z#*]\w*) # 2 - function call + \(([^\)]*)\) # 3 - arguments + | + ([1-9]\d*|\*) # 4 - variable + ) + }{ + $1 ? $1 + : $2 ? "\[$2,"._unescape($3)."]" + : "[_$4]" + }egx; + return $lh->maketext($str, @_); + }; + } + else { + die "Unknown Style: $style"; + } + + return $Loc{$pkg}, sub { + $lh = $pkg->get_handle(@_); + $lh = $pkg->get_handle(@_); + }; +} + +sub default_loc { + my ($self, %args) = @_; + my $style = lc($args{Style}); + if ($style eq 'maketext') { + return sub { + my $str = shift; + $str =~ s{((? 1) ? ($4 || "$3s") : $3) : + '' + ) : '' + ); + }egx; + return $str; +}; + +sub _escape { + my $text = shift; + $text =~ s/\b_([1-9]\d*)/%$1/g; + return $text; +} + +sub _unescape { + join(',', map { + /\A(\s*)%([1-9]\d*|\*)(\s*)\z/ ? "$1_$2$3" : $_ + } split(/,/, $_[0])); +} + +sub auto_path { + my ($self, $calldir) = @_; + $calldir =~ s#::#/#g; + my $path = $INC{$calldir . '.pm'} or return; + + # Try absolute path name. + if ($^O eq 'MacOS') { + (my $malldir = $calldir) =~ tr#/#:#; + $path =~ s#^(.*)$malldir\.pm\z#$1auto:$malldir:#s; + } else { + $path =~ s#^(.*)$calldir\.pm\z#$1auto/$calldir/#; + } + + return $path if -d $path; + + # If that failed, try relative path with normal @INC searching. + $path = "auto/$calldir/"; + foreach my $inc (@INC) { + return "$inc/$path" if -d "$inc/$path"; + } + + return; +} + +1; + +=head1 ACKNOWLEDGMENTS + +Thanks to Jos I. Boumans for suggesting this module to be written. + +Thanks to Chia-Liang Kao for suggesting C and C. + +=head1 SEE ALSO + +L, L + +=head1 AUTHORS + +Audrey Tang Ecpan@audreyt.orgE + +=head1 COPYRIGHT + +Copyright 2003, 2004, 2005, 2006 by Audrey Tang Ecpan@audreyt.orgE. + +This software is released under the MIT license cited below. Additionally, +when this software is distributed with B, you may also +redistribute it and/or modify it under the same terms as Perl itself. + +=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 diff --git a/lib/Locale/Maketext/Simple/t/0-signature.t b/lib/Locale/Maketext/Simple/t/0-signature.t new file mode 100644 index 0000000..c70c4a3 --- /dev/null +++ b/lib/Locale/Maketext/Simple/t/0-signature.t @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +use strict; +print "1..1\n"; + +if (!$ENV{TEST_SIGNATURE}) { + print "ok 1 # skip set the environment variable TEST_SIGNATURE to enable this test\n"; +} +elsif (!-s 'SIGNATURE') { + print "ok 1 # skip No signature file found\n"; +} +elsif (!eval { require Module::Signature; 1 }) { + print "ok 1 # skip ", + "Next time around, consider install Module::Signature, ", + "so you can verify the integrity of this distribution.\n"; +} +elsif (!eval { require Socket; Socket::inet_aton('pgp.mit.edu') }) { + print "ok 1 # skip ", + "Cannot connect to the keyserver\n"; +} +else { + (Module::Signature::verify() == Module::Signature::SIGNATURE_OK()) + or print "not "; + print "ok 1 # Valid signature\n"; +} + +__END__ diff --git a/lib/Locale/Maketext/Simple/t/1-basic.t b/lib/Locale/Maketext/Simple/t/1-basic.t new file mode 100644 index 0000000..91d033b --- /dev/null +++ b/lib/Locale/Maketext/Simple/t/1-basic.t @@ -0,0 +1,26 @@ +use strict; +use Test; + +BEGIN { + plan tests => 9; + $INC{'Locale/Maketext/Lexicon.pm'} = __FILE__; + $Locale::Maketext::Lexicon::VERSION = 0; +} + +use Locale::Maketext::Simple; +ok(Locale::Maketext::Simple->VERSION); +ok(loc("Just [_1] Perl [_2]", qw(another hacker)), "Just another Perl hacker"); + +{ + local $^W; # shuts up 'redefined' warnings + Locale::Maketext::Simple->reload_loc; + Locale::Maketext::Simple->import(Style => 'gettext'); +} + +ok(loc("Just %1 Perl %2", qw(another hacker)), "Just another Perl hacker"); +ok(loc_lang('fr')); +ok(loc("Just %quant(%1,Perl hacker)", 1), "Just 1 Perl hacker"); +ok(loc("Just %quant(%1,Perl hacker)", 2), "Just 2 Perl hackers"); +ok(loc("Just %quant(%1,Mad skill,Mad skillz)", 3), "Just 3 Mad skillz"); +ok(loc("Error %tense(%1,present)", 'uninstall'), "Error uninstalling"); +ok(loc("Error %tense(uninstall,present)"), "Error uninstalling");