├── src ├── Facades │ └── DeclensionNoun.php ├── Core.php ├── DeclensionNounsServiceProvider.php ├── Dictionary.php └── DeclensionNouns.php ├── composer.json ├── LICENSE ├── README.md └── config └── declension-nouns.php /src/Facades/DeclensionNoun.php: -------------------------------------------------------------------------------- 1 | 4 && $numberPositive % 100 < 20 24 | ? 2 25 | : $cases[min($numberPositive % 10, 5)]; 26 | 27 | return $options[$key]; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/DeclensionNounsServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('DeclensionNoun', function () { 26 | return new DeclensionNouns( 27 | new Dictionary(), 28 | new Core() 29 | ); 30 | }); 31 | } 32 | 33 | /** 34 | * Bootstrap services. 35 | * 36 | * @return void 37 | */ 38 | public function boot(): void 39 | { 40 | $this->publishes([ 41 | __DIR__ . '/../config/declension-nouns.php' => config_path('declension-nouns.php'), 42 | ], 'config'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Igor Drandin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Dictionary.php: -------------------------------------------------------------------------------- 1 | words = $this->wordsBase(); 23 | } 24 | 25 | /** 26 | * Массив с вариантами написания для количества 1, 2 и 5 27 | * 28 | * @return array 29 | */ 30 | private function wordsBase(): array 31 | { 32 | $this->words = config('declension-nouns.dictionary', []); 33 | return $this->words; 34 | } 35 | 36 | 37 | /** 38 | * Ищем слово в словаре, если его нет, 39 | * то возвращает массив [$word, $word, $word] 40 | * 41 | * @param string $word 42 | * @return array 43 | */ 44 | public function find(string $word): array 45 | { 46 | $wordList = $this->getWords(); 47 | 48 | $words[] = $word; 49 | $words[] = $wordList[$word][0] ?? $word; 50 | $words[] = $wordList[$word][1] ?? $word; 51 | 52 | return $words; 53 | } 54 | 55 | 56 | /** 57 | * Возвращает все слова в словаре 58 | * 59 | * @return array 60 | */ 61 | public function getWords(): array 62 | { 63 | return $this->words; 64 | } 65 | 66 | /** 67 | * Добавляет одно слово в словарь 68 | * 69 | * $item = ['слово' => ['слова', 'слов']] 70 | * 71 | * @param array $item 72 | * @return $this 73 | */ 74 | public function addWord(array $item): self 75 | { 76 | if (!empty($item) && count($item) === 1) { 77 | $this->words = array_merge($this->words, $item); 78 | } 79 | 80 | return $this; 81 | } 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/DeclensionNouns.php: -------------------------------------------------------------------------------- 1 | dictionary = $dictionary; 29 | $this->core = $core; 30 | } 31 | 32 | 33 | /** 34 | * @param int $number 35 | * @param string $word 36 | * @return string 37 | */ 38 | public function make(int $number, string $word): string 39 | { 40 | return $number . ' '. $this->plural($number, $word); 41 | } 42 | 43 | /** 44 | * @param int $number 45 | * @param string $word 46 | * @return string 47 | */ 48 | public function makeOnlyWord(int $number, string $word): string 49 | { 50 | return $this->plural($number, $word); 51 | } 52 | 53 | /** 54 | * @param string $wordOne 55 | * @param string $wordThee 56 | * @param string $wordFive 57 | * @return $this 58 | */ 59 | public function addToDictionary(string $wordOne, string $wordThee, string $wordFive): self 60 | { 61 | if ($wordOne === '') { 62 | return $this; 63 | } 64 | 65 | $this->dictionary->addWord([$wordOne => [$wordThee, $wordFive]]); 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * @return array 72 | */ 73 | public function getWords(): array 74 | { 75 | return $this->dictionary->getWords(); 76 | } 77 | 78 | /** 79 | * @param string $word 80 | * @return string 81 | */ 82 | private function cleaner(string $word): string 83 | { 84 | return mb_strtolower(trim($word)); 85 | } 86 | 87 | /** 88 | * @param int $number 89 | * @param string $word 90 | * @return string 91 | */ 92 | private function plural(int $number, string $word): string 93 | { 94 | /** 95 | * Ищем слово $word в словаре, 96 | * формируем массив вариантов $options 97 | */ 98 | $options = $this->dictionary->find($this->cleaner($word)); 99 | 100 | return $this->core->plural($number, $options); 101 | 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Declension Nouns. Склонение существительных 2 | 3 | ### Приходилось ли вам встречать на различный сайтах подобные сообщения? 4 | 5 | «Вы заказали 23 товаров», «В корзину добавлено 1 позиций», «Последний раз вы заходили на наш сайт 5 года назад», «Вы посмотрели 2 страниц каталога», «1 результатов поиска», «2 посетителей сайта просматривают данное предложение», «Вы заказали 5 смартфона». 6 | 7 | Причина таких ошибок вовсе не опечатки разработчиков. Дело в том, что проблему склонения существительных часто откладывают на потом. Некорректно написаное слово не мешает приложению работать и решать основную бизнес-задачу. 8 | 9 | Данный пакет позволит вам решить проблему склонения существительных в вашем web-проекте. 10 | 11 | ## Установка 12 | 13 | composer require drandin/declension-nouns 14 | 15 | ##### После установки пакета необходимо последовательно выполнить следующие действия: 16 | 17 | Добавить в файл конфигурации приложения _**config/app.php**_ сервис-провайдер. Строку указанную ниже следует внести в массив **'providers'**. 18 | 19 | ```php 20 | Drandin\DeclensionNouns\DeclensionNounsServiceProvider::class, 21 | ``` 22 | 23 | Для того, чтобы иметь доступ к функциям пакета через фасад следует добавить в _**config/app.php**_ в массив 'aliases' строку: 24 | 25 | ```php 26 | 'DeclensionNoun' => \Drandin\DeclensionNouns\Facades\DeclensionNoun::class, 27 | ``` 28 | 29 | Затем, выполните в консоли команду, которая скопирует файл конфигурации _**declension-nouns.php**_ в каталог _**config**_ вашего приложения: 30 | 31 | ``` 32 | php artisan vendor:publish --tag=config 33 | ``` 34 | 35 | После этого, запустите в консоли команду, которая обновит кэш конфигурации: 36 | 37 | ``` 38 | php artisan config:cache 39 | ``` 40 | 41 | Если у вас установлен пакет ide-helper, то пересоздайте файл **_ide_helper.php**: 42 | 43 | ``` 44 | php artisan ide-helper:generate 45 | ``` 46 | 47 | ## Использование 48 | 49 | Предположим у нас есть число 4, и оно означат количество лет: 50 | 51 | ```php 52 | DeclensionNoun::make(4, "год"); 53 | ``` 54 | 55 | В результате получим: 56 | 57 | 4 года 58 | 59 | Предположим у нас есть число 5, и оно означат количество страниц: 60 | 61 | ```php 62 | DeclensionNoun::make(5, "страница"); 63 | ``` 64 | 65 | В результате получим: 66 | 67 | 5 страниц 68 | 69 | Предположим у нас есть число -304, и оно означат сумму в рублях, которую должен клиент: 70 | 71 | ```php 72 | DeclensionNoun::make(-304, "рубль"); 73 | ``` 74 | 75 | В результате получим: 76 | 77 | -304 рубля 78 | 79 | Предположим у нас есть число 5, и оно означат возраст ребёнка: 80 | 81 | ```php 82 | DeclensionNoun::make(5, "год"); 83 | ``` 84 | 85 | В результате получим: 86 | 87 | 5 лет 88 | 89 | То же самое, что и в примере выше, но мы получаем только слово: 90 | 91 | ```php 92 | DeclensionNoun::makeOnlyWord(5, "год"); 93 | ``` 94 | 95 | В результате получим: 96 | 97 | лет 98 | 99 | ### Как добавить слово в словарь? 100 | 101 | Есть 2 способа. 102 | 103 | **Способ № 1** 104 | 105 | Внести в массив файла конфигурации _**config/declension-nouns.php**_ новое слово: 106 | 107 | ```php 108 | 109 | 'телефон' => [ 110 | 'телефона', 111 | 'телефонов' 112 | ], 113 | 114 | ``` 115 | 116 | Ключ элемента массива — единственное число, первый элемент - существительное, которое описывает 2 телефона, второй элемент - существительное, которое описывает 5 телефонов. 117 | 118 | **Способ № 2** 119 | 120 | Добавить слово в момент выполнения: 121 | 122 | ```php 123 | DeclensionNoun::addToDictionary('телефон', 'телефона', 'телефонов'); 124 | ``` 125 | 126 | Первым аргументом нудно передать единственное число, затем существительное, которое описывает 2 телефона, и третий аргумент - существительное, которое описывает 5 телефонов. 127 | 128 | ## Лицензия (License) 129 | 130 | [MIT license](LICENSE) 131 | -------------------------------------------------------------------------------- /config/declension-nouns.php: -------------------------------------------------------------------------------- 1 | [ 6 | 7 | 'штука' => [ 8 | 'штуки', 9 | 'штук' 10 | ], 11 | 'элемент' => [ 12 | 'элемента', 13 | 'элементов' 14 | ], 15 | 'строка' => [ 16 | 'строки', 17 | 'строк' 18 | ], 19 | 'рубль' => [ 20 | 'рубля', 21 | 'рублей' 22 | ], 23 | 'доллар' => [ 24 | 'доллара', 25 | 'долларов' 26 | ], 27 | 'часть' => [ 28 | 'части', 29 | 'частей' 30 | ], 31 | 'литр' => [ 32 | 'литра', 33 | 'литров' 34 | ], 35 | 'метр' => [ 36 | 'метра', 37 | 'метров' 38 | ], 39 | 'заказ' => [ 40 | 'заказа', 41 | 'заказов' 42 | ], 43 | 'заявка' => [ 44 | 'заявки', 45 | 'заявок' 46 | ], 47 | 'запрос' => [ 48 | 'запроса', 49 | 'запросов' 50 | ], 51 | 'комментарий' => [ 52 | 'комментария', 53 | 'комментариев' 54 | ], 55 | 'позиция' => [ 56 | 'позиции', 57 | 'позиций' 58 | ], 59 | 'клиент' => [ 60 | 'клиента', 61 | 'клиентов' 62 | ], 63 | 'товар' => [ 64 | 'товара', 65 | 'товаров' 66 | ], 67 | 'ремонт' => [ 68 | 'ремонта', 69 | 'ремонтов' 70 | ], 71 | 'год' => [ 72 | 'года', 73 | 'лет' 74 | ], 75 | 'месяц' => [ 76 | 'месяца', 77 | 'месяцев' 78 | ], 79 | 'день' => [ 80 | 'дня', 81 | 'дней' 82 | ], 83 | 'час' => [ 84 | 'часа', 85 | 'часов' 86 | ], 87 | 'минута' => [ 88 | 'минуты', 89 | 'минут' 90 | ], 91 | 'секунда' => [ 92 | 'секунды', 93 | 'секунд' 94 | ], 95 | 'копейка' => [ 96 | 'копейки', 97 | 'копеек' 98 | ], 99 | 'цент' => [ 100 | 'цента', 101 | 'центов' 102 | ], 103 | 'лист' => [ 104 | 'листа', 105 | 'листов' 106 | ], 107 | 'страница' => [ 108 | 'страницы', 109 | 'страниц' 110 | ], 111 | 'файл' => [ 112 | 'файла', 113 | 'файлов' 114 | ], 115 | 'папка' => [ 116 | 'папки', 117 | 'папок' 118 | ], 119 | 'каталог' => [ 120 | 'каталога', 121 | 'каталогов' 122 | ], 123 | 'документ' => [ 124 | 'документа', 125 | 'документов' 126 | ], 127 | 'сайт' => [ 128 | 'сайта', 129 | 'сайтов' 130 | ], 131 | 'диск' => [ 132 | 'диска', 133 | 'дисков' 134 | ], 135 | 'человек' => [ 136 | 'человека', 137 | 'человек' 138 | ], 139 | 'сотрудник' => [ 140 | 'сотрудника', 141 | 'сотрудников' 142 | ], 143 | 'менеджер' => [ 144 | 'менеджера', 145 | 'менеджеров' 146 | ], 147 | 'задача' => [ 148 | 'задачи', 149 | 'задач' 150 | ], 151 | 'инструмент' => [ 152 | 'инструмента', 153 | 'инструментов' 154 | ], 155 | 'система' => [ 156 | 'системы', 157 | 'систем' 158 | ], 159 | 'экземпляр' => [ 160 | 'экземпляра', 161 | 'экземпляров' 162 | ], 163 | 'линия' => [ 164 | 'линии', 165 | 'линий' 166 | ], 167 | 'обращение' => [ 168 | 'обращения', 169 | 'обращений' 170 | ], 171 | 'проблема' => [ 172 | 'проблемы', 173 | 'проблем' 174 | ], 175 | 176 | ] 177 | 178 | 179 | ]; 180 | --------------------------------------------------------------------------------