File Coverage

blib/lib/Lingua/String.pm
Criterion Covered Total %
statement 88 90 97.7
branch 41 46 89.1
condition 15 18 83.3
subroutine 14 14 100.0
pod 4 4 100.0
total 162 172 94.1


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