Browse Source

Implement ordinals, refactor code

Ordinals have been implemented in both English and German (tho I'm not
sure how to check correctness of German number names and ordinals).

Also, refactored code to have common rules for a language, and special
rules for each variant (in the future, French will have this structure
handy).
master
TheoryOfNekomata 3 years ago
parent
commit
1e67424067
21 changed files with 1049 additions and 613 deletions
  1. +6
    -5
      packages/core/src/index.ts
  2. +93
    -0
      packages/core/src/locales/common/de/index.ts
  3. +77
    -0
      packages/core/src/locales/common/en/index.ts
  4. +0
    -115
      packages/core/src/locales/de-DE/index.ts
  5. +0
    -104
      packages/core/src/locales/en-GB/index.ts
  6. +0
    -26
      packages/core/src/locales/en-PH/custom.test.ts
  7. +0
    -99
      packages/core/src/locales/en-PH/index.ts
  8. +12
    -12
      packages/core/src/locales/variants/de-DE/chongo.test.ts
  9. +49
    -49
      packages/core/src/locales/variants/de-DE/groups.test.ts
  10. +113
    -0
      packages/core/src/locales/variants/de-DE/index.ts
  11. +23
    -23
      packages/core/src/locales/variants/de-DE/plurals.test.ts
  12. +12
    -12
      packages/core/src/locales/variants/en-GB/chongo.test.ts
  13. +104
    -0
      packages/core/src/locales/variants/en-GB/index.ts
  14. +76
    -43
      packages/core/src/locales/variants/en-PH/chongo.test.ts
  15. +34
    -0
      packages/core/src/locales/variants/en-PH/custom.test.ts
  16. +38
    -38
      packages/core/src/locales/variants/en-PH/groups.test.ts
  17. +99
    -0
      packages/core/src/locales/variants/en-PH/index.ts
  18. +213
    -0
      packages/core/src/locales/variants/en-PH/ordinals.test.ts
  19. +16
    -11
      packages/core/src/locales/variants/en-PH/technical.test.ts
  20. +50
    -45
      packages/core/src/utils/common/latinPowers.ts
  21. +34
    -31
      packages/core/src/utils/numeric.ts

+ 6
- 5
packages/core/src/index.ts View File

@@ -1,18 +1,19 @@
import enPH from './locales/en-PH'
import enPH from './locales/variants/en-PH';
import { import {
Numeric, Numeric,
} from './utils/numeric'; } from './utils/numeric';


type Options = { type Options = {
groupSeparator: string, groupSeparator: string,
ordinal: boolean,
locale?: (xRaw: Numeric, options?: Partial<Omit<Options, 'locale'>>) => string, locale?: (xRaw: Numeric, options?: Partial<Omit<Options, 'locale'>>) => string,
} }


type GetNumberName = (number: Numeric, options?: Partial<Options>) => string type GetNumberName = (number: Numeric, options?: Partial<Options>) => string


const getNumberName: GetNumberName = (number, options = {} as Partial<Options>): string => { const getNumberName: GetNumberName = (number, options = {} as Partial<Options>): string => {
const { locale = enPH, ...etcOptions } = options
return locale(number, etcOptions)
}
const {locale = enPH, ...etcOptions} = options;
return locale(number, etcOptions);
};


export default getNumberName
export default getNumberName;

+ 93
- 0
packages/core/src/locales/common/de/index.ts View File

@@ -0,0 +1,93 @@
import BigNumber from 'bignumber.js';
import {BLANK_DIGIT} from '../../../utils/numeric';

const config = {
hundredName: 'hundert',
hundredOrdinalName: 'hundertste',
and: 'und',
onesNames: ['zero', 'ein', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun'],
onesOrdinalNames: [
'nullth',
'zuerst',
'zweite',
'dritte',
'vierte',
'fünfte',
'sechste',
'siebte',
'achte',
'neunte',
],
teensNames: [
'zehn',
'elf',
'zwölf',
'dreizehn',
'vierzehn',
'fünfzehn',
'sechzehn',
'siebzehn',
'achtzehn',
'neunzehn',
],
teensOrdinalNames: [
'zehnte',
'elfte',
'zwölfte',
'dreizehnte',
'vierzehnte',
'fünfzehnte',
'sechzehnte',
'siebzehnte',
'achtzehnte',
'neunzehnte',
],
tensNames: ['zero', 'zehn', 'zwanzig', 'dreißig', 'vierzig', 'fünfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig'],
tensOrdinalNames: [
'nullth',
'zehnte',
'zwanzigste',
'dreißigste',
'vierzigste',
'fünfzigste',
'sechzigste',
'siebzigste',
'achtzigste',
'neunzigste',
],
grouping: 3,
};

export const getGroupDigitsName = (digitsRaw: string, index: BigNumber, ordinal: boolean) => {
const digits = digitsRaw.padStart(config.grouping, BLANK_DIGIT);
const [hundreds, tens, ones] = digits.split('').map(s => Number(s));
const names = [];
if (hundreds !== 0) {
names.push(config.onesNames[hundreds]);
if (!ordinal || (tens > 0 || ones > 0)) {
names.push(config.hundredName);
}
}
if (tens === 1) {
names.push(config.teensNames[ones]);
} else if (tens > 1) {
if (ones > 0) {
names.push(config.onesNames[ones]);
names.push(config.and);
}
names.push(config.tensNames[tens]);
} else {
if (hundreds === 0) {
if (ones === 1 && index.gte(2)) {
names.push(ordinal ? config.onesOrdinalNames[ones] : config.onesNames[ones] + 'e');
} else {
names.push(ordinal ? config.onesOrdinalNames[ones] : config.onesNames[ones]);
}
} else if (ones > 0) {
names.push(ordinal ? config.onesOrdinalNames[ones] : config.onesNames[ones]);
} else if (ordinal) {
names.push(config.hundredOrdinalName);
}
}
return names.join('');
};

+ 77
- 0
packages/core/src/locales/common/en/index.ts View File

@@ -0,0 +1,77 @@
import {BLANK_DIGIT} from '../../../utils/numeric';

const config = {
onesNames: ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'],
onesOrdinalNames: ['zeroth', 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth'],
teensNames: [
'ten',
'eleven',
'twelve',
'thirteen',
'fourteen',
'fifteen',
'sixteen',
'seventeen',
'eighteen',
'nineteen',
],
teensOrdinalNames: [
'tenth',
'eleventh',
'twelfth',
'thirteenth',
'fourteenth',
'fifteenth',
'sixteenth',
'seventeenth',
'eighteenth',
'nineteenth',
],
tensNames: ['zero', 'ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'],
tensOrdinalNames: [
'zeroth',
'tenth',
'twentieth',
'thirtieth',
'fortieth',
'fiftieth',
'sixtieth',
'seventieth',
'eightieth',
'ninetieth',
],
hundredName: 'hundred',
hundredOrdinalName: 'hundredth',
grouping: 3,
};

export const getGroupDigitsName = (digitsRaw: string, ordinal: boolean) => {
const digits = digitsRaw.padStart(config.grouping, BLANK_DIGIT);
const [hundreds, tens, ones] = digits.split('').map(s => Number(s));
const names = [];
if (hundreds !== 0) {
names.push(config.onesNames[hundreds]);
if (!ordinal || (tens > 0 || ones > 0)) {
names.push(config.hundredName);
}
}
if (tens === 1) {
names.push(ordinal ? config.teensOrdinalNames[ones] : config.teensNames[ones]);
} else if (tens > 1) {
if (ones > 0) {
names.push(config.tensNames[tens]);
names.push(ordinal ? config.onesOrdinalNames[ones] : config.onesNames[ones]);
} else {
names.push(ordinal ? config.tensOrdinalNames[tens] : config.tensNames[tens]);
}
} else {
if (hundreds === 0) {
names.push(ordinal ? config.onesOrdinalNames[ones] : config.onesNames[ones]);
} else if (ones > 0) {
names.push(ordinal ? config.onesOrdinalNames[ones] : config.onesNames[ones]);
} else if (ordinal) {
names.push(config.hundredOrdinalName);
}
}
return names.join(' ');
};

+ 0
- 115
packages/core/src/locales/de-DE/index.ts View File

@@ -1,115 +0,0 @@
import BigNumber from 'bignumber.js';
import {
BLANK_DIGIT,
createBlankDigits,
deconstructNumeric,
groupDigits,
NEGATIVE_SIGN,
normalizeNumeric,
Numeric,
} from '../../utils/numeric';
import getLatinPowerName from '../../utils/common/latinPowers';

const config = {
"onesNames": ['zero', 'ein', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun'],
"teensNames": ['zehn', 'elf', 'zwölf', 'dreizehn', 'vierzehn', 'fünfzehn', 'sechzehn', 'siebzehn', 'achtzehn', 'neunzehn'],
"tensNames": ['zero', 'zehn', 'zwanzig', 'dreißig', 'vierzig', 'fünfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig'],
"hundredName": "hundert",
"thousandName": "tausend",
"millia": "millia",
"illion": "illion",
"illiard": "illiarde",
"and": "und",
"hundredsLatinNames": ["", "cen", "duocen", "trecen", "quadringen", "quingen", "sescen", "septingen", "octingen", "nongen"],
"onesLatinNames": ["", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem"],
"tensLatinNames": ["", "dec", "vigin", "trigin", "quadragin", "quinquagin", "sexagin", "septuagin", "octogin", "nonagin"],
"onesSpecialLatinNames": ["", "m", "b", "tr", "quadr", "quin", "sex", "sep", "oct", "non"],
"negative": "negative",
"grouping": 3,
"latinGrouping": 3
}

const getGroupIndexName = (index: BigNumber, digits: string) => {
if (index.eq(1)) {
return config.thousandName
}

const basicIndex = index.dividedToIntegerBy(2)
const isOdd = index.mod(2).eq(1)
const latinPowerName = getLatinPowerName(basicIndex, isOdd, config)
const latinPowerNameWithCase = latinPowerName.slice(0, 1).toUpperCase() + latinPowerName.slice(1)
if (digits.padStart(config.grouping, BLANK_DIGIT) === '001') {
return latinPowerNameWithCase
}
if (latinPowerNameWithCase.endsWith('e')) {
return latinPowerNameWithCase + 'n'
}
return latinPowerNameWithCase + 'en'
}

const getGroupDigitsName = (digitsRaw: string, index: BigNumber) => {
const { grouping, onesNames, teensNames, tensNames, hundredName } = config
const digits = digitsRaw.padStart(grouping, BLANK_DIGIT)
const [hundreds, tens, ones] = digits.split('').map(s => Number(s))
const names = []
if (hundreds !== 0) {
names.push(onesNames[hundreds])
names.push(hundredName)
}
if (tens === 1) {
names.push(teensNames[ones])
} else if (tens > 1) {
if (ones > 0) {
names.push(onesNames[ones])
names.push(config.and)
}
names.push(tensNames[tens])
} else {
if (hundreds === 0 && ones === 1 && index.gte(2)) {
names.push(onesNames[ones] + 'e')
} else if (hundreds !== 0 && ones > 0 || hundreds === 0) {
names.push(onesNames[ones])
}
}
return names.join('')
}

const getGroupName = (g: [string, BigNumber]) => {
const [digits, index] = g
if (index.lt(1)) {
return getGroupDigitsName(digits, index)
}
if (index.lt(2)) {
return [getGroupDigitsName(digits, index), getGroupIndexName(index, digits)].join('')
}
return [getGroupDigitsName(digits, index), getGroupIndexName(index, digits)].join(' ')
}

type Options = {
groupSeparator: string
}

const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
const {
groupSeparator = ' '
} = options

const x = normalizeNumeric(xRaw)
const { significandDigits, exponent } = deconstructNumeric(x)
const blankDigits = createBlankDigits(config.grouping)
const groups = groupDigits(significandDigits, exponent, config.grouping)

if (groups.length === 1) {
return getGroupName(groups[0])
}

const base = groups.filter(([digits]) => digits !== blankDigits).map(g => getGroupName(g)).join(groupSeparator)

if (x.startsWith(NEGATIVE_SIGN)) {
return [config.negative, base].join(' ')
}

return base
}

export default getLocalizedNumberName

+ 0
- 104
packages/core/src/locales/en-GB/index.ts View File

@@ -1,104 +0,0 @@
import BigNumber from 'bignumber.js';
import {
BLANK_DIGIT,
createBlankDigits,
deconstructNumeric,
groupDigits,
NEGATIVE_SIGN,
normalizeNumeric,
Numeric,
} from '../../utils/numeric';
import getLatinPowerName from '../../utils/common/latinPowers';

const config = {
"onesNames": ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"],
"teensNames": ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"],
"tensNames": ["zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"],
"hundredName": "hundred",
"thousandName": "thousand",
"millia": "millia",
"illion": "illion",
"hundredsLatinNames": ["", "cen", "duocen", "trecen", "quadringen", "quingen", "sescen", "septingen", "octingen", "nongen"],
"onesLatinNames": ["", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem"],
"tensLatinNames": ["", "dec", "vigin", "trigin", "quadragin", "quinquagin", "sexagin", "septuagin", "octogin", "nonagin"],
"onesSpecialLatinNames": ["", "m", "b", "tr", "quadr", "quin", "sex", "sep", "oct", "non"],
"negative": "negative",
"grouping": 3,
"latinGrouping": 3
}

const getGroupIndexName = (index: BigNumber) => {
if (index.eq(1)) {
return config.thousandName
}

const basicIndex = index.dividedToIntegerBy(2)
const isOdd = false
const latinPowerName = getLatinPowerName(basicIndex, isOdd, config)
if (index.mod(2).eq(1)) {
return [config.thousandName, latinPowerName].join(' ')
}

return latinPowerName
}

const getGroupDigitsName = (digitsRaw: string) => {
const { grouping, onesNames, teensNames, tensNames, hundredName } = config
const digits = digitsRaw.padStart(grouping, BLANK_DIGIT)
const [hundreds, tens, ones] = digits.split('').map(s => Number(s))
const names = []
if (hundreds !== 0) {
names.push(onesNames[hundreds])
names.push(hundredName)
}
if (tens === 1) {
names.push(teensNames[ones])
} else if (tens > 1) {
names.push(tensNames[tens])
if (ones > 0) {
names.push(onesNames[ones])
}
} else {
if (hundreds !== 0 && ones > 0 || hundreds === 0) {
names.push(onesNames[ones])
}
}
return names.join(' ')
}

const getGroupName = (g: [string, BigNumber]) => {
const [digits, index] = g
if (index.lt(1)) {
return getGroupDigitsName(digits)
}
return [getGroupDigitsName(digits), getGroupIndexName(index)].join(' ')
}

type Options = {
groupSeparator: string
}

const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
const {
groupSeparator = ' '
} = options

const x = normalizeNumeric(xRaw)
const { significandDigits, exponent } = deconstructNumeric(x)
const blankDigits = createBlankDigits(config.grouping)
const groups = groupDigits(significandDigits, exponent, config.grouping)

if (groups.length === 1) {
return getGroupName(groups[0])
}

const base = groups.filter(([digits]) => digits !== blankDigits).map(g => getGroupName(g)).join(groupSeparator)

if (x.startsWith(NEGATIVE_SIGN)) {
return [config.negative, base].join(' ')
}

return base
}

export default getLocalizedNumberName

+ 0
- 26
packages/core/src/locales/en-PH/custom.test.ts View File

@@ -1,26 +0,0 @@
import getNumberName from '../..';

describe('Custom numbers', () => {
it('converts 123456000 to one hundred twenty three million four hundred fifty six thousand', () => {
expect(getNumberName(123456000)).toBe('one hundred twenty three million four hundred fifty six thousand')
})
it('converts 123456000 to one hundred twenty three million four hundred fifty six thousand nine', () => {
expect(getNumberName(123456009)).toBe('one hundred twenty three million four hundred fifty six thousand nine')
})

it('converts 123000789 to one hundred twenty three million seven hundred eighty nine', () => {
expect(getNumberName(123000789)).toBe('one hundred twenty three million seven hundred eighty nine')
})

it('converts 123050789 to one hundred twenty three million fifty thousand seven hundred eighty nine', () => {
expect(getNumberName(123050789)).toBe('one hundred twenty three million fifty thousand seven hundred eighty nine')
})

it('converts 123456789 to one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine', () => {
expect(getNumberName(123456789)).toBe('one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine')
})

it('converts -123456789 to negative one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine', () => {
expect(getNumberName(-123456789)).toBe('negative one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine')
})
})

+ 0
- 99
packages/core/src/locales/en-PH/index.ts View File

@@ -1,99 +0,0 @@
import BigNumber from 'bignumber.js';
import {
BLANK_DIGIT,
createBlankDigits,
deconstructNumeric,
groupDigits,
NEGATIVE_SIGN,
normalizeNumeric,
Numeric,
} from '../../utils/numeric';
import getLatinPowerName from '../../utils/common/latinPowers';

const config = {
"onesNames": ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"],
"teensNames": ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"],
"tensNames": ["zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"],
"hundredName": "hundred",
"thousandName": "thousand",
"millia": "millia",
"illion": "illion",
"hundredsLatinNames": ["", "cen", "duocen", "trecen", "quadringen", "quingen", "sescen", "septingen", "octingen", "nongen"],
"onesLatinNames": ["", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem"],
"tensLatinNames": ["", "dec", "vigin", "trigin", "quadragin", "quinquagin", "sexagin", "septuagin", "octogin", "nonagin"],
"onesSpecialLatinNames": ["", "m", "b", "tr", "quadr", "quin", "sex", "sep", "oct", "non"],
"negative": "negative",
"grouping": 3,
"latinGrouping": 3
}

const getGroupIndexName = (index: BigNumber) => {
if (index.eq(1)) {
return config.thousandName
}

const basicIndex = index.minus(1)
const isOdd = false
return getLatinPowerName(basicIndex, isOdd, config)
}

const getGroupDigitsName = (digitsRaw: string) => {
const { grouping, onesNames, teensNames, tensNames, hundredName } = config
const digits = digitsRaw.padStart(grouping, BLANK_DIGIT)
const [hundreds, tens, ones] = digits.split('').map(s => Number(s))
const names = []
if (hundreds !== 0) {
names.push(onesNames[hundreds])
names.push(hundredName)
}
if (tens === 1) {
names.push(teensNames[ones])
} else if (tens > 1) {
names.push(tensNames[tens])
if (ones > 0) {
names.push(onesNames[ones])
}
} else {
if (hundreds !== 0 && ones > 0 || hundreds === 0) {
names.push(onesNames[ones])
}
}
return names.join(' ')
}

const getGroupName = (g: [string, BigNumber]) => {
const [digits, index] = g
if (index.lt(1)) {
return getGroupDigitsName(digits)
}
return [getGroupDigitsName(digits), getGroupIndexName(index)].join(' ')
}

type Options = {
groupSeparator: string
}

const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
const {
groupSeparator = ' '
} = options

const x = normalizeNumeric(xRaw)
const { significandDigits, exponent } = deconstructNumeric(x)
const blankDigits = createBlankDigits(config.grouping)
const groups = groupDigits(significandDigits, exponent, config.grouping)

if (groups.length === 1) {
return getGroupName(groups[0])
}

const base = groups.filter(([digits]) => digits !== blankDigits).map(g => getGroupName(g)).join(groupSeparator)

if (x.startsWith(NEGATIVE_SIGN)) {
return [config.negative, base].join(' ')
}

return base
}

export default getLocalizedNumberName

packages/core/src/locales/de-DE/chongo.test.ts → packages/core/src/locales/variants/de-DE/chongo.test.ts View File

@@ -1,5 +1,5 @@
import getNumberName from '../..';
import getLocalizedNumberName from '.';
import getNumberName from '../../../index';
import getLocalizedNumberName from './index';


describe('Landon\'s original test cases', () => { describe('Landon\'s original test cases', () => {
describe('Basic conversions', () => { describe('Basic conversions', () => {
@@ -12,10 +12,10 @@ describe('Landon\'s original test cases', () => {
${1000000000000} | ${'eine Billion'} ${1000000000000} | ${'eine Billion'}
${1000000000000000} | ${'eine Billiarde'} ${1000000000000000} | ${'eine Billiarde'}
${1000000000000000000} | ${'eine Trillion'} ${1000000000000000000} | ${'eine Trillion'}
`('converts $value to $traditionalEuropeanName', ({ value, traditionalEuropeanName }) => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(traditionalEuropeanName)
})
})
`('converts $value to $traditionalEuropeanName', ({value, traditionalEuropeanName}) => {
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(traditionalEuropeanName);
});
});


describe('Medium size numbers (<= 1e+63)', () => { describe('Medium size numbers (<= 1e+63)', () => {
describe('Table 1', () => { describe('Table 1', () => {
@@ -40,9 +40,9 @@ describe('Landon\'s original test cases', () => {
${'1e+57'} | ${'Nonilliarde'} ${'1e+57'} | ${'Nonilliarde'}
${'1e+60'} | ${'Decillion'} ${'1e+60'} | ${'Decillion'}
${'1e+63'} | ${'Decilliarde'} ${'1e+63'} | ${'Decilliarde'}
`('converts $value to $traditionalEuropeanName', ({ value, traditionalEuropeanName, }) => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(`eine ${traditionalEuropeanName}`)
})
})
})
})
`('converts $value to $traditionalEuropeanName', ({value, traditionalEuropeanName}) => {
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(`eine ${traditionalEuropeanName}`);
});
});
});
});

packages/core/src/locales/de-DE/groups.test.ts → packages/core/src/locales/variants/de-DE/groups.test.ts View File

@@ -1,5 +1,5 @@
import getNumberName from '../..';
import getLocalizedNumberName from '.';
import getNumberName from '../../../index';
import getLocalizedNumberName from './index';


describe('Number group conversion', () => { describe('Number group conversion', () => {
describe('0 in hundreds place', () => { describe('0 in hundreds place', () => {
@@ -21,10 +21,10 @@ describe('Number group conversion', () => {
onesName, onesName,
}) => { }) => {
it(`converts ${ones} to ${onesName}`, () => { it(`converts ${ones} to ${onesName}`, () => {
expect(getNumberName(ones, { locale: getLocalizedNumberName })).toBe(onesName)
})
})
})
expect(getNumberName(ones, {locale: getLocalizedNumberName})).toBe(onesName);
});
});
});


describe('1 in tens place', () => { describe('1 in tens place', () => {
describe.each` describe.each`
@@ -44,10 +44,10 @@ describe('Number group conversion', () => {
onesName, onesName,
}) => { }) => {
it(`converts 1${ones} to ${onesName}`, () => { it(`converts 1${ones} to ${onesName}`, () => {
expect(getNumberName(10 + ones, { locale: getLocalizedNumberName })).toBe(onesName)
})
})
})
expect(getNumberName(10 + ones, {locale: getLocalizedNumberName})).toBe(onesName);
});
});
});


describe.each` describe.each`
tens | tensName tens | tensName
@@ -59,14 +59,14 @@ describe('Number group conversion', () => {
${7} | ${'siebzig'} ${7} | ${'siebzig'}
${8} | ${'achtzig'} ${8} | ${'achtzig'}
${9} | ${'neunzig'} ${9} | ${'neunzig'}
`('$tens in tens place', ({ tens, tensName }) => {
`('$tens in tens place', ({tens, tensName}) => {
describe('0 in ones place', () => { describe('0 in ones place', () => {
const value = tens * 10
const name = tensName
const value = tens * 10;
const name = tensName;
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});


describe.each` describe.each`
ones | onesName ones | onesName
@@ -83,15 +83,15 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (tens * 10) + ones
const name = [onesName, tensName].join('und').trim()
const value = (tens * 10) + ones;
const name = [onesName, tensName].join('und').trim();


it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});
});
});


describe.each` describe.each`
hundreds | hundredsName hundreds | hundredsName
@@ -125,13 +125,13 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (hundreds * 100) + ones
const name = [hundredsName, onesName].join('').trim()
const value = (hundreds * 100) + ones;
const name = [hundredsName, onesName].join('').trim();
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});
});


describe('1 in tens place', () => { describe('1 in tens place', () => {
describe.each` describe.each`
@@ -150,13 +150,13 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (hundreds * 100) + 10 + ones
const name = [hundredsName, onesName].join('').trim()
const value = (hundreds * 100) + 10 + ones;
const name = [hundredsName, onesName].join('').trim();
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});
});


describe.each` describe.each`
tens | tensName tens | tensName
@@ -168,14 +168,14 @@ describe('Number group conversion', () => {
${7} | ${'siebzig'} ${7} | ${'siebzig'}
${8} | ${'achtzig'} ${8} | ${'achtzig'}
${9} | ${'neunzig'} ${9} | ${'neunzig'}
`('$tens in tens place', ({ tens, tensName }) => {
`('$tens in tens place', ({tens, tensName}) => {
describe('0 in ones place', () => { describe('0 in ones place', () => {
const value = (hundreds * 100) + (tens * 10)
const name = [hundredsName, tensName].join('').trim()
const value = (hundreds * 100) + (tens * 10);
const name = [hundredsName, tensName].join('').trim();
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});


describe.each` describe.each`
ones | onesName ones | onesName
@@ -192,13 +192,13 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (hundreds * 100) + (tens * 10) + ones
const name = [hundredsName, [onesName, tensName].join('und')].join('').trim()
const value = (hundreds * 100) + (tens * 10) + ones;
const name = [hundredsName, [onesName, tensName].join('und')].join('').trim();


it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
})
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});
});
});
});

+ 113
- 0
packages/core/src/locales/variants/de-DE/index.ts View File

@@ -0,0 +1,113 @@
import BigNumber from 'bignumber.js';
import {
BLANK_DIGIT,
createBlankDigits,
deconstructNumeric,
groupDigits,
NEGATIVE_SIGN,
normalizeNumeric,
Numeric,
} from '../../../utils/numeric';
import getLatinPowerName from '../../../utils/common/latinPowers';
import {getGroupDigitsName} from '../../common/de';

const config = {
thousandName: 'tausend',
thousandOrdinalName: 'tausendste',
millia: 'millia',
illion: 'illion',
illionth: 'illionste',
illiard: 'illiarde',
illiardth: 'illiardste',
hundredsLatinNames: [
'',
'cen',
'duocen',
'trecen',
'quadringen',
'quingen',
'sescen',
'septingen',
'octingen',
'nongen',
],
onesLatinNames: ['', 'un', 'duo', 'tre', 'quattuor', 'quin', 'sex', 'septen', 'octo', 'novem'],
tensLatinNames: [
'',
'dec',
'vigin',
'trigin',
'quadragin',
'quinquagin',
'sexagin',
'septuagin',
'octogin',
'nonagin',
],
onesSpecialLatinNames: ['', 'm', 'b', 'tr', 'quadr', 'quin', 'sex', 'sep', 'oct', 'non'],
negative: 'negative',
grouping: 3,
};

const getGroupIndexName = (index: BigNumber, digits: string, ordinal: boolean) => {
if (index.eq(1)) {
return ordinal ? config.thousandOrdinalName : config.thousandName;
}

const basicIndex = index.dividedToIntegerBy(2);
const isOdd = index.mod(2).eq(1);
const latinPowerName = getLatinPowerName(basicIndex, isOdd, ordinal, config);
const latinPowerNameWithCase = latinPowerName.slice(0, 1).toUpperCase() + latinPowerName.slice(1);
if (digits.padStart(config.grouping, BLANK_DIGIT) === '001' || ordinal) {
return latinPowerNameWithCase;
}
if (latinPowerNameWithCase.endsWith('e')) {
return latinPowerNameWithCase + 'n';
}
return latinPowerNameWithCase + 'en';
};

const getGroupName = (g: [string, BigNumber], ordinal: boolean) => {
const [digits, index] = g;
if (index.lt(1)) {
return getGroupDigitsName(digits, index, ordinal);
}
if (index.lt(2)) {
return [getGroupDigitsName(digits, index, false), getGroupIndexName(index, digits, ordinal)].join('');
}
return [getGroupDigitsName(digits, index, false), getGroupIndexName(index, digits, ordinal)].join(' ');
};

type Options = {
groupSeparator: string,
ordinal: boolean,
}

const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
const {
groupSeparator = ' ',
ordinal = false,
} = options;

const x = normalizeNumeric(xRaw);
const {significandDigits, exponent} = deconstructNumeric(x);
const blankDigits = createBlankDigits(config.grouping);
const groups = groupDigits(significandDigits, exponent, config.grouping);

if (groups.length === 1) {
return getGroupName(groups[0], ordinal);
}

const base = groups
.filter(([digits]) => digits !== blankDigits)
.map((g, i, gg) => getGroupName(g, ordinal ? i === gg.length - 1 : false))
.join(groupSeparator);

if (x.startsWith(NEGATIVE_SIGN)) {
return [config.negative, base].join(' ');
}

return base;
};

export default getLocalizedNumberName;

packages/core/src/locales/de-DE/plurals.test.ts → packages/core/src/locales/variants/de-DE/plurals.test.ts View File

@@ -1,14 +1,14 @@
import getNumberName from '../..';
import getLocalizedNumberName from '.';
import getNumberName from '../../../index';
import getLocalizedNumberName from './index';


describe('Plurals', () => { describe('Plurals', () => {
describe('1 in millions place', () => { describe('1 in millions place', () => {
const value = 1000000
const name = 'eine Million'
const value = 1000000;
const name = 'eine Million';
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});


describe.each` describe.each`
ones | onesName ones | onesName
@@ -24,20 +24,20 @@ describe('Plurals', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = ones * 1000000
const name = `${onesName} Millionen`
const value = ones * 1000000;
const name = `${onesName} Millionen`;
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});


describe('1 in billions place', () => { describe('1 in billions place', () => {
const value = 1000000000
const name = 'eine Milliarde'
const value = 1000000000;
const name = 'eine Milliarde';
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});


describe.each` describe.each`
ones | onesName ones | onesName
@@ -53,10 +53,10 @@ describe('Plurals', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = ones * 1000000000
const name = `${onesName} Milliarden`
const value = ones * 1000000000;
const name = `${onesName} Milliarden`;
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(name)
})
})
})
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(name);
});
});
});

packages/core/src/locales/en-GB/chongo.test.ts → packages/core/src/locales/variants/en-GB/chongo.test.ts View File

@@ -1,5 +1,5 @@
import getNumberName from '../..';
import getLocalizedNumberName from '.';
import getNumberName from '../../../index';
import getLocalizedNumberName from './index';


describe('Landon\'s original test cases', () => { describe('Landon\'s original test cases', () => {
describe('Basic conversions', () => { describe('Basic conversions', () => {
@@ -12,10 +12,10 @@ describe('Landon\'s original test cases', () => {
${1000000000000} | ${'one billion'} ${1000000000000} | ${'one billion'}
${1000000000000000} | ${'one thousand billion'} ${1000000000000000} | ${'one thousand billion'}
${1000000000000000000} | ${'one trillion'} ${1000000000000000000} | ${'one trillion'}
`('converts $value to $traditionalBritishName', ({ value, traditionalBritishName }) => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(traditionalBritishName)
})
})
`('converts $value to $traditionalBritishName', ({value, traditionalBritishName}) => {
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(traditionalBritishName);
});
});


describe('Medium size numbers (<= 1e+63)', () => { describe('Medium size numbers (<= 1e+63)', () => {
describe('Table 1', () => { describe('Table 1', () => {
@@ -40,9 +40,9 @@ describe('Landon\'s original test cases', () => {
${'1e+57'} | ${'thousand nonillion'} ${'1e+57'} | ${'thousand nonillion'}
${'1e+60'} | ${'decillion'} ${'1e+60'} | ${'decillion'}
${'1e+63'} | ${'thousand decillion'} ${'1e+63'} | ${'thousand decillion'}
`('converts $value to $traditionalBritishName', ({ value, traditionalBritishName, }) => {
expect(getNumberName(value, { locale: getLocalizedNumberName })).toBe(`one ${traditionalBritishName}`)
})
})
})
})
`('converts $value to $traditionalBritishName', ({value, traditionalBritishName}) => {
expect(getNumberName(value, {locale: getLocalizedNumberName})).toBe(`one ${traditionalBritishName}`);
});
});
});
});

+ 104
- 0
packages/core/src/locales/variants/en-GB/index.ts View File

@@ -0,0 +1,104 @@
import BigNumber from 'bignumber.js';
import {
createBlankDigits,
deconstructNumeric,
groupDigits,
NEGATIVE_SIGN,
normalizeNumeric,
Numeric,
} from '../../../utils/numeric';
import getLatinPowerName from '../../../utils/common/latinPowers';
import {getGroupDigitsName} from '../../common/en';

const config = {
thousandName: 'thousand',
thousandOrdinalName: 'thousandth',
millia: 'millia',
illion: 'illion',
illionth: 'illionth',
hundredsLatinNames: [
'',
'cen',
'duocen',
'trecen',
'quadringen',
'quingen',
'sescen',
'septingen',
'octingen',
'nongen',
],
onesLatinNames: ['', 'un', 'duo', 'tre', 'quattuor', 'quin', 'sex', 'septen', 'octo', 'novem'],
tensLatinNames: [
'',
'dec',
'vigin',
'trigin',
'quadragin',
'quinquagin',
'sexagin',
'septuagin',
'octogin',
'nonagin',
],
onesSpecialLatinNames: ['', 'm', 'b', 'tr', 'quadr', 'quin', 'sex', 'sep', 'oct', 'non'],
negative: 'negative',
grouping: 3,
};

const getGroupIndexName = (index: BigNumber, ordinal: boolean) => {
if (index.eq(1)) {
return ordinal ? config.thousandOrdinalName : config.thousandName;
}

const basicIndex = index.dividedToIntegerBy(2);
const isOdd = false;
const latinPowerName = getLatinPowerName(basicIndex, isOdd, ordinal, config);
if (index.mod(2).eq(1)) {
return [config.thousandName, latinPowerName].join(' ');
}

return latinPowerName;
};

const getGroupName = (g: [string, BigNumber], ordinal: boolean) => {
const [digits, index] = g;
if (index.lt(1)) {
return getGroupDigitsName(digits, ordinal);
}
return [getGroupDigitsName(digits, false), getGroupIndexName(index, ordinal)].join(' ');
};

type Options = {
groupSeparator: string,
ordinal: boolean,
}

const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
const {
groupSeparator = ' ',
ordinal = false,
} = options;

const x = normalizeNumeric(xRaw);
const {significandDigits, exponent} = deconstructNumeric(x);
const blankDigits = createBlankDigits(config.grouping);
const groups = groupDigits(significandDigits, exponent, config.grouping);

if (groups.length === 1) {
return getGroupName(groups[0], ordinal);
}

const base = groups
.filter(([digits]) => digits !== blankDigits)
.map((g, i, gg) => getGroupName(g, ordinal ? i === gg.length - 1 : false))
.join(groupSeparator);

if (x.startsWith(NEGATIVE_SIGN)) {
return [config.negative, base].join(' ');
}

return base;
};

export default getLocalizedNumberName;

packages/core/src/locales/en-PH/chongo.test.ts → packages/core/src/locales/variants/en-PH/chongo.test.ts View File

@@ -1,4 +1,4 @@
import getNumberName from '../..';
import getNumberName from '../../../index';


describe('Landon\'s original test cases', () => { describe('Landon\'s original test cases', () => {
describe('Basic conversions', () => { describe('Basic conversions', () => {
@@ -11,30 +11,57 @@ describe('Landon\'s original test cases', () => {
${1000000000000} | ${'one trillion'} ${1000000000000} | ${'one trillion'}
${1000000000000000} | ${'one quadrillion'} ${1000000000000000} | ${'one quadrillion'}
${1000000000000000000} | ${'one quintillion'} ${1000000000000000000} | ${'one quintillion'}
`('converts $value to $americanName', ({ value, americanName }) => {
expect(getNumberName(value)).toBe(americanName)
})
`('converts $value to $americanName', ({value, americanName}) => {
expect(getNumberName(value)).toBe(americanName);
});


it('converts 987654321 to nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one', () => {
expect(getNumberName(987654321)).toBe('nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one')
})
it(
'converts 987654321 to nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one',
() => {
expect(getNumberName(987654321))
.toBe('nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one');
},
);


it('converts 123456789246801357 to one hundred twenty three quadrillion four hundred fifty six trillion seven hundred eighty nine billion two hundred forty six million eight hundred one thousand three hundred fifty seven', () => {
expect(getNumberName('123456789246801357')).toBe('one hundred twenty three quadrillion four hundred fifty six trillion seven hundred eighty nine billion two hundred forty six million eight hundred one thousand three hundred fifty seven')
})
it('converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three duoseptuagintillion four hundred fifty six unseptuagintillion seven hundred eighty nine septuagintillion two hundred forty six novemsexagintillion eight hundred one octosexagintillion three hundred fifty seven septensexagintillion', () => {
expect(getNumberName('123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')).toBe('one hundred twenty three duoseptuagintillion four hundred fifty six unseptuagintillion seven hundred eighty nine septuagintillion two hundred forty six novemsexagintillion eight hundred one octosexagintillion three hundred fifty seven septensexagintillion')
})
it('converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three cenduoseptuagintillion four hundred fifty six cenunseptuagintillion seven hundred eighty nine censeptuagintillion two hundred forty six cennovemsexagintillion eight hundred one cenoctosexagintillion three hundred fifty seven censeptensexagintillion', () => {
expect(getNumberName('123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')).toBe('one hundred twenty three cenduoseptuagintillion four hundred fifty six cenunseptuagintillion seven hundred eighty nine censeptuagintillion two hundred forty six cennovemsexagintillion eight hundred one cenoctosexagintillion three hundred fifty seven censeptensexagintillion')
})
it(
'converts 123456789246801357 to one hundred twenty three quadrillion four hundred fifty six trillion seven hundred eighty nine billion two hundred forty six million eight hundred one thousand three hundred fifty seven',
() => {
expect(getNumberName('123456789246801357'))
.toBe(
'one hundred twenty three quadrillion four hundred fifty six trillion seven hundred eighty nine billion two hundred forty six million eight hundred one thousand three hundred fifty seven');
},
);


it('converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three trecenduoseptuagintillion four hundred fifty six trecenunseptuagintillion seven hundred eighty nine trecenseptuagintillion two hundred forty six trecennovemsexagintillion eight hundred one trecenoctosexagintillion three hundred fifty seven trecenseptensexagintillion', () => {
expect(getNumberName('123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')).toBe('one hundred twenty three trecenduoseptuagintillion four hundred fifty six trecenunseptuagintillion seven hundred eighty nine trecenseptuagintillion two hundred forty six trecennovemsexagintillion eight hundred one trecenoctosexagintillion three hundred fifty seven trecenseptensexagintillion')
})
})
it(
'converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three duoseptuagintillion four hundred fifty six unseptuagintillion seven hundred eighty nine septuagintillion two hundred forty six novemsexagintillion eight hundred one octosexagintillion three hundred fifty seven septensexagintillion',
() => {
expect(getNumberName(
'123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
.toBe(
'one hundred twenty three duoseptuagintillion four hundred fifty six unseptuagintillion seven hundred eighty nine septuagintillion two hundred forty six novemsexagintillion eight hundred one octosexagintillion three hundred fifty seven septensexagintillion');
},
);

it(
'converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three cenduoseptuagintillion four hundred fifty six cenunseptuagintillion seven hundred eighty nine censeptuagintillion two hundred forty six cennovemsexagintillion eight hundred one cenoctosexagintillion three hundred fifty seven censeptensexagintillion',
() => {
expect(getNumberName(
'123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
.toBe(
'one hundred twenty three cenduoseptuagintillion four hundred fifty six cenunseptuagintillion seven hundred eighty nine censeptuagintillion two hundred forty six cennovemsexagintillion eight hundred one cenoctosexagintillion three hundred fifty seven censeptensexagintillion');
},
);

it(
'converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three trecenduoseptuagintillion four hundred fifty six trecenunseptuagintillion seven hundred eighty nine trecenseptuagintillion two hundred forty six trecennovemsexagintillion eight hundred one trecenoctosexagintillion three hundred fifty seven trecenseptensexagintillion',
() => {
expect(getNumberName(
'123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
.toBe(
'one hundred twenty three trecenduoseptuagintillion four hundred fifty six trecenunseptuagintillion seven hundred eighty nine trecenseptuagintillion two hundred forty six trecennovemsexagintillion eight hundred one trecenoctosexagintillion three hundred fifty seven trecenseptensexagintillion');
},
);
});


describe('Medium size numbers (<= 1e+63)', () => { describe('Medium size numbers (<= 1e+63)', () => {
describe('Table 1', () => { describe('Table 1', () => {
@@ -59,11 +86,11 @@ describe('Landon\'s original test cases', () => {
${'1e+57'} | ${'octodecillion'} ${'1e+57'} | ${'octodecillion'}
${'1e+60'} | ${'novemdecillion'} ${'1e+60'} | ${'novemdecillion'}
${'1e+63'} | ${'vigintillion'} ${'1e+63'} | ${'vigintillion'}
`('converts $value to $americanName', ({ value, americanName }) => {
expect(getNumberName(value)).toBe(`one ${americanName}`)
})
})
})
`('converts $value to $americanName', ({value, americanName}) => {
expect(getNumberName(value)).toBe(`one ${americanName}`);
});
});
});


describe('Large size numbers (< 1e+303)', () => { describe('Large size numbers (< 1e+303)', () => {
it.each` it.each`
@@ -88,10 +115,10 @@ describe('Landon\'s original test cases', () => {
${'1e+243'} | ${'octogintillion'} ${'1e+243'} | ${'octogintillion'}
${'1e+273'} | ${'nonagintillion'} ${'1e+273'} | ${'nonagintillion'}
${'1e+300'} | ${'novemnonagintillion'} ${'1e+300'} | ${'novemnonagintillion'}
`('converts $value to $americanName', ({ value, americanName }) => {
expect(getNumberName(value)).toBe(`one ${americanName}`)
})
})
`('converts $value to $americanName', ({value, americanName}) => {
expect(getNumberName(value)).toBe(`one ${americanName}`);
});
});


describe('Gigantic size numbers (< 1e+3003)', () => { describe('Gigantic size numbers (< 1e+3003)', () => {
it.each` it.each`
@@ -114,15 +141,21 @@ describe('Landon\'s original test cases', () => {
${'1e+2103'} | ${'septingentillion'} ${'1e+2103'} | ${'septingentillion'}
${'1e+2403'} | ${'octingentillion'} ${'1e+2403'} | ${'octingentillion'}
${'1e+2703'} | ${'nongentillion'} ${'1e+2703'} | ${'nongentillion'}
`('converts $value to $americanName', ({ value, americanName }) => {
expect(getNumberName(value)).toBe(`one ${americanName}`)
})
})
`('converts $value to $americanName', ({value, americanName}) => {
expect(getNumberName(value)).toBe(`one ${americanName}`);
});
});


describe('Titanic size numbers (< 1e+3000003)', () => { describe('Titanic size numbers (< 1e+3000003)', () => {
it('converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three milliaduoseptuagintillion four hundred fifty six milliaunseptuagintillion seven hundred eighty nine milliaseptuagintillion two hundred forty six millianovemsexagintillion eight hundred one milliaoctosexagintillion three hundred fifty seven milliaseptensexagintillion', () => {
expect(getNumberName('123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')).toBe('one hundred twenty three milliaduoseptuagintillion four hundred fifty six milliaunseptuagintillion seven hundred eighty nine milliaseptuagintillion two hundred forty six millianovemsexagintillion eight hundred one milliaoctosexagintillion three hundred fifty seven milliaseptensexagintillion')
})
it(
'converts 123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 to one hundred twenty three milliaduoseptuagintillion four hundred fifty six milliaunseptuagintillion seven hundred eighty nine milliaseptuagintillion two hundred forty six millianovemsexagintillion eight hundred one milliaoctosexagintillion three hundred fifty seven milliaseptensexagintillion',
() => {
expect(getNumberName(
'123456789246801357000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
.toBe(
'one hundred twenty three milliaduoseptuagintillion four hundred fifty six milliaunseptuagintillion seven hundred eighty nine milliaseptuagintillion two hundred forty six millianovemsexagintillion eight hundred one milliaoctosexagintillion three hundred fifty seven milliaseptensexagintillion');
},
);


it.each` it.each`
value | americanName value | americanName
@@ -137,8 +170,8 @@ describe('Landon\'s original test cases', () => {
${'1e+1174743648579'} | ${'one trecenunnonaginmilliamilliamilliaquingenunoctoginmilliamilliaduocensexdecmilliacenduononagintillion'} ${'1e+1174743648579'} | ${'one trecenunnonaginmilliamilliamilliaquingenunoctoginmilliamilliaduocensexdecmilliacenduononagintillion'}
${'1e+3000000000003'} | ${'one milliamilliamilliamilliatillion'} ${'1e+3000000000003'} | ${'one milliamilliamilliamilliatillion'}
${'1e+696276510359811'} | ${'one duocenduotriginmilliamilliamilliamilliaduononaginmilliamilliamilliacenseptuaginmilliamilliacennovemdecmillianongensextrigintillion'} ${'1e+696276510359811'} | ${'one duocenduotriginmilliamilliamilliamilliaduononaginmilliamilliamilliacenseptuaginmilliamilliacennovemdecmillianongensextrigintillion'}
`('converts $value to $americanName', ({ value, americanName }) => {
expect(getNumberName(value)).toBe(americanName)
})
})
})
`('converts $value to $americanName', ({value, americanName}) => {
expect(getNumberName(value)).toBe(americanName);
});
});
});

+ 34
- 0
packages/core/src/locales/variants/en-PH/custom.test.ts View File

@@ -0,0 +1,34 @@
import getNumberName from '../../../index';

describe('Custom numbers', () => {
it('converts 123456000 to one hundred twenty three million four hundred fifty six thousand', () => {
expect(getNumberName(123456000)).toBe('one hundred twenty three million four hundred fifty six thousand');
});
it('converts 123456000 to one hundred twenty three million four hundred fifty six thousand nine', () => {
expect(getNumberName(123456009)).toBe('one hundred twenty three million four hundred fifty six thousand nine');
});

it('converts 123000789 to one hundred twenty three million seven hundred eighty nine', () => {
expect(getNumberName(123000789)).toBe('one hundred twenty three million seven hundred eighty nine');
});

it('converts 123050789 to one hundred twenty three million fifty thousand seven hundred eighty nine', () => {
expect(getNumberName(123050789)).toBe('one hundred twenty three million fifty thousand seven hundred eighty nine');
});

it(
'converts 123456789 to one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine',
() => {
expect(getNumberName(123456789))
.toBe('one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine');
},
);

it(
'converts -123456789 to negative one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine',
() => {
expect(getNumberName(-123456789))
.toBe('negative one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine');
},
);
});

packages/core/src/locales/en-PH/groups.test.ts → packages/core/src/locales/variants/en-PH/groups.test.ts View File

@@ -1,4 +1,4 @@
import getNumberName from '../..';
import getNumberName from '../../../index';


describe('Number group conversion', () => { describe('Number group conversion', () => {
describe('0 in hundreds place', () => { describe('0 in hundreds place', () => {
@@ -20,10 +20,10 @@ describe('Number group conversion', () => {
onesName, onesName,
}) => { }) => {
it(`converts ${ones} to ${onesName}`, () => { it(`converts ${ones} to ${onesName}`, () => {
expect(getNumberName(ones)).toBe(onesName)
})
})
})
expect(getNumberName(ones)).toBe(onesName);
});
});
});


describe('1 in tens place', () => { describe('1 in tens place', () => {
describe.each` describe.each`
@@ -43,10 +43,10 @@ describe('Number group conversion', () => {
onesName, onesName,
}) => { }) => {
it(`converts 1${ones} to ${onesName}`, () => { it(`converts 1${ones} to ${onesName}`, () => {
expect(getNumberName(10 + ones)).toBe(onesName)
})
})
})
expect(getNumberName(10 + ones)).toBe(onesName);
});
});
});


describe.each` describe.each`
tens | tensName tens | tensName
@@ -58,7 +58,7 @@ describe('Number group conversion', () => {
${7} | ${'seventy'} ${7} | ${'seventy'}
${8} | ${'eighty'} ${8} | ${'eighty'}
${9} | ${'ninety'} ${9} | ${'ninety'}
`('$tens in tens place', ({ tens, tensName }) => {
`('$tens in tens place', ({tens, tensName}) => {
describe.each` describe.each`
ones | onesName ones | onesName
${0} | ${''} ${0} | ${''}
@@ -75,15 +75,15 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (tens * 10) + ones
const name = [tensName, onesName].join(' ').trim()
const value = (tens * 10) + ones;
const name = [tensName, onesName].join(' ').trim();


it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value)).toBe(name)
})
})
})
})
expect(getNumberName(value)).toBe(name);
});
});
});
});


describe.each` describe.each`
hundreds | hundredsName hundreds | hundredsName
@@ -117,13 +117,13 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (hundreds * 100) + ones
const name = [hundredsName, onesName].join(' ').trim()
const value = (hundreds * 100) + ones;
const name = [hundredsName, onesName].join(' ').trim();
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value)).toBe(name)
})
})
})
expect(getNumberName(value)).toBe(name);
});
});
});


describe('1 in tens place', () => { describe('1 in tens place', () => {
describe.each` describe.each`
@@ -142,13 +142,13 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (hundreds * 100) + 10 + ones
const name = [hundredsName, onesName].join(' ').trim()
const value = (hundreds * 100) + 10 + ones;
const name = [hundredsName, onesName].join(' ').trim();
it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value)).toBe(name)
})
})
})
expect(getNumberName(value)).toBe(name);
});
});
});


describe.each` describe.each`
tens | tensName tens | tensName
@@ -160,7 +160,7 @@ describe('Number group conversion', () => {
${7} | ${'seventy'} ${7} | ${'seventy'}
${8} | ${'eighty'} ${8} | ${'eighty'}
${9} | ${'ninety'} ${9} | ${'ninety'}
`('$tens in tens place', ({ tens, tensName }) => {
`('$tens in tens place', ({tens, tensName}) => {
describe.each` describe.each`
ones | onesName ones | onesName
${0} | ${''} ${0} | ${''}
@@ -177,13 +177,13 @@ describe('Number group conversion', () => {
ones, ones,
onesName, onesName,
}) => { }) => {
const value = (hundreds * 100) + (tens * 10) + ones
const name = [hundredsName, tensName, onesName].join(' ').trim()
const value = (hundreds * 100) + (tens * 10) + ones;
const name = [hundredsName, tensName, onesName].join(' ').trim();


it(`converts ${value} to ${name}`, () => { it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value)).toBe(name)
})
})
})
})
})
expect(getNumberName(value)).toBe(name);
});
});
});
});
});

+ 99
- 0
packages/core/src/locales/variants/en-PH/index.ts View File

@@ -0,0 +1,99 @@
import BigNumber from 'bignumber.js';
import {
createBlankDigits,
deconstructNumeric,
groupDigits,
NEGATIVE_SIGN,
normalizeNumeric,
Numeric,
} from '../../../utils/numeric';
import getLatinPowerName from '../../../utils/common/latinPowers';
import {getGroupDigitsName} from '../../common/en';

const config = {
thousandName: 'thousand',
thousandOrdinalName: 'thousandth',
millia: 'millia',
illion: 'illion',
illionth: 'illionth',
hundredsLatinNames: [
'',
'cen',
'duocen',
'trecen',
'quadringen',
'quingen',
'sescen',
'septingen',
'octingen',
'nongen',
],
onesLatinNames: ['', 'un', 'duo', 'tre', 'quattuor', 'quin', 'sex', 'septen', 'octo', 'novem'],
tensLatinNames: [
'',
'dec',
'vigin',
'trigin',
'quadragin',
'quinquagin',
'sexagin',
'septuagin',
'octogin',
'nonagin',
],
onesSpecialLatinNames: ['', 'm', 'b', 'tr', 'quadr', 'quin', 'sex', 'sep', 'oct', 'non'],
negative: 'negative',
grouping: 3,
};

const getGroupIndexName = (index: BigNumber, ordinal: boolean) => {
if (index.eq(1)) {
return ordinal ? config.thousandOrdinalName : config.thousandName;
}

const basicIndex = index.minus(1);
const isOdd = false;
return getLatinPowerName(basicIndex, isOdd, ordinal, config);
};

const getGroupName = (g: [string, BigNumber], ordinal: boolean) => {
const [digits, index] = g;
if (index.lt(1)) {
return getGroupDigitsName(digits, ordinal);
}
return [getGroupDigitsName(digits, false), getGroupIndexName(index, ordinal)].join(' ');
};

type Options = {
groupSeparator: string,
ordinal: boolean,
}

const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
const {
groupSeparator = ' ',
ordinal = false,
} = options;

const x = normalizeNumeric(xRaw);
const {significandDigits, exponent} = deconstructNumeric(x);
const blankDigits = createBlankDigits(config.grouping);
const groups = groupDigits(significandDigits, exponent, config.grouping);

if (groups.length === 1) {
return getGroupName(groups[0], ordinal);
}

const base = groups
.filter(([digits]) => digits !== blankDigits)
.map((g, i, gg) => getGroupName(g, ordinal ? i === gg.length - 1 : false))
.join(groupSeparator);

if (x.startsWith(NEGATIVE_SIGN)) {
return [config.negative, base].join(' ');
}

return base;
};

export default getLocalizedNumberName;

+ 213
- 0
packages/core/src/locales/variants/en-PH/ordinals.test.ts View File

@@ -0,0 +1,213 @@
import getNumberName from '../../../index';

describe('Ordinals', () => {
describe('0 in hundreds place', () => {
describe('0 in tens place', () => {
describe.each`
ones | onesName
${0} | ${'zeroth'}
${1} | ${'first'}
${2} | ${'second'}
${3} | ${'third'}
${4} | ${'fourth'}
${5} | ${'fifth'}
${6} | ${'sixth'}
${7} | ${'seventh'}
${8} | ${'eighth'}
${9} | ${'ninth'}
`('$ones in ones place', ({
ones,
onesName,
}) => {
it(`converts ${ones} to ${onesName}`, () => {
expect(getNumberName(ones, {ordinal: true})).toBe(onesName);
});
});
});

describe('1 in tens place', () => {
describe.each`
ones | onesName
${0} | ${'tenth'}
${1} | ${'eleventh'}
${2} | ${'twelfth'}
${3} | ${'thirteenth'}
${4} | ${'fourteenth'}
${5} | ${'fifteenth'}
${6} | ${'sixteenth'}
${7} | ${'seventeenth'}
${8} | ${'eighteenth'}
${9} | ${'nineteenth'}
`('$ones in ones place', ({
ones,
onesName,
}) => {
it(`converts 1${ones} to ${onesName}`, () => {
expect(getNumberName(10 + ones, {ordinal: true})).toBe(onesName);
});
});
});

describe.each`
tens | tensName
${2} | ${'twenty'}
${3} | ${'thirty'}
${4} | ${'forty'}
${5} | ${'fifty'}
${6} | ${'sixty'}
${7} | ${'seventy'}
${8} | ${'eighty'}
${9} | ${'ninety'}
`('$tens in tens place', ({tens, tensName}) => {
describe('0 in ones place', () => {
const value = (tens * 10);
const name = tensName.replace(/y$/, 'ieth');

it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});

describe.each`
ones | onesName
${1} | ${'first'}
${2} | ${'second'}
${3} | ${'third'}
${4} | ${'fourth'}
${5} | ${'fifth'}
${6} | ${'sixth'}
${7} | ${'seventh'}
${8} | ${'eighth'}
${9} | ${'ninth'}
`('$ones in ones place', ({
ones,
onesName,
}) => {
const value = (tens * 10) + ones;
const name = [tensName, onesName].join(' ').trim();

it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});
});
});

describe.each`
hundreds | hundredsName
${1} | ${'one hundred'}
${2} | ${'two hundred'}
${3} | ${'three hundred'}
${4} | ${'four hundred'}
${5} | ${'five hundred'}
${6} | ${'six hundred'}
${7} | ${'seven hundred'}
${8} | ${'eight hundred'}
${9} | ${'nine hundred'}
`('$hundreds in hundreds place', ({
hundreds,
hundredsName,
}) => {
describe('0 in tens place', () => {
describe('0 in ones place', () => {
const value = (hundreds * 100);
const name = hundredsName + 'th';

it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});

describe.each`
ones | onesName
${1} | ${'first'}
${2} | ${'second'}
${3} | ${'third'}
${4} | ${'fourth'}
${5} | ${'fifth'}
${6} | ${'sixth'}
${7} | ${'seventh'}
${8} | ${'eighth'}
${9} | ${'ninth'}
`('$ones in ones place', ({
ones,
onesName,
}) => {
const value = (hundreds * 100) + ones;
const name = [hundredsName, onesName].join(' ').trim();
it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});
});

describe('1 in tens place', () => {
describe.each`
ones | onesName
${0} | ${'tenth'}
${1} | ${'eleventh'}
${2} | ${'twelfth'}
${3} | ${'thirteenth'}
${4} | ${'fourteenth'}
${5} | ${'fifteenth'}
${6} | ${'sixteenth'}
${7} | ${'seventeenth'}
${8} | ${'eighteenth'}
${9} | ${'nineteenth'}
`('$ones in ones place', ({
ones,
onesName,
}) => {
const value = (hundreds * 100) + 10 + ones;
const name = [hundredsName, onesName].join(' ').trim();
it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});
});

describe.each`
tens | tensName
${2} | ${'twenty'}
${3} | ${'thirty'}
${4} | ${'forty'}
${5} | ${'fifty'}
${6} | ${'sixty'}
${7} | ${'seventy'}
${8} | ${'eighty'}
${9} | ${'ninety'}
`('$tens in tens place', ({tens, tensName}) => {
describe('0 in ones place', () => {
const value = (hundreds * 100) + (tens * 10);
const name = [hundredsName, tensName.replace(/y$/, 'ieth')].join(' ').trim();

it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});

describe.each`
ones | onesName
${1} | ${'first'}
${2} | ${'second'}
${3} | ${'third'}
${4} | ${'fourth'}
${5} | ${'fifth'}
${6} | ${'sixth'}
${7} | ${'seventh'}
${8} | ${'eighth'}
${9} | ${'ninth'}
`('$ones in ones place', ({
ones,
onesName,
}) => {
const value = (hundreds * 100) + (tens * 10) + ones;
const name = [hundredsName, tensName, onesName].join(' ').trim();

it(`converts ${value} to ${name}`, () => {
expect(getNumberName(value, {ordinal: true})).toBe(name);
});
});
});
});
});

packages/core/src/locales/en-PH/technical.test.ts → packages/core/src/locales/variants/en-PH/technical.test.ts View File

@@ -1,12 +1,17 @@
import getNumberName from '../..';
import getNumberName from '../../../index';


describe('Technical numbers', () => { describe('Technical numbers', () => {
describe('Number.MAX_SAFE_INTEGER', () => { describe('Number.MAX_SAFE_INTEGER', () => {
it('converts Number.MAX_SAFE_INTEGER to nine quadrillion seven trillion one hundred ninety nine billion two hundred fifty four million seven hundred forty thousand nine hundred ninety one', () => {
expect(Number.MAX_SAFE_INTEGER).toBe(9_007_199_254_740_991)
expect(getNumberName(Number.MAX_SAFE_INTEGER)).toBe('nine quadrillion seven trillion one hundred ninety nine billion two hundred fifty four million seven hundred forty thousand nine hundred ninety one')
})
})
it(
'converts Number.MAX_SAFE_INTEGER to nine quadrillion seven trillion one hundred ninety nine billion two hundred fifty four million seven hundred forty thousand nine hundred ninety one',
() => {
expect(Number.MAX_SAFE_INTEGER).toBe(9_007_199_254_740_991);
expect(getNumberName(Number.MAX_SAFE_INTEGER))
.toBe(
'nine quadrillion seven trillion one hundred ninety nine billion two hundred fifty four million seven hundred forty thousand nine hundred ninety one');
},
);
});


describe('Powers of 2', () => { describe('Powers of 2', () => {
it.each` it.each`
@@ -32,8 +37,8 @@ describe('Technical numbers', () => {
${2 ** 18} | ${'two hundred sixty two thousand one hundred forty four'} ${2 ** 18} | ${'two hundred sixty two thousand one hundred forty four'}
${2 ** 19} | ${'five hundred twenty four thousand two hundred eighty eight'} ${2 ** 19} | ${'five hundred twenty four thousand two hundred eighty eight'}
${2 ** 20} | ${'one million forty eight thousand five hundred seventy six'} ${2 ** 20} | ${'one million forty eight thousand five hundred seventy six'}
`('converts $value to $name', ({ value, name }) => {
expect(getNumberName(value)).toBe(name)
})
})
})
`('converts $value to $name', ({value, name}) => {
expect(getNumberName(value)).toBe(name);
});
});
});

+ 50
- 45
packages/core/src/utils/common/latinPowers.ts View File

@@ -7,101 +7,106 @@ interface Config {
onesSpecialLatinNames: string[], onesSpecialLatinNames: string[],
tensLatinNames: string[], tensLatinNames: string[],
illion: string, illion: string,
illionth: string,
illiard?: string, illiard?: string,
illiardth?: string,
millia: string, millia: string,
} }


const getLatinPowerGroupDigitsName = (latinRaw: string, special: boolean, config: Config) => { const getLatinPowerGroupDigitsName = (latinRaw: string, special: boolean, config: Config) => {
const digits = latinRaw.padStart(3, BLANK_DIGIT)
const [hundreds, tens, ones] = digits.split('').map(s => Number(s))
const names = []
const digits = latinRaw.padStart(3, BLANK_DIGIT);
const [hundreds, tens, ones] = digits.split('').map(s => Number(s));
const names = [];


if (hundreds > 0) { if (hundreds > 0) {
names.push(config.hundredsLatinNames[hundreds])
names.push(config.onesLatinNames[ones])
names.push(config.tensLatinNames[tens])
names.push(config.hundredsLatinNames[hundreds]);
names.push(config.onesLatinNames[ones]);
names.push(config.tensLatinNames[tens]);
} else { } else {
if (tens > 0) { if (tens > 0) {
names.push(config.onesLatinNames[ones])
names.push(config.tensLatinNames[tens])
names.push(config.onesLatinNames[ones]);
names.push(config.tensLatinNames[tens]);
} else { } else {
if (special) { if (special) {
names.push(config.onesSpecialLatinNames[ones])
names.push(config.onesSpecialLatinNames[ones]);
} else if (ones > 1) { } else if (ones > 1) {
names.push(config.onesLatinNames[ones])
names.push(config.onesLatinNames[ones]);
} }
} }
} }
return names.join('')
}
return names.join('');
};


const getLatinPowerSuffix = (latinRaw: string, isOdd: boolean, special: boolean, config: Config) => {
const digits = latinRaw.padStart(3, BLANK_DIGIT)
const [hundreds, tens, ones] = digits.split('').map(s => Number(s))
const suffix = isOdd ? config.illiard : config.illion
const getLatinPowerSuffix = (latinRaw: string, isOdd: boolean, special: boolean, ordinal: boolean, config: Config) => {
const digits = latinRaw.padStart(3, BLANK_DIGIT);
const [hundreds, tens, ones] = digits.split('').map(s => Number(s));
const suffix = isOdd ? (ordinal ? config.illiardth : config.illiard) : (ordinal ? config.illionth : config.illion);


if (hundreds > 0) { if (hundreds > 0) {
if (tens !== 1) { if (tens !== 1) {
return 't' + suffix
return 't' + suffix;
} }
return suffix
return suffix;
} }
if (tens > 0) { if (tens > 0) {
switch (tens) { switch (tens) {
case 1: case 1:
return suffix
return suffix;
default: default:
break
break;
} }
return 't' + suffix
return 't' + suffix;
} }
switch (ones) { switch (ones) {
case 1: case 1:
case 2: case 2:
case 3: case 3:
case 4: case 4:
return special ? suffix : 't' + suffix
return special ? suffix : 't' + suffix;
case 5: case 5:
case 6: case 6:
case 7: case 7:
return 't' + suffix
return 't' + suffix;
case 8: case 8:
case 9: case 9:
return suffix
return suffix;
default: default:
break
break;
} }
return ''
}
return '';
};


const getLatinPowerGroupName = (g: Group, config: Config) => { const getLatinPowerGroupName = (g: Group, config: Config) => {
const [digits, index] = g
const [digits, index] = g;
if (index.lt(1)) { if (index.lt(1)) {
return getLatinPowerGroupDigitsName(digits, index.eq(0), config)
return getLatinPowerGroupDigitsName(digits, index.eq(0), config);
} }
let milliaSuffix = ''
let milliaSuffix = '';
for (let i = new BigNumber(0); i.lt(index); i = i.plus(1)) { for (let i = new BigNumber(0); i.lt(index); i = i.plus(1)) {
milliaSuffix += config.millia
milliaSuffix += config.millia;
} }
return [getLatinPowerGroupDigitsName(digits, index.eq(0), config), milliaSuffix].join('')
}
return [getLatinPowerGroupDigitsName(digits, index.eq(0), config), milliaSuffix].join('');
};


const getLatinPowerName = (latinRaw: BigNumber, isOdd: boolean, config: Config) => {
const x = normalizeNumeric(latinRaw)
const { significandDigits, exponent } = deconstructNumeric(x)
const blankDigits = createBlankDigits(3)
const groups = groupDigits(significandDigits, exponent, 3)
const getLatinPowerName = (latinRaw: BigNumber, isOdd: boolean, ordinal: boolean, config: Config) => {
const x = normalizeNumeric(latinRaw);
const {significandDigits, exponent} = deconstructNumeric(x);
const blankDigits = createBlankDigits(3);
const groups = groupDigits(significandDigits, exponent, 3);


if (groups.length === 1) { if (groups.length === 1) {
return [getLatinPowerGroupName(groups[0], config), getLatinPowerSuffix(groups[0][0], isOdd, true, config)].join('')
return [
getLatinPowerGroupName(groups[0], config),
getLatinPowerSuffix(groups[0][0], isOdd, true, ordinal, config),
].join('');
} }


const visibleGroups = groups.filter(([digits]) => digits !== blankDigits)
const [lastVisibleGroup] = visibleGroups.slice(-1)
const visibleGroups = groups.filter(([digits]) => digits !== blankDigits);
const [lastVisibleGroup] = visibleGroups.slice(-1);
return [ return [
...visibleGroups.map(g => getLatinPowerGroupName(g, config)), ...visibleGroups.map(g => getLatinPowerGroupName(g, config)),
getLatinPowerSuffix(lastVisibleGroup[0], isOdd, lastVisibleGroup[1].eq(0), config),
].join('')
}
getLatinPowerSuffix(lastVisibleGroup[0], isOdd, lastVisibleGroup[1].eq(0), ordinal, config),
].join('');
};


export default getLatinPowerName
export default getLatinPowerName;

+ 34
- 31
packages/core/src/utils/numeric.ts View File

@@ -1,11 +1,11 @@
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';


const EXPONENT_SEPARATOR = 'e'
const SIGNIFICAND_DECIMAL_POINT = '.'
const EXPONENT_SEPARATOR = 'e';
const SIGNIFICAND_DECIMAL_POINT = '.';


export const NEGATIVE_SIGN = '-'
export const NEGATIVE_SIGN = '-';


export const BLANK_DIGIT = '0' // must be a value where Number(BLANK_DIGIT) === 0
export const BLANK_DIGIT = '0'; // must be a value where Number(BLANK_DIGIT) === 0


export type Numeric = number | bigint | string | BigNumber export type Numeric = number | bigint | string | BigNumber


@@ -15,59 +15,62 @@ export const normalizeNumeric = (x: Numeric): string => {
try { try {
switch (typeof x) { switch (typeof x) {
case 'number': case 'number':
return new BigNumber(x).toString(10)
return new BigNumber(x).toString(10);
case 'bigint': case 'bigint':
return x.toString(10)
return x.toString(10);
case 'string': case 'string':
// TODO assume not all strings follow a correct format // TODO assume not all strings follow a correct format
return x
return x;
// return new BigNumber(x).toString(10) // return new BigNumber(x).toString(10)
case 'object': case 'object':
return x.toString(10)
return x.toString(10);
default: default:
break
break;
} }
} catch { } catch {
throw new RangeError('Not a valid numeric value in the current locale.')
throw new RangeError('Not a valid numeric value in the current locale.');
} }
throw new TypeError('Not a valid numeric value in any locale.')
}
throw new TypeError('Not a valid numeric value in any locale.');
};


export const deconstructNumeric = (x: string) => { export const deconstructNumeric = (x: string) => {
const absolute = x.replaceAll(NEGATIVE_SIGN, '')
const absolute = x.replaceAll(NEGATIVE_SIGN, '');
if (!absolute.includes(EXPONENT_SEPARATOR)) { if (!absolute.includes(EXPONENT_SEPARATOR)) {
return { return {
exponent: new BigNumber(absolute.length - 1), exponent: new BigNumber(absolute.length - 1),
significandDigits: absolute, significandDigits: absolute,
}
};
} }
const [significandStrExp, exponentStr] = absolute.split(EXPONENT_SEPARATOR)
const [integral] = significandStrExp.split(SIGNIFICAND_DECIMAL_POINT)
const [significandStrExp, exponentStr] = absolute.split(EXPONENT_SEPARATOR);
const [integral] = significandStrExp.split(SIGNIFICAND_DECIMAL_POINT);
return { return {
exponent: new BigNumber(exponentStr).plus(integral.length - 1), exponent: new BigNumber(exponentStr).plus(integral.length - 1),
significandDigits: significandStrExp.replaceAll(SIGNIFICAND_DECIMAL_POINT, '')
}
}
significandDigits: significandStrExp.replaceAll(SIGNIFICAND_DECIMAL_POINT, ''),
};
};


export const createBlankDigits = (grouping: number) => new Array<string>(grouping).fill(BLANK_DIGIT).join('')
export const createBlankDigits = (grouping: number) => new Array<string>(grouping).fill(BLANK_DIGIT).join('');


export const groupDigits = (significandStr: string, exponent: BigNumber, grouping: number) => { export const groupDigits = (significandStr: string, exponent: BigNumber, grouping: number) => {
const blankDigits = createBlankDigits(grouping)
const blankDigits = createBlankDigits(grouping);
return significandStr return significandStr
.split('') .split('')
.reduceRight( .reduceRight(
(theGroups, c, i): any => { (theGroups, c, i): any => {
const currentGroupIndex = exponent.minus(i).dividedToIntegerBy(grouping).minus(1)
const [lastGroup = [blankDigits, currentGroupIndex.plus(1)] as Group] = theGroups
const [digits, groupIndex] = lastGroup
const currentPlaceValue = exponent.minus(i).mod(grouping)
const currentGroupIndex = exponent.minus(i).dividedToIntegerBy(grouping).minus(1);
const [lastGroup = [blankDigits, currentGroupIndex.plus(1)] as Group] = theGroups;
const [digits, groupIndex] = lastGroup;
const currentPlaceValue = exponent.minus(i).mod(grouping);
if (currentPlaceValue.eq(0)) { if (currentPlaceValue.eq(0)) {
return [[`${blankDigits.slice(0, -c.length)}${c}`, currentGroupIndex.plus(1)], ...theGroups]
return [[`${blankDigits.slice(0, -c.length)}${c}`, currentGroupIndex.plus(1)], ...theGroups];
} }
const currentDigitStringIndex = new BigNumber(grouping).minus(1).minus(currentPlaceValue).toNumber()
const newDigits = digits.slice(0, currentDigitStringIndex) + c + digits.slice(currentDigitStringIndex + c.length)
return [[newDigits, groupIndex] as Group, ...theGroups.slice(1)]
const currentDigitStringIndex = new BigNumber(grouping).minus(1).minus(currentPlaceValue).toNumber();
const newDigits = digits.slice(
0,
currentDigitStringIndex,
) + c + digits.slice(currentDigitStringIndex + c.length);
return [[newDigits, groupIndex] as Group, ...theGroups.slice(1)];
}, },
[] as Group[], [] as Group[],
)
}
);
};

Loading…
Cancel
Save