Gets the name of a number, even if it's stupidly big. Supersedes TheoryOfNekomata/number-name.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

116 строки
3.7 KiB

  1. import BigNumber from 'bignumber.js';
  2. import {
  3. BLANK_DIGIT,
  4. createBlankDigits,
  5. deconstructNumeric,
  6. groupDigits,
  7. NEGATIVE_SIGN,
  8. normalizeNumeric,
  9. Numeric,
  10. } from '../../utils/numeric';
  11. import getLatinPowerName from '../../utils/common/latinPowers';
  12. const config = {
  13. "onesNames": ['zero', 'ein', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun'],
  14. "teensNames": ['zehn', 'elf', 'zwölf', 'dreizehn', 'vierzehn', 'fünfzehn', 'sechzehn', 'siebzehn', 'achtzehn', 'neunzehn'],
  15. "tensNames": ['zero', 'zehn', 'zwanzig', 'dreißig', 'vierzig', 'fünfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig'],
  16. "hundredName": "hundert",
  17. "thousandName": "tausend",
  18. "millia": "millia",
  19. "illion": "illion",
  20. "illiard": "illiarde",
  21. "and": "und",
  22. "hundredsLatinNames": ["", "cen", "duocen", "trecen", "quadringen", "quingen", "sescen", "septingen", "octingen", "nongen"],
  23. "onesLatinNames": ["", "un", "duo", "tre", "quattuor", "quin", "sex", "septen", "octo", "novem"],
  24. "tensLatinNames": ["", "dec", "vigin", "trigin", "quadragin", "quinquagin", "sexagin", "septuagin", "octogin", "nonagin"],
  25. "onesSpecialLatinNames": ["", "m", "b", "tr", "quadr", "quin", "sex", "sep", "oct", "non"],
  26. "negative": "negative",
  27. "grouping": 3,
  28. "latinGrouping": 3
  29. }
  30. const getGroupIndexName = (index: BigNumber, digits: string) => {
  31. if (index.eq(1)) {
  32. return config.thousandName
  33. }
  34. const basicIndex = index.dividedToIntegerBy(2)
  35. const isOdd = index.mod(2).eq(1)
  36. const latinPowerName = getLatinPowerName(basicIndex, isOdd, config)
  37. const latinPowerNameWithCase = latinPowerName.slice(0, 1).toUpperCase() + latinPowerName.slice(1)
  38. if (digits.padStart(config.grouping, BLANK_DIGIT) === '001') {
  39. return latinPowerNameWithCase
  40. }
  41. if (latinPowerNameWithCase.endsWith('e')) {
  42. return latinPowerNameWithCase + 'n'
  43. }
  44. return latinPowerNameWithCase + 'en'
  45. }
  46. const getGroupDigitsName = (digitsRaw: string, index: BigNumber) => {
  47. const { grouping, onesNames, teensNames, tensNames, hundredName } = config
  48. const digits = digitsRaw.padStart(grouping, BLANK_DIGIT)
  49. const [hundreds, tens, ones] = digits.split('').map(s => Number(s))
  50. const names = []
  51. if (hundreds !== 0) {
  52. names.push(onesNames[hundreds])
  53. names.push(hundredName)
  54. }
  55. if (tens === 1) {
  56. names.push(teensNames[ones])
  57. } else if (tens > 1) {
  58. if (ones > 0) {
  59. names.push(onesNames[ones])
  60. names.push(config.and)
  61. }
  62. names.push(tensNames[tens])
  63. } else {
  64. if (hundreds === 0 && ones === 1 && index.gte(2)) {
  65. names.push(onesNames[ones] + 'e')
  66. } else if (hundreds !== 0 && ones > 0 || hundreds === 0) {
  67. names.push(onesNames[ones])
  68. }
  69. }
  70. return names.join('')
  71. }
  72. const getGroupName = (g: [string, BigNumber]) => {
  73. const [digits, index] = g
  74. if (index.lt(1)) {
  75. return getGroupDigitsName(digits, index)
  76. }
  77. if (index.lt(2)) {
  78. return [getGroupDigitsName(digits, index), getGroupIndexName(index, digits)].join('')
  79. }
  80. return [getGroupDigitsName(digits, index), getGroupIndexName(index, digits)].join(' ')
  81. }
  82. type Options = {
  83. groupSeparator: string
  84. }
  85. const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
  86. const {
  87. groupSeparator = ' '
  88. } = options
  89. const x = normalizeNumeric(xRaw)
  90. const { significandDigits, exponent } = deconstructNumeric(x)
  91. const blankDigits = createBlankDigits(config.grouping)
  92. const groups = groupDigits(significandDigits, exponent, config.grouping)
  93. if (groups.length === 1) {
  94. return getGroupName(groups[0])
  95. }
  96. const base = groups.filter(([digits]) => digits !== blankDigits).map(g => getGroupName(g)).join(groupSeparator)
  97. if (x.startsWith(NEGATIVE_SIGN)) {
  98. return [config.negative, base].join(' ')
  99. }
  100. return base
  101. }
  102. export default getLocalizedNumberName