Skip to content

Commit

Permalink
Added Bulgarian currency transformer. (#169)
Browse files Browse the repository at this point in the history
* Added Bulgarian currency transformer.

* Added Bulgarian currency.

* Fixed PSR12 compatibility.
Added unit tests for Bulgarian currency transformer.

* Updated tests.

* Removed not needed plural form in tests.

* Fixed currency code in tests.

* Test updates.
Simplified singular and plural form.
Removed unused units.

* Updates to PSR12 compliance.

* Final touches.

* Fixed problem with teens cents in Bulgarian.

* Removed spaces.

* Updated README.md

---------

Co-authored-by: Georgi Angelov <[email protected]>
  • Loading branch information
gkangelov and georgi-angelov-bg authored Feb 24, 2024
1 parent cfab3d8 commit bf8e9d9
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Note: The Currency Transformer within this library processes integers; ensure yo
| Azerbaijani | az | + | + |
| Belgian French | fr_BE | + | - |
| Brazilian Portuguese | pt_BR | + | + |
| Bulgarian | bg | + | - |
| Bulgarian | bg | + | + |
| Czech | cs | + | - |
| Danish | dk | + | + |
| Dutch | nl | + | - |
Expand Down
1 change: 1 addition & 0 deletions src/Concerns/ManagesCurrencyTransformers.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ trait ManagesCurrencyTransformers
'ar' => Transformer\ArabicCurrencyTransformer::class,
'al' => Transformer\AlbanianCurrencyTransformer::class,
'az' => Transformer\AzerbaijaniCurrencyTransformer::class,
'bg' => Transformer\BulgarianCurrencyTransformer::class,
'de' => Transformer\GermanCurrencyTransformer::class,
'dk' => Transformer\DanishCurrencyTransformer::class,
'en' => Transformer\EnglishCurrencyTransformer::class,
Expand Down
86 changes: 86 additions & 0 deletions src/CurrencyTransformer/BulgarianCurrencyTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace NumberToWords\CurrencyTransformer;

use NumberToWords\Exception\NumberToWordsException;
use NumberToWords\Language\Bulgarian\BulgarianDictionary;
use NumberToWords\Language\Bulgarian\BulgarianExponentInflector;
use NumberToWords\Language\Bulgarian\BulgarianFemaleTripletTransformer;
use NumberToWords\Language\Bulgarian\BulgarianNounGenderInflector;
use NumberToWords\Language\Bulgarian\BulgarianTripletTransformer;
use NumberToWords\NumberTransformer\NumberTransformerBuilder;
use NumberToWords\Service\NumberToTripletsConverter;
use NumberToWords\TransformerOptions\CurrencyTransformerOptions;

class BulgarianCurrencyTransformer implements CurrencyTransformer
{
public function toWords(int $amount, string $currency, ?CurrencyTransformerOptions $options = null): string
{
$dictionary = new BulgarianDictionary();
$nounGenderInflector = new BulgarianNounGenderInflector();
$numberToTripletsConverter = new NumberToTripletsConverter();
$tripletTransformer = new BulgarianTripletTransformer($dictionary);
$femaleTripletTransformer = new BulgarianFemaleTripletTransformer($dictionary);
$exponentInflector = new BulgarianExponentInflector($nounGenderInflector);

$numberTransformer = (new NumberTransformerBuilder())
->withDictionary($dictionary)
->withWordsSeparatedBy($dictionary->getSeparator())
->transformNumbersBySplittingIntoPowerAwareTriplets($numberToTripletsConverter, $tripletTransformer)
->inflectExponentByNumbers($exponentInflector)
->build();

$decimal = (int) ($amount / 100);
$fraction = abs($amount % 100);

if ($fraction === 0) {
$fraction = null;
}

$currency = strtoupper($currency);

if (!array_key_exists($currency, BulgarianDictionary::$currencyNames)) {
throw new NumberToWordsException(
sprintf('Currency "%s" is not available for "%s" language', $currency, get_class($this))
);
}

$currencyNames = BulgarianDictionary::$currencyNames[$currency];

$words = [];

$words[] = $numberTransformer->toWords($decimal);
$words[] = $nounGenderInflector->inflectNounByNumber(
$decimal,
$currencyNames[0][0],
$currencyNames[0][1],
$currencyNames[0][1],
);

$words[] = BulgarianDictionary::$and;
if (null !== $fraction) {
$centTransformer = (new NumberTransformerBuilder())
->withDictionary($dictionary)
->withWordsSeparatedBy($dictionary->getSeparator())
->transformNumbersBySplittingIntoPowerAwareTriplets(
$numberToTripletsConverter,
$femaleTripletTransformer
)
->inflectExponentByNumbers($exponentInflector)
->build();

$words[] = $centTransformer->toWords($fraction);
$words[] = $nounGenderInflector->inflectNounByNumber(
$fraction,
$currencyNames[1][0],
$currencyNames[1][1],
$currencyNames[1][1],
);
} else {
$words[] = $dictionary->getZero();
$words[] = $currencyNames[1][1];
}

return implode(' ', $words);
}
}
166 changes: 166 additions & 0 deletions src/Language/Bulgarian/BulgarianDictionary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

namespace NumberToWords\Language\Bulgarian;

use NumberToWords\Language\Dictionary;

class BulgarianDictionary implements Dictionary
{
public const LOCALE = 'bg_BG';
public const LANGUAGE_NAME = 'Bulgarian';
public const LANGUAGE_NAME_NATIVE = 'български';

protected string $zero = "нула";
protected string $minus = "минус";
/** Token to separate words in triplets and chunks in final string */
protected string $separator = " ";

protected static array $units = ['', 'един', 'два', 'три', 'четири', 'пет', 'шест', 'седем', 'осем', 'девет'];
protected static array $unitsFemale = [
'',
'една',
'две',
'три',
'четири',
'пет',
'шест',
'седем',
'осем',
'девет',
];

protected static array $teens = [
'десет',
'единадесет',
'дванадесет',
'тринадесет',
'четиринадесет',
'петнадесет',
'шестнадесет',
'седемнадесет',
'осемнадесет',
'деветнадесет'
];

protected static array $tens = [
'',
'десет',
'двадесет',
'тридесет',
'четиридесет',
'петдесет',
'шестдесет',
'седемдесет',
'осемдесет',
'деветдесет'
];

private static array $hundreds = [
0 => '',
1 => 'сто',
2 => 'двеста',
3 => 'триста',
4 => 'четиристотин',
5 => 'петстотин',
6 => 'шестстотин',
7 => 'седемстотин',
8 => 'осемстотин',
9 => 'деветстотин'
];

public static string $and = 'и';
public static array $currencyNames = [
'ALL' => [['lek'], ['qindarka']],
'AED' => [['Dirham'], ['Fils']],
'AUD' => [['Australian dollar'], ['cent']],
'BAM' => [['convertible marka'], ['fenig']],
'BGN' => [['лев', 'лева'], ['стотинка', 'стотинки']],
'BRL' => [['real'], ['centavos']],
'BYR' => [['Belarussian rouble'], ['kopiejka']],
'CAD' => [['Canadian dollar'], ['cent']],
'CHF' => [['Swiss franc'], ['rapp']],
'CYP' => [['Cypriot pound'], ['cent']],
'CZK' => [['Czech koruna'], ['halerz']],
'DKK' => [['Danish krone'], ['ore']],
'DZD' => [['dinar'], ['cent']],
'EEK' => [['kroon'], ['senti']],
'EGP' => [['Egyptian Pound'], ['piastre']],
'EUR' => [['euro'], ['euro-cent']],
'GBP' => [['pound', 'pounds'], ['pence', 'pence']],
'HKD' => [['Hong Kong dollar'], ['cent']],
'HRK' => [['Croatian kuna'], ['lipa']],
'HUF' => [['forint'], ['filler']],
'ILS' => [['new sheqel', 'new sheqels'], ['agora', 'agorot']],
'ISK' => [['Icelandic króna'], ['aurar']],
'JPY' => [['yen'], ['sen']],
'LTL' => [['litas'], ['cent']],
'LVL' => [['lat'], ['sentim']],
'LYD' => [['dinar'], ['cent']],
'MAD' => [['dirham'], ['cent']],
'MKD' => [['Macedonian dinar'], ['deni']],
'MRO' => [['ouguiya'], ['khoums']],
'MTL' => [['Maltese lira'], ['centym']],
'NGN' => [['Naira'], ['kobo']],
'NOK' => [['Norwegian krone'], ['oere']],
'PHP' => [['peso'], ['centavo']],
'PLN' => [['zloty', 'zlotys'], ['grosz']],
'ROL' => [['Romanian leu'], ['bani']],
'RUB' => [['Russian Federation rouble'], ['kopiejka']],
'SAR' => [['Riyal'], ['Halalah']],
'SEK' => [['Swedish krona'], ['oere']],
'SIT' => [['Tolar'], ['stotinia']],
'SKK' => [['Slovak koruna'], []],
'TMT' => [['manat'], ['tenge']],
'TND' => [['dinar'], ['millime']],
'TRL' => [['lira'], ['kuruş']],
'TRY' => [['lira'], ['kuruş']],
'UAH' => [['hryvna'], ['cent']],
'USD' => [['dollar'], ['cent']],
'XAF' => [['CFA franc'], ['cent']],
'XOF' => [['CFA franc'], ['cent']],
'XPF' => [['CFP franc'], ['centime']],
'YUM' => [['dinar'], ['para']],
'ZAR' => [['rand'], ['cent']],
'UZS' => [['sum'], ['tiyin']],
];

public function getSeparator(): string
{
return $this->separator;
}

public function getZero(): string
{
return $this->zero;
}

public function getMinus(): string
{
return $this->minus;
}

public function getCorrespondingUnit(int $unit): string
{
return self::$units[$unit];
}

public function getCorrespondingUnitFemale(int $unit): string
{
return self::$unitsFemale[$unit];
}

public function getCorrespondingTen(int $ten): string
{
return self::$tens[$ten];
}

public function getCorrespondingTeen(int $teen): string
{
return self::$teens[$teen];
}

public function getCorrespondingHundred(int $hundred): string
{
return self::$hundreds[$hundred];
}
}
50 changes: 50 additions & 0 deletions src/Language/Bulgarian/BulgarianExponentInflector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace NumberToWords\Language\Bulgarian;

use NumberToWords\Language\ExponentInflector;

class BulgarianExponentInflector implements ExponentInflector
{
protected static array $exponent = [
['', ''],
['хиляда', 'хиляди'],
['милион', 'милиона'],
['милиард', 'милиарда'],
['трилион', 'трилиона'],
['квадрилион', 'квадрилиона'],
['квинтилион', 'квинтилиона'],
['секстилион', 'секстилиона'],
['септилион', 'септилиона'],
['октилион', 'октилиона'],
['ноналион', 'ноналиона'],
['декалион', 'декалиона'],
['ундекалион', 'ундекалиона'],
['дуодекалион', 'дуодекалиона'],
['тредекалион', 'тредекалиона'],
['кватордекалион', 'кватордекалиона'],
['квинтдекалион', 'квинтдекалиона'],
['сексдекалион', 'сексдекалиона'],
['септдекалион', 'септдекалиона'],
['октодекалион', 'октодекалиона'],
['новемдекалион', 'новемдекалиона'],
['вигинтилион', 'вигинтилион'],
];

protected BulgarianNounGenderInflector $inflector;

public function __construct(BulgarianNounGenderInflector $inflector)
{
$this->inflector = $inflector;
}

public function inflectExponent(int $number, int $power): string
{
return $this->inflector->inflectNounByNumber(
$number,
self::$exponent[$power][0],
self::$exponent[$power][1],
self::$exponent[$power][1],
);
}
}
40 changes: 40 additions & 0 deletions src/Language/Bulgarian/BulgarianFemaleTripletTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace NumberToWords\Language\Bulgarian;

/**
* Class used only for getting the proper translation of the cents-values to words.
* It was needed, because in Bulgarian, the main unit ("лев") and the cent-unit ("стотинка") are of different gender.
*/
class BulgarianFemaleTripletTransformer extends BulgarianTripletTransformer
{
public function transformToWords(int $number, int $power): string
{
$units = $number % 10;
$tens = (int) ($number / 10) % 10;
$hundreds = (int) ($number / 100) % 10;
$words = [];

if ($hundreds > 0) {
$words[] = $this->dictionary->getCorrespondingHundred($hundreds);
}

if ($tens === 1) {
$words[] = $this->dictionary->getCorrespondingTeen($units);
}

if ($tens > 1) {
$words[] = $this->dictionary->getCorrespondingTen($tens);
}

if ($units > 0 && ($hundreds > 0 || $tens > 1)) {
$words[] = BulgarianDictionary::$and;
}

if ($tens != 1) {
$words[] = $this->dictionary->getCorrespondingUnitFemale($units);
}

return implode($this->dictionary->getSeparator(), $words);
}
}
15 changes: 15 additions & 0 deletions src/Language/Bulgarian/BulgarianNounGenderInflector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace NumberToWords\Language\Bulgarian;

class BulgarianNounGenderInflector
{
public function inflectNounByNumber(int $number, string $singular, string $plural, string $genitivePlural): string
{
if ($number === 1) {
return $singular;
}

return $plural;
}
}
Loading

0 comments on commit bf8e9d9

Please sign in to comment.