Skip to content

Commit

Permalink
Add geographical declension to russian
Browse files Browse the repository at this point in the history
  • Loading branch information
wapmorgan committed Apr 8, 2017
1 parent 770e607 commit a4ce3ee
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/Russian/GeographicalNamesDeclension.php
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];
}
}
44 changes: 44 additions & 0 deletions tests/Russian/GeographicalNamesDeclensionTest.php
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('оаэ'),
);
}
}

0 comments on commit a4ce3ee

Please sign in to comment.