1
|
|
|
<?php |
2
|
|
|
namespace morphos\Russian; |
3
|
|
|
|
4
|
|
|
use morphos\S; |
5
|
|
|
|
6
|
|
|
/** |
7
|
|
|
* Rules are from http://morpher.ru/Russian/Noun.aspx |
8
|
|
|
*/ |
9
|
|
|
class Plurality extends \morphos\Plurality implements Cases { |
10
|
|
|
use RussianLanguage, CasesHelper; |
11
|
|
|
|
12
|
|
|
const ONE = 1; |
13
|
|
|
const TWO_FOUR = 2; |
14
|
|
|
const FIVE_OTHER = 3; |
15
|
|
|
|
16
|
|
|
static protected $neuterExceptions = array( |
17
|
|
|
'поле', |
18
|
|
|
'море', |
19
|
|
|
); |
20
|
|
|
|
21
|
|
|
static protected $immutableWords = array( |
22
|
|
|
'евро', |
23
|
|
|
'пенни', |
24
|
|
|
); |
25
|
|
|
|
26
|
|
|
static public function pluralize($word, $count = 2, $animateness = false) { |
27
|
|
|
switch (self::getNumeralForm($count)) { |
28
|
|
|
case self::ONE: |
29
|
|
|
return $word; |
30
|
|
|
case self::TWO_FOUR: |
31
|
|
|
return GeneralDeclension::getCase($word, self::RODIT, $animateness); |
32
|
|
|
case self::FIVE_OTHER: |
33
|
|
|
return Plurality::getCase($word, self::RODIT, $animateness); |
34
|
|
|
} |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
static public function getNumeralForm($count) { |
38
|
|
|
$ending = $count % 10; |
39
|
|
|
if (($count > 20 && $ending == 1) || $count == 1) |
40
|
|
|
return self::ONE; |
41
|
|
|
else if (($count > 20 && in_array($ending, range(2, 4))) || in_array($count, range(2, 4))) |
42
|
|
|
return self::TWO_FOUR; |
43
|
|
|
else |
44
|
|
|
return self::FIVE_OTHER; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
static public function getCase($word, $case, $animateness = false) { |
48
|
|
|
$case = self::canonizeCase($case); |
49
|
|
|
$forms = self::getCases($word, $animateness); |
50
|
|
|
return $forms[$case]; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
static public function getCases($word, $animateness = false) { |
54
|
|
|
$word = S::lower($word); |
55
|
|
|
$prefix = S::slice($word, 0, -1); |
56
|
|
|
$last = S::slice($word, -1); |
57
|
|
|
|
58
|
|
|
if (in_array($word, self::$immutableWords)) { |
59
|
|
|
return array( |
60
|
|
|
self::IMENIT => $word, |
61
|
|
|
self::RODIT => $word, |
62
|
|
|
self::DAT => $word, |
63
|
|
|
self::VINIT => $word, |
64
|
|
|
self::TVORIT => $word, |
65
|
|
|
self::PREDLOJ => self::choosePrepositionByFirstLetter($word, 'об', 'о').' '.$word, |
66
|
|
|
); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
if (($declension = GeneralDeclension::getDeclension($word)) == GeneralDeclension::SECOND_DECLENSION) { |
70
|
|
|
$soft_last = $last == 'й' || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && (self::isConsonant(S::slice($word, -2, -1)) || S::slice($word, -2, -1) == 'и')); |
71
|
|
|
$prefix = GeneralDeclension::getPrefixOfFirstDeclension($word, $last); |
72
|
|
|
} else if ($declension == GeneralDeclension::FIRST_DECLENSION) { |
73
|
|
|
$soft_last = self::checkLastConsonantSoftness($word); |
74
|
|
|
} else { |
75
|
|
|
$soft_last = false; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
$forms = array(); |
79
|
|
|
|
80
|
|
|
if ($last == 'ч' || S::slice($word, -2) == 'чь' || (self::isVowel($last) && in_array(S::slice($word, -2, -1), array('ч', 'к')))) // before ч, чь, ч+vowel, к+vowel |
81
|
|
|
$forms[Cases::IMENIT] = $prefix.'и'; |
82
|
|
View Code Duplication |
else if ($last == 'н') |
|
|
|
|
83
|
|
|
$forms[Cases::IMENIT] = $prefix.'ы'; |
84
|
|
|
else |
85
|
|
|
$forms[Cases::IMENIT] = self::chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а'); |
86
|
|
|
|
87
|
|
|
|
88
|
|
|
// RODIT |
89
|
|
|
if (in_array($last, array('о', 'е'))) { |
90
|
|
|
// exceptions |
91
|
|
|
if (in_array($word, self::$neuterExceptions)) |
92
|
|
|
$forms[Cases::RODIT] = $prefix.'ей'; |
93
|
|
View Code Duplication |
else if (S::slice($word, -2, -1) == 'и') |
|
|
|
|
94
|
|
|
$forms[Cases::RODIT] = $prefix.'й'; |
95
|
|
|
else |
96
|
|
|
$forms[Cases::RODIT] = $prefix; |
97
|
|
|
} |
98
|
|
|
else if (S::slice($word, -2) == 'ка') { // words ending with -ка: чашка, вилка, ложка, тарелка, копейка, батарейка |
99
|
|
|
if (S::slice($word, -3, -2) == 'л') $forms[Cases::RODIT] = S::slice($word, 0, -2).'ок'; |
100
|
|
|
else if (S::slice($word, -3, -2) == 'й') $forms[Cases::RODIT] = S::slice($word, 0, -3).'ек'; |
101
|
|
|
else $forms[Cases::RODIT] = S::slice($word, 0, -2).'ек'; |
102
|
|
|
} |
103
|
|
|
else if (in_array($last, array('а'))) // обида, ябеда |
104
|
|
|
$forms[Cases::RODIT] = $prefix; |
105
|
|
|
else if (in_array($last, array('я'))) // молния |
106
|
|
|
$forms[Cases::RODIT] = $prefix.'й'; |
107
|
|
|
else if (RussianLanguage::isHissingConsonant($last) || ($soft_last && $last != 'й') || S::slice($word, -2) == 'чь') |
108
|
|
|
$forms[Cases::RODIT] = $prefix.'ей'; |
109
|
|
View Code Duplication |
else if ($last == 'й') |
|
|
|
|
110
|
|
|
$forms[Cases::RODIT] = $prefix.'ев'; |
111
|
|
|
else // (self::isConsonant($last) && !RussianLanguage::isHissingConsonant($last)) |
112
|
|
|
$forms[Cases::RODIT] = $prefix.'ов'; |
113
|
|
|
|
114
|
|
|
// DAT |
115
|
|
|
$forms[Cases::DAT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ям', $prefix.'ам'); |
116
|
|
|
|
117
|
|
|
// VINIT |
118
|
|
|
$forms[Cases::VINIT] = GeneralDeclension::getVinitCaseByAnimateness($forms, $animateness); |
119
|
|
|
|
120
|
|
|
// TVORIT |
121
|
|
|
// my personal rule |
122
|
|
|
if ($last == 'ь' && $declension == GeneralDeclension::THIRD_DECLENSION && S::slice($word, -2) != 'чь') { |
123
|
|
|
$forms[Cases::TVORIT] = $prefix.'ми'; |
124
|
|
|
} else { |
125
|
|
|
$forms[Cases::TVORIT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ями', $prefix.'ами'); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// PREDLOJ |
129
|
|
|
$forms[Cases::PREDLOJ] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ях', $prefix.'ах'); |
130
|
|
|
$forms[Cases::PREDLOJ] = self::choosePrepositionByFirstLetter($forms[Cases::PREDLOJ], 'об', 'о').' '.$forms[Cases::PREDLOJ]; |
131
|
|
|
return $forms; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.