2
0
mirror of https://github.com/tenrok/numeralize-ru.git synced 2026-05-15 11:59:43 +03:00

Rewritten to Typescript, added support for different module systems

This commit is contained in:
anotherpit
2023-11-10 21:21:33 -03:00
parent 8785d5ee0a
commit 558659e6f8
19 changed files with 3907 additions and 1507 deletions
-13
View File
@@ -1,13 +0,0 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[package.json]
indent_size = 2
+11
View File
@@ -0,0 +1,11 @@
{
"root": true,
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
]
}
+1
View File
@@ -101,3 +101,4 @@ Network Trash Folder
Temporary Items Temporary Items
.apdisk .apdisk
lib
-104
View File
@@ -1,104 +0,0 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Node template
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
### OSX template
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
test
+23 -24
View File
@@ -16,53 +16,53 @@ npm install --save numeralize-ru
+ `number` — число, для которого надо записать числительное; + `number` — число, для которого надо записать числительное;
+ `gender` — пол: + `gender` — пол:
+ `numeralize.GENDER_MASCULINE` — мужской (по умолчанию); + `Gender.Masculine` — мужской (по умолчанию);
+ `numeralize.GENDER_FEMININE` — женский; + `Gender.Feminine` — женский;
+ `numeralize.GENDER_NEUTER` — средний; + `Gender.Neuter` — средний;
+ `kase` — падеж (`case` является ключевым словом, поэтому не может быть использован в качестве имени переменной): + `kase` — падеж (`case` является ключевым словом, поэтому не может быть использован в качестве имени переменной):
+ `numeralize.CASE_NOMINATIVE` — именительный (по умолчанию); + `Case.Nominative` — именительный (по умолчанию);
+ `numeralize.CASE_GENITIVE` — родительный; + `Case.Genitive` — родительный;
+ `numeralize.CASE_DATIVE` — дательный; + `Case.Dative` — дательный;
+ `numeralize.CASE_ACCUSATIVE` — винительный; + `Case.Accusative` — винительный;
+ `numeralize.CASE_INSTRUMENTAL` — творительный; + `Case.Instrumental` — творительный;
+ `numeralize.CASE_PREPOSITIONAL` — предложный; + `Case.Prepositional` — предложный;
+ `animate` — являются ли перечисляемые предметы одушевлёнными (влияет на форму винительного падежа некоторых числительных) + `animate` — являются ли перечисляемые предметы одушевлёнными (влияет на форму винительного падежа некоторых числительных)
```javascript ```typescript
const numeralize = require('numeralize-ru'); import {Case, Gender, numeralize, pluralize} from 'numeralize-ru';
numeralize(5122981121); numeralize(5122981121);
// мужской род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один' // мужской род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один'
numeralize(5122981121, numeralize.GENDER_FEMININE); numeralize(5122981121, Gender.Feminine);
// женский род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одна' // женский род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одна'
numeralize(5122981121, numeralize.GENDER_NEUTER); numeralize(5122981121, Gender.Neuter);
// средний род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одно' // средний род, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать одно'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_NOMINATIVE); numeralize(5122981121, Gender.Masculine, Case.Nominative);
// мужской род, именительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один' // мужской род, именительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одна тысяча сто двадцать один'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_GENITIVE); numeralize(5122981121, Gender.Masculine, Case.Genitive);
// мужской род, родительный падеж, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного' // мужской род, родительный падеж, 'пяти миллиардов ста двадцати двух миллионов девятисот восьмидесяти одной тысячи ста двадцати одного'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_DATIVE); numeralize(5122981121, Gender.Masculine, Case.Dative);
// мужской род, дательный падеж, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному' // мужской род, дательный падеж, 'пяти миллиардам ста двадцати двум миллионам девятистам восьмидесяти одной тысяче ста двадцати одному'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_ACCUSATIVE); numeralize(5122981121, Gender.Masculine, Case.Accusative);
// мужской род, винительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать один' // мужской род, винительный падеж, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать один'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_ACCUSATIVE, true); numeralize(5122981121, Gender.Masculine, Case.Accusative, true);
// мужской род, винительный падеж, одушевлённые предметы, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одного' // мужской род, винительный падеж, одушевлённые предметы, 'пять миллиардов сто двадцать два миллиона девятьсот восемьдесят одну тысячу сто двадцать одного'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_INSTRUMENTAL); numeralize(5122981121, Gender.Masculine, Case.Instrumental);
// мужской род, творительный падеж, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним' // мужской род, творительный падеж, 'пятью миллиардами ста двадцатью двумя миллионами девятьюстами восемьюдесятью одной тысячей ста двадцатью одним'
numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_PREPOSITIONAL); numeralize(5122981121, Gender.Masculine, Case.Prepositional);
// мужской род, творительный падеж, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном' // мужской род, творительный падеж, 'пяти миллиардах ста двадцати двух миллионах девятистах восьмидесяти одной тысяче ста двадцати одном'
``` ```
### `numeralize.pluralize(count, one, two, five)` ### `pluralize(count, one, two, five)`
Выбирает нужную форму существительного в зависимости от количества. Выбирает нужную форму существительного в зависимости от количества.
@@ -71,8 +71,8 @@ numeralize(5122981121, numeralize.GENDER_MASCULINE, numeralize.CASE_PREPOSITIONA
+ `two` — форма существительного для двух предметов, например, _рубля_; + `two` — форма существительного для двух предметов, например, _рубля_;
+ `five` — форма существительного для пяти предметов, например, _рублей_; + `five` — форма существительного для пяти предметов, например, _рублей_;
```javascript ```typescript
const pluralize = require('numeralize-ru').pluralize; import {pluralize} from 'numeralize-ru';
pluralize(0, 'рубль', 'рубля', 'рублей'); pluralize(0, 'рубль', 'рубля', 'рублей');
// 'рублей' // 'рублей'
@@ -96,7 +96,6 @@ pluralize(22, 'рубль', 'рубля', 'рублей');
// 'рубля' // 'рубля'
``` ```
# Roadmap # Roadmap
+ Порядковые числительные (ordinal numerals): _первый_, _вторым_, _третьими_ и т.д. + Порядковые числительные (ordinal numerals): _первый_, _вторым_, _третьими_ и т.д.
Vendored
-20
View File
@@ -1,20 +0,0 @@
export = numeralize;
declare function numeralize(number: number, gender?: numeralize.Gender, kase?: numeralize.Case, animate?: boolean): string;
declare namespace numeralize {
export type Gender = number;
export const GENDER_MASCULINE: Gender;
export const GENDER_FEMININE: Gender;
export const GENDER_NEUTER: Gender;
export type Case = number;
export const CASE_NOMINATIVE: Case;
export const CASE_GENITIVE: Case;
export const CASE_DATIVE: Case;
export const CASE_ACCUSATIVE: Case;
export const CASE_INSTRUMENTAL: Case;
export const CASE_PREPOSITIONAL: Case;
export function pluralize(count: number, one: string, two: string, five: string): string;
}
-240
View File
@@ -1,240 +0,0 @@
;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.numeralize = factory();
}
}(this, function() {
'use strict';
/**
* Numeralize number
* @param {number} number Integer
* @param {number} [gender=numeralize.GENDER_MASCULINE]
* @param {number} [kase=numeralize.CASE_NOMINATIVE]
* @param {boolean} [animate=false]
* @returns {string}
*/
function numeralize(number, gender, kase, animate) {
// Normalize params
number = Math.abs(parseInt(number, 10));
gender = gender || numeralize.GENDER_MASCULINE;
kase = kase || numeralize.CASE_NOMINATIVE;
animate = !!animate;
// Collect chunks
var result = [];
// Descend known powers of thousand
for (var l = LARGES.length, i = l; i >= 0; i--) {
var base = Math.pow(10, i * 3);
var current = Math.floor(number / base);
number = number % base;
if (current) {
var words = i ? LARGES[i] : null;
var numeral = small(current, words ? words[0] : gender, kase, words ? false : animate);
if (numeral) {
result.push(numeral);
if (words) {
var plural = pluralize.apply(null, [current].concat(words[kase + 1]));
result.push(plural);
}
}
}
}
// Zero
if (!result.length) {
return MINORS[0][kase];
}
// Return
return result.join(" ");
}
/**
* Numeralize small number (< 1000)
* @private
* @param {number} number Non-negative integer < 1000
* @param {number} gender
* @param {number} kase
* @param {boolean} animate
* @returns {string}
*/
function small(number, gender, kase, animate) {
// Zero
if (0 === number) { return ""; }
// Collect chunks
var result = [];
// Hundreds
var hundreds = Math.floor(number / 100);
if (HUNDREDS[hundreds]) {
result.push(HUNDREDS[hundreds][kase]);
}
// Tens
var tens = Math.floor(number % 100 / 10);
if (TENS[tens]) {
result.push(TENS[tens][kase]);
}
// Minors
var minors = number % 100;
if (minors >= MINORS.length) {
minors = number % 10;
}
if (minors) {
minors = MINORS[minors][kase];
if ("string" !== typeof minors) {
minors = minors[gender];
if ("string" !== typeof minors) {
minors = minors[animate ? 0 : 1];
}
}
result.push(minors);
}
// Return
return result.join(" ");
}
/**
* Pluralize noun according to count
* @param {number} count Number of items
* @param {string} one E.g. «рубль»
* @param {string} two E.g. «рубля»
* @param {string} five E.g. «рублей»
* @returns {string}
*/
function pluralize(count, one, two, five) {
count = Math.floor(Math.abs(count)) % 100;
if (count > 10 && count < 20) {
return five;
}
count = count % 10;
if (1 === count) { return one; }
if (count >= 2 && count <= 4) { return two; }
return five;
}
numeralize.GENDER_MASCULINE = 0;
numeralize.GENDER_FEMININE = 1;
numeralize.GENDER_NEUTER = 2;
numeralize.CASE_NOMINATIVE = 0;
numeralize.CASE_GENITIVE = 1;
numeralize.CASE_DATIVE = 2;
numeralize.CASE_ACCUSATIVE = 3;
numeralize.CASE_INSTRUMENTAL = 4;
numeralize.CASE_PREPOSITIONAL = 5;
numeralize.pluralize = pluralize;
var MINORS = [
['ноль', 'нуля', 'нулю', 'ноль', 'нулём', 'нуле'],
[
['один', 'одна', 'одно'],
['одного', 'одной', 'одного'],
['одному', 'одной', 'одному'],
[['одного', 'один'], 'одну', 'одно'],
['одним', 'одной', 'одним'],
['одном', 'одной', 'одном']
],
[
['два', 'две', 'два'],
'двух',
'двум',
[['двух', 'два'], ['двух', 'две'], 'два'],
'двумя',
'двух'
],
[
'три',
'трёх',
'трём',
[['трёх', 'три'], ['трёх', 'три'], 'три'],
'тремя',
'трёх'
],
[
'четыре',
'четырёх',
'четырём',
[['четырёх', 'четыре'], ['четырёх', 'четыре'], 'четыре'],
'четырьмя',
'четырёх'
],
['пять', 'пяти', 'пяти', 'пять', 'пятью', 'пяти'],
['шесть', 'шести', 'шести', 'шесть', 'шестью', 'шести'],
['семь', 'семи', 'семи', 'семь', 'семью', 'семи'],
['восемь', 'восьми', 'восьми', 'восемь', 'восемью', 'восьми'],
['девять', 'девяти', 'девяти', 'девять', 'девятью', 'девяти'],
['десять', 'десяти', 'десяти', 'десять', 'десятью', 'десяти']
].concat(
['один', 'две', 'три', 'четыр', 'пят', 'шест', 'сем', 'восем', 'девят'].map(function(prefix) {
return ['надцать', 'надцати', 'надцати', 'надцать', 'надцатью', 'надцати'].map(function(suffix) {
return prefix + suffix;
});
})
);
var TENS = [
false,
false,
['двадцать', 'двадцати', 'двадцати', 'двадцать', 'двадцатью', 'двадцати'],
['тридцать', 'тридцати', 'тридцати', 'тридцать', 'тридцатью', 'тридцати'],
['сорок', 'сорока', 'сорока', 'сорок', 'сорока', 'сорока'],
['пятьдесят', 'пятидесяти', 'пятидесяти', 'пятьдесят', 'пятьюдесятью', 'пятидесяти'],
['шестьдесят', 'шестидесяти', 'шестидесяти', 'шестьдесят', 'шестьюдесятью', 'шестидесяти'],
['семьдесят', 'семидесяти', 'семидесяти', 'семьдесят', 'семьюдесятью', 'семидесяти'],
['восемьдесят', 'восьмидесяти', 'восьмидесяти', 'восемьдесят', 'восемьюдесятью', 'восьмидесяти'],
['девяносто', 'девяноста', 'девяноста', 'девяносто', 'девяноста', 'девяноста']
];
var HUNDREDS = [
false,
['сто', 'ста', 'ста', 'сто', 'ста', 'ста'],
['двести', 'двухсот', 'двумстам', 'двести', 'двумястами', 'двухстах'],
['триста', 'трёхсот', 'трёмстам', 'триста', 'тремястами', 'трёхстах'],
['четыреста', 'четырёхсот', 'четырёмстам', 'четыреста', 'четырьмястами', 'четырёхстах'],
['пятьсот', 'пятисот', 'пятистам', 'пятьсот', 'пятьюстами', 'пятистах'],
['шестьсот', 'шестисот', 'шестистам', 'шестьсот', 'шестьюстами', 'шестистах'],
['семьсот', 'семисот', 'семистам', 'семьсот', 'семьюстами', 'семистах'],
['восемьсот', 'восьмисот', 'восьмистам', 'восемьсот', 'восемьюстами', 'восьмистах'],
['девятьсот', 'девятисот', 'девятистам', 'девятьсот', 'девятьюстами', 'девятистах']
];
var LARGES = [
false,
[
numeralize.GENDER_FEMININE,
['тысяча', 'тысячи', 'тысяч'],
['тысячи', 'тысяч', 'тысяч'],
['тысяче', 'тысячам', 'тысячам'],
['тысячу', 'тысячи', 'тысяч'],
['тысячей', 'тысячами', 'тысячами'],
['тысяче', 'тысячах', 'тысячах']
]
].concat(['миллион', 'миллиард', 'триллион'].map(function(base) {
return [numeralize.GENDER_MASCULINE]
.concat([
['', 'а', 'ов'],
['а', 'ов', 'ов'],
['у', 'ам', 'ам'],
['', 'а', 'ов'],
['ом', 'ами', 'ами'],
['е', 'ах', 'ах']
].map(function(kase) {
return kase.map(function(suffix) {
return base + suffix;
});
}));
}));
return numeralize;
}));
+2449
View File
File diff suppressed because it is too large Load Diff
+31 -9
View File
@@ -1,13 +1,29 @@
{ {
"name": "numeralize-ru", "name": "numeralize-ru",
"version": "1.0.1", "version": "2.0.0-alpha.1",
"description": "Russian numerals", "description": "Russian numerals",
"main": "index.js", "type": "module",
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"require": "./lib/cjs/index.js",
"import": "./lib/esm/index.js",
"default": "./lib/esm/index.js"
}
},
"main": "lib/cjs/index.js",
"files": [
"lib/",
"LICENSE.md",
"README.md",
"package.json"
],
"scripts": { "scripts": {
"test": "npm run test:js; npm run test:ts", "dev": "ts-mocha src/**.spec.ts -w --watch-files 'src/**.ts'",
"test:js": "./node_modules/.bin/mocha", "test": "ts-mocha src/**.spec.ts",
"test:ts": "./node_modules/.bin/tsc ./test/*.d.spec.ts --noEmit --noImplicitAny --strictNullChecks --target ES6", "lint": "eslint src/*",
"dev": "./node_modules/.bin/mocha --watch" "build": "rm -rf ./lib && tsc --build ./tsconfig.amd.json ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json ./tsconfig.umd.json",
"prepublish": "npm run lint && npm run test && npm run build"
}, },
"keywords": [ "keywords": [
"russian", "russian",
@@ -23,11 +39,17 @@
"author": "anotherpit <anotherpit@gmail.com>", "author": "anotherpit <anotherpit@gmail.com>",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"mocha": "^2.4.5", "@types/mocha": "^10.0.2",
"typescript": "^2.0.10" "@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"eslint": "^8.51.0",
"mocha": "^10.2.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/anotherpit/numeralize-ru.git" "url": "git+https://github.com/anotherpit/numeralize-ru.git"
} }
} }
+1029
View File
File diff suppressed because it is too large Load Diff
+216
View File
@@ -0,0 +1,216 @@
export enum Gender {
Masculine = 0,
Feminine = 1,
Neuter = 2
}
export enum Case {
Nominative = 0,
Genitive = 1,
Dative = 2,
Accusative = 3,
Instrumental = 4,
Prepositional = 5
}
const MINORS = [
['ноль', 'нуля', 'нулю', 'ноль', 'нулём', 'нуле'],
[
['один', 'одна', 'одно'],
['одного', 'одной', 'одного'],
['одному', 'одной', 'одному'],
[['одного', 'один'], 'одну', 'одно'],
['одним', 'одной', 'одним'],
['одном', 'одной', 'одном']
],
[
['два', 'две', 'два'],
'двух',
'двум',
[['двух', 'два'], ['двух', 'две'], 'два'],
'двумя',
'двух'
],
[
'три',
'трёх',
'трём',
[['трёх', 'три'], ['трёх', 'три'], 'три'],
'тремя',
'трёх'
],
[
'четыре',
'четырёх',
'четырём',
[['четырёх', 'четыре'], ['четырёх', 'четыре'], 'четыре'],
'четырьмя',
'четырёх'
],
['пять', 'пяти', 'пяти', 'пять', 'пятью', 'пяти'],
['шесть', 'шести', 'шести', 'шесть', 'шестью', 'шести'],
['семь', 'семи', 'семи', 'семь', 'семью', 'семи'],
['восемь', 'восьми', 'восьми', 'восемь', 'восемью', 'восьми'],
['девять', 'девяти', 'девяти', 'девять', 'девятью', 'девяти'],
['десять', 'десяти', 'десяти', 'десять', 'десятью', 'десяти'],
...['один', 'две', 'три', 'четыр', 'пят', 'шест', 'сем', 'восем', 'девят'].map((prefix) =>
['надцать', 'надцати', 'надцати', 'надцать', 'надцатью', 'надцати'].map((suffix) =>
`${prefix}${suffix}`
)
)
] as const;
const TENS = [
false,
false,
['двадцать', 'двадцати', 'двадцати', 'двадцать', 'двадцатью', 'двадцати'],
['тридцать', 'тридцати', 'тридцати', 'тридцать', 'тридцатью', 'тридцати'],
['сорок', 'сорока', 'сорока', 'сорок', 'сорока', 'сорока'],
['пятьдесят', 'пятидесяти', 'пятидесяти', 'пятьдесят', 'пятьюдесятью', 'пятидесяти'],
['шестьдесят', 'шестидесяти', 'шестидесяти', 'шестьдесят', 'шестьюдесятью', 'шестидесяти'],
['семьдесят', 'семидесяти', 'семидесяти', 'семьдесят', 'семьюдесятью', 'семидесяти'],
['восемьдесят', 'восьмидесяти', 'восьмидесяти', 'восемьдесят', 'восемьюдесятью', 'восьмидесяти'],
['девяносто', 'девяноста', 'девяноста', 'девяносто', 'девяноста', 'девяноста']
] as const;
const HUNDREDS = [
false,
['сто', 'ста', 'ста', 'сто', 'ста', 'ста'],
['двести', 'двухсот', 'двумстам', 'двести', 'двумястами', 'двухстах'],
['триста', 'трёхсот', 'трёмстам', 'триста', 'тремястами', 'трёхстах'],
['четыреста', 'четырёхсот', 'четырёмстам', 'четыреста', 'четырьмястами', 'четырёхстах'],
['пятьсот', 'пятисот', 'пятистам', 'пятьсот', 'пятьюстами', 'пятистах'],
['шестьсот', 'шестисот', 'шестистам', 'шестьсот', 'шестьюстами', 'шестистах'],
['семьсот', 'семисот', 'семистам', 'семьсот', 'семьюстами', 'семистах'],
['восемьсот', 'восьмисот', 'восьмистам', 'восемьсот', 'восемьюстами', 'восьмистах'],
['девятьсот', 'девятисот', 'девятистам', 'девятьсот', 'девятьюстами', 'девятистах']
] as const;
const LARGES = [
false,
[
Gender.Feminine,
['тысяча', 'тысячи', 'тысяч'],
['тысячи', 'тысяч', 'тысяч'],
['тысяче', 'тысячам', 'тысячам'],
['тысячу', 'тысячи', 'тысяч'],
['тысячей', 'тысячами', 'тысячами'],
['тысяче', 'тысячах', 'тысячах']
],
...['миллион', 'миллиард', 'триллион'].map<[Gender, ...string[][]]>((base) =>
[
Gender.Masculine,
...[
['', 'а', 'ов'],
['а', 'ов', 'ов'],
['у', 'ам', 'ам'],
['', 'а', 'ов'],
['ом', 'ами', 'ами'],
['е', 'ах', 'ах']
].map((kase) => kase.map((suffix) => `${base}${suffix}`))
]
)
] as const;
/**
* Pluralize noun according to count, e.g. `pluralize(42, 'рубль', 'рубля', 'рублей')`
*/
export function pluralize(count: number, one: string, two: string, five: string): string {
count = Math.floor(Math.abs(count)) % 100;
if (count > 10 && count < 20) {
return five;
}
count = count % 10;
if (1 === count) {
return one;
}
if (count >= 2 && count <= 4) {
return two;
}
return five;
}
function small(number: number, gender: Gender, kase: Case, animate: boolean): string {
// Zero
if (0 === number) {
return '';
}
// Collect chunks
const result: string[] = [];
// Hundreds
const hundreds = HUNDREDS[Math.floor(number / 100)];
if (hundreds) {
result.push(hundreds[kase]);
}
// Tens
const tens = TENS[Math.floor(number % 100 / 10)];
if (tens) {
result.push(tens[kase]);
}
// Minors
let minors = number % 100;
if (minors >= MINORS.length) {
minors = number % 10;
}
if (minors) {
let part;
if (
((part = MINORS[minors][kase]) && typeof part === 'string')
|| ((part = MINORS[minors][kase][gender]) && typeof part === 'string')
|| (part = MINORS[minors][kase][gender][animate ? 0 : 1])
) {
result.push(part)
}
}
// Return
return result.join(" ");
}
/**
* Numeralize number
*/
export function numeralize(
number: number,
gender: Gender = Gender.Masculine,
kase: Case = Case.Nominative,
animate: boolean = false
): string {
// Normalize params
number = Math.abs(parseInt(String(number), 10));
// Collect chunks
const result: string[] = [];
// Descend known powers of thousand
for (let l = LARGES.length, i = l; i >= 0; i--) {
const base = Math.pow(10, i * 3);
const current = Math.floor(number / base);
number = number % base;
if (current) {
const large = i ? LARGES[i] : null;
const numeral = small(current, large ? large[0] : gender, kase, large ? false : animate);
if (numeral) {
result.push(numeral);
if (large) {
const [, ...forms] = large
const plural = pluralize(current, forms[kase][0], forms[kase][1], forms[kase][2]);
result.push(plural);
}
}
}
}
// Zero
if (!result.length) {
result.push(MINORS[0][kase]);
}
// Return
return result.join(' ');
}
-19
View File
@@ -1,19 +0,0 @@
import * as numeralize from '../index';
let g: numeralize.Gender;
g = numeralize.GENDER_MASCULINE;
g = numeralize.GENDER_FEMININE;
g = numeralize.GENDER_NEUTER;
let c: numeralize.Case;
c = numeralize.CASE_NOMINATIVE;
c = numeralize.CASE_GENITIVE;
c = numeralize.CASE_DATIVE;
c = numeralize.CASE_ACCUSATIVE;
c = numeralize.CASE_INSTRUMENTAL;
c = numeralize.CASE_PREPOSITIONAL;
let s: string;
s = numeralize(123, g, c, true);
s = numeralize.pluralize(123, 'рубль', 'рубля', 'рублей');
-1078
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "umd",
"outDir": "./lib/amd/"
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "./lib/cjs/"
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "es2020",
"outDir": "./lib/esm/"
}
}
+111
View File
@@ -0,0 +1,111 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es3",
/* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "NodeNext",
/* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
"types": ["node", "mocha"], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
"declaration": false,
/* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "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. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true,
/* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true,
/* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true,
/* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true
/* Skip type checking all .d.ts files. */
},
"include": ["./src/index.ts"]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./lib/types",
"declaration": true,
"emitDeclarationOnly": true
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "umd",
"outDir": "./lib/umd/"
}
}