NumberToText.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <?php
  2. namespace app\components;
  3. use yii\base\Component;
  4. class NumberToText extends Component
  5. {
  6. private array $units = [
  7. ['нуль'],
  8. ['один', 'одна'],
  9. ['два', 'дві'],
  10. ['три'],
  11. ['чотири'],
  12. ['п’ять'],
  13. ['шість'],
  14. ['сім'],
  15. ['вісім'],
  16. ['дев’ять'],
  17. ];
  18. private array $teens = [
  19. 'десять','одинадцять','дванадцять','тринадцять',
  20. 'чотирнадцять','п’ятнадцять','шістнадцять',
  21. 'сімнадцять','вісімнадцять','дев’ятнадцять'
  22. ];
  23. private array $tens = [
  24. '', '', 'двадцять','тридцять','сорок',
  25. 'п’ятдесят','шістдесят','сімдесят',
  26. 'вісімдесят','дев’яносто'
  27. ];
  28. private array $hundreds = [
  29. 'сто','двісті','триста','чотириста',
  30. 'п’ятсот','шістсот','сімсот',
  31. 'вісімсот','дев’ятсот'
  32. ];
  33. private array $orders = [
  34. [ ['тисяча','тисячі','тисяч'], 'f'],
  35. [ ['мільйон','мільйони','мільйонів'], 'm'],
  36. [ ['мільярд','мільярди','мільярдів'], 'm'],
  37. ];
  38. public function money(float $amount): string
  39. {
  40. $amount = round($amount, 2);
  41. $hryvnias = floor($amount);
  42. $kopecks = (int)round(($amount - $hryvnias) * 100);
  43. $result = $this->convert($hryvnias, ['гривня','гривні','гривень'], 'f');
  44. $result .= ' ';
  45. $result .= $this->convert($kopecks, ['копійка','копійки','копійок'], 'f');
  46. return $result;
  47. }
  48. private function convert(int $number, array $forms, string $gender): string
  49. {
  50. if ($number === 0) {
  51. return 'нуль ' . $forms[2];
  52. }
  53. $result = [];
  54. $order = 0;
  55. while ($number > 0) {
  56. $chunk = $number % 1000;
  57. if ($chunk > 0) {
  58. $chunkGender = $order === 0 ? $gender : $this->orders[$order-1][1];
  59. $words = $this->convertChunk($chunk, $chunkGender);
  60. $plural = $this->pluralForm($chunk);
  61. if ($order === 0) {
  62. $words[] = $forms[$plural];
  63. } else {
  64. $words[] = $this->orders[$order-1][0][$plural];
  65. }
  66. $result = array_merge($words, $result);
  67. }
  68. $number = intdiv($number, 1000);
  69. $order++;
  70. }
  71. return implode(' ', $result);
  72. }
  73. private function convertChunk(int $number, string $gender): array
  74. {
  75. $words = [];
  76. if ($number >= 100) {
  77. $words[] = $this->hundreds[intdiv($number,100)-1];
  78. $number %= 100;
  79. }
  80. if ($number >= 10 && $number <= 19) {
  81. $words[] = $this->teens[$number-10];
  82. return $words;
  83. }
  84. if ($number >= 20) {
  85. $words[] = $this->tens[intdiv($number,10)];
  86. $number %= 10;
  87. }
  88. if ($number > 0) {
  89. if ($number == 1 || $number == 2) {
  90. $words[] = $this->units[$number][$gender === 'f' ? 1 : 0];
  91. } else {
  92. $words[] = $this->units[$number][0];
  93. }
  94. }
  95. return $words;
  96. }
  97. private function pluralForm(int $number): int
  98. {
  99. $number %= 100;
  100. if ($number >= 11 && $number <= 19) return 2;
  101. $number %= 10;
  102. if ($number == 1) return 0;
  103. if ($number >= 2 && $number <= 4) return 1;
  104. return 2;
  105. }
  106. public function moneyFormatted(float $amount): string
  107. {
  108. $amount = round($amount, 2);
  109. $hryvnias = floor($amount);
  110. $kopecks = (int)round(($amount - $hryvnias) * 100);
  111. $numberFormatted = number_format($amount, 2, ',', ' ');
  112. $textHryvnias = $this->convert(
  113. $hryvnias,
  114. ['','',''],
  115. 'f'
  116. );
  117. $plural = $this->pluralForm($hryvnias);
  118. $forms = ['гривня','гривні','гривень'];
  119. return sprintf(
  120. "%s (%s) %s %02d копійок",
  121. $numberFormatted,
  122. $textHryvnias,
  123. $forms[$plural],
  124. $kopecks
  125. );
  126. }
  127. }