├── .gitignore ├── README.md ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plural-ru 2 | 3 | 4 | Склонение русских существительных и глаголов! 5 | 6 | ### Установка 7 | 8 | ``` 9 | npm install --save plural-ru 10 | ``` 11 | 12 | 13 | Примеры: 14 | 15 | ```js 16 | const plural = require('plural-ru'); 17 | 18 | // склонение существительного 19 | plural(1, 'файл', 'файла', 'файлов'); // файл 20 | plural(2, 'файл', 'файла', 'файлов'); // файла 21 | plural(5, 'файл', 'файла', 'файлов'); // файлов 22 | 23 | // склонение существительного + плейсхолдер 24 | plural(1, '%d файл', '%d файла', '%d файлов'); // 1 файл 25 | plural(2, '%d файл', '%d файла', '%d файлов'); // 2 файла 26 | plural(5, '%d файл', '%d файла', '%d файлов'); // 5 файлов 27 | 28 | // единственное и множественное число (если переданы 2 склонения) 29 | plural(1, 'Оформить товар', 'Оформить товары'); // Оформить товар 30 | plural(21, 'Оформить товар', 'Оформить товары'); // Оформить товары 31 | 32 | // склонение глаголов 💥 33 | plural.verb(1, 'нашлась', 'нашлись', 'нашлось'); // нашлась 34 | plural.verb(2, 'нашлась', 'нашлись', 'нашлось'); // нашлись 35 | plural.verb(5, 'нашлась', 'нашлись', 'нашлось'); // нашлось 36 | ``` 37 | 38 | Сложный пример из жизни: 39 | 40 | ```js 41 | const found = plural.verb( 42 | foundCount, 43 | 'нашлась', 44 | 'нашлись', 45 | 'нашлось' 46 | ); 47 | const ideas = plural( 48 | foundCount, 49 | 'идея', 50 | 'идеи', 51 | 'идей' 52 | ); 53 | 54 | console.log(`${found} ${foundCount} ${ideas} ${forSite}`); 55 | // нашлось 1000000 идей для вашего сайта! 56 | ``` 57 | 58 | 59 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'plural-ru' { 2 | type ThreeForms = [string, string, string]; 3 | type TwoForms = [string, string]; 4 | 5 | const noun: (num: number, ...forms: TwoForms | ThreeForms) => string; 6 | const verb: (num: number, ...forms: ThreeForms) => string; 7 | 8 | export { 9 | noun, 10 | verb, 11 | }; 12 | 13 | export default noun; 14 | } 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var slice = Array.prototype.slice; 4 | 5 | function getPluralNoun(num) { 6 | var forms = slice.call(arguments, 1); 7 | 8 | var str; 9 | 10 | switch (forms.length) { 11 | case 1: 12 | throw new Error('Not enough forms'); 13 | break; 14 | 15 | case 2: 16 | str = num > 1 ? forms[1] : forms[0]; 17 | break; 18 | 19 | default: 20 | str = forms[getNounPluralForm(num)]; 21 | break; 22 | } 23 | 24 | return str.replace(/%d/g, num); 25 | } 26 | 27 | function getPluralVerb(num) { 28 | var forms = slice.call(arguments, 1); 29 | var str = forms[getVerbPluralForm(num)]; 30 | 31 | return str.replace(/%d/g, num); 32 | } 33 | 34 | function getVerbPluralForm(a) { 35 | if (a > 1000000) { 36 | return 2; 37 | } 38 | 39 | if (a > 1000 && a < 1000000 && /000$/.test(a)) { 40 | a /= 1000; 41 | } 42 | 43 | if (a % 10 === 1 && a % 100 !== 11 || /1000$/.test((a).toString())) { 44 | return 0; 45 | } else if (a % 10 >= 2 && a % 10 <= 4 && (a % 100 < 10 || a % 100 >= 20)) { 46 | return 1; 47 | } else { 48 | return 2; 49 | } 50 | } 51 | 52 | function getNounPluralForm(a) { 53 | if (a % 10 === 1 && a % 100 !== 11) { 54 | return 0; 55 | } else if (a % 10 >= 2 && a % 10 <= 4 && (a % 100 < 10 || a % 100 >= 20)) { 56 | return 1; 57 | } else { 58 | return 2; 59 | } 60 | } 61 | 62 | module.exports = getPluralNoun; 63 | module.exports.noun = getPluralNoun; 64 | module.exports.verb = getPluralVerb; 65 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plural-ru", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "assertion-error": { 8 | "version": "1.0.2", 9 | "resolved": "http://storage.mds.yandex.net/get-npm/38095/assertion-error-1.0.2.tgz", 10 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", 11 | "dev": true 12 | }, 13 | "chai": { 14 | "version": "3.5.0", 15 | "resolved": "http://storage.mds.yandex.net/get-npm/38095/chai-3.5.0.tgz", 16 | "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", 17 | "dev": true, 18 | "requires": { 19 | "assertion-error": "1.0.2", 20 | "deep-eql": "0.1.3", 21 | "type-detect": "1.0.0" 22 | } 23 | }, 24 | "commander": { 25 | "version": "2.3.0", 26 | "resolved": "http://storage.mds.yandex.net/get-npm/38095/commander-2.3.0.tgz", 27 | "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", 28 | "dev": true 29 | }, 30 | "debug": { 31 | "version": "2.2.0", 32 | "resolved": "http://storage.mds.yandex.net/get-npm/66641/debug-2.2.0.tgz", 33 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 34 | "dev": true, 35 | "requires": { 36 | "ms": "0.7.1" 37 | } 38 | }, 39 | "deep-eql": { 40 | "version": "0.1.3", 41 | "resolved": "http://storage.mds.yandex.net/get-npm/35308/deep-eql-0.1.3.tgz", 42 | "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", 43 | "dev": true, 44 | "requires": { 45 | "type-detect": "0.1.1" 46 | }, 47 | "dependencies": { 48 | "type-detect": { 49 | "version": "0.1.1", 50 | "resolved": "http://storage.mds.yandex.net/get-npm/66641/type-detect-0.1.1.tgz", 51 | "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", 52 | "dev": true 53 | } 54 | } 55 | }, 56 | "diff": { 57 | "version": "1.4.0", 58 | "resolved": "http://storage.mds.yandex.net/get-npm/35308/diff-1.4.0.tgz", 59 | "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", 60 | "dev": true 61 | }, 62 | "escape-string-regexp": { 63 | "version": "1.0.2", 64 | "resolved": "http://storage.mds.yandex.net/get-npm/69187/escape-string-regexp-1.0.2.tgz", 65 | "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", 66 | "dev": true 67 | }, 68 | "glob": { 69 | "version": "3.2.11", 70 | "resolved": "http://storage.mds.yandex.net/get-npm/45674/glob-3.2.11.tgz", 71 | "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", 72 | "dev": true, 73 | "requires": { 74 | "inherits": "2.0.3", 75 | "minimatch": "0.3.0" 76 | } 77 | }, 78 | "growl": { 79 | "version": "1.9.2", 80 | "resolved": "http://storage.mds.yandex.net/get-npm/38095/growl-1.9.2.tgz", 81 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 82 | "dev": true 83 | }, 84 | "inherits": { 85 | "version": "2.0.3", 86 | "resolved": "http://storage.mds.yandex.net/get-npm/45674/inherits-2.0.3.tgz", 87 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 88 | "dev": true 89 | }, 90 | "jade": { 91 | "version": "0.26.3", 92 | "resolved": "http://storage.mds.yandex.net/get-npm/38095/jade-0.26.3.tgz", 93 | "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", 94 | "dev": true, 95 | "requires": { 96 | "commander": "0.6.1", 97 | "mkdirp": "0.3.0" 98 | }, 99 | "dependencies": { 100 | "commander": { 101 | "version": "0.6.1", 102 | "resolved": "http://storage.mds.yandex.net/get-npm/66641/commander-0.6.1.tgz", 103 | "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", 104 | "dev": true 105 | }, 106 | "mkdirp": { 107 | "version": "0.3.0", 108 | "resolved": "http://storage.mds.yandex.net/get-npm/35308/mkdirp-0.3.0.tgz", 109 | "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", 110 | "dev": true 111 | } 112 | } 113 | }, 114 | "lru-cache": { 115 | "version": "2.7.3", 116 | "resolved": "http://storage.mds.yandex.net/get-npm/45674/lru-cache-2.7.3.tgz", 117 | "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", 118 | "dev": true 119 | }, 120 | "minimatch": { 121 | "version": "0.3.0", 122 | "resolved": "http://storage.mds.yandex.net/get-npm/69187/minimatch-0.3.0.tgz", 123 | "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", 124 | "dev": true, 125 | "requires": { 126 | "lru-cache": "2.7.3", 127 | "sigmund": "1.0.1" 128 | } 129 | }, 130 | "minimist": { 131 | "version": "0.0.8", 132 | "resolved": "http://storage.mds.yandex.net/get-npm/66641/minimist-0.0.8.tgz", 133 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 134 | "dev": true 135 | }, 136 | "mkdirp": { 137 | "version": "0.5.1", 138 | "resolved": "http://storage.mds.yandex.net/get-npm/69187/mkdirp-0.5.1.tgz", 139 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 140 | "dev": true, 141 | "requires": { 142 | "minimist": "0.0.8" 143 | } 144 | }, 145 | "mocha": { 146 | "version": "2.5.3", 147 | "resolved": "http://storage.mds.yandex.net/get-npm/69187/mocha-2.5.3.tgz", 148 | "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", 149 | "dev": true, 150 | "requires": { 151 | "commander": "2.3.0", 152 | "debug": "2.2.0", 153 | "diff": "1.4.0", 154 | "escape-string-regexp": "1.0.2", 155 | "glob": "3.2.11", 156 | "growl": "1.9.2", 157 | "jade": "0.26.3", 158 | "mkdirp": "0.5.1", 159 | "supports-color": "1.2.0", 160 | "to-iso-string": "0.0.2" 161 | } 162 | }, 163 | "ms": { 164 | "version": "0.7.1", 165 | "resolved": "http://storage.mds.yandex.net/get-npm/45674/ms-0.7.1.tgz", 166 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 167 | "dev": true 168 | }, 169 | "sigmund": { 170 | "version": "1.0.1", 171 | "resolved": "http://storage.mds.yandex.net/get-npm/38095/sigmund-1.0.1.tgz", 172 | "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", 173 | "dev": true 174 | }, 175 | "supports-color": { 176 | "version": "1.2.0", 177 | "resolved": "http://storage.mds.yandex.net/get-npm/35308/supports-color-1.2.0.tgz", 178 | "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", 179 | "dev": true 180 | }, 181 | "to-iso-string": { 182 | "version": "0.0.2", 183 | "resolved": "http://storage.mds.yandex.net/get-npm/45674/to-iso-string-0.0.2.tgz", 184 | "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", 185 | "dev": true 186 | }, 187 | "type-detect": { 188 | "version": "1.0.0", 189 | "resolved": "http://storage.mds.yandex.net/get-npm/69187/type-detect-1.0.0.tgz", 190 | "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", 191 | "dev": true 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plural-ru", 3 | "version": "2.0.2", 4 | "description": "Pluralization of russian words in simpliest way", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/megatolya/plural-ru.git" 12 | }, 13 | "keywords": [ 14 | "plural", 15 | "russian", 16 | "words", 17 | "pluralization", 18 | "noun", 19 | "verb" 20 | ], 21 | "author": "Anatoly Ostrovsky ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/megatolya/plural-ru/issues" 25 | }, 26 | "homepage": "https://github.com/megatolya/plural-ru#readme", 27 | "devDependencies": { 28 | "chai": "^3.5.0", 29 | "mocha": "^2.5.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var plural = require('../'); 3 | 4 | describe('plural-ru', function() { 5 | it('Zero', function () { 6 | const one = '%d файл'; 7 | const several = '%d файла'; 8 | const many = '%d файлов'; 9 | 10 | assert.equal('0 файл', plural(0, one, many)); 11 | assert.equal('0 файлов', plural(0, one, several, many)); 12 | }); 13 | 14 | it('Several or one', function() { 15 | const one = 'Скачать файл'; 16 | const many = 'Скачать файлы'; 17 | 18 | assert.equal(one, plural(1, one, many)); 19 | assert.equal(many, plural(2, one, many)); 20 | assert.equal(many, plural(10, one, many)); 21 | assert.equal(many, plural(21, one, many)); 22 | assert.equal(one, plural(1, one, many, many)); 23 | assert.equal(many, plural(2, one, many, many)); 24 | assert.equal(many, plural(3, one, many, many)); 25 | assert.equal(one, plural(21, one, many, many)); 26 | }); 27 | 28 | it('Several and many', function() { 29 | const one = 'Один файл'; 30 | const several = 'Два файла'; 31 | const many = 'Много файлов'; 32 | assert.equal(one, plural(1, one, several, many)); 33 | assert.equal(several, plural(2, one, several, many)); 34 | assert.equal(several, plural(3, one, several, many)); 35 | assert.equal(several, plural(4, one, several, many)); 36 | assert.equal(many, plural(5, one, several, many)); 37 | }); 38 | 39 | it('Several and many (noun)', function() { 40 | const one = 'Один файл'; 41 | const several = 'Два файла'; 42 | const many = 'Много файлов'; 43 | assert.equal(one, plural.noun(1, one, several, many)); 44 | assert.equal(several, plural.noun(2, one, several, many)); 45 | assert.equal(several, plural.noun(3, one, several, many)); 46 | assert.equal(several, plural.noun(4, one, several, many)); 47 | assert.equal(many, plural.noun(5, one, several, many)); 48 | }); 49 | 50 | it('Templating', function () { 51 | const one = '%d файл %d'; 52 | const several = '%d файла %d'; 53 | const many = '%d файлов %d'; 54 | 55 | assert.equal('1 файл 1', plural(1, one, several, many)); 56 | assert.equal('2 файла 2', plural(2, one, several, many)); 57 | assert.equal('3 файла 3', plural(3, one, several, many)); 58 | assert.equal('4 файла 4', plural(4, one, several, many)); 59 | assert.equal('5 файлов 5', plural(5, one, several, many)); 60 | assert.equal('101 файл 101', plural(101, one, several, many)); 61 | assert.equal('110 файлов 110', plural(110, one, several, many)); 62 | assert.equal('1000000 файлов 1000000', plural(1000000, one, several, many)); 63 | }); 64 | 65 | it('Several and many (verb)', function() { 66 | const one = 'Нашлась %d'; 67 | const several = 'Нашлись %d'; 68 | const many = 'Нашлось %d'; 69 | assert.equal('Нашлась 1', plural.verb(1, one, several, many)); 70 | assert.equal('Нашлись 2', plural.verb(2, one, several, many)); 71 | assert.equal('Нашлись 3', plural.verb(3, one, several, many)); 72 | assert.equal('Нашлись 4', plural.verb(4, one, several, many)); 73 | assert.equal('Нашлось 5', plural.verb(5, one, several, many)); 74 | assert.equal('Нашлось 6', plural.verb(6, one, several, many)); 75 | assert.equal('Нашлось 7', plural.verb(7, one, several, many)); 76 | assert.equal('Нашлось 8', plural.verb(8, one, several, many)); 77 | assert.equal('Нашлось 9', plural.verb(9, one, several, many)); 78 | assert.equal('Нашлось 10', plural.verb(10, one, several, many)); 79 | assert.equal('Нашлось 11', plural.verb(11, one, several, many)); 80 | assert.equal('Нашлось 12', plural.verb(12, one, several, many)); 81 | assert.equal('Нашлась 1000', plural.verb(1000, one, several, many)); 82 | assert.equal('Нашлась 21000', plural.verb(21000, one, several, many)); 83 | assert.equal('Нашлись 2000', plural.verb(2000, one, several, many)); 84 | assert.equal('Нашлись 3000', plural.verb(3000, one, several, many)); 85 | assert.equal('Нашлись 4000', plural.verb(4000, one, several, many)); 86 | assert.equal('Нашлось 5000', plural.verb(5000, one, several, many)); 87 | assert.equal('Нашлось 6000', plural.verb(6000, one, several, many)); 88 | assert.equal('Нашлось 7000', plural.verb(7000, one, several, many)); 89 | assert.equal('Нашлось 8000', plural.verb(8000, one, several, many)); 90 | assert.equal('Нашлось 9000', plural.verb(9000, one, several, many)); 91 | assert.equal('Нашлось 10000', plural.verb(10000, one, several, many)); 92 | assert.equal('Нашлось 1000000', plural.verb(1000000, one, several, many)); 93 | assert.equal('Нашлось 1001000', plural.verb(1001000, one, several, many)); 94 | }); 95 | }); 96 | --------------------------------------------------------------------------------