New data for Unicode on older versions, thanks to Nicholas
[p5sagit/p5-mst-13.2.git] / lib / Locale / Maketext / Simple.pm
CommitLineData
c9d0c046 1package Locale::Maketext::Simple;
2$Locale::Maketext::Simple::VERSION = '0.18';
3
4use strict;
5use 5.004;
6
7=head1 NAME
8
9Locale::Maketext::Simple - Simple interface to Locale::Maketext::Lexicon
10
11=head1 VERSION
12
13This document describes version 0.18 of Locale::Maketext::Simple,
14released Septermber 8, 2006.
15
16=head1 SYNOPSIS
17
18Minimal setup (looks for F<auto/Foo/*.po> and F<auto/Foo/*.mo>):
19
20 package Foo;
21 use Locale::Maketext::Simple; # exports 'loc'
22 loc_lang('fr'); # set language to French
23 sub hello {
24 print loc("Hello, [_1]!", "World");
25 }
26
27More sophisticated example:
28
29 package Foo::Bar;
30 use Locale::Maketext::Simple (
31 Class => 'Foo', # search in auto/Foo/
32 Style => 'gettext', # %1 instead of [_1]
33 Export => 'maketext', # maketext() instead of loc()
34 Subclass => 'L10N', # Foo::L10N instead of Foo::I18N
35 Decode => 1, # decode entries to unicode-strings
36 Encoding => 'locale', # but encode lexicons in current locale
37 # (needs Locale::Maketext::Lexicon 0.36)
38 );
39 sub japh {
40 print maketext("Just another %1 hacker", "Perl");
41 }
42
43=head1 DESCRIPTION
44
45This module is a simple wrapper around B<Locale::Maketext::Lexicon>,
46designed to alleviate the need of creating I<Language Classes> for
47module authors.
48
49If B<Locale::Maketext::Lexicon> is not present, it implements a
50minimal localization function by simply interpolating C<[_1]> with
51the first argument, C<[_2]> with the second, etc. Interpolated
52function like C<[quant,_1]> are treated as C<[_1]>, with the sole
53exception of C<[tense,_1,X]>, which will append C<ing> to C<_1> when
54X is C<present>, or appending C<ed> to <_1> otherwise.
55
56=head1 OPTIONS
57
58All options are passed either via the C<use> statement, or via an
59explicit C<import>.
60
61=head2 Class
62
63By default, B<Locale::Maketext::Simple> draws its source from the
64calling package's F<auto/> directory; you can override this behaviour
65by explicitly specifying another package as C<Class>.
66
67=head2 Path
68
69If your PO and MO files are under a path elsewhere than C<auto/>,
70you may specify it using the C<Path> option.
71
72=head2 Style
73
74By default, this module uses the C<maketext> style of C<[_1]> and
75C<[quant,_1]> for interpolation. Alternatively, you can specify the
76C<gettext> style, which uses C<%1> and C<%quant(%1)> for interpolation.
77
78This option is case-insensitive.
79
80=head2 Export
81
82By default, this module exports a single function, C<loc>, into its
83caller's namespace. You can set it to another name, or set it to
84an empty string to disable exporting.
85
86=head2 Subclass
87
88By default, this module creates an C<::I18N> subclass under the
89caller's package (or the package specified by C<Class>), and stores
90lexicon data in its subclasses. You can assign a name other than
91C<I18N> via this option.
92
93=head2 Decode
94
95If set to a true value, source entries will be converted into
96utf8-strings (available in Perl 5.6.1 or later). This feature
97needs the B<Encode> or B<Encode::compat> module.
98
99=head2 Encoding
100
101Specifies an encoding to store lexicon entries, instead of
102utf8-strings. If set to C<locale>, the encoding from the current
103locale setting is used. Implies a true value for C<Decode>.
104
105=cut
106
107sub import {
108 my ($class, %args) = @_;
109
110 $args{Class} ||= caller;
111 $args{Style} ||= 'maketext';
112 $args{Export} ||= 'loc';
113 $args{Subclass} ||= 'I18N';
114
115 my ($loc, $loc_lang) = $class->load_loc(%args);
116 $loc ||= $class->default_loc(%args);
117
118 no strict 'refs';
119 *{caller(0) . "::$args{Export}"} = $loc if $args{Export};
120 *{caller(0) . "::$args{Export}_lang"} = $loc_lang || sub { 1 };
121}
122
123my %Loc;
124
125sub reload_loc { %Loc = () }
126
127sub load_loc {
128 my ($class, %args) = @_;
129
130 my $pkg = join('::', grep { defined and length } $args{Class}, $args{Subclass});
131 return $Loc{$pkg} if exists $Loc{$pkg};
132
133 eval { require Locale::Maketext::Lexicon; 1 } or return;
134 $Locale::Maketext::Lexicon::VERSION > 0.20 or return;
135 eval { require File::Spec; 1 } or return;
136
137 my $path = $args{Path} || $class->auto_path($args{Class}) or return;
138 my $pattern = File::Spec->catfile($path, '*.[pm]o');
139 my $decode = $args{Decode} || 0;
140 my $encoding = $args{Encoding} || undef;
141
142 $decode = 1 if $encoding;
143
144 $pattern =~ s{\\}{/}g; # to counter win32 paths
145
146 eval "
147 package $pkg;
148 use base 'Locale::Maketext';
149 %${pkg}::Lexicon = ( '_AUTO' => 1 );
150 Locale::Maketext::Lexicon->import({
151 'i-default' => [ 'Auto' ],
152 '*' => [ Gettext => \$pattern ],
153 _decode => \$decode,
154 _encoding => \$encoding,
155 });
156 *tense = sub { \$_[1] . ((\$_[2] eq 'present') ? 'ing' : 'ed') }
157 unless defined &tense;
158
159 1;
160 " or die $@;
161
162 my $lh = eval { $pkg->get_handle } or return;
163 my $style = lc($args{Style});
164 if ($style eq 'maketext') {
165 $Loc{$pkg} = sub {
166 $lh->maketext(@_)
167 };
168 }
169 elsif ($style eq 'gettext') {
170 $Loc{$pkg} = sub {
171 my $str = shift;
172 $str =~ s{([\~\[\]])}{~$1}g;
173 $str =~ s{
174 ([%\\]%) # 1 - escaped sequence
175 |
176 % (?:
177 ([A-Za-z#*]\w*) # 2 - function call
178 \(([^\)]*)\) # 3 - arguments
179 |
180 ([1-9]\d*|\*) # 4 - variable
181 )
182 }{
183 $1 ? $1
184 : $2 ? "\[$2,"._unescape($3)."]"
185 : "[_$4]"
186 }egx;
187 return $lh->maketext($str, @_);
188 };
189 }
190 else {
191 die "Unknown Style: $style";
192 }
193
194 return $Loc{$pkg}, sub {
195 $lh = $pkg->get_handle(@_);
196 $lh = $pkg->get_handle(@_);
197 };
198}
199
200sub default_loc {
201 my ($self, %args) = @_;
202 my $style = lc($args{Style});
203 if ($style eq 'maketext') {
204 return sub {
205 my $str = shift;
206 $str =~ s{((?<!~)(?:~~)*)\[_([1-9]\d*|\*)\]}
207 {$1%$2}g;
208 $str =~ s{((?<!~)(?:~~)*)\[([A-Za-z#*]\w*),([^\]]+)\]}
209 {"$1%$2(" . _escape($3) . ')'}eg;
210 _default_gettext($str, @_);
211 };
212 }
213 elsif ($style eq 'gettext') {
214 return \&_default_gettext;
215 }
216 else {
217 die "Unknown Style: $style";
218 }
219}
220
221sub _default_gettext {
222 my $str = shift;
223 $str =~ s{
224 % # leading symbol
225 (?: # either one of
226 \d+ # a digit, like %1
227 | # or
228 (\w+)\( # a function call -- 1
229 (?: # either
230 %\d+ # an interpolation
231 | # or
232 ([^,]*) # some string -- 2
233 ) # end either
234 (?: # maybe followed
235 , # by a comma
236 ([^),]*) # and a param -- 3
237 )? # end maybe
238 (?: # maybe followed
239 , # by another comma
240 ([^),]*) # and a param -- 4
241 )? # end maybe
242 [^)]* # and other ignorable params
243 \) # closing function call
244 ) # closing either one of
245 }{
246 my $digit = $2 || shift;
247 $digit . (
248 $1 ? (
249 ($1 eq 'tense') ? (($3 eq 'present') ? 'ing' : 'ed') :
250 ($1 eq 'quant') ? ' ' . (($digit > 1) ? ($4 || "$3s") : $3) :
251 ''
252 ) : ''
253 );
254 }egx;
255 return $str;
256};
257
258sub _escape {
259 my $text = shift;
260 $text =~ s/\b_([1-9]\d*)/%$1/g;
261 return $text;
262}
263
264sub _unescape {
265 join(',', map {
266 /\A(\s*)%([1-9]\d*|\*)(\s*)\z/ ? "$1_$2$3" : $_
267 } split(/,/, $_[0]));
268}
269
270sub auto_path {
271 my ($self, $calldir) = @_;
272 $calldir =~ s#::#/#g;
273 my $path = $INC{$calldir . '.pm'} or return;
274
275 # Try absolute path name.
276 if ($^O eq 'MacOS') {
277 (my $malldir = $calldir) =~ tr#/#:#;
278 $path =~ s#^(.*)$malldir\.pm\z#$1auto:$malldir:#s;
279 } else {
280 $path =~ s#^(.*)$calldir\.pm\z#$1auto/$calldir/#;
281 }
282
283 return $path if -d $path;
284
285 # If that failed, try relative path with normal @INC searching.
286 $path = "auto/$calldir/";
287 foreach my $inc (@INC) {
288 return "$inc/$path" if -d "$inc/$path";
289 }
290
291 return;
292}
293
2941;
295
296=head1 ACKNOWLEDGMENTS
297
298Thanks to Jos I. Boumans for suggesting this module to be written.
299
300Thanks to Chia-Liang Kao for suggesting C<Path> and C<loc_lang>.
301
302=head1 SEE ALSO
303
304L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
305
306=head1 AUTHORS
307
308Audrey Tang E<lt>cpan@audreyt.orgE<gt>
309
310=head1 COPYRIGHT
311
312Copyright 2003, 2004, 2005, 2006 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
313
314This software is released under the MIT license cited below. Additionally,
315when this software is distributed with B<Perl Kit, Version 5>, you may also
316redistribute it and/or modify it under the same terms as Perl itself.
317
318=head2 The "MIT" License
319
320Permission is hereby granted, free of charge, to any person obtaining a copy
321of this software and associated documentation files (the "Software"), to deal
322in the Software without restriction, including without limitation the rights
323to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
324copies of the Software, and to permit persons to whom the Software is
325furnished to do so, subject to the following conditions:
326
327The above copyright notice and this permission notice shall be included in
328all copies or substantial portions of the Software.
329
330THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
331OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
332FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
333THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
334LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
335FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
336DEALINGS IN THE SOFTWARE.
337
338=cut