-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add geographical declension to russian
- Loading branch information
Showing
2 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
<?php | ||
namespace morphos\Russian; | ||
|
||
use morphos\S; | ||
|
||
/** | ||
* Rules are from: https://ru.wikipedia.org/wiki/%D0%A1%D0%BA%D0%BB%D0%BE%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B3%D0%B5%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B9_%D0%B2_%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%BE%D0%BC_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B5 | ||
*/ | ||
class GeographicalNamesDeclension extends \morphos\GeneralDeclension implements Cases { | ||
use RussianLanguage, CasesHelper; | ||
|
||
static protected $abbreviations = array( | ||
'сша', | ||
'оаэ', | ||
'ссср', | ||
'юар', | ||
); | ||
|
||
static public function isMutable($name) { | ||
$name = S::lower($name); | ||
// // ends with 'ы' or 'и': plural form | ||
// if (in_array(S::slice($name, -1), array('и', 'ы'))) | ||
// return false; | ||
if (in_array($name, self::$abbreviations)) | ||
return false; | ||
// ends with 'е' or 'о', but not with 'ово/ёво/ево/ино/ыно' | ||
if (in_array(S::slice($name, -1), array('е', 'о')) && !in_array(S::slice($name, -3, -1), array('ов', 'ёв', 'ев', 'ин', 'ын'))) | ||
return false; | ||
return true; | ||
} | ||
|
||
static public function getCases($name) { | ||
$name = S::lower($name); | ||
if (!in_array($name, self::$abbreviations)) { | ||
if (S::slice($name, -1) == 'а') { | ||
// Москва, Рига | ||
$prefix = S::name(S::slice($name, 0, -1)); | ||
return array( | ||
self::IMENIT => $prefix.'а', | ||
self::RODIT => $prefix.(self::isVelarConsonant(S::slice($name, -2, -1)) ? 'и' : 'ы'), | ||
self::DAT => $prefix.'е', | ||
self::VINIT => $prefix.'у', | ||
self::TVORIT => $prefix.'ой', | ||
self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е', | ||
); | ||
} else if (S::slice($name, -1) == 'я') { | ||
// Азия | ||
$prefix = S::name(S::slice($name, 0, -1)); | ||
return array( | ||
self::IMENIT => S::name($name), | ||
self::RODIT => $prefix.'и', | ||
self::DAT => $prefix.'и', | ||
self::VINIT => $prefix.'ю', | ||
self::TVORIT => $prefix.'ей', | ||
self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и', | ||
); | ||
} else if (S::slice($name, -1) == 'й') { | ||
// Ишимбай | ||
$prefix = S::name(S::slice($name, 0, -1)); | ||
return array( | ||
self::IMENIT => $prefix.'й', | ||
self::RODIT => $prefix.'я', | ||
self::DAT => $prefix.'ю', | ||
self::VINIT => $prefix.'й', | ||
self::TVORIT => $prefix.'ем', | ||
self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е', | ||
); | ||
} else if (self::isConsonant(S::slice($name, -1))) { | ||
$prefix = S::name($name); | ||
// Париж, Валаам, Киев | ||
return array( | ||
self::IMENIT => $prefix, | ||
self::RODIT => $prefix.'а', | ||
self::DAT => $prefix.'у', | ||
self::VINIT => $prefix, | ||
self::TVORIT => $prefix.(self::isVelarConsonant(S::slice($name, -2, -1)) ? 'ем' : 'ом'), | ||
self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е', | ||
); | ||
} | ||
|
||
$suffixes = array('ов', 'ёв', 'ев', 'ин', 'ын'); | ||
if ((in_array(S::slice($name, -1), array('е', 'о')) && in_array(S::slice($name, -3, -1), $suffixes)) || in_array(S::slice($name, -2), $suffixes)) { | ||
// ово, ёво, ... | ||
if (in_array(S::slice($name, -3, -1), $suffixes)) { | ||
$prefix = S::name(S::slice($name, 0, -1)); | ||
} | ||
// ов, её, ... | ||
else if (in_array(S::slice($name, -2), $suffixes)) { | ||
$prefix = S::name($name); | ||
} | ||
return array( | ||
self::IMENIT => S::name($name), | ||
self::RODIT => $prefix.'а', | ||
self::DAT => $prefix.'у', | ||
self::VINIT => S::name($name), | ||
self::TVORIT => $prefix.'ем', | ||
self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е', | ||
); | ||
} | ||
} | ||
|
||
// if no rules matches or name is immutable | ||
$name = in_array($name, self::$abbreviations) ? S::upper($name) : S::name($name); | ||
return array_fill_keys(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT), $name) + array(self::PREDLOJ => self::choosePrepositionByFirstLetter($name, 'об', 'о').' '.$name); | ||
} | ||
|
||
static public function getCase($name, $case) { | ||
$case = self::canonizeCase($case); | ||
$forms = self::getCases($name); | ||
return $forms[$case]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
namespace morhos\test\Russian; | ||
require_once __DIR__.'/../../vendor/autoload.php'; | ||
|
||
use morphos\Russian\GeographicalNamesDeclension; | ||
|
||
class GeographicalNamesDeclensionTest extends \PHPUnit_Framework_TestCase { | ||
/** | ||
* @dataProvider wordsProvider | ||
*/ | ||
public function testDeclenation($word, $declenated) { | ||
$this->assertEquals($declenated, array_values(GeographicalNamesDeclension::getCases($word))); | ||
} | ||
|
||
public function wordsProvider() { | ||
return array( | ||
array('москва', array('Москва', 'Москвы', 'Москве', 'Москву', 'Москвой', 'о Москве')), | ||
array('Киев', array('Киев', 'Киева', 'Киеву', 'Киев', 'Киевом', 'о Киеве')), | ||
array('США', array('США', 'США', 'США', 'США', 'США', 'о США')), | ||
array('Санкт-Петербург', array('Санкт-Петербург', 'Санкт-Петербурга', 'Санкт-Петербургу', 'Санкт-Петербург', 'Санкт-Петербургом', 'о Санкт-Петербурге')), | ||
array('Ишимбай', array('Ишимбай', 'Ишимбая', 'Ишимбаю', 'Ишимбай', 'Ишимбаем', 'об Ишимбае')), | ||
array('Африка', array('Африка', 'Африки', 'Африке', 'Африку', 'Африкой', 'об Африке')), | ||
array('Уругвай', array('Уругвай', 'Уругвая', 'Уругваю', 'Уругвай', 'Уругваем', 'об Уругвае')), | ||
array('Европа', array('Европа', 'Европы', 'Европе', 'Европу', 'Европой', 'о Европе')), | ||
array('Азия', array('Азия', 'Азии', 'Азии', 'Азию', 'Азией', 'об Азии')), | ||
array('Рига', array('Рига', 'Риги', 'Риге', 'Ригу', 'Ригой', 'о Риге')), | ||
array('Волга', array('Волга', 'Волги', 'Волге', 'Волгу', 'Волгой', 'о Волге')), | ||
); | ||
} | ||
|
||
/** | ||
* @dataProvider immutableWordsProvider | ||
*/ | ||
public function testImmutableWords($word) { | ||
$this->assertFalse(GeographicalNamesDeclension::isMutable($word)); | ||
} | ||
|
||
public function immutableWordsProvider() { | ||
return array( | ||
array('сша'), | ||
array('оаэ'), | ||
); | ||
} | ||
} |