Published on Mar 1, 2014, updated on Nov 2, 2019

Číslo na slovo - Prevod v PHP

Nižšie uvedená PHP class prevedie číslo na slová.

Kód bol prevzatý z anglickej verzie a implementuje všetky výnimky slovenského pravopisu. Zaujímavá štúdia pre porovnanie zložitosti jazykov - slovenčina má mnoho výnimiek a implementácia algoritmu je značne zložitejšia, než pre angličtinu.

Inštalácia cez composer - popis na Github.com

Okrem slovenskej lokalizácie sú v repozitári dostupné aj varianty pre češtinu a angličtinu.

Otestovať môžete napríklad na šekovej poukážke alebo zadajte číslo nižšie.

Pre porovnanie sa vypisuje aj prevod cez PHP rozšírenie INTL, ktoré však je ťažko v praxi použiteľné - vracia mnoho chýb. Konverziu cez INTL pozrite nižšie v metóde Number2words::convertByIntl().

Pozn: Desatinná časť sa pri PHP implementácii prevádza do hodnoty 1000, pre väčší počet desatinných miest sa číslice vymenujú - podobne ako v praxi.


/**
* Helper class for converting arbitrary float number to words in Slovak language
*/
class Number2words
{
	/**
	* Original english implementation: http://www.karlrixon.co.uk/writing/convert-numbers-to-words-with-php/
	* @param float $number
	* @param int $units
	* @param int $level
	*/
	public static function convertSk($number, $units = null, $level = -1)
	{
		++$level;
		$hyphen      = ''; // in english "-", v slovencine ziadny
		$conjunction = ' '; // in english ' and ' v slovencine nepouzivame
		$separator   = ' ';
		$negative    = 'mínus ';
		$decimal     = ' celé ';
		$dictionary  = array(
			0                   => 'nula',
			1                   => 'jeden', // jeden milion, jedna miliarda
			2                   => 'dva', // dvojtvar dve, dve - napr. 22000 - dvadsatDVA tisic, 200 = DVE sto
			3                   => 'tri',
			4                   => 'štyri',
			5                   => 'päť',
			6                   => 'šesť',
			7                   => 'sedem',
			8                   => 'osem',
			9                   => 'deväť',
			10                  => 'desať',
			11                  => 'jedenásť',
			12                  => 'dvanásť',
			13                  => 'trinásť',
			14                  => 'štrnásť',
			15                  => 'pätnásť',
			16                  => 'šestnásť',
			17                  => 'sedemnásť',
			18                  => 'osemnásť',
			19                  => 'devätnásť',
			20                  => 'dvadsať',
			30                  => 'tridsať',
			40                  => 'štyridsať',
			50                  => 'päťdesiat',
			60                  => 'šesťdesiat',
			70                  => 'sedemdesiat',
			80                  => 'osemdesiat',
			90                  => 'deväťdesiat',
			100                 => 'sto',
			1000                => 'tisíc',
			1000000             => 'milión',   // e6
			1000000000          => 'miliarda|miliardy|miliárd', // e9
			1000000000000       => 'bilión',   // e12
			1000000000000000    => 'biliarda|biliardy|biliárd', // e15
			1000000000000000000 => 'trilión',  // e18
			// https://sk.wikipedia.org/wiki/Veľké_čísla
		);

		// fix common typo [,] instead of dot
		$number = str_replace(',', '.', $number);

		if (!is_numeric($number)) {
			return false;
		}

		if (($number >= 0 && (int) $number < 0) || (int) $number < 0 - PHP_INT_MAX) {
			// overflow
			throw new \Exception('Invalid number range - value must be between ' . PHP_INT_MAX . ' and ' . PHP_INT_MAX.'.');
		}

		if ($number < 0) {
			return $negative . self::convertSk(abs($number));
		}

		$string = $fraction = null;

		if (strpos($number, '.') !== false) {
			list($number, $fraction) = explode('.', $number);
		}

		switch (true) {
			case $number < 21:
				$dict = $dictionary[$number];
				if($units){
					if($number == 1){
						// ludia chcu "jednosto"
						$dict = ''; // nie jedentisic, jedensto
						if($level <= 1){ // first loop = 0, pridame "jedno"sto na zaciatku slova
							if($units == 100){
								$dict = 'jedno'; // jednosto
							}elseif(in_array($units, [1e3, 1e6])){
								$dict = 'jeden'; // jedentisic
							}elseif(in_array($units, [1e9, 1e15])){
								$dict = 'jedna'; // jedna miliarda
							}
						}
					}elseif($number == 2 && in_array($units, [1e3, 1e9, 1e15])){
						$dict = 'dve'; // dvetisic, nie dvatisic, dve miliardy nie dva miliardy
					}
				}
				$string = $dict;
				break;
			case $number < 100:
				$tens   = ((int) ($number / 10)) * 10;
				$units  = $number % 10;
				$string = $dictionary[$tens];
				if ($units) {
					$string .= $hyphen . $dictionary[$units];
				}
				break;
			case $number < 1000:
				$hundreds  = floor($number / 100);
				$remainder = $number % 100;
				if($hundreds == 1){
					// ludia chcu "jednosto"
					$dict = ''; // nie styritisic jedenstoosemdesiat, jedenstopatnast
					if(!$level){ // jednosto na zaciatku slova
						$dict = $dictionary[$hundreds]; // nie styritisic jedenstoosemdesiat, jedenstopatnast
						if($number < 200){
							$dict = 'jedno'; // jednostodvanast, nie jedensto
						}
					}
				}elseif($hundreds == 2){
					$dict = 'dve'; // dvesto, nie dvasto
				}else{
					$dict = $dictionary[$hundreds];
				}
				$string = $dict . $dictionary[100];
				if ($remainder) {
					$string .= self::convertSk($remainder, null, $level);
				}
				break;
			default:
				$baseUnit = pow(1000, floor(log($number, 1000)));
				$numBaseUnits = (int) ($number / $baseUnit);
				$remainder = $number - ($baseUnit * $numBaseUnits);
				// SK declination
				$append = $dictionary[$baseUnit];
				if($baseUnit > 1000){
					$bigNumSep = ' ';
					// nesklonujeme tisicky, len milion a vyssie
					if(in_array($baseUnit, [1e9, 1e15])){
						$append = explode('|', $append);
						if($numBaseUnits >= 2 && $numBaseUnits <= 4){
							$append = $append[1]; // 2, 3, 4 miliardy, biliardy
						}elseif($numBaseUnits >= 5 ){
							$append = $append[2]; // 5,6 ... 99 miliárd, biliárd
						}else{
							$append = $append[0]; // 1 miliarda, 1 biliarda
						}
					}else{
						if($numBaseUnits >= 2 && $numBaseUnits <= 4){
							$append .= 'y'; // 2, 3, 4 miliony, biliony, triliony, ..
						}elseif($numBaseUnits >= 5 ){
							$append .= 'ov'; // 5,6 ... 99 milionov
						}
					}
				}else{
					$bigNumSep = '';
				}
				$string = self::convertSk($numBaseUnits, $baseUnit, $level) . $bigNumSep . $append;
				if ($remainder) {
					$string .= $remainder < 100 ? $conjunction : $separator;
					$string .= self::convertSk($remainder, null, $level);
				}
				break;
		}

		if ('' !== trim($fraction) && is_numeric($fraction)) {
			$string .= $decimal;
			$fraction = intval($fraction);
			if($fraction < 1000){
				// up to 3 decimals - full convert
				$string .= self::convertSk($fraction);
			}else{
				// 3+ decimals - spell out single digits
				$words = [];
				foreach (str_split((string) $fraction) as $number) {
					$words[] = $dictionary[$number];
				}
				$string .= implode(' ', $words);
			}
		}

		return $string;
	}

	/**
	* Convert number ot words by using intl / ICU formatting
	* At least up to ICU 57.1 - slovak implementation is buggy - try e.g. 12456.78 - grammar typos "dvaásť tisíc .."
	* @param float|integer $num
	* @param string $langCode EN|DE|SK ... case insensitive
	*/
	public static function convertByIntl($num, $langCode = 'en')
	{
		if(extension_loaded('intl')){
			$formatter = new \NumberFormatter($langCode, \NumberFormatter::SPELLOUT);
			return $formatter->format($num);
		}
	}
}

Got a question?

Synet.sk

Professional development of web applications and custom solutions. Consultancy services.

Demo

Contact


https://synet.sk