File Coverage

blib/lib/Lingua/Text.pm
Criterion Covered Total %
statement 85 87 97.7
branch 39 44 88.6
condition 15 18 83.3
subroutine 14 14 100.0
pod 4 4 100.0
total 157 167 94.0


line stmt bran cond sub pod time code
1             package Lingua::Text;
2              
3 5     5   967557 use strict;
  5         12  
  5         200  
4 5     5   22 use warnings;
  5         8  
  5         217  
5              
6 5     5   21 use Carp;
  5         6  
  5         299  
7 5     5   2649 use HTML::Entities;
  5         30665  
  5         505  
8 5     5   2493 use Params::Get;
  5         60397  
  5         283  
9 5     5   42 use Scalar::Util;
  5         10  
  5         429  
10              
11             # TODO: Investigate Locale::Maketext
12              
13             =head1 NAME
14              
15             Lingua::Text - Class to contain text many different languages
16              
17             =head1 VERSION
18              
19             Version 0.07
20              
21             =cut
22              
23             our $VERSION = '0.07';
24              
25             use overload (
26             # '==' => \&equal,
27             # '!=' => \¬_equal,
28             '""' => \&as_string,
29 62     62   193 bool => sub { 1 },
30 5         45 fallback => 1 # So that boolean tests don't cause as_string to be called
31 5     5   28 );
  5         8  
32              
33             =head1 SYNOPSIS
34              
35             Hold many texts in one object,
36             thereby encapsulating internationalized text.
37              
38             use Lingua::Text;
39              
40             my $str = Lingua::Text->new();
41              
42             $str->fr('Bonjour Tout le Monde');
43             $str->en('Hello, World');
44              
45             $ENV{'LANG'} = 'en_GB';
46             print "$str\n"; # Prints Hello, World
47             $ENV{'LANG'} = 'fr_FR';
48             print "$str\n"; # Prints Bonjour Tout le Monde
49             $ENV{'LANG'} = 'de_DE';
50             print "$str\n"; # Prints nothing
51              
52             my $text = Lingua::Text->new('hello'); # Initialises the 'current' language
53              
54             =cut
55              
56             =head1 METHODS
57              
58             =head2 new
59              
60             Create a Lingua::Text object.
61              
62             use Lingua::Text;
63              
64             my $str = Lingua::Text->new({ 'en' => 'Here', 'fr' => 'Ici' });
65              
66             Accepts various input formats, e.g. HASH or reference to a HASH.
67             Clones existing objects with or without modifications.
68             Uses Carp::carp to log warnings for incorrect usage or potential mistakes.
69              
70             =cut
71              
72             sub new {
73 15     15 1 854305 my $class = shift;
74              
75             # Handle hash or hashref arguments
76 15         31 my %args;
77 15 100 100     164 if((scalar(@_) == 1) && (!ref($_[0])) && (my $lang = _get_language())) {
    100 66        
78 2         4 $args{$lang} = $_[0];
79             } elsif(my $params = Params::Get::get_params(undef, @_)) {
80 6         205 %args = %{$params};
  6         32  
81             }
82              
83 15 100       776 if(!defined($class)) {
    100          
84 1 50       3 if((scalar keys %args) > 0) {
85             # Using Lingua::Text->new(), not Lingua::Text::new()
86 0         0 carp(__PACKAGE__, ' use ->new() not ::new() to instantiate');
87 0         0 return;
88             }
89              
90             # FIXME: this only works when no arguments are given
91 1         2 $class = __PACKAGE__;
92             } elsif(Scalar::Util::blessed($class)) {
93             # If $class is an object, clone it with new arguments
94 2 100       24 if(scalar(%args)) {
95 1         3 return bless { texts => {%{$class->{'texts'}}, %args} }, ref($class);
  1         13  
96             }
97 1         2 return bless { %{$class} }, ref($class);
  1         6  
98             }
99              
100             # Return the blessed object
101 13 100       38 if(scalar(%args)) {
102 7         44 return bless { texts => \%args }, $class;
103             }
104              
105 6         39 return bless { }, $class;
106             }
107              
108             =head2 set
109              
110             Sets a text in a language.
111              
112             $str->set({ text => 'House', lang => 'en' });
113              
114             Autoload will do this for you as
115              
116             $str->en('House');
117              
118             =cut
119              
120             sub set
121             {
122 9     9 1 5725 my $self = shift;
123 9         41 my $params = Params::Get::get_params('text', @_);
124              
125 8   100     342 my $lang = $params->{'lang'} || $self->_get_language();
126 8 100       28 if(!defined($lang)) {
127 1         6 Carp::carp(__PACKAGE__, ': usage: set(text => text, lang => $language)');
128 1         518 return;
129             }
130              
131 7         17 my $text = $params->{'text'};
132 7 100       21 if(!defined($text)) {
133 2         7 Carp::carp(__PACKAGE__, ': usage: set(text => text, lang => $language)');
134 2         1103 return;
135             }
136              
137 5         16 $self->{'texts'}->{$lang} = $text;
138              
139 5         46 return $self;
140             }
141              
142             # https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
143             # https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html
144             sub _get_language
145             {
146 23 100 100 23   132 if(($ENV{'LANGUAGE'}) && ($ENV{'LANGUAGE'} =~ /^([a-z]{2})/i)) {
147 2         14 return lc($1);
148             }
149              
150 21         57 foreach my $variable('LC_ALL', 'LC_MESSAGES', 'LANG') {
151 47         106 my $val = $ENV{$variable};
152 47 100       133 next unless(defined($val));
153              
154 19 50       118 if($val =~ /^([a-z]{2})/i) {
155 19         172 return lc($1);
156             }
157             }
158              
159             # if(defined($ENV{'LANG'}) && (($ENV{'LANG'} =~ /^C\./) || ($ENV{'LANG'} eq 'C'))) {
160             # return 'en';
161             # }
162 2 50 33     10 return 'en' if defined $ENV{'LANG'} && $ENV{'LANG'} =~ /^C(\.|$)/;
163 2         11 return; # undef
164             }
165              
166             =head2 as_string
167              
168             Returns the text in the language requested in the parameter.
169             If that parameter is not given, the system language is used.
170              
171             my $text = Lingua::Text->new(en => 'boat', fr => 'bateau');
172             print $text->as_string(), "\n";
173             print $text->as_string('fr'), "\n";
174             print $text->as_string({ lang => 'en' }), "\n";
175              
176             =cut
177              
178             sub as_string {
179 24     24 1 4269 my $self = shift;
180 24         51 my %params;
181              
182 24 100       122 if(ref($_[0]) eq 'HASH') {
    100          
183 3         8 %params = %{$_[0]};
  3         13  
184             } elsif((scalar(@_) % 2) == 0) {
185 19 100       73 if(defined($_[0])) {
186 1         6 %params = @_;
187             }
188             } else {
189 2         8 $params{'lang'} = shift;
190             }
191              
192 24 100 100     138 if(my $lang = ($params{'lang'} || $self->_get_language())) {
193 23         224 return $self->{'texts'}->{$lang};
194             }
195 1         4 Carp::carp(__PACKAGE__, ': usage: as_string(lang => $language)');
196             }
197              
198             =head2 encode
199              
200             =encoding utf-8
201              
202             Turns the encapsulated texts into HTML entities
203              
204             my $text = Lingua::Text->new(en => 'study', fr => 'étude')->encode();
205             print $text->fr(), "\n"; # Prints étude
206              
207             =cut
208              
209             sub encode {
210 3     3 1 1020 my $self = shift;
211              
212 3         8 while(my($k, $v) = each(%{$self->{'texts'}})) {
  9         245  
213 6 100       35 utf8::decode($v) unless utf8::is_utf8($v); # Only decode if not already UTF-8
214 6         22 $self->{'texts'}->{$k} = HTML::Entities::encode_entities($v);
215             }
216 3         20 return $self;
217             }
218              
219             sub AUTOLOAD
220             {
221 31     31   5920 our $AUTOLOAD;
222 31 50       258 my $self = shift or return;
223              
224             # Extract the key name from the AUTOLOAD variable
225 31         251 my ($key) = $AUTOLOAD =~ /::(\w+)$/;
226              
227             # Skip if called on destruction
228 31 100       624 return if $key eq 'DESTROY';
229              
230             # Ensure the key is called on the correct package object
231 16 50       76 return unless ref($self) eq __PACKAGE__;
232              
233 16 100       53 if(my $value = shift) {
234             # Set the requested language ($key) to the given text ($value)
235 5         17 $self->{'texts'}->{$key} = $value;
236             }
237              
238             # Get the requested language ($key)
239 16         106 return $self->{'texts'}->{$key};
240             }
241              
242             =head1 AUTHOR
243              
244             Nigel Horne, C<< >>
245              
246             =head1 BUGS
247              
248             There's no decode() (yet) so you'll have to be extra careful to avoid
249             double encoding.
250              
251             =head1 SEE ALSO
252              
253             =head1 SUPPORT
254              
255             This module is provided as-is without any warranty.
256              
257             You can find documentation for this module with the perldoc command.
258              
259             perldoc Lingua::Text
260              
261             You can also look for information at:
262              
263             =over 4
264              
265             =item * MetaCPAN
266              
267             L
268              
269             =item * RT: CPAN's request tracker
270              
271             L
272              
273             =item * CPANTS
274              
275             L
276              
277             =item * CPAN Testers' Matrix
278              
279             L
280              
281             =item * CPAN Testers Dependencies
282              
283             L
284              
285             =back
286              
287             =head1 LICENCE AND COPYRIGHT
288              
289             Copyright 2021-2025 Nigel Horne.
290              
291             This program is released under the following licence: GPL2 for personal use on
292             a single computer.
293             All other users (for example Commercial, Charity, Educational, Government)
294             must apply in writing for a licence for use from Nigel Horne at ``.
295              
296             =cut
297              
298             1;