├── .github └── FUNDING.yml ├── .eslintrc.json ├── LICENSE.md ├── package.json ├── .gitignore ├── README.md ├── src ├── index.ts └── index.spec.ts └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: anotherpit 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:@typescript-eslint/recommended" 6 | ], 7 | "parser": "@typescript-eslint/parser", 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | 3 | Copyright © 2016 anotherpit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "numeralize-ru", 3 | "version": "2.0.0", 4 | "description": "Russian numerals", 5 | "type": "module", 6 | "exports": { 7 | ".": { 8 | "types": "./lib/index.d.ts", 9 | "require": "./lib/index.js", 10 | "import": "./lib/index.js", 11 | "default": "./lib/index.js", 12 | "browser": "./lib/index.browser.js" 13 | } 14 | }, 15 | "main": "lib/index.js", 16 | "files": [ 17 | "lib/", 18 | "LICENSE.md", 19 | "README.md", 20 | "package.json" 21 | ], 22 | "scripts": { 23 | "dev": "ts-mocha src/**.spec.ts -w --watch-files 'src/**.ts'", 24 | "test": "ts-mocha src/**.spec.ts", 25 | "lint": "eslint src/*", 26 | "build": "rm -rf ./lib && tsc --build && esbuild ./lib/index.js --outfile=lib/index.browser.js --format=iife --global-name=numeralize", 27 | "prepublish": "npm run lint && npm run test && npm run build" 28 | }, 29 | "keywords": [ 30 | "russian", 31 | "language", 32 | "number", 33 | "numeral", 34 | "cardinal", 35 | "ordinal", 36 | "collective", 37 | "numeralize", 38 | "ru" 39 | ], 40 | "author": "anotherpit ", 41 | "license": "MIT", 42 | "devDependencies": { 43 | "@types/mocha": "^10.0.2", 44 | "@typescript-eslint/eslint-plugin": "^6.8.0", 45 | "@typescript-eslint/parser": "^6.8.0", 46 | "esbuild": "^0.19.5", 47 | "eslint": "^8.51.0", 48 | "mocha": "^10.2.0", 49 | "ts-mocha": "^10.0.0", 50 | "ts-node": "^10.9.1", 51 | "typescript": "^4.9.5" 52 | }, 53 | "repository": { 54 | "type": "git", 55 | "url": "git+https://github.com/anotherpit/numeralize-ru.git" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 4 | 5 | *.iml 6 | 7 | ## Directory-based project format: 8 | .idea/ 9 | # if you remove the above rule, at least ignore the following: 10 | 11 | # User-specific stuff: 12 | # .idea/workspace.xml 13 | # .idea/tasks.xml 14 | # .idea/dictionaries 15 | 16 | # Sensitive or high-churn files: 17 | # .idea/dataSources.ids 18 | # .idea/dataSources.xml 19 | # .idea/sqlDataSources.xml 20 | # .idea/dynamic.xml 21 | # .idea/uiDesigner.xml 22 | 23 | # Gradle: 24 | # .idea/gradle.xml 25 | # .idea/libraries 26 | 27 | # Mongo Explorer plugin: 28 | # .idea/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.ipr 32 | *.iws 33 | 34 | ## Plugin-specific files: 35 | 36 | # IntelliJ 37 | /out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Crashlytics plugin (for Android Studio and IntelliJ) 46 | com_crashlytics_export_strings.xml 47 | crashlytics.properties 48 | crashlytics-build.properties 49 | ### Node template 50 | # Logs 51 | logs 52 | *.log 53 | npm-debug.log* 54 | 55 | # Runtime data 56 | pids 57 | *.pid 58 | *.seed 59 | 60 | # Directory for instrumented libs generated by jscoverage/JSCover 61 | lib-cov 62 | 63 | # Coverage directory used by tools like istanbul 64 | coverage 65 | 66 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 67 | .grunt 68 | 69 | # node-waf configuration 70 | .lock-wscript 71 | 72 | # Compiled binary addons (http://nodejs.org/api/addons.html) 73 | build/Release 74 | 75 | # Dependency directory 76 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 77 | node_modules 78 | ### OSX template 79 | .DS_Store 80 | .AppleDouble 81 | .LSOverride 82 | 83 | # Icon must end with two \r 84 | Icon 85 | 86 | # Thumbnails 87 | ._* 88 | 89 | # Files that might appear in the root of a volume 90 | .DocumentRevisions-V100 91 | .fseventsd 92 | .Spotlight-V100 93 | .TemporaryItems 94 | .Trashes 95 | .VolumeIcon.icns 96 | 97 | # Directories potentially created on remote AFP share 98 | .AppleDB 99 | .AppleDesktop 100 | Network Trash Folder 101 | Temporary Items 102 | .apdisk 103 | 104 | lib 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # numeralize-ru 2 | 3 | Написание числительных на русском языке с учётом пола и падежа. 4 | 5 | ## Использование в NodeJS 6 | 7 | ``` 8 | npm install --save numeralize-ru 9 | ``` 10 | 11 | ```javascript 12 | import {numeralize, Case, Gender} from "numeralize-ru"; 13 | 14 | console.log(numeralize(5122981121, Gender.Masculine, Case.Nominative)); 15 | ``` 16 | 17 | ## Использование в [дореволюционных](https://caniuse.com/?search=ESM) браузерах 18 | 19 | ```html 20 | 21 | 24 | ``` 25 | 26 | ## API 27 | 28 | ```typescript 29 | /** 30 | * Возвращает числительное, соответствующее числу 31 | * 32 | * @param {number} number Целое число, для которого надо записать числительное 33 | * 34 | * @param {Gender} [gender=Gender.Masculine] Пол: 35 | * - {@link Gender.Masculine} – мужской (по умолчанию); 36 | * - {@link Gender.Feminine} – женский; 37 | * - {@link Gender.Neuter} – средний. 38 | * 39 | * @param {Case} [kase=Case.Nominative] Падеж 40 | * (`case` является ключевым словом, поэтому не может быть использован в качестве имени переменной): 41 | * - {@link Case.Nominative} — именительный (по умолчанию); 42 | * - {@link Case.Genitive} — родительный; 43 | * - {@link Case.Dative} — дательный; 44 | * - {@link Case.Accusative} — винительный; 45 | * - {@link Case.Instrumental} — творительный; 46 | * - {@link Case.Prepositional} — предложный. 47 | * 48 | * @param {boolean} [animate=false] Являются ли перечисляемые предметы одушевлёнными 49 | * (влияет на форму винительного падежа некоторых числительных) 50 | * 51 | * @example 52 | * // мужской род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один' 53 | * numeralize(5122981121); 54 | * 55 | * @example 56 | * // женский род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одна' 57 | * numeralize(5122981121, Gender.Feminine); 58 | * 59 | * @example 60 | * // средний род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одно' 61 | * numeralize(5122981121, Gender.Neuter); 62 | * 63 | * @example 64 | * // мужской род, именительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один' 65 | * numeralize(5122981121, Gender.Masculine, Case.Nominative); 66 | * 67 | * @example 68 | * // мужской род, родительный падеж, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного' 69 | * numeralize(5122981121, Gender.Masculine, Case.Genitive); 70 | * 71 | * @example 72 | * // мужской род, дательный падеж, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному' 73 | * numeralize(5122981121, Gender.Masculine, Case.Dative); 74 | * 75 | * @example 76 | * // мужской род, винительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать один' 77 | * numeralize(5122981121, Gender.Masculine, Case.Accusative); 78 | * 79 | * @example 80 | * // мужской род, винительный падеж, одушевлённые предметы, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одного' 81 | * numeralize(5122981121, Gender.Masculine, Case.Accusative, true); 82 | * 83 | * @example 84 | * // мужской род, творительный падеж, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним' 85 | * numeralize(5122981121, Gender.Masculine, Case.Instrumental); 86 | * 87 | * @example 88 | * // мужской род, творительный падеж, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном' 89 | * numeralize(5122981121, Gender.Masculine, Case.Prepositional); 90 | */ 91 | declare function numeralize( 92 | number: number, 93 | gender: Gender = Gender.Masculine, 94 | kase: Case = Case.Nominative, 95 | animate: boolean = false 96 | ): string; 97 | 98 | /** 99 | * Выбирает нужную форму существительного в зависимости от количества. 100 | * 101 | * @param {number} count Количество 102 | * @param {string} one Форма существительного для одного предмета, например, «рубль»; 103 | * @param {string} two Форма существительного для двух предметов, например, «рубля»; 104 | * @param {string} five Форма существительного для пяти предмета, например, «рублей»; 105 | * 106 | * @example 107 | * // 'рублей' 108 | * pluralize(0, 'рубль', 'рубля', 'рублей'); 109 | * 110 | * @example 111 | * // 'рубль' 112 | * pluralize(1, 'рубль', 'рубля', 'рублей'); 113 | * 114 | * @example 115 | * // 'рубля' 116 | * pluralize(2, 'рубль', 'рубля', 'рублей'); 117 | * 118 | * @example 119 | * // 'рублей' 120 | * pluralize(5, 'рубль', 'рубля', 'рублей'); 121 | * 122 | * @example 123 | * // 'рублей' 124 | * pluralize(11, 'рубль', 'рубля', 'рублей'); 125 | * 126 | * @example 127 | * // 'рубль' 128 | * pluralize(21, 'рубль', 'рубля', 'рублей'); 129 | * 130 | * @example 131 | * // 'рубля' 132 | * pluralize(22, 'рубль', 'рубля', 'рублей'); 133 | */ 134 | declare function pluralize(count: number, one: string, two: string, five: string): string 135 | ``` 136 | 137 | ## Спонсорство 138 | 139 | [Стать первым спонсором проекта](https://github.com/sponsors/anotherpit) 140 | 141 | ## Roadmap 142 | 143 | * Порядковые числительные (ordinal numerals): _первый_, _вторым_, _третьими_ и т.д. 144 | * Собирательные числительные (collective numerals): _трое_, _четверых_, _пятерыми_ и т.д. 145 | * Особые формы единственного и множественного числа: _одни сутки_, _два дня_, _пять суток_ и т.д. 146 | 147 | ## См.также 148 | 149 | + Подробно о склонении числительных в русском языке с примерами: http://numeralonline.ru/ 150 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export enum Gender { 2 | Masculine = 0, 3 | Feminine = 1, 4 | Neuter = 2 5 | } 6 | 7 | export enum Case { 8 | Nominative = 0, 9 | Genitive = 1, 10 | Dative = 2, 11 | Accusative = 3, 12 | Instrumental = 4, 13 | Prepositional = 5 14 | } 15 | 16 | const MINORS = [ 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 | ...['один', 'две', 'три', 'четыр', 'пят', 'шест', 'сем', 'восем', 'девят'].map((prefix) => 57 | ['надцать', 'надцати', 'надцати', 'надцать', 'надцатью', 'надцати'].map((suffix) => 58 | `${prefix}${suffix}` 59 | ) 60 | ) 61 | ] as const; 62 | 63 | const TENS = [ 64 | false, 65 | false, 66 | ['двадцать', 'двадцати', 'двадцати', 'двадцать', 'двадцатью', 'двадцати'], 67 | ['тридцать', 'тридцати', 'тридцати', 'тридцать', 'тридцатью', 'тридцати'], 68 | ['сорок', 'сорока', 'сорока', 'сорок', 'сорока', 'сорока'], 69 | ['пятьдесят', 'пятидесяти', 'пятидесяти', 'пятьдесят', 'пятьюдесятью', 'пятидесяти'], 70 | ['шестьдесят', 'шестидесяти', 'шестидесяти', 'шестьдесят', 'шестьюдесятью', 'шестидесяти'], 71 | ['семьдесят', 'семидесяти', 'семидесяти', 'семьдесят', 'семьюдесятью', 'семидесяти'], 72 | ['восемьдесят', 'восьмидесяти', 'восьмидесяти', 'восемьдесят', 'восемьюдесятью', 'восьмидесяти'], 73 | ['девяносто', 'девяноста', 'девяноста', 'девяносто', 'девяноста', 'девяноста'] 74 | ] as const; 75 | 76 | const HUNDREDS = [ 77 | false, 78 | ['сто', 'ста', 'ста', 'сто', 'ста', 'ста'], 79 | ['двести', 'двухсот', 'двумстам', 'двести', 'двумястами', 'двухстах'], 80 | ['триста', 'трёхсот', 'трёмстам', 'триста', 'тремястами', 'трёхстах'], 81 | ['четыреста', 'четырёхсот', 'четырёмстам', 'четыреста', 'четырьмястами', 'четырёхстах'], 82 | ['пятьсот', 'пятисот', 'пятистам', 'пятьсот', 'пятьюстами', 'пятистах'], 83 | ['шестьсот', 'шестисот', 'шестистам', 'шестьсот', 'шестьюстами', 'шестистах'], 84 | ['семьсот', 'семисот', 'семистам', 'семьсот', 'семьюстами', 'семистах'], 85 | ['восемьсот', 'восьмисот', 'восьмистам', 'восемьсот', 'восемьюстами', 'восьмистах'], 86 | ['девятьсот', 'девятисот', 'девятистам', 'девятьсот', 'девятьюстами', 'девятистах'] 87 | ] as const; 88 | 89 | const LARGES = [ 90 | false, 91 | [ 92 | Gender.Feminine, 93 | ['тысяча', 'тысячи', 'тысяч'], 94 | ['тысячи', 'тысяч', 'тысяч'], 95 | ['тысяче', 'тысячам', 'тысячам'], 96 | ['тысячу', 'тысячи', 'тысяч'], 97 | ['тысячей', 'тысячами', 'тысячами'], 98 | ['тысяче', 'тысячах', 'тысячах'] 99 | ], 100 | ...['миллион', 'миллиард', 'триллион'].map<[Gender, ...string[][]]>((base) => 101 | [ 102 | Gender.Masculine, 103 | ...[ 104 | ['', 'а', 'ов'], 105 | ['а', 'ов', 'ов'], 106 | ['у', 'ам', 'ам'], 107 | ['', 'а', 'ов'], 108 | ['ом', 'ами', 'ами'], 109 | ['е', 'ах', 'ах'] 110 | ].map((kase) => kase.map((suffix) => `${base}${suffix}`)) 111 | ] 112 | ) 113 | ] as const; 114 | 115 | function small(number: number, gender: Gender, kase: Case, animate: boolean): string { 116 | // Zero 117 | if (0 === number) { 118 | return ''; 119 | } 120 | 121 | // Collect chunks 122 | const result: string[] = []; 123 | 124 | // Hundreds 125 | const hundreds = HUNDREDS[Math.floor(number / 100)]; 126 | if (hundreds) { 127 | result.push(hundreds[kase]); 128 | } 129 | 130 | // Tens 131 | const tens = TENS[Math.floor(number % 100 / 10)]; 132 | if (tens) { 133 | result.push(tens[kase]); 134 | } 135 | 136 | // Minors 137 | let minors = number % 100; 138 | if (minors >= MINORS.length) { 139 | minors = number % 10; 140 | } 141 | if (minors) { 142 | let part; 143 | if ( 144 | ((part = MINORS[minors][kase]) && typeof part === 'string') 145 | || ((part = MINORS[minors][kase][gender]) && typeof part === 'string') 146 | || (part = MINORS[minors][kase][gender][animate ? 0 : 1]) 147 | ) { 148 | result.push(part) 149 | } 150 | } 151 | 152 | // Return 153 | return result.join(" "); 154 | } 155 | 156 | /** 157 | * Выбирает нужную форму существительного в зависимости от количества. 158 | * 159 | * @param {number} count Количество 160 | * @param {string} one Форма существительного для одного предмета, например, «рубль»; 161 | * @param {string} two Форма существительного для двух предметов, например, «рубля»; 162 | * @param {string} five Форма существительного для пяти предмета, например, «рублей»; 163 | * 164 | * @example 165 | * // 'рублей' 166 | * pluralize(0, 'рубль', 'рубля', 'рублей'); 167 | * 168 | * @example 169 | * // 'рубль' 170 | * pluralize(1, 'рубль', 'рубля', 'рублей'); 171 | * 172 | * @example 173 | * // 'рубля' 174 | * pluralize(2, 'рубль', 'рубля', 'рублей'); 175 | * 176 | * @example 177 | * // 'рублей' 178 | * pluralize(5, 'рубль', 'рубля', 'рублей'); 179 | * 180 | * @example 181 | * // 'рублей' 182 | * pluralize(11, 'рубль', 'рубля', 'рублей'); 183 | * 184 | * @example 185 | * // 'рубль' 186 | * pluralize(21, 'рубль', 'рубля', 'рублей'); 187 | * 188 | * @example 189 | * // 'рубля' 190 | * pluralize(22, 'рубль', 'рубля', 'рублей'); 191 | */ 192 | export function pluralize(count: number, one: string, two: string, five: string): string { 193 | count = Math.floor(Math.abs(count)) % 100; 194 | if (count > 10 && count < 20) { 195 | return five; 196 | } 197 | count = count % 10; 198 | if (1 === count) { 199 | return one; 200 | } 201 | if (count >= 2 && count <= 4) { 202 | return two; 203 | } 204 | return five; 205 | } 206 | 207 | /** 208 | * Возвращает числительное, соответствующее числу 209 | * 210 | * @param {number} number Целое число, для которого надо записать числительное 211 | * 212 | * @param {Gender} [gender=Gender.Masculine] Пол: 213 | * - {@link Gender.Masculine} – мужской (по умолчанию); 214 | * - {@link Gender.Feminine} – женский; 215 | * - {@link Gender.Neuter} – средний. 216 | * 217 | * @param {Case} [kase=Case.Nominative] Падеж 218 | * (`case` является ключевым словом, поэтому не может быть использован в качестве имени переменной): 219 | * - {@link Case.Nominative} — именительный (по умолчанию); 220 | * - {@link Case.Genitive} — родительный; 221 | * - {@link Case.Dative} — дательный; 222 | * - {@link Case.Accusative} — винительный; 223 | * - {@link Case.Instrumental} — творительный; 224 | * - {@link Case.Prepositional} — предложный. 225 | * 226 | * @param {boolean} [animate=false] Являются ли перечисляемые предметы одушевлёнными 227 | * (влияет на форму винительного падежа некоторых числительных) 228 | * 229 | * @example 230 | * // мужской род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один' 231 | * numeralize(5122981121); 232 | * 233 | * @example 234 | * // женский род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одна' 235 | * numeralize(5122981121, Gender.Feminine); 236 | * 237 | * @example 238 | * // средний род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одно' 239 | * numeralize(5122981121, Gender.Neuter); 240 | * 241 | * @example 242 | * // мужской род, именительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один' 243 | * numeralize(5122981121, Gender.Masculine, Case.Nominative); 244 | * 245 | * @example 246 | * // мужской род, родительный падеж, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного' 247 | * numeralize(5122981121, Gender.Masculine, Case.Genitive); 248 | * 249 | * @example 250 | * // мужской род, дательный падеж, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному' 251 | * numeralize(5122981121, Gender.Masculine, Case.Dative); 252 | * 253 | * @example 254 | * // мужской род, винительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать один' 255 | * numeralize(5122981121, Gender.Masculine, Case.Accusative); 256 | * 257 | * @example 258 | * // мужской род, винительный падеж, одушевлённые предметы, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одного' 259 | * numeralize(5122981121, Gender.Masculine, Case.Accusative, true); 260 | * 261 | * @example 262 | * // мужской род, творительный падеж, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним' 263 | * numeralize(5122981121, Gender.Masculine, Case.Instrumental); 264 | * 265 | * @example 266 | * // мужской род, творительный падеж, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном' 267 | * numeralize(5122981121, Gender.Masculine, Case.Prepositional); 268 | */ 269 | export function numeralize( 270 | number: number, 271 | gender: Gender = Gender.Masculine, 272 | kase: Case = Case.Nominative, 273 | animate: boolean = false 274 | ): string { 275 | // Normalize params 276 | number = Math.abs(parseInt(String(number), 10)); 277 | 278 | // Collect chunks 279 | const result: string[] = []; 280 | 281 | // Descend known powers of thousand 282 | for (let l = LARGES.length, i = l; i >= 0; i--) { 283 | const base = Math.pow(10, i * 3); 284 | const current = Math.floor(number / base); 285 | number = number % base; 286 | 287 | if (current) { 288 | const large = i ? LARGES[i] : null; 289 | const numeral = small(current, large ? large[0] : gender, kase, large ? false : animate); 290 | if (numeral) { 291 | result.push(numeral); 292 | if (large) { 293 | const [, ...forms] = large 294 | const plural = pluralize(current, forms[kase][0], forms[kase][1], forms[kase][2]); 295 | result.push(plural); 296 | } 297 | } 298 | } 299 | } 300 | 301 | // Zero 302 | if (!result.length) { 303 | result.push(MINORS[0][kase]); 304 | } 305 | 306 | // Return 307 | return result.join(' '); 308 | } 309 | 310 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es3", 15 | /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 16 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 17 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 18 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 19 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 20 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 21 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 22 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 23 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 24 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 25 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 26 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 27 | 28 | /* Modules */ 29 | "module": "NodeNext", 30 | /* Specify what module code is generated. */ 31 | // "rootDir": "./", /* Specify the root folder within your source files. */ 32 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 33 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 34 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 35 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 36 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 37 | "types": ["node", "mocha"], /* Specify type package names to be included without being referenced in a source file. */ 38 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 39 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 40 | // "resolveJsonModule": true, /* Enable importing .json files. */ 41 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 42 | 43 | /* JavaScript Support */ 44 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 45 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 46 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 47 | 48 | /* Emit */ 49 | "declaration": true, 50 | /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 51 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 52 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 53 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 54 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 55 | "outDir": "./lib", /* Specify an output folder for all emitted files. */ 56 | // "removeComments": true, /* Disable emitting comments. */ 57 | // "noEmit": true, /* Disable emitting files from a compilation. */ 58 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 59 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 60 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 61 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 62 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 63 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 64 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 65 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 66 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 67 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 68 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 69 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 70 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 71 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 72 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 73 | 74 | /* Interop Constraints */ 75 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 76 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 77 | "esModuleInterop": true, 78 | /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 79 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 80 | "forceConsistentCasingInFileNames": true, 81 | /* Ensure that casing is correct in imports. */ 82 | 83 | /* Type Checking */ 84 | "strict": true, 85 | /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true 108 | /* Skip type checking all .d.ts files. */ 109 | }, 110 | "include": ["./src/index.ts"] 111 | } 112 | -------------------------------------------------------------------------------- /src/index.spec.ts: -------------------------------------------------------------------------------- 1 | import {numeralize, Gender, Case} from "./index"; 2 | import * as assert from "assert"; 3 | 4 | describe(numeralize.name, () => { 5 | type SimpleTestCase = Array; 6 | type ComplexTestCase = Array<[Gender, ...Array<[Case, [true, string], [false, string]]>]>; 7 | 8 | function isComplex(testCase: SimpleTestCase | ComplexTestCase): testCase is ComplexTestCase { 9 | return Array.isArray(testCase[0]) 10 | } 11 | 12 | const testCases: Array< 13 | [string, ...Array< 14 | [number, ...(SimpleTestCase | ComplexTestCase)] 15 | >] 16 | > = [ 17 | ['0 to 19 (minors)', 18 | [1, 19 | [Gender.Masculine, 20 | [Case.Nominative, [true, 'один'], [false, 'один']], 21 | [Case.Genitive, [true, 'одного'], [false, 'одного']], 22 | [Case.Dative, [true, 'одному'], [false, 'одному']], 23 | [Case.Accusative, [true, 'одного'], [false, 'один']], 24 | [Case.Instrumental, [true, 'одним'], [false, 'одним']], 25 | [Case.Prepositional, [true, 'одном'], [false, 'одном']]], 26 | [Gender.Feminine, 27 | [Case.Nominative, [true, 'одна'], [false, 'одна']], 28 | [Case.Genitive, [true, 'одной'], [false, 'одной']], 29 | [Case.Dative, [true, 'одной'], [false, 'одной']], 30 | [Case.Accusative, [true, 'одну'], [false, 'одну']], 31 | [Case.Instrumental, [true, 'одной'], [false, 'одной']], 32 | [Case.Prepositional, [true, 'одной'], [false, 'одной']]], 33 | [Gender.Neuter, 34 | [Case.Nominative, [true, 'одно'], [false, 'одно']], 35 | [Case.Genitive, [true, 'одного'], [false, 'одного']], 36 | [Case.Dative, [true, 'одному'], [false, 'одному']], 37 | [Case.Accusative, [true, 'одно'], [false, 'одно']], 38 | [Case.Instrumental, [true, 'одним'], [false, 'одним']], 39 | [Case.Prepositional, [true, 'одном'], [false, 'одном']]]], 40 | [2, 41 | [Gender.Masculine, 42 | [Case.Nominative, [true, 'два'], [false, 'два']], 43 | [Case.Genitive, [true, 'двух'], [false, 'двух']], 44 | [Case.Dative, [true, 'двум'], [false, 'двум']], 45 | [Case.Accusative, [true, 'двух'], [false, 'два']], 46 | [Case.Instrumental, [true, 'двумя'], [false, 'двумя']], 47 | [Case.Prepositional, [true, 'двух'], [false, 'двух']]], 48 | [Gender.Feminine, 49 | [Case.Nominative, [true, 'две'], [false, 'две']], 50 | [Case.Genitive, [true, 'двух'], [false, 'двух']], 51 | [Case.Dative, [true, 'двум'], [false, 'двум']], 52 | [Case.Accusative, [true, 'двух'], [false, 'две']], 53 | [Case.Instrumental, [true, 'двумя'], [false, 'двумя']], 54 | [Case.Prepositional, [true, 'двух'], [false, 'двух']]], 55 | [Gender.Neuter, 56 | [Case.Nominative, [true, 'два'], [false, 'два']], 57 | [Case.Genitive, [true, 'двух'], [false, 'двух']], 58 | [Case.Dative, [true, 'двум'], [false, 'двум']], 59 | [Case.Accusative, [true, 'два'], [false, 'два']], 60 | [Case.Instrumental, [true, 'двумя'], [false, 'двумя']], 61 | [Case.Prepositional, [true, 'двух'], [false, 'двух']]]], 62 | [3, 63 | [Gender.Masculine, 64 | [Case.Nominative, [true, 'три'], [false, 'три']], 65 | [Case.Genitive, [true, 'трёх'], [false, 'трёх']], 66 | [Case.Dative, [true, 'трём'], [false, 'трём']], 67 | [Case.Accusative, [true, 'трёх'], [false, 'три']], 68 | [Case.Instrumental, [true, 'тремя'], [false, 'тремя']], 69 | [Case.Prepositional, [true, 'трёх'], [false, 'трёх']]], 70 | [Gender.Feminine, 71 | [Case.Nominative, [true, 'три'], [false, 'три']], 72 | [Case.Genitive, [true, 'трёх'], [false, 'трёх']], 73 | [Case.Dative, [true, 'трём'], [false, 'трём']], 74 | [Case.Accusative, [true, 'трёх'], [false, 'три']], 75 | [Case.Instrumental, [true, 'тремя'], [false, 'тремя']], 76 | [Case.Prepositional, [true, 'трёх'], [false, 'трёх']]], 77 | [Gender.Neuter, 78 | [Case.Nominative, [true, 'три'], [false, 'три']], 79 | [Case.Genitive, [true, 'трёх'], [false, 'трёх']], 80 | [Case.Dative, [true, 'трём'], [false, 'трём']], 81 | [Case.Accusative, [true, 'три'], [false, 'три']], 82 | [Case.Instrumental, [true, 'тремя'], [false, 'тремя']], 83 | [Case.Prepositional, [true, 'трёх'], [false, 'трёх']]]], 84 | [4, 85 | [Gender.Masculine, 86 | [Case.Nominative, [true, 'четыре'], [false, 'четыре']], 87 | [Case.Genitive, [true, 'четырёх'], [false, 'четырёх']], 88 | [Case.Dative, [true, 'четырём'], [false, 'четырём']], 89 | [Case.Accusative, [true, 'четырёх'], [false, 'четыре']], 90 | [Case.Instrumental, 91 | [true, 'четырьмя'], 92 | [false, 'четырьмя']], 93 | [Case.Prepositional, 94 | [true, 'четырёх'], 95 | [false, 'четырёх']]], 96 | [Gender.Feminine, 97 | [Case.Nominative, [true, 'четыре'], [false, 'четыре']], 98 | [Case.Genitive, [true, 'четырёх'], [false, 'четырёх']], 99 | [Case.Dative, [true, 'четырём'], [false, 'четырём']], 100 | [Case.Accusative, [true, 'четырёх'], [false, 'четыре']], 101 | [Case.Instrumental, 102 | [true, 'четырьмя'], 103 | [false, 'четырьмя']], 104 | [Case.Prepositional, 105 | [true, 'четырёх'], 106 | [false, 'четырёх']]], 107 | [Gender.Neuter, 108 | [Case.Nominative, [true, 'четыре'], [false, 'четыре']], 109 | [Case.Genitive, [true, 'четырёх'], [false, 'четырёх']], 110 | [Case.Dative, [true, 'четырём'], [false, 'четырём']], 111 | [Case.Accusative, [true, 'четыре'], [false, 'четыре']], 112 | [Case.Instrumental, 113 | [true, 'четырьмя'], 114 | [false, 'четырьмя']], 115 | [Case.Prepositional, 116 | [true, 'четырёх'], 117 | [false, 'четырёх']]]], 118 | [5, 119 | [Gender.Masculine, 120 | [Case.Nominative, [true, 'пять'], [false, 'пять']], 121 | [Case.Genitive, [true, 'пяти'], [false, 'пяти']], 122 | [Case.Dative, [true, 'пяти'], [false, 'пяти']], 123 | [Case.Accusative, [true, 'пять'], [false, 'пять']], 124 | [Case.Instrumental, [true, 'пятью'], [false, 'пятью']], 125 | [Case.Prepositional, [true, 'пяти'], [false, 'пяти']]], 126 | [Gender.Feminine, 127 | [Case.Nominative, [true, 'пять'], [false, 'пять']], 128 | [Case.Genitive, [true, 'пяти'], [false, 'пяти']], 129 | [Case.Dative, [true, 'пяти'], [false, 'пяти']], 130 | [Case.Accusative, [true, 'пять'], [false, 'пять']], 131 | [Case.Instrumental, [true, 'пятью'], [false, 'пятью']], 132 | [Case.Prepositional, [true, 'пяти'], [false, 'пяти']]], 133 | [Gender.Neuter, 134 | [Case.Nominative, [true, 'пять'], [false, 'пять']], 135 | [Case.Genitive, [true, 'пяти'], [false, 'пяти']], 136 | [Case.Dative, [true, 'пяти'], [false, 'пяти']], 137 | [Case.Accusative, [true, 'пять'], [false, 'пять']], 138 | [Case.Instrumental, [true, 'пятью'], [false, 'пятью']], 139 | [Case.Prepositional, [true, 'пяти'], [false, 'пяти']]]], 140 | [6, 141 | [Gender.Masculine, 142 | [Case.Nominative, [true, 'шесть'], [false, 'шесть']], 143 | [Case.Genitive, [true, 'шести'], [false, 'шести']], 144 | [Case.Dative, [true, 'шести'], [false, 'шести']], 145 | [Case.Accusative, [true, 'шесть'], [false, 'шесть']], 146 | [Case.Instrumental, [true, 'шестью'], [false, 'шестью']], 147 | [Case.Prepositional, [true, 'шести'], [false, 'шести']]], 148 | [Gender.Feminine, 149 | [Case.Nominative, [true, 'шесть'], [false, 'шесть']], 150 | [Case.Genitive, [true, 'шести'], [false, 'шести']], 151 | [Case.Dative, [true, 'шести'], [false, 'шести']], 152 | [Case.Accusative, [true, 'шесть'], [false, 'шесть']], 153 | [Case.Instrumental, [true, 'шестью'], [false, 'шестью']], 154 | [Case.Prepositional, [true, 'шести'], [false, 'шести']]], 155 | [Gender.Neuter, 156 | [Case.Nominative, [true, 'шесть'], [false, 'шесть']], 157 | [Case.Genitive, [true, 'шести'], [false, 'шести']], 158 | [Case.Dative, [true, 'шести'], [false, 'шести']], 159 | [Case.Accusative, [true, 'шесть'], [false, 'шесть']], 160 | [Case.Instrumental, [true, 'шестью'], [false, 'шестью']], 161 | [Case.Prepositional, [true, 'шести'], [false, 'шести']]]], 162 | [7, 163 | [Gender.Masculine, 164 | [Case.Nominative, [true, 'семь'], [false, 'семь']], 165 | [Case.Genitive, [true, 'семи'], [false, 'семи']], 166 | [Case.Dative, [true, 'семи'], [false, 'семи']], 167 | [Case.Accusative, [true, 'семь'], [false, 'семь']], 168 | [Case.Instrumental, [true, 'семью'], [false, 'семью']], 169 | [Case.Prepositional, [true, 'семи'], [false, 'семи']]], 170 | [Gender.Feminine, 171 | [Case.Nominative, [true, 'семь'], [false, 'семь']], 172 | [Case.Genitive, [true, 'семи'], [false, 'семи']], 173 | [Case.Dative, [true, 'семи'], [false, 'семи']], 174 | [Case.Accusative, [true, 'семь'], [false, 'семь']], 175 | [Case.Instrumental, [true, 'семью'], [false, 'семью']], 176 | [Case.Prepositional, [true, 'семи'], [false, 'семи']]], 177 | [Gender.Neuter, 178 | [Case.Nominative, [true, 'семь'], [false, 'семь']], 179 | [Case.Genitive, [true, 'семи'], [false, 'семи']], 180 | [Case.Dative, [true, 'семи'], [false, 'семи']], 181 | [Case.Accusative, [true, 'семь'], [false, 'семь']], 182 | [Case.Instrumental, [true, 'семью'], [false, 'семью']], 183 | [Case.Prepositional, [true, 'семи'], [false, 'семи']]]], 184 | [8, 185 | [Gender.Masculine, 186 | [Case.Nominative, [true, 'восемь'], [false, 'восемь']], 187 | [Case.Genitive, [true, 'восьми'], [false, 'восьми']], 188 | [Case.Dative, [true, 'восьми'], [false, 'восьми']], 189 | [Case.Accusative, [true, 'восемь'], [false, 'восемь']], 190 | [Case.Instrumental, 191 | [true, 'восемью'], 192 | [false, 'восемью']], 193 | [Case.Prepositional, [true, 'восьми'], [false, 'восьми']]], 194 | [Gender.Feminine, 195 | [Case.Nominative, [true, 'восемь'], [false, 'восемь']], 196 | [Case.Genitive, [true, 'восьми'], [false, 'восьми']], 197 | [Case.Dative, [true, 'восьми'], [false, 'восьми']], 198 | [Case.Accusative, [true, 'восемь'], [false, 'восемь']], 199 | [Case.Instrumental, 200 | [true, 'восемью'], 201 | [false, 'восемью']], 202 | [Case.Prepositional, [true, 'восьми'], [false, 'восьми']]], 203 | [Gender.Neuter, 204 | [Case.Nominative, [true, 'восемь'], [false, 'восемь']], 205 | [Case.Genitive, [true, 'восьми'], [false, 'восьми']], 206 | [Case.Dative, [true, 'восьми'], [false, 'восьми']], 207 | [Case.Accusative, [true, 'восемь'], [false, 'восемь']], 208 | [Case.Instrumental, 209 | [true, 'восемью'], 210 | [false, 'восемью']], 211 | [Case.Prepositional, [true, 'восьми'], [false, 'восьми']]]], 212 | [9, 213 | [Gender.Masculine, 214 | [Case.Nominative, [true, 'девять'], [false, 'девять']], 215 | [Case.Genitive, [true, 'девяти'], [false, 'девяти']], 216 | [Case.Dative, [true, 'девяти'], [false, 'девяти']], 217 | [Case.Accusative, [true, 'девять'], [false, 'девять']], 218 | [Case.Instrumental, 219 | [true, 'девятью'], 220 | [false, 'девятью']], 221 | [Case.Prepositional, [true, 'девяти'], [false, 'девяти']]], 222 | [Gender.Feminine, 223 | [Case.Nominative, [true, 'девять'], [false, 'девять']], 224 | [Case.Genitive, [true, 'девяти'], [false, 'девяти']], 225 | [Case.Dative, [true, 'девяти'], [false, 'девяти']], 226 | [Case.Accusative, [true, 'девять'], [false, 'девять']], 227 | [Case.Instrumental, 228 | [true, 'девятью'], 229 | [false, 'девятью']], 230 | [Case.Prepositional, [true, 'девяти'], [false, 'девяти']]], 231 | [Gender.Neuter, 232 | [Case.Nominative, [true, 'девять'], [false, 'девять']], 233 | [Case.Genitive, [true, 'девяти'], [false, 'девяти']], 234 | [Case.Dative, [true, 'девяти'], [false, 'девяти']], 235 | [Case.Accusative, [true, 'девять'], [false, 'девять']], 236 | [Case.Instrumental, 237 | [true, 'девятью'], 238 | [false, 'девятью']], 239 | [Case.Prepositional, [true, 'девяти'], [false, 'девяти']]]], 240 | [10, 241 | [Gender.Masculine, 242 | [Case.Nominative, [true, 'десять'], [false, 'десять']], 243 | [Case.Genitive, [true, 'десяти'], [false, 'десяти']], 244 | [Case.Dative, [true, 'десяти'], [false, 'десяти']], 245 | [Case.Accusative, [true, 'десять'], [false, 'десять']], 246 | [Case.Instrumental, 247 | [true, 'десятью'], 248 | [false, 'десятью']], 249 | [Case.Prepositional, [true, 'десяти'], [false, 'десяти']]], 250 | [Gender.Feminine, 251 | [Case.Nominative, [true, 'десять'], [false, 'десять']], 252 | [Case.Genitive, [true, 'десяти'], [false, 'десяти']], 253 | [Case.Dative, [true, 'десяти'], [false, 'десяти']], 254 | [Case.Accusative, [true, 'десять'], [false, 'десять']], 255 | [Case.Instrumental, 256 | [true, 'десятью'], 257 | [false, 'десятью']], 258 | [Case.Prepositional, [true, 'десяти'], [false, 'десяти']]], 259 | [Gender.Neuter, 260 | [Case.Nominative, [true, 'десять'], [false, 'десять']], 261 | [Case.Genitive, [true, 'десяти'], [false, 'десяти']], 262 | [Case.Dative, [true, 'десяти'], [false, 'десяти']], 263 | [Case.Accusative, [true, 'десять'], [false, 'десять']], 264 | [Case.Instrumental, 265 | [true, 'десятью'], 266 | [false, 'десятью']], 267 | [Case.Prepositional, [true, 'десяти'], [false, 'десяти']]]], 268 | [11, 269 | [Gender.Masculine, 270 | [Case.Nominative, 271 | [true, 'одиннадцать'], 272 | [false, 'одиннадцать']], 273 | [Case.Genitive, 274 | [true, 'одиннадцати'], 275 | [false, 'одиннадцати']], 276 | [Case.Dative, 277 | [true, 'одиннадцати'], 278 | [false, 'одиннадцати']], 279 | [Case.Accusative, 280 | [true, 'одиннадцать'], 281 | [false, 'одиннадцать']], 282 | [Case.Instrumental, 283 | [true, 'одиннадцатью'], 284 | [false, 'одиннадцатью']], 285 | [Case.Prepositional, 286 | [true, 'одиннадцати'], 287 | [false, 'одиннадцати']]], 288 | [Gender.Feminine, 289 | [Case.Nominative, 290 | [true, 'одиннадцать'], 291 | [false, 'одиннадцать']], 292 | [Case.Genitive, 293 | [true, 'одиннадцати'], 294 | [false, 'одиннадцати']], 295 | [Case.Dative, 296 | [true, 'одиннадцати'], 297 | [false, 'одиннадцати']], 298 | [Case.Accusative, 299 | [true, 'одиннадцать'], 300 | [false, 'одиннадцать']], 301 | [Case.Instrumental, 302 | [true, 'одиннадцатью'], 303 | [false, 'одиннадцатью']], 304 | [Case.Prepositional, 305 | [true, 'одиннадцати'], 306 | [false, 'одиннадцати']]], 307 | [Gender.Neuter, 308 | [Case.Nominative, 309 | [true, 'одиннадцать'], 310 | [false, 'одиннадцать']], 311 | [Case.Genitive, 312 | [true, 'одиннадцати'], 313 | [false, 'одиннадцати']], 314 | [Case.Dative, 315 | [true, 'одиннадцати'], 316 | [false, 'одиннадцати']], 317 | [Case.Accusative, 318 | [true, 'одиннадцать'], 319 | [false, 'одиннадцать']], 320 | [Case.Instrumental, 321 | [true, 'одиннадцатью'], 322 | [false, 'одиннадцатью']], 323 | [Case.Prepositional, 324 | [true, 'одиннадцати'], 325 | [false, 'одиннадцати']]]], 326 | [12, 327 | [Gender.Masculine, 328 | [Case.Nominative, 329 | [true, 'двенадцать'], 330 | [false, 'двенадцать']], 331 | [Case.Genitive, 332 | [true, 'двенадцати'], 333 | [false, 'двенадцати']], 334 | [Case.Dative, 335 | [true, 'двенадцати'], 336 | [false, 'двенадцати']], 337 | [Case.Accusative, 338 | [true, 'двенадцать'], 339 | [false, 'двенадцать']], 340 | [Case.Instrumental, 341 | [true, 'двенадцатью'], 342 | [false, 'двенадцатью']], 343 | [Case.Prepositional, 344 | [true, 'двенадцати'], 345 | [false, 'двенадцати']]], 346 | [Gender.Feminine, 347 | [Case.Nominative, 348 | [true, 'двенадцать'], 349 | [false, 'двенадцать']], 350 | [Case.Genitive, 351 | [true, 'двенадцати'], 352 | [false, 'двенадцати']], 353 | [Case.Dative, 354 | [true, 'двенадцати'], 355 | [false, 'двенадцати']], 356 | [Case.Accusative, 357 | [true, 'двенадцать'], 358 | [false, 'двенадцать']], 359 | [Case.Instrumental, 360 | [true, 'двенадцатью'], 361 | [false, 'двенадцатью']], 362 | [Case.Prepositional, 363 | [true, 'двенадцати'], 364 | [false, 'двенадцати']]], 365 | [Gender.Neuter, 366 | [Case.Nominative, 367 | [true, 'двенадцать'], 368 | [false, 'двенадцать']], 369 | [Case.Genitive, 370 | [true, 'двенадцати'], 371 | [false, 'двенадцати']], 372 | [Case.Dative, 373 | [true, 'двенадцати'], 374 | [false, 'двенадцати']], 375 | [Case.Accusative, 376 | [true, 'двенадцать'], 377 | [false, 'двенадцать']], 378 | [Case.Instrumental, 379 | [true, 'двенадцатью'], 380 | [false, 'двенадцатью']], 381 | [Case.Prepositional, 382 | [true, 'двенадцати'], 383 | [false, 'двенадцати']]]], 384 | [13, 385 | [Gender.Masculine, 386 | [Case.Nominative, 387 | [true, 'тринадцать'], 388 | [false, 'тринадцать']], 389 | [Case.Genitive, 390 | [true, 'тринадцати'], 391 | [false, 'тринадцати']], 392 | [Case.Dative, 393 | [true, 'тринадцати'], 394 | [false, 'тринадцати']], 395 | [Case.Accusative, 396 | [true, 'тринадцать'], 397 | [false, 'тринадцать']], 398 | [Case.Instrumental, 399 | [true, 'тринадцатью'], 400 | [false, 'тринадцатью']], 401 | [Case.Prepositional, 402 | [true, 'тринадцати'], 403 | [false, 'тринадцати']]], 404 | [Gender.Feminine, 405 | [Case.Nominative, 406 | [true, 'тринадцать'], 407 | [false, 'тринадцать']], 408 | [Case.Genitive, 409 | [true, 'тринадцати'], 410 | [false, 'тринадцати']], 411 | [Case.Dative, 412 | [true, 'тринадцати'], 413 | [false, 'тринадцати']], 414 | [Case.Accusative, 415 | [true, 'тринадцать'], 416 | [false, 'тринадцать']], 417 | [Case.Instrumental, 418 | [true, 'тринадцатью'], 419 | [false, 'тринадцатью']], 420 | [Case.Prepositional, 421 | [true, 'тринадцати'], 422 | [false, 'тринадцати']]], 423 | [Gender.Neuter, 424 | [Case.Nominative, 425 | [true, 'тринадцать'], 426 | [false, 'тринадцать']], 427 | [Case.Genitive, 428 | [true, 'тринадцати'], 429 | [false, 'тринадцати']], 430 | [Case.Dative, 431 | [true, 'тринадцати'], 432 | [false, 'тринадцати']], 433 | [Case.Accusative, 434 | [true, 'тринадцать'], 435 | [false, 'тринадцать']], 436 | [Case.Instrumental, 437 | [true, 'тринадцатью'], 438 | [false, 'тринадцатью']], 439 | [Case.Prepositional, 440 | [true, 'тринадцати'], 441 | [false, 'тринадцати']]]], 442 | [14, 443 | [Gender.Masculine, 444 | [Case.Nominative, 445 | [true, 'четырнадцать'], 446 | [false, 'четырнадцать']], 447 | [Case.Genitive, 448 | [true, 'четырнадцати'], 449 | [false, 'четырнадцати']], 450 | [Case.Dative, 451 | [true, 'четырнадцати'], 452 | [false, 'четырнадцати']], 453 | [Case.Accusative, 454 | [true, 'четырнадцать'], 455 | [false, 'четырнадцать']], 456 | [Case.Instrumental, 457 | [true, 'четырнадцатью'], 458 | [false, 'четырнадцатью']], 459 | [Case.Prepositional, 460 | [true, 'четырнадцати'], 461 | [false, 'четырнадцати']]], 462 | [Gender.Feminine, 463 | [Case.Nominative, 464 | [true, 'четырнадцать'], 465 | [false, 'четырнадцать']], 466 | [Case.Genitive, 467 | [true, 'четырнадцати'], 468 | [false, 'четырнадцати']], 469 | [Case.Dative, 470 | [true, 'четырнадцати'], 471 | [false, 'четырнадцати']], 472 | [Case.Accusative, 473 | [true, 'четырнадцать'], 474 | [false, 'четырнадцать']], 475 | [Case.Instrumental, 476 | [true, 'четырнадцатью'], 477 | [false, 'четырнадцатью']], 478 | [Case.Prepositional, 479 | [true, 'четырнадцати'], 480 | [false, 'четырнадцати']]], 481 | [Gender.Neuter, 482 | [Case.Nominative, 483 | [true, 'четырнадцать'], 484 | [false, 'четырнадцать']], 485 | [Case.Genitive, 486 | [true, 'четырнадцати'], 487 | [false, 'четырнадцати']], 488 | [Case.Dative, 489 | [true, 'четырнадцати'], 490 | [false, 'четырнадцати']], 491 | [Case.Accusative, 492 | [true, 'четырнадцать'], 493 | [false, 'четырнадцать']], 494 | [Case.Instrumental, 495 | [true, 'четырнадцатью'], 496 | [false, 'четырнадцатью']], 497 | [Case.Prepositional, 498 | [true, 'четырнадцати'], 499 | [false, 'четырнадцати']]]], 500 | [15, 501 | [Gender.Masculine, 502 | [Case.Nominative, 503 | [true, 'пятнадцать'], 504 | [false, 'пятнадцать']], 505 | [Case.Genitive, 506 | [true, 'пятнадцати'], 507 | [false, 'пятнадцати']], 508 | [Case.Dative, 509 | [true, 'пятнадцати'], 510 | [false, 'пятнадцати']], 511 | [Case.Accusative, 512 | [true, 'пятнадцать'], 513 | [false, 'пятнадцать']], 514 | [Case.Instrumental, 515 | [true, 'пятнадцатью'], 516 | [false, 'пятнадцатью']], 517 | [Case.Prepositional, 518 | [true, 'пятнадцати'], 519 | [false, 'пятнадцати']]], 520 | [Gender.Feminine, 521 | [Case.Nominative, 522 | [true, 'пятнадцать'], 523 | [false, 'пятнадцать']], 524 | [Case.Genitive, 525 | [true, 'пятнадцати'], 526 | [false, 'пятнадцати']], 527 | [Case.Dative, 528 | [true, 'пятнадцати'], 529 | [false, 'пятнадцати']], 530 | [Case.Accusative, 531 | [true, 'пятнадцать'], 532 | [false, 'пятнадцать']], 533 | [Case.Instrumental, 534 | [true, 'пятнадцатью'], 535 | [false, 'пятнадцатью']], 536 | [Case.Prepositional, 537 | [true, 'пятнадцати'], 538 | [false, 'пятнадцати']]], 539 | [Gender.Neuter, 540 | [Case.Nominative, 541 | [true, 'пятнадцать'], 542 | [false, 'пятнадцать']], 543 | [Case.Genitive, 544 | [true, 'пятнадцати'], 545 | [false, 'пятнадцати']], 546 | [Case.Dative, 547 | [true, 'пятнадцати'], 548 | [false, 'пятнадцати']], 549 | [Case.Accusative, 550 | [true, 'пятнадцать'], 551 | [false, 'пятнадцать']], 552 | [Case.Instrumental, 553 | [true, 'пятнадцатью'], 554 | [false, 'пятнадцатью']], 555 | [Case.Prepositional, 556 | [true, 'пятнадцати'], 557 | [false, 'пятнадцати']]]], 558 | [16, 559 | [Gender.Masculine, 560 | [Case.Nominative, 561 | [true, 'шестнадцать'], 562 | [false, 'шестнадцать']], 563 | [Case.Genitive, 564 | [true, 'шестнадцати'], 565 | [false, 'шестнадцати']], 566 | [Case.Dative, 567 | [true, 'шестнадцати'], 568 | [false, 'шестнадцати']], 569 | [Case.Accusative, 570 | [true, 'шестнадцать'], 571 | [false, 'шестнадцать']], 572 | [Case.Instrumental, 573 | [true, 'шестнадцатью'], 574 | [false, 'шестнадцатью']], 575 | [Case.Prepositional, 576 | [true, 'шестнадцати'], 577 | [false, 'шестнадцати']]], 578 | [Gender.Feminine, 579 | [Case.Nominative, 580 | [true, 'шестнадцать'], 581 | [false, 'шестнадцать']], 582 | [Case.Genitive, 583 | [true, 'шестнадцати'], 584 | [false, 'шестнадцати']], 585 | [Case.Dative, 586 | [true, 'шестнадцати'], 587 | [false, 'шестнадцати']], 588 | [Case.Accusative, 589 | [true, 'шестнадцать'], 590 | [false, 'шестнадцать']], 591 | [Case.Instrumental, 592 | [true, 'шестнадцатью'], 593 | [false, 'шестнадцатью']], 594 | [Case.Prepositional, 595 | [true, 'шестнадцати'], 596 | [false, 'шестнадцати']]], 597 | [Gender.Neuter, 598 | [Case.Nominative, 599 | [true, 'шестнадцать'], 600 | [false, 'шестнадцать']], 601 | [Case.Genitive, 602 | [true, 'шестнадцати'], 603 | [false, 'шестнадцати']], 604 | [Case.Dative, 605 | [true, 'шестнадцати'], 606 | [false, 'шестнадцати']], 607 | [Case.Accusative, 608 | [true, 'шестнадцать'], 609 | [false, 'шестнадцать']], 610 | [Case.Instrumental, 611 | [true, 'шестнадцатью'], 612 | [false, 'шестнадцатью']], 613 | [Case.Prepositional, 614 | [true, 'шестнадцати'], 615 | [false, 'шестнадцати']]]], 616 | [17, 617 | [Gender.Masculine, 618 | [Case.Nominative, 619 | [true, 'семнадцать'], 620 | [false, 'семнадцать']], 621 | [Case.Genitive, 622 | [true, 'семнадцати'], 623 | [false, 'семнадцати']], 624 | [Case.Dative, 625 | [true, 'семнадцати'], 626 | [false, 'семнадцати']], 627 | [Case.Accusative, 628 | [true, 'семнадцать'], 629 | [false, 'семнадцать']], 630 | [Case.Instrumental, 631 | [true, 'семнадцатью'], 632 | [false, 'семнадцатью']], 633 | [Case.Prepositional, 634 | [true, 'семнадцати'], 635 | [false, 'семнадцати']]], 636 | [Gender.Feminine, 637 | [Case.Nominative, 638 | [true, 'семнадцать'], 639 | [false, 'семнадцать']], 640 | [Case.Genitive, 641 | [true, 'семнадцати'], 642 | [false, 'семнадцати']], 643 | [Case.Dative, 644 | [true, 'семнадцати'], 645 | [false, 'семнадцати']], 646 | [Case.Accusative, 647 | [true, 'семнадцать'], 648 | [false, 'семнадцать']], 649 | [Case.Instrumental, 650 | [true, 'семнадцатью'], 651 | [false, 'семнадцатью']], 652 | [Case.Prepositional, 653 | [true, 'семнадцати'], 654 | [false, 'семнадцати']]], 655 | [Gender.Neuter, 656 | [Case.Nominative, 657 | [true, 'семнадцать'], 658 | [false, 'семнадцать']], 659 | [Case.Genitive, 660 | [true, 'семнадцати'], 661 | [false, 'семнадцати']], 662 | [Case.Dative, 663 | [true, 'семнадцати'], 664 | [false, 'семнадцати']], 665 | [Case.Accusative, 666 | [true, 'семнадцать'], 667 | [false, 'семнадцать']], 668 | [Case.Instrumental, 669 | [true, 'семнадцатью'], 670 | [false, 'семнадцатью']], 671 | [Case.Prepositional, 672 | [true, 'семнадцати'], 673 | [false, 'семнадцати']]]], 674 | [18, 675 | [Gender.Masculine, 676 | [Case.Nominative, 677 | [true, 'восемнадцать'], 678 | [false, 'восемнадцать']], 679 | [Case.Genitive, 680 | [true, 'восемнадцати'], 681 | [false, 'восемнадцати']], 682 | [Case.Dative, 683 | [true, 'восемнадцати'], 684 | [false, 'восемнадцати']], 685 | [Case.Accusative, 686 | [true, 'восемнадцать'], 687 | [false, 'восемнадцать']], 688 | [Case.Instrumental, 689 | [true, 'восемнадцатью'], 690 | [false, 'восемнадцатью']], 691 | [Case.Prepositional, 692 | [true, 'восемнадцати'], 693 | [false, 'восемнадцати']]], 694 | [Gender.Feminine, 695 | [Case.Nominative, 696 | [true, 'восемнадцать'], 697 | [false, 'восемнадцать']], 698 | [Case.Genitive, 699 | [true, 'восемнадцати'], 700 | [false, 'восемнадцати']], 701 | [Case.Dative, 702 | [true, 'восемнадцати'], 703 | [false, 'восемнадцати']], 704 | [Case.Accusative, 705 | [true, 'восемнадцать'], 706 | [false, 'восемнадцать']], 707 | [Case.Instrumental, 708 | [true, 'восемнадцатью'], 709 | [false, 'восемнадцатью']], 710 | [Case.Prepositional, 711 | [true, 'восемнадцати'], 712 | [false, 'восемнадцати']]], 713 | [Gender.Neuter, 714 | [Case.Nominative, 715 | [true, 'восемнадцать'], 716 | [false, 'восемнадцать']], 717 | [Case.Genitive, 718 | [true, 'восемнадцати'], 719 | [false, 'восемнадцати']], 720 | [Case.Dative, 721 | [true, 'восемнадцати'], 722 | [false, 'восемнадцати']], 723 | [Case.Accusative, 724 | [true, 'восемнадцать'], 725 | [false, 'восемнадцать']], 726 | [Case.Instrumental, 727 | [true, 'восемнадцатью'], 728 | [false, 'восемнадцатью']], 729 | [Case.Prepositional, 730 | [true, 'восемнадцати'], 731 | [false, 'восемнадцати']]]], 732 | [19, 733 | [Gender.Masculine, 734 | [Case.Nominative, 735 | [true, 'девятнадцать'], 736 | [false, 'девятнадцать']], 737 | [Case.Genitive, 738 | [true, 'девятнадцати'], 739 | [false, 'девятнадцати']], 740 | [Case.Dative, 741 | [true, 'девятнадцати'], 742 | [false, 'девятнадцати']], 743 | [Case.Accusative, 744 | [true, 'девятнадцать'], 745 | [false, 'девятнадцать']], 746 | [Case.Instrumental, 747 | [true, 'девятнадцатью'], 748 | [false, 'девятнадцатью']], 749 | [Case.Prepositional, 750 | [true, 'девятнадцати'], 751 | [false, 'девятнадцати']]], 752 | [Gender.Feminine, 753 | [Case.Nominative, 754 | [true, 'девятнадцать'], 755 | [false, 'девятнадцать']], 756 | [Case.Genitive, 757 | [true, 'девятнадцати'], 758 | [false, 'девятнадцати']], 759 | [Case.Dative, 760 | [true, 'девятнадцати'], 761 | [false, 'девятнадцати']], 762 | [Case.Accusative, 763 | [true, 'девятнадцать'], 764 | [false, 'девятнадцать']], 765 | [Case.Instrumental, 766 | [true, 'девятнадцатью'], 767 | [false, 'девятнадцатью']], 768 | [Case.Prepositional, 769 | [true, 'девятнадцати'], 770 | [false, 'девятнадцати']]], 771 | [Gender.Neuter, 772 | [Case.Nominative, 773 | [true, 'девятнадцать'], 774 | [false, 'девятнадцать']], 775 | [Case.Genitive, 776 | [true, 'девятнадцати'], 777 | [false, 'девятнадцати']], 778 | [Case.Dative, 779 | [true, 'девятнадцати'], 780 | [false, 'девятнадцати']], 781 | [Case.Accusative, 782 | [true, 'девятнадцать'], 783 | [false, 'девятнадцать']], 784 | [Case.Instrumental, 785 | [true, 'девятнадцатью'], 786 | [false, 'девятнадцатью']], 787 | [Case.Prepositional, 788 | [true, 'девятнадцати'], 789 | [false, 'девятнадцати']] 790 | ] 791 | ] 792 | ], 793 | ['10, 20,.. 90 (tens)', 794 | [20, 'двадцать', 'двадцати', 'двадцати', 'двадцать', 'двадцатью', 'двадцати'], 795 | [30, 'тридцать', 'тридцати', 'тридцати', 'тридцать', 'тридцатью', 'тридцати'], 796 | [40, 'сорок', 'сорока', 'сорока', 'сорок', 'сорока', 'сорока'], 797 | [50, 'пятьдесят', 'пятидесяти', 'пятидесяти', 'пятьдесят', 'пятьюдесятью', 'пятидесяти'], 798 | [60, 'шестьдесят', 'шестидесяти', 'шестидесяти', 'шестьдесят', 'шестьюдесятью', 'шестидесяти'], 799 | [70, 'семьдесят', 'семидесяти', 'семидесяти', 'семьдесят', 'семьюдесятью', 'семидесяти'], 800 | [80, 'восемьдесят', 'восьмидесяти', 'восьмидесяти', 'восемьдесят', 'восемьюдесятью', 'восьмидесяти'], 801 | [90, 'девяносто', 'девяноста', 'девяноста', 'девяносто', 'девяноста', 'девяноста'] 802 | ], 803 | ['100, 200,.. 900 (hundreds)', 804 | [100, 'сто', 'ста', 'ста', 'сто', 'ста', 'ста'], 805 | [200, 'двести', 'двухсот', 'двумстам', 'двести', 'двумястами', 'двухстах'], 806 | [300, 'триста', 'трёхсот', 'трёмстам', 'триста', 'тремястами', 'трёхстах'], 807 | [400, 'четыреста', 'четырёхсот', 'четырёмстам', 'четыреста', 'четырьмястами', 'четырёхстах'], 808 | [500, 'пятьсот', 'пятисот', 'пятистам', 'пятьсот', 'пятьюстами', 'пятистах'], 809 | [600, 'шестьсот', 'шестисот', 'шестистам', 'шестьсот', 'шестьюстами', 'шестистах'], 810 | [700, 'семьсот', 'семисот', 'семистам', 'семьсот', 'семьюстами', 'семистах'], 811 | [800, 'восемьсот', 'восьмисот', 'восьмистам', 'восемьсот', 'восемьюстами', 'восьмистах'], 812 | [900, 'девятьсот', 'девятисот', 'девятистам', 'девятьсот', 'девятьюстами', 'девятистах'] 813 | ], 814 | ['1000', 815 | [1000, 'одна тысяча', 'одной тысячи', 'одной тысяче', 'одну тысячу', 'одной тысячей', 'одной тысяче'], 816 | [2000, 'две тысячи', 'двух тысяч', 'двум тысячам', 'две тысячи', 'двумя тысячами', 'двух тысячах'], 817 | [5000, 'пять тысяч', 'пяти тысяч', 'пяти тысячам', 'пять тысяч', 'пятью тысячами', 'пяти тысячах'] 818 | ], 819 | ['1000²', 820 | [1000000, 821 | 'один миллион', 822 | 'одного миллиона', 823 | 'одному миллиону', 824 | 'один миллион', 825 | 'одним миллионом', 826 | 'одном миллионе' 827 | ], 828 | [2000000, 829 | 'два миллиона', 830 | 'двух миллионов', 831 | 'двум миллионам', 832 | 'два миллиона', 833 | 'двумя миллионами', 834 | 'двух миллионах' 835 | ], 836 | [5000000, 837 | 'пять миллионов', 838 | 'пяти миллионов', 839 | 'пяти миллионам', 840 | 'пять миллионов', 841 | 'пятью миллионами', 842 | 'пяти миллионах' 843 | ] 844 | ], 845 | ['1000³', 846 | [1000000000, 847 | 'один миллиард', 848 | 'одного миллиарда', 849 | 'одному миллиарду', 850 | 'один миллиард', 851 | 'одним миллиардом', 852 | 'одном миллиарде' 853 | ], 854 | [2000000000, 855 | 'два миллиарда', 856 | 'двух миллиардов', 857 | 'двум миллиардам', 858 | 'два миллиарда', 859 | 'двумя миллиардами', 860 | 'двух миллиардах' 861 | ], 862 | [5000000000, 863 | 'пять миллиардов', 864 | 'пяти миллиардов', 865 | 'пяти миллиардам', 866 | 'пять миллиардов', 867 | 'пятью миллиардами', 868 | 'пяти миллиардах' 869 | ] 870 | ], 871 | ['1000⁴', 872 | [1000000000000, 873 | 'один триллион', 874 | 'одного триллиона', 875 | 'одному триллиону', 876 | 'один триллион', 877 | 'одним триллионом', 878 | 'одном триллионе' 879 | ], 880 | [2000000000000, 881 | 'два триллиона', 882 | 'двух триллионов', 883 | 'двум триллионам', 884 | 'два триллиона', 885 | 'двумя триллионами', 886 | 'двух триллионах' 887 | ], 888 | [5000000000000, 889 | 'пять триллионов', 890 | 'пяти триллионов', 891 | 'пяти триллионам', 892 | 'пять триллионов', 893 | 'пятью триллионами', 894 | 'пяти триллионах' 895 | ] 896 | ], 897 | ['misc', 898 | [5122981121, 899 | [Gender.Masculine, 900 | [Case.Nominative, 901 | [true, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один'], 902 | [false, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один'] 903 | ], 904 | [Case.Genitive, 905 | [true, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного'], 906 | [false, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного'] 907 | ], 908 | [Case.Dative, 909 | [true, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному'], 910 | [false, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному'] 911 | ], 912 | [Case.Accusative, 913 | [true, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одного'], 914 | [false, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать один'] 915 | ], 916 | [Case.Instrumental, 917 | [true, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним'], 918 | [false, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним'] 919 | ], 920 | [Case.Prepositional, 921 | [true, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном'], 922 | [false, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном'] 923 | ] 924 | ], 925 | [Gender.Feminine, 926 | [Case.Nominative, 927 | [true, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одна'], 928 | [false, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одна'] 929 | ], 930 | [Case.Genitive, 931 | [true, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одной'], 932 | [false, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одной'] 933 | ], 934 | [Case.Dative, 935 | [true, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одной'], 936 | [false, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одной'] 937 | ], 938 | [Case.Accusative, 939 | [true, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одну'], 940 | [false, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одну'] 941 | ], 942 | [Case.Instrumental, 943 | [true, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одной'], 944 | [false, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одной'] 945 | ], 946 | [Case.Prepositional, 947 | [true, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одной'], 948 | [false, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одной'] 949 | ] 950 | ], 951 | [Gender.Neuter, 952 | [Case.Nominative, 953 | [true, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одно'], 954 | [false, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одно'] 955 | ], 956 | [Case.Genitive, 957 | [true, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного'], 958 | [false, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного'] 959 | ], 960 | [Case.Dative, 961 | [true, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному'], 962 | [false, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному'] 963 | ], 964 | [Case.Accusative, 965 | [true, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одно'], 966 | [false, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одно'] 967 | ], 968 | [Case.Instrumental, 969 | [true, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним'], 970 | [false, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним'] 971 | ], 972 | [Case.Prepositional, 973 | [true, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном'], 974 | [false, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном'] 975 | ] 976 | ] 977 | ] 978 | ] 979 | ] 980 | ; 981 | 982 | const genders = Object.values(Gender).filter((gender): gender is Gender => typeof gender === 'number'); 983 | const cases = Object.values(Case).filter((kase): kase is Case => typeof kase === 'number'); 984 | 985 | testCases.forEach(([title, ...rest]) => { 986 | describe(title, () => { 987 | rest.forEach(([argNumber, ...rest]) => { 988 | if (isComplex(rest)) { 989 | rest.forEach(([argGender, ...rest]) => { 990 | rest.forEach(([argCase, ...rest]) => { 991 | rest.forEach(([argAnimate, expected]) => { 992 | it(`numeralize(${argNumber}, Gender.${Gender[argGender]}, Case.${Case[argCase]}, ${argAnimate}) → ${expected}`, () => { 993 | assert.strictEqual( 994 | numeralize( 995 | argNumber, 996 | argGender, 997 | argCase, 998 | argAnimate 999 | ), 1000 | expected 1001 | ); 1002 | }); 1003 | }); 1004 | }); 1005 | }); 1006 | } else { 1007 | genders.forEach((argGender) => { 1008 | cases.forEach((argCase, i) => { 1009 | [true, false].forEach((argAnimate) => { 1010 | const expected = rest[i]; 1011 | it(`numeralize(${argNumber}, Gender.${Gender[argGender]}, Case.${Case[argCase]}, ${argAnimate}) → ${expected}`, () => { 1012 | assert.strictEqual( 1013 | numeralize( 1014 | argNumber, 1015 | argGender, 1016 | argCase, 1017 | argAnimate 1018 | ), 1019 | expected 1020 | ); 1021 | }); 1022 | }) 1023 | }) 1024 | }) 1025 | } 1026 | }); 1027 | }); 1028 | }); 1029 | }); 1030 | --------------------------------------------------------------------------------