File Coverage

blib/lib/Lingua/Conjunction.pm
Criterion Covered Total %
statement 41 56 73.2
branch 16 24 66.6
condition 4 6 66.6
subroutine 10 13 76.9
pod 7 7 100.0
total 78 106 73.5


line stmt bran cond sub pod time code
1             package Lingua::Conjunction;
2              
3             # ABSTRACT: Convert lists into simple linguistic conjunctions
4              
5 3     3   140619 use 5.008;
  3         13  
6              
7 3     3   18 use strict;
  3         6  
  3         64  
8 3     3   14 use warnings;
  3         7  
  3         143  
9              
10 3     3   28 use Carp qw/ croak /;
  3         10  
  3         192  
11 3     3   22 use Exporter qw/ import /;
  3         6  
  3         3546  
12              
13             our @EXPORT = qw( conjunction );
14             our @EXPORT_OK = @EXPORT;
15              
16             =head1 NAME
17              
18             Lingua::Conjunction - Convert lists into simple linguistic conjunctions
19              
20             =head1 VERSION
21              
22             Version 2.5
23              
24             =cut
25              
26             our $VERSION = '2.5';
27              
28             =head1 SYNOPSIS
29              
30             Language-specific definitions.
31             These may not be correct, and certainly they are not complete.
32             E-mail corrections and additions to C<< >>,
33             and an updated version will be released.
34              
35             =cut
36              
37             # Format of %language is as follows:
38             # Two-letter ISO language codes... see L from CPAN for more details.
39             # sep = item separator (usually a comma)
40             # alt = alternate ("phrase") separator
41             # pen = 1 = use penultimate separator/0 = don't use penultimate
42             # (i.e., "Jack, Jill and Spot" vs. "Jack, Jill, and Spot")
43             # con = conjunction ("and")
44             # dis = disjunction ("or"), well, grammatically still a "conjunction"...
45              
46             my %language = (
47             'af' => { sep => ',', alt => ';', pen => 1, con => 'en', dis => 'of' },
48             'da' => { sep => ',', alt => ';', pen => 1, con => 'og', dis => 'eller' },
49             'de' => { sep => ',', alt => ';', pen => 1, con => 'und', dis => 'oder' },
50             'en' => { sep => ',', alt => ';', pen => 1, con => 'and', dis => 'or' },
51             'es' => { sep => ',', alt => ';', pen => 1, con => 'y', dis => 'o' },
52             'fi' => { sep => ',', alt => ';', pen => 1, con => 'ja', dis => 'tai' },
53             'fr' => { sep => ',', alt => ';', pen => 0, con => 'et', dis => 'ou' },
54             'id' => { sep => ',', alt => ';', pen => 1, con => 'dan', dis => 'atau' },
55             'it' => { sep => ',', alt => ';', pen => 1, con => 'e', dis => 'o' },
56             'la' => { sep => ',', alt => ';', pen => 1, con => 'et', dis => 'vel' },
57             'nl' => { sep => ',', alt => ';', pen => 1, con => 'en', dis => 'of' },
58             'no' => { sep => ',', alt => ';', pen => 0, con => 'og', dis => 'eller' },
59             'pt' => { sep => ',', alt => ';', pen => 1, con => 'e', dis => 'ou' },
60             'sw' => { sep => ',', alt => ';', pen => 1, con => 'na', dis => 'au' },
61             );
62              
63             # Conjunction types. TODO: Someday we'll add either..or, neither..nor
64             my %types = (
65             'and' => 'con',
66             'or' => 'dis'
67             );
68              
69             my %punct = %{ $language{_get_language()} };
70             my $list_type = $types{'and'};
71              
72             =head1 SUBROUTINES/METHODS
73              
74             =head2 conjunction
75              
76             Lingua::Conjunction exports a single subroutine, C, that
77             converts a list into a properly punctuated text string.
78              
79             You can cause C to use the connectives of other languages, by
80             calling the appropriate subroutine:
81              
82             Lingua::Conjunction->lang('en'); # use 'and'
83             Lingua::Conjunction->lang('es'); # use 'y'
84             Lingua::Conjunction->lang(); # Tries to determine your language, otherwise falls back to 'en'
85              
86             Supported languages in this version are
87             Afrikaans,
88             Danish,
89             Dutch,
90             English,
91             French,
92             German,
93             Indonesian,
94             Italian,
95             Latin,
96             Norwegian,
97             Portuguese,
98             Spanish,
99             and Swahili.
100              
101             You can also set connectives individually:
102              
103             Lingua::Conjunction->separator("...");
104             Lingua::Conjunction->separator_phrase("--");
105             Lingua::Conjunction->connector_type("or");
106              
107             # emits "Jack... Jill... or Spot"
108             $name_list = conjunction('Jack', 'Jill', 'Spot');
109              
110             =cut
111              
112             sub conjunction {
113             # See List::ToHumanString
114 17   100 17 1 1279 my @list = grep defined && /\S/, @_;
115              
116 17 50       45 return if(scalar(@list) == 0);
117 17 100       45 return $list[0] if(scalar(@list) == 1);
118 15 100       80 return join(" $punct{$list_type} ", @list) if(scalar(@list) == 2);
119              
120 6 100       18 if ( $punct{pen} ) {
121 4 100       67 return join "$punct{sep} ", @list[ 0 .. $#list - 1 ],
122             "$punct{$list_type} $list[-1]",
123             unless grep /$punct{sep}/, @list;
124 1         10 return join "$punct{alt} ", @list[ 0 .. $#list - 1 ],
125             "$punct{$list_type} $list[-1]";
126             } else {
127 2 50       50 return join "$punct{sep} ", @list[ 0 .. $#list - 2 ],
128             "$list[-2] $punct{$list_type} $list[-1]",
129             unless grep /$punct{sep}/, @list;
130 0         0 return join "$punct{alt} ", @list[ 0 .. $#list - 2 ],
131             "$list[-2] $punct{$list_type} $list[-1]";
132             }
133             }
134              
135             =head2 separator
136              
137             Sets the separator, usually ',' or ';'.
138              
139             Lingua::Conjunction->separator(',');
140              
141             Returns the previous value.
142              
143             =cut
144              
145             sub separator {
146 0     0 1 0 my $rc = $punct{'sep'};
147              
148 0         0 $punct{sep} = $_[1];
149 0         0 return $rc;
150             }
151              
152             =head2 separator_phrase
153              
154             Sets the alternate (phrase) separator.
155              
156             Lingua::Conjunction->separator_phrase(';');
157              
158             The C is used whenever the separator already appears in
159             an item of the list. For example:
160              
161             # emits "Doe, a deer; Ray; and Me"
162             $name_list = conjunction('Doe, a deer', 'Ray', 'Me');
163              
164             Returns the previous value;
165              
166             =cut
167              
168             sub separator_phrase {
169 0     0 1 0 my $rc = $punct{'alt'};
170              
171 0         0 $punct{alt} = $_[1];
172 0         0 return $rc;
173             }
174              
175             =head2 penultimate
176              
177             Enables/disables penultimate separator.
178              
179             You may use the C routine to disable the separator after the
180             next to last item.
181             In English, The Oxford Comma is a highly debated issue.
182              
183             # emits "Jack, Jill and Spot"
184             Lingua::Conjunction->penultimate(0);
185             $name_list = conjunction('Jack', 'Jill', 'Spot');
186              
187             The original author was told that the penultimate comma is not standard for some
188             languages, such as Norwegian.
189             Hence the defaults set in the C<%languages>.
190              
191             Lingua::Conjunction->penultimate(0);
192              
193             Returns the previous value.
194              
195             =cut
196              
197             sub penultimate {
198 1     1 1 2 my $rc = $punct{'pen'};
199              
200 1         2 $punct{pen} = $_[1];
201 1         2 return $rc;
202             }
203              
204             =head2 connector_type
205              
206             Use "and" or "or", with appropriate translation for the current language
207              
208             Lingua::Conjunction->connector_type('and');
209              
210             =cut
211              
212             sub connector_type {
213 1 50   1 1 5 if($types{ $_[1]}) {
214 1         2 $list_type = $types{ $_[1] };
215             } else {
216 0         0 croak "Undefined connector type \`$_[1]\'"
217             }
218              
219 1         2 return $list_type;
220             }
221              
222             =head2 connector
223              
224             Sets the for the current connector_type.
225              
226             Lingua::Conjunction->connector(SCALAR)
227              
228             Returns the previous value.
229              
230             =cut
231              
232             sub connector {
233 0     0 1 0 my $rc = $punct{'list_type'};
234              
235 0         0 $punct{$list_type} = $_[1];
236 0         0 return $rc;
237             }
238              
239             =head2 lang
240              
241             Sets the language to use.
242             If no arguments are given,
243             it tries its best to guess.
244              
245             Lingua::Conjunction->lang('de'); # Changes the language to German
246              
247             =cut
248              
249             sub lang {
250 1   33 1 1 5 my $language = $_[1] || _get_language();
251              
252 1 50       11 if(defined($language{$language})) {
253 1         16 %punct = %{ $language{$language} };
  1         10  
254             } else {
255 0         0 croak "Undefined language \`$language\'";
256             }
257              
258 1         3 return $language;
259             }
260              
261             # https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html
262             # https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html
263             sub _get_language
264             {
265 3 100   3   13 if($ENV{'LANGUAGE'}) {
266 2         8 foreach my $l(split/:/, $ENV{'LANGUAGE'}) {
267 2 50       6 if($language{$l}) {
268 2         14 return $l;
269             }
270             }
271             }
272 1         3 foreach my $variable('LC_ALL', 'LC_MESSAGES', 'LANG') {
273 3         6 my $val = $ENV{$variable};
274 3 50       8 next unless(defined($val));
275              
276 0         0 $val = substr($val, 0, 2);
277 0 0       0 if($language{$val}) {
278 0         0 return $val;
279             }
280             }
281 1         7 return 'en';
282             }
283              
284             =head1 AUTHORS
285              
286             =over 4
287              
288             =item *
289              
290             Robert Rothenberg
291              
292             =item *
293              
294             Damian Conway
295              
296             =back
297              
298             =head1 MAINTAINER
299              
300             2021-present Maintained by Nigel Horne, C<< >>
301              
302             =head1 CONTRIBUTORS
303              
304             =for stopwords Ade Ishs Mohammad S Anwar Nigel Horne
305              
306             =over 4
307              
308             =item *
309              
310             Ade Ishs
311              
312             =item *
313              
314             Mohammad S Anwar
315              
316             =item *
317              
318             Nigel Horne C<< >>
319              
320             =back
321              
322             =head1 SEE ALSO
323              
324             C, C
325              
326             The I in Section 4.2 has a similar subroutine called
327             C. The differences are that
328             1. this routine handles multiple languages and
329             2. being a module, you do not have to add the subroutine to a script every time you need it.
330              
331             =head1 SOURCE
332              
333             The development version is on github at L
334             and may be cloned from L
335              
336             =head1 SUPPORT
337              
338             You can find documentation for this module with the perldoc command.
339              
340             perldoc Lingua::Conjunction
341              
342             You can also look for information at:
343              
344             =over 4
345              
346             =item * MetaCPAN
347              
348             L
349              
350             =item * RT: CPAN's request tracker
351              
352             L
353              
354             =item * CPANTS
355              
356             L
357              
358             =item * CPAN Testers' Matrix
359              
360             L
361              
362             =item * CPAN Ratings
363              
364             L
365              
366             =item * CPAN Testers Dependencies
367              
368             L
369              
370             =back
371              
372             =head1 BUGS AND LIMITATIONS
373              
374             Please report any bugs or feature requests on the bugtracker website
375             L
376              
377             When submitting a bug or request, please include a test-file or a
378             patch to an existing test-file that illustrates the bug or desired
379             feature.
380              
381             =head1 LICENSE AND COPYRIGHT
382              
383             This software is Copyright (c) 1999-2020 by Robert Rothenberg.
384              
385             This is free software, licensed under:
386              
387             The Artistic License 2.0 (GPL Compatible)
388              
389             =cut
390              
391             1;