From e78b7385697d05f94e36794d2c5c2cd49cfcbe67 Mon Sep 17 00:00:00 2001 From: wapmorgan Date: Fri, 7 Sep 2018 22:56:59 +0300 Subject: [PATCH] Add run-away vowels list of Geographical names and fix some problems in geographical inflection --- src/Russian/GeographicalNamesInflection.php | 111 +++++++++++------- src/Russian/NounPluralization.php | 12 +- .../GeographicalNamesInflectionTest.php | 8 ++ 3 files changed, 79 insertions(+), 52 deletions(-) diff --git a/src/Russian/GeographicalNamesInflection.php b/src/Russian/GeographicalNamesInflection.php index 8f94924..4476249 100644 --- a/src/Russian/GeographicalNamesInflection.php +++ b/src/Russian/GeographicalNamesInflection.php @@ -31,6 +31,24 @@ class GeographicalNamesInflection extends \morphos\BaseInflection implements Cas 'санкт', ]; + protected static $runawayVowelsExceptions = [ + 'торжо*к', + 'волоче*к', + 'оре*л', + ]; + + /** + * @return array|bool + */ + protected static function getRunAwayVowelsList() + { + $runawayVowelsNormalized = []; + foreach (self::$runawayVowelsExceptions as $word) { + $runawayVowelsNormalized[str_replace('*', null, $word)] = S::indexOf($word, '*') - 1; + } + return $runawayVowelsNormalized; + } + /** * Проверяет, склоняемо ли название * @param string $name Название @@ -128,24 +146,24 @@ public static function getCases($name) case 'ий': $prefix = S::name(S::slice($name, 0, -2)); return [ - self::IMENIT => $prefix . 'ий', - self::RODIT => $prefix . (self::isVelarConsonant(S::slice($name, -3, -2)) ? 'ого' : 'его'), - self::DAT => $prefix . (self::isVelarConsonant(S::slice($name, -3, -2)) ? 'ому' : 'ему'), - self::VINIT => $prefix . 'ий', - self::TVORIT => $prefix . 'им', - self::PREDLOJ => $prefix . (self::chooseEndingBySonority($prefix, 'ем', 'ом')), + self::IMENIT => $prefix.'ий', + self::RODIT => $prefix.(self::isVelarConsonant(S::slice($name, -3, -2)) ? 'ого' : 'его'), + self::DAT => $prefix.(self::isVelarConsonant(S::slice($name, -3, -2)) ? 'ому' : 'ему'), + self::VINIT => $prefix.'ий', + self::TVORIT => $prefix.'им', + self::PREDLOJ => $prefix.(self::chooseEndingBySonority($prefix, 'ем', 'ом')), ]; // Ростовская case 'ая': $prefix = S::name(S::slice($name, 0, -2)); return [ - self::IMENIT => $prefix . 'ая', - self::RODIT => $prefix . 'ой', - self::DAT => $prefix . 'ой', - self::VINIT => $prefix . 'ую', - self::TVORIT => $prefix . 'ой', - self::PREDLOJ => $prefix . 'ой', + self::IMENIT => $prefix.'ая', + self::RODIT => $prefix.'ой', + self::DAT => $prefix.'ой', + self::VINIT => $prefix.'ую', + self::TVORIT => $prefix.'ой', + self::PREDLOJ => $prefix.'ой', ]; // Россошь @@ -160,40 +178,39 @@ public static function getCases($name) self::PREDLOJ => $prefix.'и', ]; - // Орёл - case 'ел': - $prefix = S::name(S::slice($name, 0, -2)); - return [ - self::IMENIT => $prefix.'ёл', - self::RODIT => $prefix.'ла', - self::DAT => $prefix.'лу', - self::VINIT => $prefix.'ла', - self::TVORIT => $prefix.'лом', - self::PREDLOJ => $prefix.'ле', - ]; - // Грозный, Благодарный case 'ый': $prefix = S::name(S::slice($name, 0, -2)); return [ - self::IMENIT => $prefix . 'ый', - self::RODIT => $prefix . 'ого', - self::DAT => $prefix . 'ому', - self::VINIT => $prefix . 'ый', - self::TVORIT => $prefix . 'ым', - self::PREDLOJ => $prefix . 'ом', + self::IMENIT => $prefix.'ый', + self::RODIT => $prefix.'ого', + self::DAT => $prefix.'ому', + self::VINIT => $prefix.'ый', + self::TVORIT => $prefix.'ым', + self::PREDLOJ => $prefix.'ом', ]; // Ставрополь, Ярославль, Электросталь case 'ль': $prefix = S::name(S::slice($name, 0, -1)); + + if ($name === 'электросталь') + return [ + self::IMENIT => $prefix.'ь', + self::RODIT => $prefix.'и', + self::DAT => $prefix.'и', + self::VINIT => $prefix.'ь', + self::TVORIT => $prefix.'ью', + self::PREDLOJ => $prefix.'и', + ]; + return [ - self::IMENIT => $prefix . 'ь', - self::RODIT => $prefix . 'я', - self::DAT => $prefix . 'ю', - self::VINIT => $prefix . 'ь', - self::TVORIT => $prefix . 'ем', - self::PREDLOJ => $prefix.(S::name(S::slice($name == 'электросталь', -1)) ? 'и' : 'е'), + self::IMENIT => $prefix.'ь', + self::RODIT => $prefix.'я', + self::DAT => $prefix.'ю', + self::VINIT => $prefix.'ь', + self::TVORIT => $prefix.'ем', + self::PREDLOJ => $prefix.'е', ]; // Тверь, Анадырь @@ -294,7 +311,6 @@ public static function getCases($name) ]; } - switch (S::slice($name, -1)) { case 'р': // Бор @@ -307,12 +323,13 @@ public static function getCases($name) self::TVORIT => $prefix.'ром', self::PREDLOJ => $prefix.'ру', ]; + case 'ы': // Чебоксары, Шахты $prefix = S::name(S::slice($name, 0, -1)); return [ self::IMENIT => $prefix.'ы', - self::RODIT => $prefix.(self::isVelarConsonant(S::slice($name, -1, -1)) == ' '), + self::RODIT => $prefix, self::DAT => $prefix.'ам', self::VINIT => $prefix.'ы', self::TVORIT => $prefix.'ами', @@ -357,13 +374,22 @@ public static function getCases($name) } if (self::isConsonant(S::slice($name, -1)) && !in_array($name, self::$ovAbnormalExceptions, true)) { + $runaway_vowels_list = static::getRunAwayVowelsList(); + + // if run-away vowel in name + if (isset($runaway_vowels_list[$name])) { + $runaway_vowel_offset = $runaway_vowels_list[$name]; + $prefix = S::name(S::slice($name, 0, $runaway_vowel_offset) . S::slice($name, $runaway_vowel_offset + 1)); + } else { + $prefix = S::name($name); + } + // Париж, Валаам, Киев - $prefix = S::name($name); return [ - self::IMENIT => $prefix, + self::IMENIT => S::name($name), self::RODIT => $prefix . 'а', self::DAT => $prefix . 'у', - self::VINIT => $prefix, + self::VINIT => S::name($name), self::TVORIT => $prefix . (self::isVelarConsonant(S::slice($name, -2, -1)) ? 'ем' : 'ом'), self::PREDLOJ => $prefix . 'е', ]; @@ -375,9 +401,6 @@ public static function getCases($name) // ово, ёво, ... if (in_array(S::slice($name, -3, -1), $suffixes, true)) { $prefix = S::name(S::slice($name, 0, -1)); - return [ - self::PREDLOJ => $prefix.'о', - ]; } // ов, её, ... elseif (in_array(S::slice($name, -2), $suffixes, true)) { diff --git a/src/Russian/NounPluralization.php b/src/Russian/NounPluralization.php index 9e95b91..2519436 100644 --- a/src/Russian/NounPluralization.php +++ b/src/Russian/NounPluralization.php @@ -42,20 +42,16 @@ class NounPluralization extends \morphos\NounPluralization implements Cases 'глото*к', ]; - protected static $runawayVowelsNormalized = false; - /** * @return array|bool */ protected static function getRunAwayVowelsList() { - if (self::$runawayVowelsNormalized === false) { - self::$runawayVowelsNormalized = []; - foreach (self::$runawayVowelsExceptions as $word) { - self::$runawayVowelsNormalized[str_replace('*', null, $word)] = S::indexOf($word, '*') - 1; - } + $runawayVowelsNormalized = []; + foreach (self::$runawayVowelsExceptions as $word) { + $runawayVowelsNormalized[str_replace('*', null, $word)] = S::indexOf($word, '*') - 1; } - return self::$runawayVowelsNormalized; + return $runawayVowelsNormalized; } /** diff --git a/tests/Russian/GeographicalNamesInflectionTest.php b/tests/Russian/GeographicalNamesInflectionTest.php index 6f8c281..2c73df0 100644 --- a/tests/Russian/GeographicalNamesInflectionTest.php +++ b/tests/Russian/GeographicalNamesInflectionTest.php @@ -48,6 +48,13 @@ public function wordsProvider() ['Киров', 'Кирова', 'Кирову', 'Киров', 'Кировом', 'Кирове'], ['Керчь', 'Керчи', 'Керчи', 'Керчь', 'Керчью', 'Керчи'], ['Анадырь', 'Анадыря', 'Анадырю', 'Анадырь', 'Анадырем', 'Анадыре'], + ['Россошь', 'Россоши', 'Россоши', 'Россошь', 'Россошью', 'Россоши'], + ['Чебоксары', 'Чебоксар', 'Чебоксарам', 'Чебоксары', 'Чебоксарами', 'Чебоксарах'], + + // с беглой гласной + ['Торжок', 'Торжка', 'Торжку', 'Торжок', 'Торжком', 'Торжке'], + ['Вышний Волочек', 'Вышнего Волочка', 'Вышнему Волочку', 'Вышний Волочек', 'Вышним Волочком', 'Вышнем Волочке'], + ['Орел', 'Орла', 'Орлу', 'Орел', 'Орлом', 'Орле'], // сложные названия ['Санкт-Петербург', 'Санкт-Петербурга', 'Санкт-Петербургу', 'Санкт-Петербург', 'Санкт-Петербургом', 'Санкт-Петербурге'], @@ -72,6 +79,7 @@ public function wordsProvider() // исключения ['Великие Луки', 'Великих Лук', 'Великим Лукам', 'Великие Луки', 'Великими Луками', 'Великих Луках'], + ['Электросталь', 'Электростали', 'Электростали', 'Электросталь', 'Электросталью', 'Электростали'], // неизменяемые названия ['США', 'США', 'США', 'США', 'США', 'США'],