1
|
|
|
<?php |
2
|
|
|
namespace morphos\Russian; |
3
|
|
|
|
4
|
|
|
/** |
5
|
|
|
* Rules are from http://morpher.ru/Russian/Noun.aspx |
6
|
|
|
*/ |
7
|
|
|
class Plurality extends \morphos\Plurality implements Cases { |
8
|
|
|
use RussianLanguage; |
9
|
|
|
|
10
|
|
|
const ONE = 1; |
11
|
|
|
const TWO_FOUR = 2; |
12
|
|
|
const FIVE_OTHER = 3; |
13
|
|
|
|
14
|
|
|
protected $neuterExceptions = array( |
15
|
|
|
'поле', |
16
|
|
|
'море', |
17
|
|
|
); |
18
|
|
|
|
19
|
|
|
static public function pluralize($word, $count, $animateness = false) { |
20
|
|
|
static $dec, $plu; |
21
|
|
|
if ($dec === null) $dec = new GeneralDeclension(); |
22
|
|
|
if ($plu === null) $plu = new self(); |
23
|
|
|
|
24
|
|
|
switch (self::getNumeralForm($count)) { |
25
|
|
|
case self::ONE: |
26
|
|
|
return $word; |
27
|
|
|
case self::TWO_FOUR: |
28
|
|
|
return $dec->getForm($word, self::RODIT_2, $animateness); |
29
|
|
|
case self::FIVE_OTHER: |
30
|
|
|
$forms = $plu->getForms($word, $animateness); |
31
|
|
|
return $forms[self::RODIT_2]; |
32
|
|
|
} |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
static public function getNumeralForm($count) { |
36
|
|
|
$ending = $count % 10; |
37
|
|
|
if (($count > 20 && $ending == 1) || $count == 1) |
38
|
|
|
return self::ONE; |
39
|
|
|
else if (($count > 20 && in_array($ending, range(2, 4))) || in_array($count, range(2, 4))) |
40
|
|
|
return self::TWO_FOUR; |
41
|
|
|
else |
42
|
|
|
return self::FIVE_OTHER; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
public function getForm($word, $form, $animateness = false) { |
46
|
|
|
$forms = $this->getForms($word, $animateness); |
47
|
|
|
return $forms[$form]; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
public function getForms($word, $animateness = false) { |
51
|
|
|
$word = lower($word); |
52
|
|
|
$prefix = slice($word, 0, -1); |
53
|
|
|
$last = slice($word, -1); |
54
|
|
|
|
55
|
|
|
if (($declension = GeneralDeclension::getDeclension($word)) == GeneralDeclension::FIRST_DECLENSION) { |
56
|
|
|
$soft_last = $last == 'й' || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && self::isConsonant(slice($word, -2, -1))); |
57
|
|
|
$prefix = GeneralDeclension::getPrefixOfFirstDeclension($word, $last); |
58
|
|
|
} else if ($declension == GeneralDeclension::SECOND_DECLENSION) { |
59
|
|
|
$soft_last = $this->checkLastConsonantSoftness($word); |
60
|
|
|
} else { |
61
|
|
|
$soft_last = false; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
$forms = array(); |
65
|
|
|
|
66
|
|
|
if ($last == 'ч' || slice($word, -2) == 'чь' || ($this->isVowel($last) && slice($word, -2, -1) == 'ч')) |
67
|
|
|
$forms[Cases::IMENIT_1] = $prefix.'и'; |
68
|
|
View Code Duplication |
else if ($last == 'н') |
|
|
|
|
69
|
|
|
$forms[Cases::IMENIT_1] = $prefix.'ы'; |
70
|
|
|
else |
71
|
|
|
$forms[Cases::IMENIT_1] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а'); |
72
|
|
|
|
73
|
|
|
|
74
|
|
|
// RODIT_2 |
75
|
|
|
if (in_array($last, array('о', 'е'))) { |
76
|
|
|
// exceptions |
77
|
|
|
if (in_array($word, $this->neuterExceptions)) |
78
|
|
|
$forms[Cases::RODIT_2] = $prefix.'ей'; |
79
|
|
|
else |
80
|
|
|
$forms[Cases::RODIT_2] = $prefix; |
81
|
|
|
} |
82
|
|
|
else if (slice($word, -2) == 'ка') { |
83
|
|
|
// чашка, вилка, ложка, тарелка |
84
|
|
|
if (slice($word, -3, -2) == 'л') $forms[Cases::RODIT_2] = slice($word, 0, -2).'ок'; |
85
|
|
|
else $forms[Cases::RODIT_2] = slice($word, 0, -2).'ек'; |
86
|
|
|
} |
87
|
|
|
else if (in_array($last, array('а'))) // обида, ябеда |
88
|
|
|
$forms[Cases::RODIT_2] = $prefix; |
89
|
|
|
else if (in_array($last, array('я'))) // молния |
90
|
|
|
$forms[Cases::RODIT_2] = $prefix.'й'; |
91
|
|
|
else if (RussianLanguage::isHissingConsonant($last) || ($soft_last && $last != 'й') || slice($word, -2) == 'чь') |
92
|
|
|
$forms[Cases::RODIT_2] = $prefix.'ей'; |
93
|
|
|
else if ($last == 'й') |
94
|
|
|
$forms[Cases::RODIT_2] = $prefix.'ев'; |
95
|
|
|
else // (self::isConsonant($last) && !RussianLanguage::isHissingConsonant($last)) |
96
|
|
|
$forms[Cases::RODIT_2] = $prefix.'ов'; |
97
|
|
|
|
98
|
|
|
// DAT_3 |
99
|
|
|
$forms[Cases::DAT_3] = $this->chooseVowelAfterConsonant($last, $soft_last && slice($word, -2, -1) != 'ч', $prefix.'ям', $prefix.'ам'); |
100
|
|
|
|
101
|
|
|
// VINIT_4 |
102
|
|
|
$forms[Cases::VINIT_4] = GeneralDeclension::getVinitCaseByAnimateness($forms, $animateness); |
103
|
|
|
|
104
|
|
|
// TVORIT_5 |
105
|
|
|
// my personal rule |
106
|
|
|
if ($last == 'ь' && $declension == GeneralDeclension::THIRD_DECLENSION && slice($word, -2) != 'чь') { |
107
|
|
|
$forms[Cases::TVORIT_5] = $prefix.'ми'; |
108
|
|
|
} else { |
109
|
|
|
$forms[Cases::TVORIT_5] = $this->chooseVowelAfterConsonant($last, $soft_last && slice($word, -2, -1) != 'ч', $prefix.'ями', $prefix.'ами'); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
// PREDLOJ_6 |
113
|
|
|
$forms[Cases::PREDLOJ_6] = $this->chooseVowelAfterConsonant($last, $soft_last && slice($word, -2, -1) != 'ч', $prefix.'ях', $prefix.'ах'); |
114
|
|
|
$forms[Cases::PREDLOJ_6] = $this->choosePrepositionByFirstLetter($forms[Cases::PREDLOJ_6], 'об', 'о').' '.$forms[Cases::PREDLOJ_6]; |
115
|
|
|
return $forms; |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
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.