Вывод суммы прописью

Специально по просьбе Wassi Wassinen достал из нафталина, отряхнул пыль и публикую свой класс вывода сумм прописью, который основан на вот этих методах.

В оригинальной версии копейки писались цифрами, а теперь — буквами. Ради этого пришлось переписать больше половины кода.


А вот и готовый сниппет:
<?php
if (!class_exists('num2text')) {
class num2text {
	public $zero = 'ноль';
	public $male = array(
		'single' => array(1 => 'один','два','три','четыре','пять','шесть','семь','восемь','девять'),
		'ten' => array(1 => 'десять','одиннадцать','двенадцать','тринадцать','четырнадцать' ,'пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать'),
		'twenty' => array(2 => 'двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят' ,'восемьдесят','девяносто'),
		'hundred' => array(1 => 'сто','двести','триста','четыреста','пятьсот','шестьсот', 'семьсот','восемьсот','девятьсот')
	);
	public $female = array(
		'single' => array(1 => 'одна','две','три','четыре','пять','шесть','семь','восемь','девять'),
		'ten' => array(1 => 'десять','одиннадцать','двенадцать','тринадцать','четырнадцать' ,'пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать'),
		'twenty' => array(2 => 'двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят' ,'восемьдесят','девяносто'),
		'hundred' => array(1 => 'сто','двести','триста','четыреста','пятьсот','шестьсот', 'семьсот','восемьсот','девятьсот')
	);
	public $units = array(
		array('триллион', 'триллиона', 'триллионов', 'male'),
		array('миллиард', 'милиарда', 'миллиардов', 'male'),
		array('миллион', 'миллиона', 'миллионов', 'male'),
		array('тысяча', 'тысячи', 'тысяч', 'female'),
	);
	
	
	public function morph($num, array $words) {
		$num = abs(intval(str_replace(' ', '', $num)));

		$n = $num % 100;
		if ($num === 0 || ($n > 10 && $n < 20)) {
			return $words[2];
		}
		else {
			$n = $n % 10;
			if ($n > 1 && $n < 5) {
				return $words[1];
				
			}
			elseif ($n == 1) {
				return $words[0];
			}
			else {
				return $words[2];
			}
		}
	}
	
	public function transform($num, array $values) {
		$array = array();
		$triplet = 0;
		$num = sprintf('%0'.((count($this->units) * 3) + 3).'.0f', str_replace(' ', '', $num));

		// Split by 3 symbols
		$tmp = str_split($num, 3);
		foreach($tmp as $k => $v) {
			list($i1, $i2, $i3) = array_map('intval', str_split($v, 1));
			if (!$i1 && !$i2 && !$i3) {
				$triplet ++;
				continue;
			}
			
			if (isset($this->units[$triplet])) {
				$gender = $this->units[$triplet][3];
				$values = $this->$gender;
			}

			if (is_null($i3)) {$i3 = $i2; $i2 = $i1; $i1 = null;}

			// 1xx-9xx
			if (!is_null($i1)) {
				$array[] = $values['hundred'][$i1];
			}
			// 20-99
			if ($i2 > 1) {
				$array[] = $values['twenty'][$i2].' '.$values['single'][$i3];
			}
			// 10-19 || 1-9
			else {
				$array[] = $i2 > 0
					? $values['ten'][$i3 + 1]
					: $values['single'][$i3];
			}
			
			// thousands, billions etc.
			if (isset($this->units[$triplet])) {
				$array[]= $this->morph($v, $this->units[$triplet]);
			}
			$triplet ++;
		}
		return implode(' ', $array);
	}

	function ucfirst($input) {
		mb_internal_encoding('UTF-8');
		$string = mb_strtolower(trim($input));
		$output = mb_strtoupper(mb_substr($string, 0, 1));
		$output .= mb_substr($string, 1, mb_strlen($string));

		return $output;
	}
}
}

$output = array();
$class = new num2text();

$num = str_replace(',', '.', $input);
list($integer, $decimal) = explode('.', $num);
if ($decimal > 99) {$decimal = 99;}

// Integer
$output[] = empty($integer)
	? $class->zero
	: $class->transform($integer, $class->male);
$words = array('рубль', 'рубля', 'рублей');
$output[] = $class->morph($integer, $words);

// Decimal
if ($decimal < 10) {
	$output[] = $class->zero;
}
$output[] = $class->transform($decimal, $class->female);
$words = array('копейка', 'копейки', 'копеек');
$output[] = $class->morph($decimal, $words);

$output = $class->ucfirst(implode(' ',$output));
return $output;

Скопировать, вставить, сохранить и вызывать как фильтр вывода или сниппет:
[[+price:num2text]]
[[+old_price:num2text]]
[[!num2text?&input=`[[+order_cost]]`]]

Сам класс вполне универсален и не завязан на MODX.
После его объявления идёт логика работы сниппета, которую можно легко изменить для вывода количества комментариев, или чего-то еще.
Василий Наумкин
30 марта 2014, 11:19
modx.pro
6
3 193
+5

Комментарии: 5

Wassi Wassinen
01 апреля 2014, 12:05
0
Немножко запоздал. Отправил. :) Еще раз спасибо, думаю всем будет полезно иметь такой функционал.
Афанасий Павлов
02 апреля 2014, 12:53
0
$input = 5.50;
выдаёт:
Пять рублей ноль пять копеек
со всеми нулями происходит так же
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
5