From 9363b4714747b6d4f63465d4217ad22e473bd680 Mon Sep 17 00:00:00 2001 From: rmunate Date: Mon, 4 Dec 2023 19:06:12 -0500 Subject: [PATCH] Standar New Version --- README.md | 1 + config/spell-number.php | 46 +- src/Bases/BaseSpellNumber.php | 80 ++- src/Bases/BaseSpellNumberValidator.php | 7 - src/Callback/DataResponse.php | 114 ---- src/Callback/SpellNumberCallable.php | 62 ++ src/Exceptions/SpellNumberExceptions.php | 2 +- src/Langs/Langs.php | 3 +- src/Langs/Replaces.php | 94 ++- src/Miscellaneous/Utilities.php | 136 ++--- src/Miscellaneous/Words.php | 86 ++- src/SpellNumber.php | 719 +++++++++++++++++++---- src/Traits/Accesor.php | 6 - src/Traits/Constants.php | 31 + src/Traits/Locale.php | 29 - src/Traits/Processor.php | 41 ++ src/Traits/Spell.php | 36 ++ src/Wrappers/NumberFormatterWrapper.php | 32 + 18 files changed, 1044 insertions(+), 481 deletions(-) delete mode 100644 src/Callback/DataResponse.php create mode 100644 src/Callback/SpellNumberCallable.php create mode 100644 src/Traits/Constants.php delete mode 100644 src/Traits/Locale.php create mode 100644 src/Traits/Processor.php create mode 100644 src/Traits/Spell.php diff --git a/README.md b/README.md index cff59ba..b97acb1 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ To whom we thank for supporting making programming easier. - [Alejandro Diaz](https://github.com/alejandrodiazpinilla) (Readme And Icon) - [Frank Sepulveda](https://github.com/socieboy) (Ordinal Texts) - [Ngô Quốc Đạt](https://github.com/datlechin) (Vietnamese Language) +- [Mustafa Yusuf Özcan](https://github.com/mustafayusufozcan) (Turkish Language) ## License This project is under the [MIT License](https://choosealicense.com/licenses/mit/). diff --git a/config/spell-number.php b/config/spell-number.php index 589ba71..8625f87 100644 --- a/config/spell-number.php +++ b/config/spell-number.php @@ -7,8 +7,8 @@ | Global Configuration for Number to Words Conversion. |-------------------------------------------------------------------------- | - | See https://rmunate.github.io/SpellNumber/ - | Define a global configuration to make the translation of values to words easier and faster. + | Visit https://rmunate.github.io/SpellNumber/ for detailed documentation. + | Define a global configuration to facilitate and expedite the translation of values to words. | */ @@ -20,27 +20,11 @@ |-------------------------------------------------------------------------- | | Define the language over which values will be translated to words. - | Remember that you only have the following options available: - | 'de', 'en', 'es', 'fa', 'fr', 'hi', 'it', 'pl', 'pt', 'ro'. | */ 'locale' => 'en', - /* - |-------------------------------------------------------------------------- - | Try using a "locale" not listed in the previous option. - |-------------------------------------------------------------------------- - | - | If you want to use an option different from those previously listed, - | you must set the following indicator to true, so that the conversion to the supplied - | location can be attempted. To list possible additional options to use with the package, - | you can run the function SpellNumber::getAllLocales(); - | - */ - - 'specific_locale' => false, - /* |-------------------------------------------------------------------------- | Define the Currency. @@ -83,11 +67,14 @@ /* |-------------------------------------------------------------------------- - | Sometimes you need to replace certain things in the number to words outputs. + | Global Output Replacements |-------------------------------------------------------------------------- | - | If so, it's very easy to do. - | Use the structure of an associative array to apply these replacements to your outputs. + | If you need to make any replacements to the translation output, which is common + | in cases with abbreviated numbers or other language contractions, you can + | easily do so here through an associative array. + | + | Remember that the main index should match the locale you are using. | */ @@ -97,19 +84,4 @@ // ], ], - /* - |-------------------------------------------------------------------------- - | Encountered a specific adjustment in the number to words output?. - |-------------------------------------------------------------------------- - | - | Here, you have the option to adjust whatever you need, the best part! - | This adjustment will apply to all your number to words outputs. - | - */ - - 'callback_output' => function ($data) { - // Your logic here... - - return $data->getWords(); - }, -]; +]; \ No newline at end of file diff --git a/src/Bases/BaseSpellNumber.php b/src/Bases/BaseSpellNumber.php index 044380e..843842e 100644 --- a/src/Bases/BaseSpellNumber.php +++ b/src/Bases/BaseSpellNumber.php @@ -6,7 +6,7 @@ use Rmunate\Utilities\Validator\SpellNumberValidator; abstract class BaseSpellNumber -{ +{ /** * Creates an instance of the child class with the provided value for conversion. * @@ -17,7 +17,7 @@ abstract class BaseSpellNumber * * @return static An instance of the child class. */ - public static function value($value) + public static function value(mixed $value) { $type = SpellNumberValidator::check('mixed', $value)->result(); @@ -33,7 +33,7 @@ public static function value($value) * * @return static An instance of the child class. */ - public static function integer($value) + public static function integer(int $value) { SpellNumberValidator::check('integer', $value)->result(); @@ -49,13 +49,37 @@ public static function integer($value) * * @return static An instance of the child class. */ - public static function float($value) + public static function float(string|float $value) { SpellNumberValidator::check('double', $value)->result(); return new static($value, 'double'); } + /** + * Creates an instance of the child class with the provided text value for conversion. + * + * @param string $text The text value to be processed. + * + * @return static An instance of the child class configured for text processing. + */ + public static function text(string $text) + { + return new static($text, 'text'); + } + + /** + * Creates an instance of the child class with the provided date value for conversion. + * + * @param string $date The date value to be processed. + * + * @return static An instance of the child class configured for date processing. + */ + public static function date(string $date) + { + return new static($date, 'date'); + } + /** * Returns the list of available premises according to PHP. * @@ -93,24 +117,42 @@ public static function getAvailableLanguages() } /** - * DEPRECATED - Delete Method Soon - * Retrieves a list of all available locales supported by the NumberFormatter class. + * This method allows querying the constants existing within SpellNumber + * along with their values for use in the package. * - * @return array An array containing all available locales. - */ - public static function getLocales() - { - return self::getAvailableLocales(); - } - - /** - * DEPRECATED - Delete Method Soon - * Retrieves a list of all available TimeZones by the NumberFormatter class. + * @param string|null $category The optional category of constants to retrieve. * - * @return array An array containing all available locales. + * @return mixed */ - public static function getLanguages() + public static function getConstants(?string $category = null) { - return self::getAvailableLanguages(); + $data = [ + "spell" => [ + "SpellNumber::TO_LETTERS" => "%value-to-letters", + "SpellNumber::TO_ORDINAL" => "%value-to-ordinal", + "SpellNumber::TO_MONEY" => "%value-to-money", + "SpellNumber::TO_CLOCK" => "%value-to-clock", + "SpellNumber::TO_SCIENTIFIC" => "%value-to-scientific", + "SpellNumber::TO_PERCENT" => "%value-to-percent", + "SpellNumber::TO_CURRENCY" => "%value-to-currency", + "SpellNumber::TO_SUMMARY" => "%value-to-summary", + "SpellNumber::TO_ROMAN_NUMERAL" => "%value-to-roman-numeral", + ], + "ordinal-numbers" => [ + "SpellNumber::ORDINAL_DEFAULT" => '%spellout-ordinal', + "SpellNumber::ORDINAL_FEMALE" => '%spellout-ordinal-feminine', + "SpellNumber::ORDINAL_MALE" => '%spellout-ordinal-masculine', + ], + "date" => [ + "SpellNumber::DATE_SHORT" => "%date-format-short", + "SpellNumber::DATE_MEDIUM" => "%date-format-medium", + "SpellNumber::DATE_LONG" => "%date-format-long", + "SpellNumber::DATE_FULL" => "%date-format-full", + ], + ]; + + $output = !empty($category) ? ($data[$category] ?? []) : $data; + + return json_decode(json_encode($output), false); } } diff --git a/src/Bases/BaseSpellNumberValidator.php b/src/Bases/BaseSpellNumberValidator.php index e085bb4..a44a9ef 100644 --- a/src/Bases/BaseSpellNumberValidator.php +++ b/src/Bases/BaseSpellNumberValidator.php @@ -2,13 +2,6 @@ namespace Rmunate\Utilities\Bases; -/** - * This class serves as a base validator for common data types. - * - * @method static BaseValidator mixed(). - * @method static BaseValidator integer(). - * @method static BaseValidator float(). - */ abstract class BaseSpellNumberValidator { /** diff --git a/src/Callback/DataResponse.php b/src/Callback/DataResponse.php deleted file mode 100644 index 34f39ea..0000000 --- a/src/Callback/DataResponse.php +++ /dev/null @@ -1,114 +0,0 @@ -method = $data['method'] ?? null; - $this->type = $data['type'] ?? null; - $this->value = $data['value'] ?? null; - $this->words = $data['words'] ?? null; - $this->lang = $data['lang'] ?? null; - $this->locale = $data['locale'] ?? null; - $this->mode = $data['mode'] ?? null; - $this->currency = $data['currency'] ?? null; - $this->fraction = $data['fraction'] ?? null; - } - - /** - * Get the method associated with the response. - * - * @return string The method name. - */ - public function getMethod() - { - return $this->method; - } - - /** - * Get the type associated with the response. - * - * @return string The response type. - */ - public function getType() - { - return $this->type; - } - - /** - * Get the value associated with the response. - * - * @return mixed The response value. - */ - public function getValue() - { - return $this->value; - } - - /** - * Get the words associated with the response. - * - * @return string The response words. - */ - public function getWords() - { - return $this->words; - } - - /** - * Get the language associated with the response. - * - * @return string The response language. - */ - public function getLang() - { - return $this->lang; - } - - /** - * Get the locale associated with the response. - * - * @return string The response locale. - */ - public function getLocale() - { - return $this->locale; - } - - /** - * Get the currency associated with the response. - * - * @return string The response currency. - */ - public function getCurrency() - { - return $this->currency; - } - - /** - * Get the fraction associated with the response. - * - * @return string The response fraction. - */ - public function getFraction() - { - return $this->fraction; - } -} diff --git a/src/Callback/SpellNumberCallable.php b/src/Callback/SpellNumberCallable.php new file mode 100644 index 0000000..1cec91b --- /dev/null +++ b/src/Callback/SpellNumberCallable.php @@ -0,0 +1,62 @@ + $value) { + $this->{$key} = $value; + } + } + + /** + * Get the value associated with the response. + * + * @return mixed|null The response value. + */ + public function getValue() + { + return $this->value; + } + + /** + * Get the words associated with the response. + * + * @return string|null The response words. + */ + public function getWords() + { + return $this->output; + } + + /** + * Get the output associated with the response. + * + * @return string|null The response output. + */ + public function getOutput() + { + return $this->output; + } + + /** + * Perform a spell-related action based on the specified method and optional ordinal mode. + * + * @param string $method The method to perform. + * @param string|null $modeOrdinal The optional ordinal mode when using TO_ORDINAL. + * + * @return mixed|null The response instance. + */ + public function spell(string $method, ?string $modeOrdinal = null) + { + return ($this->spell)($method, $modeOrdinal); + } +} diff --git a/src/Exceptions/SpellNumberExceptions.php b/src/Exceptions/SpellNumberExceptions.php index 7b89ea5..e7520f1 100644 --- a/src/Exceptions/SpellNumberExceptions.php +++ b/src/Exceptions/SpellNumberExceptions.php @@ -21,7 +21,7 @@ final class SpellNumberExceptions extends Exception public static function create($message, $code = 0, Throwable $previous = null) { return new self( - 'Rmunate\Utilities\SpellNumber::class - '.$message.'- See https://rmunate.github.io/SpellNumber/', + '\Rmunate\Utilities\SpellNumber::class - '.$message.' - See https://rmunate.github.io/SpellNumber/', $code, $previous ); diff --git a/src/Langs/Langs.php b/src/Langs/Langs.php index 1430035..d62f8d8 100644 --- a/src/Langs/Langs.php +++ b/src/Langs/Langs.php @@ -25,6 +25,7 @@ final class Langs 'pt' => 'Portuguese', // Portuguese from Portugal. 'ro' => 'Romanian', // Romanian from Romania. 'vi' => 'Vietnamese', // Vietnamese from Vietnam. + 'tr' => 'Türkçe', // Turkish from Turkey. ]; /** @@ -52,8 +53,6 @@ final class Langs * @var array */ public const LOCALES_CONNECTORS_MONEY = [ - 'de' => 'von', // German frm Germany: "von" - 'en' => 'of', // English from the United States: "of" 'es' => 'de', // Spanish from Spain: "de" 'fa' => 'از', // Farsi from Iran: "از" 'fr' => 'de', // French from France: "de" diff --git a/src/Langs/Replaces.php b/src/Langs/Replaces.php index 22cdf9d..95803fe 100644 --- a/src/Langs/Replaces.php +++ b/src/Langs/Replaces.php @@ -2,6 +2,7 @@ namespace Rmunate\Utilities\Langs; +use Rmunate\Utilities\Langs\Langs; use Rmunate\Utilities\Traits\Accesor; final class Replaces @@ -9,88 +10,65 @@ final class Replaces use Accesor; /** - * ->toMoney() - * The primary value must be there, that is, "es" instead of "es_ES" or similar. + * Replacements for the currency formatting methods. */ public const TO_MONEY = [ 'de' => [ - // 'illion' => 'illion Von', + //... ], 'en' => [ - 'illion' => 'illion of', + //... ], 'es' => [ - 'veintiuno' => 'veintiún', - 'treinta Y Uno' => 'treintaiún', - 'cuarenta y uno' => 'cuarentaiún', - 'cincuenta y uno' => 'cincuentaiún', - 'sesenta y uno' => 'sesentaiún', - 'setenta y uno' => 'setentaiún', - 'ochenta y uno' => 'ochentaiún', - 'noventa y uno' => 'noventaiún', - 'ciento uno' => 'ciento un', - 'doscientos uno' => 'doscientos un', - 'trescientos uno' => 'trescientos un', - 'cuatrocientos uno' => 'cuatrocientos un', - 'quinientos uno' => 'quinientos un', - 'seiscientos uno' => 'seiscientos un', - 'setecientos uno' => 'setecientos un', - 'ochocientos uno' => 'ochocientos un', - 'novecientos uno' => 'novecientos un', - 'mil uno' => 'mil un', - 'uno millón' => 'un millón', - 'illón' => 'illón De', - 'illones' => 'illones De', - 'uno pesos' => 'un peso', - 'uno soles' => 'un sol', - 'uno euros' => 'un euro', - 'uno bolívares' => 'un bolívar', - 'uno bolivares' => 'un bolívar', - 'uno quetzales' => 'un quetzal', - 'uno lempiras' => 'un lempira', - 'uno córdobas' => 'un córdoba', - 'uno cordobas' => 'un córdoba', - 'uno colónes' => 'un colón', - 'uno colones' => 'un colón', - 'uno balboas' => 'un balboa', - 'uno centavos' => 'un centavo', - 'uno céntimos' => 'un céntimo', - 'uno centimos' => 'un céntimo', - 'uno centimo' => 'un céntimo', + 'uno millón' => 'un millón', + 'uno pesos' => 'un peso', + 'uno soles' => 'un sol', + 'uno euros' => 'un euro', + 'uno bolívares' => 'un bolívar', + 'uno bolivares' => 'un bolívar', + 'uno quetzales' => 'un quetzal', + 'uno lempiras' => 'un lempira', + 'uno córdobas' => 'un córdoba', + 'uno cordobas' => 'un córdoba', + 'uno colónes' => 'un colón', + 'uno colones' => 'un colón', + 'uno balboas' => 'un balboa', + 'uno centavos' => 'un centavo', + 'uno céntimos' => 'un céntimo', + 'uno centimos' => 'un céntimo', + 'uno centimo' => 'un céntimo', ], 'fa' => [ - 'ilion' => 'میلیون و', + //... ], 'fr' => [ - 'illion' => 'illion de', - 'illions' => 'illions de', + //... ], 'hi' => [ //... ], 'it' => [ - 'ilione' => 'ilione di', - 'ilioni' => 'ilioni di', + //... ], 'pl' => [ //... ], 'pt' => [ - 'ilhão' => 'ilhão de', - 'ilhões' => 'ilhões de', + //... ], 'ro' => [ - 'ilion' => 'ilion de', - 'ilioane' => 'ilioane de', + //.. ], 'vi' => [ - 'illion' => 'illion của', + //... + ], + 'tr' => [ + //... ], ]; /** - * ->toLetters() - * The primary value must be there, that is, "es" instead of "es_ES" or similar. + * Replacements for the number to words output methods. */ public const TO_LETTERS = [ 'de' => [ @@ -126,11 +104,13 @@ final class Replaces 'vi' => [ //... ], + 'tr' => [ + //... + ], ]; /** - * ->toOrdinal() - * The primary value must be there, that is, "es" instead of "es_ES" or similar. + * Replacements for the ordinal number output methods. */ public const TO_ORDINAL = [ 'de' => [ @@ -166,5 +146,9 @@ final class Replaces 'vi' => [ //... ], + 'tr' => [ + //... + ], ]; + } diff --git a/src/Miscellaneous/Utilities.php b/src/Miscellaneous/Utilities.php index 38d3de8..2171fc5 100644 --- a/src/Miscellaneous/Utilities.php +++ b/src/Miscellaneous/Utilities.php @@ -93,6 +93,7 @@ public static function isNotExceedMax($value) { // Implementation for checking if the value does not exceed the maximum is provided here. if (self::isValidString($value) || self::isValidDouble($value)) { + $parts = explode('.', $value); $integerPart = intval($parts[0]); $decimalPart = intval($parts[1] ?? 0); @@ -106,82 +107,36 @@ public static function isNotExceedMax($value) return is_numeric($value) && filter_var($value, FILTER_VALIDATE_INT) !== false; } - /** - * Check if the given locale is valid and supported. - * - * @param string $locale The locale code to check. - * - * @return bool True if the locale is valid and supported, false otherwise. - */ - public static function isValidLocale($locale) - { - $timezones = array_keys(Langs::LOCALES_AVAILABLE); - - return in_array($locale, $timezones); - } - /** * Get the connector for the given locale to use in regular sentences. - * - * @param string $locale The locale code to get the connector for. - * - * @return string The connector for the given locale. - */ - public static function connector($locale) - { - $connector = Langs::LOCALES_CONNECTORS; - $locale = self::extractPrimaryLocale($locale); - - return $connector[$locale] ?? '?'; - } - - /** - * Get the connector for the given locale to use in sentences related to money. - * - * @param string $locale The locale code to get the money connector for. - * - * @return string The money connector for the given locale. - */ - public static function connectorMoney($locale) - { - $connector = Langs::LOCALES_CONNECTORS_MONEY; - $locale = self::extractPrimaryLocale($locale); - - return $connector[$locale] ?? '?'; - } - - /** - * Set the numeric value where the trailing zero is removed. - * - * @param mixed $value - * - * @return mixed */ - public static function decimal($value) + public static function connector(?string $connector, string $locale, string $partOne, string $partTwo) { - $result = match ($value) { - '01' => 1, - '1' => 10, - '02' => 2, - '2' => 20, - '03' => 3, - '3' => 30, - '04' => 4, - '4' => 40, - '05' => 5, - '5' => 50, - '06' => 6, - '6' => 60, - '07' => 7, - '7' => 70, - '08' => 8, - '8' => 80, - '09' => 9, - '9' => 90, - default => $value, - }; + if (!is_null($connector)) { + + //Custom + if ($connector === false || $connector === "") { + $output = sprintf('%s %s', $partOne, $partTwo); + } else if (in_array($connector, [".", ","])) { + $output = sprintf('%s%s %s', $partOne, trim($connector), $partTwo); + } else { + $output = sprintf('%s %s %s', $partOne, trim($connector), $partTwo); + } + + } else { + + //This Package + $locale = self::extractPrimaryLocale($locale); + $connectorPackage = Langs::LOCALES_CONNECTORS[$locale] ?? null; + + if (is_null($connectorPackage) || $connectorPackage === false || $connectorPackage == "") { + $output = sprintf('%s ? %s', $partOne, $partTwo); + } else { + $output = sprintf('%s %s %s', $partOne, $connectorPackage, $partTwo); + } + } - return $result; + return $output; } /** @@ -193,7 +148,11 @@ public static function decimal($value) */ public static function extractPrimaryLocale($string) { - return mb_strtolower(substr($string, 0, 2)); + if (strlen($string) > 2) { + $string = substr($string, 0, 2); + } + + return mb_strtolower($string); } /** @@ -206,17 +165,17 @@ public static function extractPrimaryLocale($string) public static function textOrdinalMode(?string $value) { return match ($value) { - 'default' => '%spellout-ordinal', 'male' => '%spellout-ordinal-masculine', - 'female' => '%spellout-ordinal-feminine', 'masculine' => '%spellout-ordinal-masculine', - 'feminine' => '%spellout-ordinal-feminine', - '%spellout-ordinal' => '%spellout-ordinal', '%spellout-ordinal-masculine' => '%spellout-ordinal-masculine', - '%spellout-ordinal-feminine' => '%spellout-ordinal-feminine', - 'ordinal' => '%spellout-ordinal', 'ordinal-masculine' => '%spellout-ordinal-masculine', + 'feminine' => '%spellout-ordinal-feminine', + 'female' => '%spellout-ordinal-feminine', + '%spellout-ordinal-feminine' => '%spellout-ordinal-feminine', 'ordinal-feminine' => '%spellout-ordinal-feminine', + 'default' => '%spellout-ordinal', + '%spellout-ordinal' => '%spellout-ordinal', + 'ordinal' => '%spellout-ordinal', default => '%spellout-ordinal', }; } @@ -236,4 +195,25 @@ public static function textOrdinalModeHuman(?string $value) '%spellout-ordinal' => 'default', }; } + + /** + * Checks if a string ends with a specified substring. + * + * @param string $haystack The complete string. + * @param string $needle The substring to check for at the end. + * + * @return bool True if the string ends with the specified substring, false otherwise. + */ + function static endsWith($haystack, $needle) { + // Get the length of the substring. + $length = strlen($needle); + + // If the length of the substring is zero, the string technically ends with it. + if ($length == 0) { + return true; + } + + // Compare the end of the string with the specified substring. + return (substr($haystack, -$length) === $needle); + } } diff --git a/src/Miscellaneous/Words.php b/src/Miscellaneous/Words.php index b91810e..23b62cc 100644 --- a/src/Miscellaneous/Words.php +++ b/src/Miscellaneous/Words.php @@ -3,6 +3,7 @@ namespace Rmunate\Utilities\Miscellaneous; use Rmunate\Utilities\Langs\Replaces; +use Rmunate\Utilities\Miscellaneous\Utilities; final class Words { @@ -17,21 +18,61 @@ final class Words */ public static function replaceLocale(string $value, string $locale, string $type) { - // Primary Locale + // Extract the primary locale from the given locale. $locale = Utilities::extractPrimaryLocale($locale); - //Value - $value = mb_strtolower($value); - - // Replace final strings for each language. + // Retrieve replacements based on language and type. $replacesLocale = Replaces::constant($type)[$locale] ?? []; foreach ($replacesLocale as $search => $replace) { - $search = mb_strtolower($search); - $replace = mb_strtolower($replace); $value = str_replace($search, $replace, $value); } - // Return the adjusted text. + // Perform additional replacements specific to TO_MONEY type. + if ($type == "TO_MONEY") { + + switch ($locale) { + case 'es': + $connector = Langs::LOCALES_CONNECTORS_MONEY['es']; + if (Utilities::endsWith($value, 'illón') || Utilities::endsWith($value, 'illones')) { + $value .= " {$connector} ", + } + + break; + + case 'fr': + $connector = Langs::LOCALES_CONNECTORS_MONEY['fr']; + if (Utilities::endsWith($value, 'illion') || Utilities::endsWith($value, 'illions')) { + $value .= " {$connector} ", + } + + break; + + case 'it': + $connector = Langs::LOCALES_CONNECTORS_MONEY['it']; + if (Utilities::endsWith($value, 'ilione') || Utilities::endsWith($value, 'ilioni')) { + $value .= " {$connector} ", + } + + break; + + case 'pt': + $connector = Langs::LOCALES_CONNECTORS_MONEY['pt']; + if (Utilities::endsWith($value, 'ilhão') || Utilities::endsWith($value, 'ilhões')) { + $value .= " {$connector} ", + } + + break; + + case 'ro': + $connector = Langs::LOCALES_CONNECTORS_MONEY['ro']; + if (Utilities::endsWith($value, 'ilion') || Utilities::endsWith($value, 'ilioane')) { + $value .= " {$connector} ", + } + + break; + } + } + return $value; } @@ -45,21 +86,30 @@ public static function replaceLocale(string $value, string $locale, string $type */ public static function replaceFromConfig(string $value, string $locale) { - // Primary Locale - $locale = Utilities::extractPrimaryLocale($locale); - - //Value - $value = mb_strtolower($value); - - //From Config. + // Retrieve replacements from the config file based on the language. $replacesLocaleConfig = config("spell-number.replacements.$locale") ?? []; foreach ($replacesLocaleConfig as $search => $replace) { - $search = mb_strtolower($search); - $replace = mb_strtolower($replace); $value = str_replace($search, $replace, $value); } - // Return the adjusted text. + return $value; + } + + /** + * Replace user-supplied values. + * + * @param string $value + * @param array $custom + * + * @return string + */ + public static function replaceCustom(string $value, array $custom) + { + // Perform replacements based on user-supplied values. + foreach ($custom as $search => $replace) { + $value = str_replace($search, $replace, $value); + } + return $value; } } diff --git a/src/SpellNumber.php b/src/SpellNumber.php index c230846..9b3819a 100644 --- a/src/SpellNumber.php +++ b/src/SpellNumber.php @@ -2,40 +2,38 @@ namespace Rmunate\Utilities; +use Closure; +use NumberFormatter; +use IntlDateFormatter; +use Rmunate\Utilities\Langs\Langs; +use Illuminate\Support\Facades\App; +use Rmunate\Utilities\Traits\Spell; +use Rmunate\Utilities\Traits\Constants; +use Rmunate\Utilities\Traits\Processor; use Illuminate\Support\Traits\Macroable; +use Rmunate\Utilities\Miscellaneous\Words; use Rmunate\Utilities\Bases\BaseSpellNumber; use Rmunate\Utilities\Callback\DataResponse; -use Rmunate\Utilities\Exceptions\SpellNumberExceptions; -use Rmunate\Utilities\Langs\Langs; use Rmunate\Utilities\Miscellaneous\Utilities; -use Rmunate\Utilities\Miscellaneous\Words; use Rmunate\Utilities\Validator\Traits\CommonValidate; use Rmunate\Utilities\Wrappers\NumberFormatterWrapper; +use Rmunate\Utilities\Exceptions\SpellNumberExceptions; class SpellNumber extends BaseSpellNumber { use CommonValidate; + use Constants; + use Processor; use Macroable; - - /* Constants Locale */ - public const SPECIFIC_LOCALE = true; - - /* Constants Ordinal Text */ - public const ORDINAL_DEFAULT = '%spellout-ordinal'; - public const ORDINAL_MALE = '%spellout-ordinal-masculine'; - public const ORDINAL_FEMALE = '%spellout-ordinal-feminine'; - - /* Constants Macros */ - public const TO_LETTERS = 'TO_LETTERS'; - public const TO_MONEY = 'TO_MONEY'; - public const TO_ORDINAL = 'TO_ORDINAL'; - - /* Propierties */ + use Spell; + + private $type; private $value; - private string $type; - protected string $locale; - protected string $currency; - protected string $fraction; + protected $locale; + protected $currency; + protected $fraction; + protected $connector; + protected $replacements; /** * Constructor. @@ -47,9 +45,11 @@ public function __construct($value, string $type) { $this->value = $value; $this->type = $type; - $this->locale = config('spell-number.default.lang') ?? config('spell-number.default.locale') ?? Langs::getLocaleLaravel(); + $this->locale = config('spell-number.default.locale') ?? App::getLocale(); $this->currency = config('spell-number.default.currency') ?? 'dollars'; $this->fraction = config('spell-number.default.fraction') ?? 'cents'; + $this->connector = null; + $this->replacements = []; } /** @@ -61,19 +61,9 @@ public function __construct($value, string $type) * * @return SpellNumber The SpellNumber instance with the updated locale. */ - public function locale(string $locale, ?bool $specific_locale = null) + public function locale(string $locale, bool $specific_locale = true) { - $valueConfig = config('spell-number.default.specific_locale'); - - $specific_locale = $specific_locale ?? $valueConfig ?? false; - - if ($specific_locale === false) { - if (!Utilities::isValidLocale($locale)) { - throw SpellNumberExceptions::create('The provided value is not valid. To view the available options, you can use the SpellNumber::getAvailableLocales() method, or you can pass the SpellNumber::SPECIFIC_LOCALE constant as the second parameter to enable specific or customized usage.'); - } - } - - $this->locale = $locale; + $this->locale = trim($locale); return $this; } @@ -87,7 +77,7 @@ public function locale(string $locale, ?bool $specific_locale = null) */ public function currency(string $currency) { - $this->currency = $currency; + $this->currency = trim($currency); return $this; } @@ -101,111 +91,196 @@ public function currency(string $currency) */ public function fraction(string $fraction) { - $this->fraction = $fraction; + $this->fraction = trim($fraction); return $this; } + /** + * Set the replacements. + * + * @param array $replacements + * + * @return SpellNumber The SpellNumber instance with the updated replacements. + */ + public function replacements(array $replacements) + { + $this->replacements = $replacements; + + return $this; + } + + /** + * Set the connector. + * + * @param string $connector + * + * @return SpellNumber The SpellNumber instance with the updated connector. + */ + public function connector(string $connector) + { + $this->connector = trim($connector); + + return $this; + } + + + + + + + + + + + + + + + + + + + + + + /** * Convert the numeric value to words. * + * @param Closure|null $callback Optional callback function to handle custom responses. * @return string The textual representation of the numeric value. */ - public function toLetters() + public function toLetters(Closure $callback = null) { - $callback = config('spell-number.callback_output'); + $words = ($this->type == 'integer') ? $this->integerToLetters() : $this->doubleToLetters(); if (!empty($callback) && is_callable($callback)) { - $words = ($this->type == 'integer') ? $this->integerToLetters() : $this->doubleToLetters(); - - return $callback(new DataResponse([ - 'method' => 'toLetters', - 'type' => $this->type, - 'value' => $this->value, - 'words' => $words, - 'lang' => Utilities::extractPrimaryLocale($this->locale), - 'locale' => $this->locale, - 'currency' => null, - 'fraction' => null, + + return $callback(new SpellNumberCallable([ + 'method' => 'toLetters', + 'type' => $this->type, + 'value' => $this->value, + 'locale' => $this->locale, + 'connector' => $this->connector, + 'replaces' => $this->replacements, + 'output' => $words, + 'lang' => Utilities::extractPrimaryLocale($this->locale), + 'spell' => function(string $method, ?string $modeOrdinal = null){ + return $this->spell($method, $modeOrdinal); + } ])); } - return ($this->type == 'integer') ? $this->integerToLetters() : $this->doubleToLetters(); + return $words; } /** * Convert the numeric value to a money representation. * + * @param Closure|null $callback Optional callback function to handle custom responses. * @return string The textual representation of the money value. */ - public function toMoney() + public function toMoney(Closure $callback = null) { - $callback = config('spell-number.callback_output'); + $words = ($this->type == 'integer') ? $this->integerToMoney() : $this->doubleToMoney(); if (!empty($callback) && is_callable($callback)) { - $words = ($this->type == 'integer') ? $this->integerToMoney() : $this->doubleToMoney(); - - return $callback(new DataResponse([ - 'method' => 'toMoney', - 'type' => $this->type, - 'value' => $this->value, - 'words' => $words, - 'lang' => Utilities::extractPrimaryLocale($this->locale), - 'locale' => $this->locale, - 'currency' => $this->currency, - 'fraction' => $this->fraction, + + return $callback(new SpellNumberCallable([ + 'method' => 'toMoney', + 'type' => $this->type, + 'value' => $this->value, + 'locale' => $this->locale, + 'connector' => $this->connector, + 'replaces' => $this->replacements, + 'currency' => $this->currency, + 'fraction' => $this->fraction, + 'output' => $words, + 'lang' => Utilities::extractPrimaryLocale($this->locale), + 'spell' => function(string $method, ?string $modeOrdinal = null){ + return $this->spell($method, $modeOrdinal); + } ])); } - return ($this->type == 'integer') ? $this->integerToMoney() : $this->doubleToMoney(); + return $words; } /** * Convert the numeric value to ordinal representation. * + * @param string|null $attr Optional attribute for ordinal representation (e.g., 'male', 'female'). + * @param Closure|null $callback Optional callback function to handle custom responses. * @return string The textual representation of the ordinal value. */ - public function toOrdinal(?string $attr = null) + public function toOrdinal(?string $attr = null, Closure $callback = null) { $valueConfig = config('spell-number.default.ordinal_output'); $attr = Utilities::textOrdinalMode($attr ?? $valueConfig); - $callback = config('spell-number.callback_output'); + $words = $this->integerToOrdinal($attr); if (!empty($callback) && is_callable($callback)) { - $words = $this->integerToOrdinal($attr); - - return $callback(new DataResponse([ - 'method' => 'toOrdinal', - 'type' => $this->type, - 'value' => $this->value, - 'words' => $words, - 'lang' => Utilities::extractPrimaryLocale($this->locale), - 'locale' => $this->locale, - 'mode' => Utilities::textOrdinalModeHuman($attr), + + return $callback(new SpellNumberCallable([ + 'method' => 'toOrdinal', + 'type' => $this->type, + 'value' => $this->value, + 'locale' => $this->locale, + 'replaces' => $this->replacements, + 'output' => $words, + 'lang' => Utilities::extractPrimaryLocale($this->locale), + 'mode' => Utilities::textOrdinalModeHuman($attr), + 'spell' => function(string $method, ?string $modeOrdinal = null){ + return $this-> spell($method, $modeOrdinal); + } ])); } - return $this->integerToOrdinal($attr); + return $words; } - /** - * Convert the integer numeric value to its ordinal textual representation. - * - * @return string The textual representation of the ordinal value. - */ - private function integerToOrdinal($attr) - { - if ($this->type == 'double') { - throw SpellNumberExceptions::create('To convert to ordinal numbers, an integer value is required as input'); - } - $formatter = NumberFormatterWrapper::format($this->value, $this->locale, true, $attr); - $formatter = Words::replaceLocale($formatter, $this->locale, self::TO_ORDINAL); - return Words::replaceFromConfig($formatter, $this->locale); - } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /** * Convert the integer numeric value to its textual representation. @@ -214,10 +289,19 @@ private function integerToOrdinal($attr) */ private function integerToLetters() { + // Text format $formatter = NumberFormatterWrapper::format($this->value, $this->locale); + + // Replacements from the same package $formatter = Words::replaceLocale($formatter, $this->locale, self::TO_LETTERS); - return Words::replaceFromConfig($formatter, $this->locale); + // Replacements from configuration + $formatter = Words::replaceFromConfig($formatter, $this->locale); + + // Apply user-supplied replacements + $formatter = Words::replaceCustom($formatter, $this->replacements); + + return $formatter; } /** @@ -227,21 +311,32 @@ private function integerToLetters() */ private function doubleToLetters() { + // Ensure the second value after the decimal point is not 0 $parts = explode('.', $this->value); - if (!array_key_exists(1, $parts)) { return $this->integerToLetters(); } + // Adjust the decimal value $parts[1] = Utilities::decimal($parts[1]); + // Translate the values separately $letters1 = NumberFormatterWrapper::format($parts[0], $this->locale); $letters2 = NumberFormatterWrapper::format($parts[1], $this->locale); - $output = sprintf('%s %s %s', $letters1, Utilities::connector($this->locale), $letters2); + // Define the connector for the output + $output = Utilities::connector($this->connector, $this->locale, $letters1, $letters2); + + // Replacements from the same package $output = Words::replaceLocale($output, $this->locale, self::TO_LETTERS); - return Words::replaceFromConfig($output, $this->locale); + // Replacements from the configuration + $output = Words::replaceFromConfig($output, $this->locale); + + // User-supplied replacements + $output = Words::replaceCustom($output, $this->replacements); + + return $output; } /** @@ -251,10 +346,19 @@ private function doubleToLetters() */ private function integerToMoney() { - $letters = NumberFormatterWrapper::format($this->value, $this->locale).' '.$this->currency; + // Translate the value + $letters = NumberFormatterWrapper::format($this->value, $this->locale) . ' ' . $this->currency; + + // Replacements from the same package $letters = Words::replaceLocale($letters, $this->locale, self::TO_MONEY); - return Words::replaceFromConfig($letters, $this->locale); + // Replacements from the configuration + $letters = Words::replaceFromConfig($letters, $this->locale); + + // User-supplied replacements + $letters = Words::replaceCustom($letters, $this->replacements); + + return $letters; } /** @@ -264,40 +368,425 @@ private function integerToMoney() */ private function doubleToMoney() { + // Ensure the second value after the decimal point is not 0 $parts = explode('.', $this->value); - if (!array_key_exists(1, $parts)) { return $this->integerToMoney(); } + // Adjust the decimal value $parts[1] = Utilities::decimal($parts[1]); - $letters1 = NumberFormatterWrapper::format($parts[0], $this->locale).' '.$this->currency; - $letters2 = NumberFormatterWrapper::format($parts[1], $this->locale).' '.$this->fraction; + // Translate values separately + $letters1 = NumberFormatterWrapper::format($parts[0], $this->locale) . ' ' . $this->currency; + $letters2 = NumberFormatterWrapper::format($parts[1], $this->locale) . ' ' . $this->fraction; - $output = $letters1.' '.Utilities::connector($this->locale).' '.$letters2; + // Define the connector for the output + $output = Utilities::connector($this->connector, $this->locale, $letters1, $letters2); + + // Replacements from the same package $output = Words::replaceLocale($output, $this->locale, self::TO_MONEY); - return Words::replaceFromConfig($output, $this->locale); + // Replacements from the configuration + $output = Words::replaceFromConfig($output, $this->locale); + + // User-supplied replacements + $output = Words::replaceCustom($output, $this->replacements); + + return $output; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /** + * Converts spelled-out numbers to numeric values. + * + * @param Closure|null $callback An optional callback function to process the result. + * + * @return mixed The numeric value or the result of the callback. + * + * @throws SpellNumberException If the provided value cannot be parsed. + */ + public function toNumbers(Closure $callback = null) + { + if (!in_array($this->type, ["text"])) { + throw SpellNumberExceptions::create("The 'toNumbers' method requires the class initializer to be 'text()'"); + } + + // Use NumberFormatter to parse the spelled-out number + $formatter = new NumberFormatter($this->locale, NumberFormatter::SPELLOUT); + $numericValue = $formatter->parse($this->value); + + // Check if parsing was successful + if (empty($numericValue) || $numericValue === false) { + // Throw an exception with an improved and English error message + throw SpellNumberExceptions::create("The provided value could not be read in '{$this->locale}', please check if it is correctly written."); + } + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $this->value, + 'value' => $numericValue, + 'locale' => $this->locale, + ])); + } + + // If no callback, return the numeric value + return $numericValue; + } + + /** + * Converts a decimal number to a Roman numeral. + * + * @param Closure|null $callback An optional callback function to process the result. + * + * @return mixed The Roman numeral or the result of the callback. + */ + public function toRomanNumeral(Closure $callback = null) + { + if (!in_array($this->type, ["integer"])) { + throw SpellNumberExceptions::create("The 'toRomanNumeral' method requires the supplied value to be of integer type"); + } + + $formatter = new NumberFormatter('@numbers=roman', NumberFormatter::DECIMAL); + $result = $formatter->format($this->value); + + // Replacements from the configuration + $words = Words::replaceFromConfig($result, $this->locale); + + // User-supplied replacements + $words = Words::replaceCustom($words, $this->replacements); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'locale' => '@numbers=roman', + 'replacements' => $this->replacements, + ])); + } + + // If no callback, return the numeric value + return $words; + } + + /** + * Convert a numeric value to a summarized representation with units. + * + * @param int $precision The precision of the result. + * @param Closure $callback An optional callback function to process the result. + * + * @return mixed The summarized representation or the result of the callback. + */ + public function toSummary(int $precision = 0, Closure $callback = null) + { + if (!in_array($this->type, ["integer", "double"])) { + throw SpellNumberExceptions::create("The 'toSummary' method requires the supplied value to be of integer or float type"); + } + + $units = [ + 3 => 'K', + 6 => 'M', + 9 => 'B', + 12 => 'T', + 15 => 'Q', + ]; + + $number = $this->value; + + switch (true) { + case $number === 0: + return '0'; + case $number < 0: + return sprintf('-%s', $this->toSummary(abs($number), $precision, $maxPrecision, $units)); + case $number >= 1e15: + return sprintf('%s'.end($units), $this->toSummary($number / 1e15, $precision, $maxPrecision, $units)); + } + + $numberExponent = floor(log10($number)); + $displayExponent = $numberExponent - ($numberExponent % 3); + $number /= pow(10, $displayExponent); + + $formatter = new NumberFormatter($this->locale, NumberFormatter::DECIMAL); + $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $precision); + $result = $formatter->format($number); + + $result = trim(sprintf('%s%s', $result, $units[$displayExponent] ?? '')); + + // Replacements from the configuration + $words = Words::replaceFromConfig($result, $this->locale); + + // User-supplied replacements + $words = Words::replaceCustom($words, $this->replacements); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'precision' => $precision, + 'locale' => $this->locale, + 'replacements' => $this->replacements, + ])); + } + + // If no callback, return the numeric value + return $words; + } + + /** + * Format the numeric value as currency. + * + * @param string|null $currency The currency code to use for formatting. + * @param Closure $callback An optional callback function to process the result. + * + * @return mixed The formatted currency or the result of the callback. + */ + public function toCurrency(?string $currency, Closure $callback = null) + { + if (!in_array($this->type, ["integer", "double"])) { + throw SpellNumberExceptions::create("The 'toCurrency' method requires the supplied value to be of integer or float type"); + } + + $formatter = new NumberFormatter($this->locale, NumberFormatter::CURRENCY); + $result = $formatter->formatCurrency($this->value, $currency); + + // Replacements from the configuration + $words = Words::replaceFromConfig($result, $this->locale); + + // User-supplied replacements + $words = Words::replaceCustom($words, $this->replacements); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'currency' => $currency, + 'locale' => $this->locale, + 'replacements' => $this->replacements, + ])); + } + + // If no callback, return the numeric value + return $words; } /** - * Perform a spell-related action based on the specified method and optional ordinal mode. + * Convert a date to words using the specified pattern. * - * @param string $method The method to perform (TO_LETTERS, TO_MONEY, or TO_ORDINAL). - * @param string|null $modeOrdinal The optional ordinal mode when using TO_ORDINAL. + * @param string $pattern The format pattern for the date. Defaults to 'dd MMMM yyyy'. + * @param Closure|null $callback An optional callback function to process the result. * - * @throws SpellNumberExceptions When the requested action is invalid. + * @return mixed The date in words or the result of the callback. * - * @return mixed The result of the specified action. + * @throws SpellNumberException If the class initializer is not of type 'date('yyyy-mm-dd')'. */ - private function spell(string $method, ?string $modeOrdinal = null) + public function inWords($pattern = 'dd MMMM yyyy', Closure $callback = null) { - return match ($method) { - 'TO_LETTERS' => $this->toLetters(), - 'TO_MONEY' => $this->toMoney(), - 'TO_ORDINAL' => $this->toOrdinal($modeOrdinal), - default => throw SpellNumberExceptions::create("The requested action does not exist. The 'spell' method only accepts one of the following three options: SpellNumber::TO_LETTERS, SpellNumber::TO_MONEY, or SpellNumber::TO_ORDINAL."), - }; + if (!in_array($this->type, ["date"])) { + throw SpellNumberExceptions::create("The 'inWords' method requiere que el inicializador de la clase sea 'date('yyyy-mm-dd')'"); + } + + $formatter = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::NONE, + null, + null, + $pattern + ); + + $result = $formatter->format(strtotime($this->value)); + + // Replacements from the configuration + $words = Words::replaceFromConfig($result, $this->locale); + + // User-supplied replacements + $words = Words::replaceCustom($words, $this->replacements); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'locale' => $this->locale, + 'pattern' => $pattern, + 'replacements' => $this->replacements, + ])); + } + + // If no callback, return the numeric value + return $words; + } + + /** + * Convert a numeric value to a percentage format. + * + * @param int $digits The number of digits to include in the percentage. Defaults to 2. + * @param Closure|null $callback An optional callback function to process the result. + * + * @return mixed The numeric value formatted as a percentage or the result of the callback. + * + * @throws SpellNumberException If the supplied value is not of integer or float type. + */ + public function toPercent(int $digits = 2, Closure $callback = null) + { + if (!in_array($this->type, ["integer", "double"])) { + throw SpellNumberExceptions::create("The 'toPercent' method requires the supplied value to be of integer or float type"); + } + + $formatter = new NumberFormatter($this->locale, NumberFormatter::PERCENT); + $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $digits); + $words = $formatter->format($this->value); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'digits' => $this->digits, + 'locale' => $this->locale, + ])); + } + + // If no callback, return the numeric value + return $words; + } + + /** + * Convert a numeric value to scientific notation. + * + * @param Closure|null $callback An optional callback function to process the result. + * + * @return mixed The numeric value formatted in scientific notation or the result of the callback. + * + * @throws SpellNumberException If the supplied value is not of integer or float type. + */ + public function toScientific(Closure $callback = null) + { + if (!in_array($this->type, ["integer", "double"])) { + throw SpellNumberExceptions::create("The 'toScientific' method requires the supplied value to be of integer or float type"); + } + + $formatter = new NumberFormatter($this->locale, NumberFormatter::SCIENTIFIC); + $words = $formatter->format($this->value); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'locale' => $this->locale, + ])); + } + + // If no callback, return the numeric value + return $words; + } + + /** + * Convert a numeric value to clock format. + * + * @param Closure|null $callback An optional callback function to process the result. + * + * @return mixed The numeric value formatted as clock or the result of the callback. + * + * @throws SpellNumberException If the supplied value is not of integer or float type. + */ + public function toClock(Closure $callback = null){ + + if (!in_array($this->type, ["integer", "double"])) { + throw SpellNumberExceptions::create("The 'toClock' method requires the supplied value to be of integer or float type"); + } + + $formatter = new NumberFormatter($this->locale, NumberFormatter::DURATION); + $words = $formatter->format($this->value); + + // If a callback is provided and is callable, execute it + if (!empty($callback) && is_callable($callback)) { + // Return the result of the callback, passing a DataResponse object + return $callback(new SpellNumberCallable([ + 'spell' => function (string $method, ?string $modeOrdinal = null) { + return $this->spell($method, $modeOrdinal); + }, + 'output' => $words, + 'value' => $this->value, + 'locale' => $this->locale, + ])); + } + + // If no callback, return the numeric value + return $words; } } diff --git a/src/Traits/Accesor.php b/src/Traits/Accesor.php index 135e0da..cb73fe3 100644 --- a/src/Traits/Accesor.php +++ b/src/Traits/Accesor.php @@ -2,11 +2,6 @@ namespace Rmunate\Utilities\Traits; -/** - * Trait Accesor. - * - * This trait provides a method to access constants of a class that uses it. - */ trait Accesor { /** @@ -18,7 +13,6 @@ trait Accesor */ public static function constant(string $name) { - // Use the constant() function to access the constant. return constant(get_called_class().'::'.$name); } } diff --git a/src/Traits/Constants.php b/src/Traits/Constants.php new file mode 100644 index 0000000..625313b --- /dev/null +++ b/src/Traits/Constants.php @@ -0,0 +1,31 @@ +type == 'double') { + throw SpellNumberExceptions::create('To convert to ordinal numbers, an integer value is required as input'); + } + + // Return the output value for INT. + $formatter = NumberFormatterWrapper::format($this->value, $this->locale, true, $attr); + + // Apply replacements from the same package. + $formatter = Words::replaceLocale($formatter, $this->locale, self::TO_ORDINAL); + + // Apply replacements loaded in the configuration file. + $formatter = Words::replaceFromConfig($formatter, $this->locale); + + // Apply user-supplied replacements. + $formatter = Words::replaceCustom($formatter, $this->replacements); + + return $formatter; + } + +} diff --git a/src/Traits/Spell.php b/src/Traits/Spell.php new file mode 100644 index 0000000..3956c96 --- /dev/null +++ b/src/Traits/Spell.php @@ -0,0 +1,36 @@ + $this->toLetters(), + '%value-to-ordinal' => $this->toOrdinal($modeOrdinal), + '%value-to-money' => $this->toMoney(), + '%value-to-clock' => $this->toClock(), + '%value-to-scientific' => $this->toScientific(), + '%value-to-percent' => $this->toPercent(), + '%value-to-currency' => $this->toCurrency(), + '%value-to-summary' => $this->toSummary(), + '%value-to-roman-numeral' => $this->toRomanNumeral(), + default => throw SpellNumberExceptions::create($messageException), + }; + } +} diff --git a/src/Wrappers/NumberFormatterWrapper.php b/src/Wrappers/NumberFormatterWrapper.php index 90a8f87..532e91f 100644 --- a/src/Wrappers/NumberFormatterWrapper.php +++ b/src/Wrappers/NumberFormatterWrapper.php @@ -7,6 +7,38 @@ final class NumberFormatterWrapper { + private $value; + private $locale; + private $ruleset; + + public function __construct($value) + { + $this->value = $value; + } + + public function locale(string $locale) + { + $this->locale = $locale; + + return $this; + } + + public function ruleset($ruleset) + { + $this->ruleset = $ruleset; + + return $this; + } + + public function text() + { + + } + + + + + /** * Format a given numeric value as a spelled-out string. *