File Coverage

blib/lib/Locale/Babelfish/Phrase/Pluralizer.pm
Criterion Covered Total %
statement 21 21 100.0
branch 3 4 75.0
condition 1 2 50.0
subroutine 8 8 100.0
pod 3 3 100.0
total 36 38 94.7


line stmt bran cond sub pod time code
1             package Locale::Babelfish::Phrase::Pluralizer;
2              
3             # ABSTRACT: Babelfish pluralizer.
4              
5 4     4   25 use utf8;
  4         10  
  4         30  
6 4     4   114 use strict;
  4         15  
  4         68  
7 4     4   37 use warnings;
  4         9  
  4         137  
8              
9 4     4   22 use List::Util qw( first );
  4         8  
  4         7456  
10              
11             our $VERSION = '2.10'; # VERSION
12              
13              
14             my %rules;
15              
16              
17             sub add {
18 84     84 1 163 my ( $locales, $rule ) = @_;
19 84 50       173 $locales = [ $locales ] unless ref($locales);
20              
21 84         656 $rules{$_} = $rule for @$locales;
22             }
23              
24              
25             sub find_rule {
26 15     15 1 122 my ( $locale ) = @_;
27              
28             $locale = ( first {
29 258     258   2286 $locale =~ m/\A\Q$_\E[\-_]/s
30             } keys %rules ) // 'en'
31 15 100 50     199 unless $rules{$locale};
32              
33 15         80 return $rules{$locale};
34             }
35              
36              
37             sub is_int {
38 12     12 1 27 my ( $input ) = @_;
39 12         40 return (0 == $input % 1);
40             }
41              
42             ## PLURALIZATION RULES
43             ## https://github.com/nodeca/babelfish/blob/master/lib/babelfish/pluralizer.js#L51
44              
45             # Azerbaijani, Bambara, Burmese, Chinese, Dzongkha, Georgian, Hungarian, Igbo,
46             # Indonesian, Japanese, Javanese, Kabuverdianu, Kannada, Khmer, Korean,
47             # Koyraboro Senni, Lao, Makonde, Malay, Persian, Root, Sakha, Sango,
48             # Sichuan Yi, Thai, Tibetan, Tonga, Turkish, Vietnamese, Wolof, Yoruba
49              
50             add(['az', 'bm', 'my', 'zh', 'dz', 'ka', 'hu', 'ig',
51             'id', 'ja', 'jv', 'kea', 'kn', 'km', 'ko',
52             'ses', 'lo', 'kde', 'ms', 'fa', 'root', 'sah', 'sg',
53             'ii', 'th', 'bo', 'to', 'tr', 'vi', 'wo', 'yo'
54             ], sub {
55             return 0;
56             });
57              
58             # Manx
59              
60             add(['gv'], sub {
61             my ( $n ) = @_;
62             my ($m10, $m20) = ($n % 10, $n % 20);
63              
64             if (($m10 == 1 || $m10 == 2 || $m20 == 0) && is_int($n)) {
65             return 0;
66             }
67              
68             return 1;
69             });
70              
71              
72             # Central Morocco Tamazight
73              
74             add(['tzm'], sub {
75             my ( $n ) = @_;
76             if ($n == 0 || $n == 1 || (11 <= $n && $n <= 99 && is_int($n))) {
77             return 0;
78             }
79              
80             return 1;
81             });
82              
83              
84             # Macedonian
85              
86             add(['mk'], sub {
87             my ( $n ) = @_;
88             if (($n % 10 == 1) && ($n != 11) && is_int($n)) {
89             return 0;
90             }
91              
92             return 1;
93             });
94              
95              
96             # Akan, Amharic, Bihari, Filipino, Gun, Hindi,
97             # Lingala, Malagasy, Northern Sotho, Tagalog, Tigrinya, Walloon
98              
99             add(['ak', 'am', 'bh', 'fil', 'guw', 'hi',
100             'ln', 'mg', 'nso', 'tl', 'ti', 'wa'
101             ], sub {
102             my ( $n ) = @_;
103             return ($n == 0 || $n == 1) ? 0 : 1;
104             });
105              
106              
107             # Afrikaans, Albanian, Basque, Bemba, Bengali, Bodo, Bulgarian, Catalan,
108             # Cherokee, Chiga, Danish, Divehi, Dutch, English, Esperanto, Estonian, Ewe,
109             # Faroese, Finnish, Friulian, Galician, Ganda, German, Greek, Gujarati, Hausa,
110             # Hawaiian, Hebrew, Icelandic, Italian, Kalaallisut, Kazakh, Kurdish,
111             # Luxembourgish, Malayalam, Marathi, Masai, Mongolian, Nahuatl, Nepali,
112             # Norwegian, Norwegian Bokmål, Norwegian Nynorsk, Nyankole, Oriya, Oromo,
113             # Papiamento, Pashto, Portuguese, Punjabi, Romansh, Saho, Samburu, Soga,
114             # Somali, Spanish, Swahili, Swedish, Swiss German, Syriac, Tamil, Telugu,
115             # Turkmen, Urdu, Walser, Western Frisian, Zulu
116              
117             add(['af', 'sq', 'eu', 'bem', 'bn', 'brx', 'bg', 'ca',
118             'chr', 'cgg', 'da', 'dv', 'nl', 'en', 'eo', 'et', 'ee',
119             'fo', 'fi', 'fur', 'gl', 'lg', 'de', 'el', 'gu', 'ha',
120             'haw', 'he', 'is', 'it', 'kl', 'kk', 'ku',
121             'lb', 'ml', 'mr', 'mas', 'mn', 'nah', 'ne',
122             'no', 'nb', 'nn', 'nyn', 'or', 'om',
123             'pap', 'ps', 'pt', 'pa', 'rm', 'ssy', 'saq', 'xog',
124             'so', 'es', 'sw', 'sv', 'gsw', 'syr', 'ta', 'te',
125             'tk', 'ur', 'wae', 'fy', 'zu'
126             ], sub {
127             my ( $n ) = @_;
128             return (1 == $n) ? 0 : 1;
129             });
130              
131              
132             # Latvian
133              
134             add(['lv'], sub {
135             my ( $n ) = @_;
136             if ($n == 0) {
137             return 0;
138             }
139              
140             if (($n % 10 == 1) && ($n % 100 != 11) && is_int($n)) {
141             return 1;
142             }
143              
144             return 2;
145             });
146              
147              
148             # Colognian
149              
150             add(['ksh'], sub {
151             my ( $n ) = @_;
152             return ($n == 0) ? 0 : (($n == 1) ? 1 : 2);
153             });
154              
155              
156             # Cornish, Inari Sami, Inuktitut, Irish, Lule Sami, Northern Sami,
157             # Sami Language, Skolt Sami, Southern Sami
158              
159             add(['kw', 'smn', 'iu', 'ga', 'smj', 'se',
160             'smi', 'sms', 'sma'
161             ], sub {
162             my ( $n ) = @_;
163             return ($n == 1) ? 0 : (($n == 2) ? 1 : 2);
164             });
165              
166              
167             # Belarusian, Bosnian, Croatian, Russian, Serbian, Serbo-Croatian, Ukrainian
168              
169             add(['be', 'bs', 'hr', 'ru', 'sr', 'sh', 'uk'], sub {
170             my ( $n ) = @_;
171             my ($m10, $m100) = ($n % 10, $n % 100);
172              
173             if (!is_int($n)) {
174             return 3;
175             }
176              
177             # one → n mod 10 is 1 and n mod 100 is not 11;
178             if (1 == $m10 && 11 != $m100) {
179             return 0;
180             }
181              
182             # few → n mod 10 in 2..4 and n mod 100 not in 12..14;
183             if (2 <= $m10 && $m10 <= 4 && !(12 <= $m100 && $m100 <= 14)) {
184             return 1;
185             }
186              
187             ## many → n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
188             ## if (0 === m10 || (5 <= m10 && m10 <= 9) || (11 <= m100 && m100 <= 14)) {
189             ## return 2;
190             ## }
191              
192             ## other
193             ## return 3;
194             return 2;
195             });
196              
197              
198             # Polish
199              
200             add(['pl'], sub {
201             my ( $n ) = @_;
202             my ($m10, $m100) = ($n % 10, $n % 100);
203              
204             if (!is_int($n)) {
205             return 3;
206             }
207              
208             # one → n is 1;
209             if ($n == 1) {
210             return 0;
211             }
212              
213             # few → n mod 10 in 2..4 and n mod 100 not in 12..14;
214             if (2 <= $m10 && $m10 <= 4 && !(12 <= $m100 && $m100 <= 14)) {
215             return 1;
216             }
217              
218             # many → n is not 1 and n mod 10 in 0..1 or
219             # n mod 10 in 5..9 or n mod 100 in 12..14
220             # (all other except partials)
221             return 2;
222             });
223              
224              
225             # Lithuanian
226              
227             add(['lt'], sub {
228             my ( $n ) = @_;
229             my ($m10, $m100) = ($n % 10, $n % 100);
230              
231             if (!is_int($n)) {
232             return 2;
233             }
234              
235             # one → n mod 10 is 1 and n mod 100 not in 11..19
236             if ($m10 == 1 && !(11 <= $m100 && $m100 <= 19)) {
237             return 0;
238             }
239              
240             # few → n mod 10 in 2..9 and n mod 100 not in 11..19
241             if (2 <= $m10 && $m10 <= 9 && !(11 <= $m100 && $m100 <= 19)) {
242             return 1;
243             }
244              
245             # other
246             return 2;
247             });
248              
249              
250             # Tachelhit
251              
252             add(['shi'], sub {
253             my ( $n ) = @_;
254             return (0 <= $n && $n <= 1) ? 0 : ((is_int($n) && 2 <= $n && $n <= 10) ? 1 : 2);
255             });
256              
257              
258             # Moldavian, Romanian
259              
260             add(['mo', 'ro'], sub {
261             my ( $n ) = @_;
262             my $m100 = $n % 100;
263              
264             if (!is_int($n)) {
265             return 2;
266             }
267              
268             # one → n is 1
269             if ($n == 1) {
270             return 0;
271             }
272              
273             # few → n is 0 OR n is not 1 AND n mod 100 in 1..19
274             if ($n == 0 || (1 <= $m100 && $m100 <= 19)) {
275             return 1;
276             }
277              
278             # other
279             return 2;
280             });
281              
282              
283             ## Czech, Slovak
284              
285             add(['cs', 'sk'], sub {
286             my ( $n ) = @_;
287             # one → n is 1
288             if ($n == 1) {
289             return 0;
290             }
291              
292             # few → n in 2..4
293             if ($n == 2 || $n == 3 || $n == 4) {
294             return 1;
295             }
296              
297             # other
298             return 2;
299             });
300              
301              
302              
303             # Slovenian
304              
305             add(['sl'], sub {
306             my ( $n ) = @_;
307             my $m100 = $n % 100;
308              
309             if (!is_int($n)) {
310             return 3;
311             }
312              
313             # one → n mod 100 is 1
314             if ($m100 == 1) {
315             return 0;
316             }
317              
318             # one → n mod 100 is 2
319             if ($m100 == 2) {
320             return 1;
321             }
322              
323             # one → n mod 100 in 3..4
324             if ($m100 == 3 || $m100 == 4) {
325             return 2;
326             }
327              
328             # other
329             return 3;
330             });
331              
332              
333             # Maltese
334              
335             add(['mt'], sub {
336             my ( $n ) = @_;
337             my $m100 = $n % 100;
338              
339             if (!is_int($n)) {
340             return 3;
341             }
342              
343             # one → n is 1
344             if ($n == 1) {
345             return 0;
346             }
347              
348             # few → n is 0 or n mod 100 in 2..10
349             if ($n == 0 || (2 <= $m100 && $m100 <= 10)) {
350             return 1;
351             }
352              
353             # many → n mod 100 in 11..19
354             if (11 <= $m100 && $m100 <= 19) {
355             return 2;
356             }
357              
358             # other
359             return 3;
360             });
361              
362              
363             # Arabic
364              
365             add(['ar'], sub {
366             my ( $n ) = @_;
367             my $m100 = $n % 100;
368              
369             if (!is_int($n)) {
370             return 5;
371             }
372              
373             if ($n == 0) {
374             return 0;
375             }
376             if ($n == 1) {
377             return 1;
378             }
379             if ($n == 2) {
380             return 2;
381             }
382              
383             # few → n mod 100 in 3..10
384             if (3 <= $m100 && $m100 <= 10) {
385             return 3;
386             }
387              
388             # many → n mod 100 in 11..99
389             if (11 <= $m100 && $m100 <= 99) {
390             return 4;
391             }
392              
393             # other
394             return 5;
395             });
396              
397              
398             # Breton, Welsh
399              
400             add(['br', 'cy'], sub {
401             my ( $n ) = @_;
402             if ($n == 0) {
403             return 0;
404             }
405             if ($n == 1) {
406             return 1;
407             }
408             if ($n == 2) {
409             return 2;
410             }
411             if ($n == 3) {
412             return 3;
413             }
414             if ($n == 6) {
415             return 4;
416             }
417              
418             return 5;
419             });
420              
421              
422             ## FRACTIONAL PARTS - SPECIAL CASES
423              
424             # French, Fulah, Kabyle
425              
426             add(['fr', 'ff', 'kab'], sub {
427             my ( $n ) = @_;
428             return (0 <= $n && $n < 2) ? 0 : 1;
429             });
430              
431              
432             # Langi
433              
434             add(['lag'], sub {
435             my ( $n ) = @_;
436             return ($n == 0) ? 0 : ((0 < $n && $n < 2) ? 1 : 2);
437             });
438              
439             1;
440              
441             __END__
442              
443             =pod
444              
445             =encoding UTF-8
446              
447             =head1 NAME
448              
449             Locale::Babelfish::Phrase::Pluralizer - Babelfish pluralizer.
450              
451             =head1 VERSION
452              
453             version 2.10
454              
455             =head1 DESCRIPTION
456              
457             Pluralization implementation.
458              
459             =head1 METHODS
460              
461             =head2 add
462              
463             Adds locale pluralization rule. Should not be called directly.
464              
465             =head2 find_rule
466              
467             find_rule( $locale )
468              
469             Finds locale pluralization rule. It is coderef.
470              
471             =head2 is_int
472              
473             is_int( $input )
474              
475             Check if number is int or float.
476              
477             =head1 AUTHORS
478              
479             =over 4
480              
481             =item *
482              
483             Akzhan Abdulin <akzhan@cpan.org>
484              
485             =item *
486              
487             Igor Mironov <grif@cpan.org>
488              
489             =item *
490              
491             Victor Efimov <efimov@reg.ru>
492              
493             =item *
494              
495             REG.RU LLC
496              
497             =item *
498              
499             Kirill Sysoev <k.sysoev@me.com>
500              
501             =item *
502              
503             Alexandr Tkach <tkach@reg.ru>
504              
505             =back
506              
507             =head1 COPYRIGHT AND LICENSE
508              
509             This software is Copyright (c) 2014 by REG.RU LLC.
510              
511             This is free software, licensed under:
512              
513             The MIT (X11) License
514              
515             =cut