├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── config └── spell-number.php ├── docs ├── .vitepress │ └── config.mts ├── contribute │ ├── contribution.md │ └── report-bugs.md ├── getting-started │ ├── changelog.md │ ├── installation.md │ ├── introduction.md │ └── publish-vendor.md ├── index.md ├── public │ ├── css │ │ └── style.css │ └── img │ │ ├── favicon.svg │ │ ├── laravel-red.svg │ │ ├── laravel_black.svg │ │ ├── logo-full-scream.png │ │ ├── logo-github.png │ │ └── logo.png └── usage │ ├── config-custom-callback.md │ ├── config-file.md │ ├── languages-available.md │ ├── macroable.md │ ├── numbers-to-letters.md │ ├── numbers-to-money.md │ └── numbers-to-ordinal.md ├── package-lock.json ├── package.json ├── src ├── Bases │ ├── BaseSpellNumber.php │ └── BaseSpellNumberValidator.php ├── Callback │ └── DataResponse.php ├── Exceptions │ └── SpellNumberExceptions.php ├── Langs │ ├── Langs.php │ └── Replaces.php ├── Miscellaneous │ ├── Utilities.php │ └── Words.php ├── Providers │ └── SpellNumberProvider.php ├── SpellNumber.php ├── Traits │ ├── Accesor.php │ └── Locale.php ├── Validator │ ├── SpellNumberValidator.php │ └── Traits │ │ └── CommonValidate.php └── Wrappers │ └── NumberFormatterWrapper.php └── test └── SpellNumberTest.php /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy VitePress site to Pages 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 17 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 18 | concurrency: 19 | group: pages 20 | cancel-in-progress: false 21 | 22 | jobs: 23 | # Build job 24 | build: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 0 # Not needed if lastUpdated is not enabled 31 | # - uses: pnpm/action-setup@v2 # Uncomment this if you're using pnpm 32 | # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun 33 | - name: Setup Node 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 18 37 | cache: npm # or pnpm / yarn 38 | - name: Setup Pages 39 | uses: actions/configure-pages@v3 40 | - name: Install dependencies 41 | run: npm ci # or pnpm install / yarn install / bun install 42 | - name: Build with VitePress 43 | run: | 44 | npm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build 45 | touch docs/.vitepress/dist/.nojekyll 46 | - name: Upload artifact 47 | uses: actions/upload-pages-artifact@v2 48 | with: 49 | path: docs/.vitepress/dist 50 | 51 | # Deployment job 52 | deploy: 53 | environment: 54 | name: github-pages 55 | url: ${{ steps.deployment.outputs.page_url }} 56 | needs: build 57 | runs-on: ubuntu-latest 58 | name: Deploy 59 | steps: 60 | - name: Deploy to GitHub Pages 61 | id: deployment 62 | uses: actions/deploy-pages@v2 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore common macOS files and directories 2 | **/.DS_Store 3 | 4 | # Ignore automatically generated files and directories 5 | /vendor/ 6 | /.fleet 7 | /.idea 8 | /.vscode 9 | 10 | # Ignore Composer generated directory and files 11 | /vendor 12 | 13 | # Ignore development tool generated files and directories 14 | .fleet 15 | .idea 16 | .vscode 17 | composer.lock 18 | 19 | #Node 20 | node_modules 21 | docs/.vitepress/cache 22 | docs/.vitepress/dist -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Raúl Mauricio Uñate Castro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Convert Numbers to Words in Laravel 2 | 3 | [![Project Discontinued](https://img.shields.io/badge/Project%20Discontinued-red?style=for-the-badge)](https://laravel.com/docs/11.x/helpers#method-number-spell) 4 | 5 | **This project has been discontinued.** 6 | 7 | Now, this functionality is part of the **Core of Laravel Framework**. Check out the usage of the `Illuminate\Support\Number` class in the [official Laravel documentation](https://laravel.com/docs/11.x/helpers#method-number-spell). 8 | 9 | ### Details: 10 | - **Version:** 10.41 11 | - **Pull Request:** [PR #48845](https://github.com/laravel/framework/pull/48845) 12 | 13 | --- 14 | --- 15 | --- 16 | --- 17 | 18 | ## Introduction 19 | 20 | Easily convert numbers to words in Laravel using this library, which leverages the native `PHP INTL` extension to perform conversion effortlessly. With this library, you can convert numbers to words in various languages and also obtain the value in currency format according to the selected language. Supported languages include English, Spanish, Portuguese, French, Italian, Romanian, Hindi, Polish, Vietnamese and Persian (Farsi). 21 | 22 | **This library is compatible with PHP +8.0 and Laravel versions 8.0 and higher** 23 | 24 | ![Spell Number Logo](https://github.com/alejandrodiazpinilla/SpellNumber/assets/51100789/e51cf045-26d0-44e0-a873-3034deaea046) 25 | 26 | ## Requirements 27 | 28 | For this solution to work properly you must have at least a version of PHP 8.0 since the package in its CORE has typed data. 29 | 30 | You must have Laravel Framework version 8 or higher. 31 | 32 | Finally, our package relies on the native PHP `INTL` extension, check in your `php.ini` that you have it. 33 | 34 | ## Install 35 | 36 | ### Composer 37 | 38 | To install the dependency via Composer, execute the following command: 39 | 40 | ``` bash 41 | composer require rmunate/spell-number 42 | ``` 43 | 44 | The package will automatically register itself. 45 | 46 | **That's all!** 47 | 48 | ## Usage 49 | 50 | [![📖📖📖 **FULL DOCUMENTATION** 📖📖📖](https://img.shields.io/badge/FULL%20DOCUMENTATION-Visit%20Here-blue?style=for-the-badge)](https://rmunate.github.io/SpellNumber/) 51 | 52 | # Contributing 53 | 54 | If you want to add support for a new language or want to develop new features, you can submit your requests to the main branch of the repository. 55 | 56 | To date, the following world-class developers have contributed their knowledge. 57 | 58 | To whom we thank for supporting making programming easier. 59 | 60 | - [Ashok Devatwal](https://github.com/ashokdevatwal) (Hindi Language) 61 | - [Olsza](https://github.com/olsza) (Polish Language) 62 | - [Siros Fakhri](https://github.com/sirosfakhri) (Farsi Language) 63 | - [Jens Twesmann](https://github.com/jetwes) (German Language) 64 | - [Gabriel Rausch](https://github.com/gdsrmygdsrjr) (Zero Decimal Correction) 65 | - [Alejandro Diaz](https://github.com/alejandrodiazpinilla) (Readme And Icon) 66 | - [Frank Sepulveda](https://github.com/socieboy) (Ordinal Texts) 67 | - [Ngô Quốc Đạt](https://github.com/datlechin) (Vietnamese Language) 68 | 69 | ## License 70 | This project is under the [MIT License](https://choosealicense.com/licenses/mit/). 71 | 72 | 🌟 Support My Projects! 🚀 73 | 74 | [![Become a Sponsor](https://img.shields.io/badge/-Become%20a%20Sponsor-blue?style=for-the-badge&logo=github)](https://github.com/sponsors/rmunate) 75 | 76 | Make any contributions you see fit; the code is entirely yours. Together, we can do amazing things and improve the world of development. Your support is invaluable. ✨ 77 | 78 | If you have ideas, suggestions, or just want to collaborate, we are open to everything! Join our community and be part of our journey to success! 🌐👩‍💻👨‍💻 -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rmunate/spell-number", 3 | "description": "Easily convert numbers to letters, this library supports the native PHP INTL extension with which we can easily convert numbers to letters. This library provides us with the possibility of converting only to numbers or to currency formats depending on the language to be used, this package has support in the languages en: English, es: Spanish, pt: Portuguese, fr: French, it: Italian, ro: Romanian, fa: Farsi, hi: Hindi, pl: Polish.", 4 | "keywords": [ 5 | "towords", 6 | "toletters", 7 | "numbers", 8 | "spell", 9 | "spell-number", 10 | "spell-numbers", 11 | "code-maestro", 12 | "rmunate" 13 | ], 14 | "homepage": "https://github.com/rmunate/SpellNumber", 15 | "type": "library", 16 | "license": "MIT", 17 | "autoload": { 18 | "psr-4": { 19 | "Rmunate\\Utilities\\": "src/" 20 | } 21 | }, 22 | "authors": [ 23 | { 24 | "name": "Raul Mauricio Uñate Castro", 25 | "email": "raulmauriciounate@gmail.com", 26 | "homepage": "https://github.com/rmunate", 27 | "role": "owner" 28 | }, 29 | { 30 | "name": "Olsza", 31 | "homepage": "https://github.com/olsza", 32 | "role": "developer" 33 | }, 34 | { 35 | "name": "Er. Ashok Devatwal", 36 | "email": "ashokdev78@gmail.com", 37 | "homepage": "https://github.com/ashokdevatwal", 38 | "role": "developer" 39 | }, 40 | { 41 | "name": "Siros Fakhri", 42 | "homepage": "https://github.com/sirosfakhri", 43 | "role": "developer" 44 | }, 45 | { 46 | "name": "Jens Twesmann", 47 | "homepage": "https://github.com/jetwes", 48 | "role": "developer" 49 | }, 50 | { 51 | "name": "Gabriel Rausch", 52 | "homepage": "https://github.com/gdsrmygdsrjr", 53 | "role": "developer" 54 | }, 55 | { 56 | "name": "Alejandro Diaz", 57 | "homepage": "https://github.com/alejandrodiazpinilla", 58 | "role": "developer" 59 | } 60 | ], 61 | "require": { 62 | "php": "^8.0", 63 | "ext-intl": "*", 64 | "illuminate/support": "^8.0|^9.0|^10.0" 65 | }, 66 | "require-dev": { 67 | "phpunit/phpunit": "^10.4" 68 | }, 69 | "extra" : { 70 | "branch-alias" : { 71 | "dev-main": "1.0.x-dev" 72 | }, 73 | "laravel": { 74 | "providers": [ 75 | "Rmunate\\Utilities\\Providers\\SpellNumberProvider" 76 | ] 77 | } 78 | }, 79 | "minimum-stability": "dev", 80 | "prefer-stable": true 81 | } 82 | -------------------------------------------------------------------------------- /config/spell-number.php: -------------------------------------------------------------------------------- 1 | [ 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Default Locale for Number to Words Translation. 20 | |-------------------------------------------------------------------------- 21 | | 22 | | Define the language over which values will be translated to words. 23 | | Remember that you only have the following options available: 24 | | 'de', 'en', 'es', 'fa', 'fr', 'hi', 'it', 'pl', 'pt', 'ro'. 25 | | 26 | */ 27 | 28 | 'locale' => 'en', 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Try using a "locale" not listed in the previous option. 33 | |-------------------------------------------------------------------------- 34 | | 35 | | If you want to use an option different from those previously listed, 36 | | you must set the following indicator to true, so that the conversion to the supplied 37 | | location can be attempted. To list possible additional options to use with the package, 38 | | you can run the function SpellNumber::getAllLocales(); 39 | | 40 | */ 41 | 42 | 'specific_locale' => false, 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Define the Currency. 47 | |-------------------------------------------------------------------------- 48 | | 49 | | Define the name of the currency to use globally. 50 | | This will reduce the amount of code when using the library. 51 | | 52 | */ 53 | 54 | 'currency' => 'dollars', 55 | 56 | /* 57 | |-------------------------------------------------------------------------- 58 | | Define the Fraction. 59 | |-------------------------------------------------------------------------- 60 | | 61 | | Define the name of the currency fraction to use globally. 62 | | This will reduce the amount of code when using the library. 63 | | Cents? What will you use?. 64 | | 65 | */ 66 | 67 | 'fraction' => 'cents', 68 | 69 | /* 70 | |-------------------------------------------------------------------------- 71 | | Output type ordinal texts 72 | |-------------------------------------------------------------------------- 73 | | 74 | | The output usually depends on the language to be used, 75 | | so you can use any of the following three options. 76 | | 77 | | Options: 'default', 'male', 'female' 78 | | 79 | */ 80 | 81 | 'ordinal_output' => 'default', 82 | ], 83 | 84 | /* 85 | |-------------------------------------------------------------------------- 86 | | Sometimes you need to replace certain things in the number to words outputs. 87 | |-------------------------------------------------------------------------- 88 | | 89 | | If so, it's very easy to do. 90 | | Use the structure of an associative array to apply these replacements to your outputs. 91 | | 92 | */ 93 | 94 | 'replacements' => [ 95 | // 'en' => [ 96 | // 'that' => 'this', 97 | // ], 98 | ], 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Encountered a specific adjustment in the number to words output?. 103 | |-------------------------------------------------------------------------- 104 | | 105 | | Here, you have the option to adjust whatever you need, the best part! 106 | | This adjustment will apply to all your number to words outputs. 107 | | 108 | */ 109 | 110 | 'callback_output' => function ($data) { 111 | // Your logic here... 112 | 113 | return $data->getWords(); 114 | }, 115 | ]; 116 | -------------------------------------------------------------------------------- /docs/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vitepress' 2 | 3 | export default defineConfig({ 4 | title: "Laravel SpellNumber", 5 | description: "Easily convert numbers to words in Laravel Framework.", 6 | lang: 'en-US', 7 | lastUpdated: false, 8 | base: '/SpellNumber', 9 | themeConfig: { 10 | footer: { 11 | message: 'Released under the MIT License.', 12 | copyright: 'Copyright © 2021-2023 Raul Mauricio Uñate' 13 | }, 14 | editLink: { 15 | pattern: 'https://github.com/rmunate/SpellNumber/tree/main/docs/:path' 16 | }, 17 | logo: '/img/logo.png', 18 | nav: [ 19 | {text: 'v4.2.2', link: '/'}, 20 | ], 21 | sidebar: [ 22 | { 23 | text: 'Getting Started', 24 | collapsed: false, 25 | items: [ 26 | {text: 'Introduction', link: '/getting-started/introduction'}, 27 | {text: 'Installation', link: '/getting-started/installation'}, 28 | {text: 'Publish Vendor', link: '/getting-started/publish-vendor'}, 29 | {text: 'Release Notes', link: '/getting-started/changelog'}, 30 | ] 31 | }, { 32 | text: 'Usage', 33 | collapsed: false, 34 | items: [ 35 | {text: 'Languages Available', link: '/usage/languages-available.md'}, 36 | {text: 'Numbers To Letters', link: '/usage/numbers-to-letters'}, 37 | {text: 'Numbers To Money', link: '/usage/numbers-to-money'}, 38 | {text: 'Numbers To Ordinal', link: '/usage/numbers-to-ordinal'}, 39 | {text: 'Config File', link: '/usage/config-file'}, 40 | {text: 'Config Custom Callback', link: '/usage/config-custom-callback'}, 41 | {text: 'Macroable', link: '/usage/macroable'}, 42 | ] 43 | }, { 44 | text: 'Contribute', 45 | collapsed: false, 46 | items: [ 47 | {text: 'Bug Report', link: 'contribute/report-bugs'}, 48 | {text: 'Contribution', link: 'contribute/contribution'} 49 | ] 50 | } 51 | ], 52 | 53 | socialLinks: [ 54 | {icon: 'github', link: 'https://github.com/rmunate/SpellNumber'} 55 | ], 56 | search: { 57 | provider: 'local' 58 | } 59 | }, 60 | head: [ 61 | ['link', { 62 | rel: 'stylesheet', 63 | href: '/SpellNumber/css/style.css' 64 | } 65 | ], 66 | ['link', { 67 | rel: 'icon', 68 | href: '/SpellNumber/img/logo.png', 69 | type: 'image/png' 70 | } 71 | ], 72 | ['meta', { 73 | property: 'og:image', 74 | content: '/SpellNumber/img/logo-github.png' 75 | } 76 | ], 77 | ['meta', { 78 | property: 'og:image:secure_url', 79 | content: '/SpellNumber/img/logo-github.png' 80 | } 81 | ], 82 | ['meta', { 83 | property: 'og:image:width', 84 | content: '600' 85 | } 86 | ], 87 | ['meta', { 88 | property: 'og:image:height', 89 | content: '400' 90 | } 91 | ], 92 | ['meta', { 93 | property: 'og:title', 94 | content: 'SpellNumber' 95 | } 96 | ], 97 | ['meta', { 98 | property: 'og:description', 99 | content: 'Effortlessly Convert Numbers to Words in Laravel! 🚀' 100 | } 101 | ], 102 | ['meta', { 103 | property: 'og:url', 104 | content: 'https://rmunate.github.io/SpellNumber/' 105 | } 106 | ], 107 | ['meta', { 108 | property: 'og:type', 109 | content: 'website' 110 | } 111 | ], 112 | ], 113 | }) 114 | -------------------------------------------------------------------------------- /docs/contribute/contribution.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | 81 | 82 | # Contributing 83 | 84 | If you want to add support for a new language or want to develop new features, you can submit your requests to the main branch of the repository. 85 | 86 | ## Contributors 87 | 88 | To date, the following world-class developers have contributed their knowledge. 89 | 90 | To whom we thank for supporting making programming easier. 91 | 92 | 93 | 94 | ## License 95 | This project is under the [MIT License](https://choosealicense.com/licenses/mit/). 96 | 97 | 🌟 Support My Projects! 🚀 98 | 99 | [![Become a Sponsor](https://img.shields.io/badge/-Become%20a%20Sponsor-blue?style=for-the-badge&logo=github)](https://github.com/sponsors/rmunate) 100 | 101 | Make any contributions you see fit; the code is entirely yours. Together, we can do amazing things and improve the world of development. Your support is invaluable. ✨ 102 | 103 | If you have ideas, suggestions, or just want to collaborate, we are open to everything! Join our community and be part of our journey to success! 🌐👩‍💻👨‍💻 -------------------------------------------------------------------------------- /docs/contribute/report-bugs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bug Report 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Bug Report 8 | 9 | If you find errors or opportunities within the package, you can create an incident that we will attend to in the shortest time possible. 10 | 11 | Here!: 12 | [https://github.com/rmunate/SpellNumber/issues/new](https://github.com/rmunate/SpellNumber/issues/new) 13 | 14 | Remember that you can also contribute as a collaborator of this solution. 15 | -------------------------------------------------------------------------------- /docs/getting-started/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Release Notes 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Release Notes 8 | 9 | ## [4.2.0] - 2023-11-20 10 | 11 | ### Added 12 | 13 | - **Vietnamese Language Support**: Introduced connectors and replacements for the Vietnamese language. 14 | 15 | ### Changed 16 | 17 | - **TitleCase to LowerCase**: In response to specific language nuances, output is now in lowercase. Developers have the option to modify the output as needed, keeping in mind the use of the callback function. 18 | 19 | To swiftly revert to title-formatted output, simply publish the configuration file: 20 | 21 | ```bash 22 | php artisan vendor:publish --provider="Rmunate\\Utilities\\Providers\\SpellNumberProvider" --tag="config" 23 | ``` 24 | 25 | Then, locate the callback function and adjust the return statement in the configuration file: 26 | 27 | ```php 28 | return [ 29 | //... 30 | 'callback_output' => function ($data) { 31 | 32 | // Your logic here... 33 | 34 | return \Illuminate\Support\Str::title($data->getWords()); 35 | }, 36 | ] 37 | ``` 38 | 39 | ## [4.2.2] - 2023-11-28 40 | 41 | ### Changed 42 | 43 | - **Zero-width spaces:** Characters (\u{AD}, \u{200B}) are removed from the translation outputs. -------------------------------------------------------------------------------- /docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Installation 8 | 9 | ## Requirements 10 | 11 | For this solution to work correctly, you must have at least PHP 8.0 installed, as the package includes typed data in its core. 12 | 13 | You must have Laravel Framework version 8 or higher. 14 | 15 | Finally, our package relies on the native PHP `INTL` extension, check in your `php.ini` that you have it. 16 | 17 | ## Install 18 | 19 | ### Composer 20 | 21 | To install the dependency via Composer, execute the following command: 22 | 23 | ``` bash 24 | composer require rmunate/spell-number 25 | ``` 26 | 27 | The package will automatically register itself. 28 | 29 | **That's all!** 30 | -------------------------------------------------------------------------------- /docs/getting-started/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | ![logo-spell-number](/img/logo-full-scream.png) 8 | 9 | ## Introduction 10 | 11 | Easily convert numbers to words in Laravel using this library, which leverages the native `PHP INTL` extension to perform conversion effortlessly. With this library, you can convert numbers to words in various languages and also obtain the value in currency format according to the selected language. 12 | 13 | Supported languages include English, Spanish, Portuguese, French, Italian, Romanian, Hindi, Polish and Persian (Farsi). 14 | 15 | **This library is compatible with PHP +8.0 and Laravel versions 8.0 and higher** 16 | -------------------------------------------------------------------------------- /docs/getting-started/publish-vendor.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuration File 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Laravel Configuration File 8 | 9 | This is optional. 10 | 11 | You can publish the config-file with: 12 | 13 | ``` bash 14 | php artisan vendor:publish --provider="Rmunate\\Utilities\\Providers\\SpellNumberProvider" --tag="config" 15 | ``` 16 | 17 | [View File on GitHub](https://github.com/rmunate/SpellNumber/blob/1cca5565d6b8c049683357bcbb964b70bcfc4a92/config/spell-number.php) 18 | 19 | The configuration file will prove valuable if you intend to consistently use a single target language for converting numbers into letters. 20 | 21 | It also provides a comprehensive global output manipulation feature, empowering you to implement various adjustments or restore settings to the default package letter output. 22 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: Laravel 6 | text: Spell Number 7 | tagline: Effortlessly Convert Numbers to Words in Laravel! 🚀 8 | image: 9 | src: /img/logo.png 10 | alt: SpellNumber 11 | actions: 12 | - theme: brand 13 | text: Get Started 14 | link: /getting-started/introduction 15 | - theme: alt 16 | text: View on GitHub 17 | link: https://github.com/rmunate/SpellNumber 18 | - theme: alt 19 | text: View on Youtube 20 | link: https://www.youtube.com/watch?v=jm7_ypPZbjk&list=PL-SK4hjbvgxLmNUAyDpUa44cCIx4hVTqE 21 | 22 | features: 23 | - icon: 📝 24 | title: Convert to Words 25 | details: Effortlessly convert numbers to words. Choose whether you want to translate an integer or a floating-point number; we're ready for any scenario. 26 | - icon: 💵 27 | title: Words in Currency Format 28 | details: Represent a currency value in words easily by specifying the currency and its fraction. 29 | - icon: 🔢 30 | title: Convert to Ordinals 31 | details: Planning to use the values for ordering? We've got you covered—easily transform your values into ordinal numbers. 32 | --- -------------------------------------------------------------------------------- /docs/public/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-home-hero-name-color: #09a292; 3 | --vp-button-brand-border: #427d84; 4 | --vp-button-brand-bg: #4c99a2; 5 | --vp-button-brand-hover-bg: #01403d; 6 | --vp-c-brand-1: #09a292; 7 | } 8 | 9 | .tagline{ 10 | font-size: 22px !important; 11 | } -------------------------------------------------------------------------------- /docs/public/img/laravel-red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/public/img/laravel_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/img/logo-full-scream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmunate/SpellNumber/22b4bd162b016628f6f8a58bccd53f289611e1f9/docs/public/img/logo-full-scream.png -------------------------------------------------------------------------------- /docs/public/img/logo-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmunate/SpellNumber/22b4bd162b016628f6f8a58bccd53f289611e1f9/docs/public/img/logo-github.png -------------------------------------------------------------------------------- /docs/public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmunate/SpellNumber/22b4bd162b016628f6f8a58bccd53f289611e1f9/docs/public/img/logo.png -------------------------------------------------------------------------------- /docs/usage/config-custom-callback.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Callback 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Callback 8 | 9 | In the same configuration file `spell-number.php`, you will have a callback that you can use to do any type of treatment at the output of the packet processing. 10 | 11 | ``` php 12 | return [ 13 | //... 14 | 'callback_output' => function ($data) { 15 | 16 | // Your logic here... 17 | 18 | return $data->getWords(); 19 | }, 20 | ] 21 | ``` 22 | 23 | Inside the callback you will have a variable `$data`, which contains the necessary information so that you can execute any type of process within this space. 24 | 25 | If you print the $data variable, you will find an instance of the `Rmunate\Utilities\Callback\DataResponse` object that allows you to access the following values. 26 | 27 | ``` php 28 | dd($data); 29 | 30 | // Rmunate\Utilities\Callback\DataResponse { 31 | // #method: "toMoney" 32 | // #type: "double" 33 | // #lang: "en" 34 | // #locale: "en_US" 35 | // #currency: "Dollars" 36 | // #fraction: "Cents" 37 | // +value: "12345.230" 38 | // +words: "Twelve Thousand Three Hundred Forty-Five Dollars And Two Hundred Thirty Cents" 39 | // } 40 | ``` 41 | 42 | Easily access any property of this object with the syntax `$data->words`, likewise, this object contains getters to obtain the values if you prefer it that way `$data->getWords()`. 43 | 44 | ::: warning IMPORTANT 45 | Remember that you must return the final value after executing all the actions you want on the package output values. 46 | `return $value`. 47 | 48 | -------------------------------------------------------------------------------- /docs/usage/config-file.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Config File 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Config File 8 | 9 | Since version 4.0 of the package, you will have the option to configure your environment so that the translations are easier to write in your controllers or classes, now you will have a file inside the `config` folder that will allow you to adjust by default the values to be used in the conversions. 10 | 11 | You will also have a unique functionality, where you can create your own logic for any output of the values processed in the package. 12 | 13 | ## Export file from vendor to project 14 | 15 | To export the configuration file from the vendor to the `config` folder, you can use the command: 16 | 17 | ``` bash 18 | php artisan vendor:publish --provider="Rmunate\\Utilities\\Providers\\SpellNumberProvider" --tag="config" 19 | ``` 20 | 21 | ## Locale. 22 | 23 | Now that you have the `spell-number.php` file in the `config` folder, you can adjust the language that will be used for all translations of the package from numbers to letters, avoiding having to use it when invoking the use of the library. 24 | 25 | ``` php 26 | return [ 27 | //... 28 | 'default' => [ 29 | //... 30 | 'locale' => 'en', 31 | //... 32 | ] 33 | ] 34 | ``` 35 | 36 | In the index `locale` you can assign the one you are going to use globally. 37 | 38 | ## Specific Locale 39 | 40 | By default the package only works with the list of supported languages, a list that can be obtained with the `SpellNumber::getAvailableLocales()` method, now if what you are looking for is to use a more precise `locale`, such as `es_CO`, `es_MX`,`en_US`, in that case you can leave this configuration defined to avoid having to pass the constant `SpellNumber::SPECIFIC_LOCALE` in the `->locale(...)` method. 41 | 42 | ``` php 43 | return [ 44 | //... 45 | 'default' => [ 46 | //... 47 | 'specific_locale' => true, //false (default) 48 | //... 49 | ] 50 | ] 51 | ``` 52 | 53 | ## Currency Values 54 | 55 | Just as you can set the global translation language, you can also define the currency values that should be used in all library processing outputs. 56 | 57 | ``` php 58 | return [ 59 | //... 60 | 'default' => [ 61 | //... 62 | 'currency' => 'dollars', 63 | //... 64 | 'fraction' => 'cents', 65 | //... 66 | ] 67 | ] 68 | ``` 69 | 70 | ## Output type ordinal texts 71 | 72 | If you are going to use the package to generate ordinal values, in that case you can also determine a global output for all calls of the method, for that you can define the output mode of the ordinal text from the configuration file, this avoids you having to go through the `SpellNumber::ORDINAL_DEFAULT`, `SpellNumber::ORDINAL_MALE`, `SpellNumber::ORDINAL_FEMALE` constants in the `->toOrdinal()` method. 73 | 74 | ``` php 75 | return [ 76 | //... 77 | 'default' => [ 78 | //... 79 | 'ordinal_output' => 'default', // 'default', 'male', 'female' 80 | //... 81 | ] 82 | ] 83 | ``` 84 | 85 | ## Replacements 86 | 87 | Some people around the world have seen that some small fragments of the packet processing outputs must conform to the current language of the country or region, usually they are small fragments that could be replaced by a specific term or form of writing, for that you now have the possibility of defining all the replacements of text fragments that you consider necessary. 88 | 89 | Relying on the structure of an associative array, you will put the value to be searched as an index and the text to be assigned as its replacement as a value. 90 | 91 | The primary value must be there, that is, "es" instead of "es_MX" or similar. 92 | 93 | ``` php 94 | return [ 95 | //... 96 | 'replacements' => [ 97 | 'es' => [ 98 | 'uno' => 'un', 99 | ], 100 | ], 101 | ] 102 | ``` 103 | 104 | You must define the language on which the replacement should be applied in all the outputs of the internal processing of the package. 105 | 106 | ## Callback 107 | 108 | If what you would like is to have a programmable way to modify the output of the package value, in that case this solution is what you need. 109 | 110 | Go to the next page => -------------------------------------------------------------------------------- /docs/usage/languages-available.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Supported Languages 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Supported Languages 8 | 9 | We currently have 11 preset languages to easily work with this package. _Although you can use other configurations with arbitrary outputs_, we list the pre-configured languages below: 10 | 11 | - German. 12 | - English. 13 | - Spanish. 14 | - Farsi. 15 | - French. 16 | - Hindi. 17 | - Italian. 18 | - Polish. 19 | - Portuguese. 20 | - Romanian. 21 | - Vietnamese. 22 | 23 | If you want to get the supported languages directly from the package, you have two ways to do it. 24 | 25 | ## Available Locales 26 | Execute the method `getAvailableLocales`, as output it will give you an array with the available values. 27 | 28 | ```php 29 | use Rmunate\Utilities\SpellNumber; 30 | 31 | SpellNumber::getAvailableLocales(); 32 | 33 | // array:10 [▼ 34 | // 0 => "de" 35 | // 1 => "en" 36 | // 2 => "es" 37 | // 3 => "fa" 38 | // 4 => "fr" 39 | // 5 => "hi" 40 | // 6 => "it" 41 | // 7 => "pl" 42 | // 8 => "pt" 43 | // 9 => "ro" 44 | // 10 => "vi" 45 | // ] 46 | ``` 47 | 48 | ## Available Languages 49 | 50 | Execute the method `getAvailableLanguages`, as output it will give you an associative array with the values of available languages. 51 | 52 | ```php 53 | use Rmunate\Utilities\SpellNumber; 54 | 55 | SpellNumber::getAvailableLanguages(); 56 | 57 | // array:10 [▼ 58 | // "de" => "German" 59 | // "en" => "English" 60 | // "es" => "Spanish" 61 | // "fa" => "Farsi" 62 | // "fr" => "French" 63 | // "hi" => "Hindi" 64 | // "it" => "Italian" 65 | // "pl" => "Polish" 66 | // "pt" => "Portuguese" 67 | // "ro" => "Romanian" 68 | // "vi" => "Vietnamese" 69 | // ] 70 | ``` 71 | 72 | ## Other Locales 73 | 74 | If you would like to try a language other than the 10 previously mentioned, you can consult the different values that you can supply to the package to try to generate the output translated to said area and language. 75 | 76 | In order to use any of these options, you must use the constant `SpellNumber::SPECIFIC_LOCALE` in the `locale` method, example `->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)`. 77 | 78 | 79 | ```php 80 | use Rmunate\Utilities\SpellNumber; 81 | 82 | SpellNumber::getAllLocales(); 83 | 84 | // array:805 [▼ 85 | // 0 => "af" 86 | // 1 => "af_NA" 87 | // 2 => "af_ZA" 88 | // 3 => "agq" 89 | // 4 => "agq_CM" 90 | // 5 => "ak" 91 | // 6 => "ak_GH" 92 | // 7 => "am" 93 | // 8 => "am_ET" 94 | // 9 => "ar" 95 | // 10 => "ar_001" 96 | // ... 97 | // ] 98 | ``` -------------------------------------------------------------------------------- /docs/usage/macroable.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Macroable 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Macroable 8 | 9 | You likely have excellent ideas for methods that could greatly benefit from the capabilities of **SpellNumber**. We encourage you to create them yourself. 10 | 11 | ## Creating a Macro 12 | 13 | To develop a custom method, you can establish a new provider and register it within your application, following the guidelines in the [Laravel Documentation](https://laravel.com/docs/10.x/providers). This approach can lead to a more organized and clear code structure. For this guide's purposes, we'll utilize the `AppServiceProvider`, which is readily available and located at `app/Providers/AppServiceProvider.php`. 14 | 15 | Within the `register()` method of the Provider, you can craft your custom method. For practical demonstration, let's create the `toLettersWithSymbol` method. This method will transform the supplied value into words, followed by the currency symbol and its abbreviation. The output will be **'Two hundred - $MXN'**. 16 | 17 | ```php 18 | namespace App\Providers; 19 | 20 | use Rmunate\Utilities\SpellNumber; 21 | use Illuminate\Support\ServiceProvider; 22 | 23 | class AppServiceProvider extends ServiceProvider 24 | { 25 | /** 26 | * Register any application services. 27 | */ 28 | public function register(): void 29 | { 30 | SpellNumber::macro('toLettersWithSymbol', function (string $symbol) { 31 | 32 | $value = $this->spell(SpellNumber::TO_LETTERS); 33 | 34 | return "$value - $symbol"; 35 | }); 36 | } 37 | } 38 | ``` 39 | 40 | ## The Spell Method 41 | 42 | If you observe, the "spell" method is used within the constructed macro. This method is exclusively available in this context and cannot be utilized elsewhere. It facilitates the effortless conversion of the provided value into words, as needed. 43 | 44 | To Letters: 45 | ```php 46 | $this->spell(SpellNumber::TO_LETTERS) 47 | ``` 48 | 49 | To Money Format: 50 | ```php 51 | $this->spell(SpellNumber::TO_MONEY) 52 | ``` 53 | 54 | To Ordinal Text: 55 | ```php 56 | $this->spell(SpellNumber::TO_ORDINAL, SpellNumber::ORDINAL_DEFAULT) 57 | ``` 58 | 59 | ## How to Use My Macro 60 | 61 | Now that you have created your macro within the service provider, simply call it like an original method of the SpellNumber class. 62 | 63 | ```php 64 | SpellNumber::value(200)->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->toLettersWithSymbol('$MXN') 65 | // "Doscientos - $MXN" 66 | ``` 67 | 68 | You now possess the ability to create unique solutions within your applications. -------------------------------------------------------------------------------- /docs/usage/numbers-to-letters.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Numbers To Letters 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Numbers To Letters 8 | 9 | ## Method Value 10 | 11 | ### Integers 12 | 13 | You can easily convert whole numbers to words by defining the locale to be applied. If you do not define a locale, "en" (English) will be applied by default. Remember that if you export the vendor configuration file, it will not require this definition. 14 | 15 | ```php 16 | use Rmunate\Utilities\SpellNumber; 17 | 18 | SpellNumber::value(100)->locale('en')->toLetters(); 19 | // "one hundred" 20 | 21 | SpellNumber::value(100)->locale('es')->toLetters(); 22 | // "cien" 23 | 24 | SpellNumber::value(100)->locale('fa')->toLetters(); 25 | // "صد" 26 | 27 | SpellNumber::value(100)->locale('hi')->toLetters(); 28 | // "एक सौ" 29 | ``` 30 | 31 | ### Floating Point 32 | 33 | If required, you can pass a floating point number as an argument to convert it to words. Only the first two digits after the floating point will be taken. 34 | 35 | ```php 36 | use Rmunate\Utilities\SpellNumber; 37 | 38 | SpellNumber::value(123456789.12)->locale('en')->toLetters(); 39 | // "one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine and twelve" 40 | 41 | SpellNumber::value(123456789.12)->locale('es')->toLetters(); 42 | // "ciento veintitrés millones cuatrocientos cincuenta y seis mil setecientos ochenta y nueve con doce" 43 | 44 | SpellNumber::value(123456789.12)->locale('hi')->toLetters(); 45 | // "बारह करोड़ चौंतीस लाख छप्पन हज़ार सात सौ नवासी और बारह" 46 | ``` 47 | 48 | ## Method Integer 49 | 50 | You can also rely on the `Integer` method to define that the input is a new integer. 51 | Remember to ensure that the input value is of type `int`. 52 | 53 | ```php 54 | use Rmunate\Utilities\SpellNumber; 55 | 56 | SpellNumber::integer(100)->locale('en')->toLetters(); 57 | // "one hundred" 58 | 59 | SpellNumber::integer(100)->locale('es')->toLetters(); 60 | // "cien" 61 | ``` 62 | 63 | ## Method Float 64 | 65 | Now, if you require it to be translated into letters of more than two decimal places, the solution may be to use the Float method. This method necessarily receives a string value that allows the library to read the complete value sent after the floating point. 66 | 67 | ```php 68 | use Rmunate\Utilities\SpellNumber; 69 | 70 | SpellNumber::float('12345.230')->locale('en')->toLetters(); 71 | // "twelve thousand three hundred forty-five and two hundred thirty" 72 | 73 | SpellNumber::float('12345.230')->locale('es')->toLetters(); 74 | // "doce mil trescientos cuarenta y cinco con doscientos treinta" 75 | ``` 76 | 77 | ## Especific Locale 78 | 79 | If you want to use a specific locale, you should always use the constant `SpellNumber::SPECIFIC_LOCALE` 80 | 81 | ```php 82 | use Rmunate\Utilities\SpellNumber; 83 | 84 | SpellNumber::value(100)->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->toLetters(); 85 | // "cien" 86 | 87 | SpellNumber::integer(100)->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->toLetters(); 88 | // "cien" 89 | 90 | SpellNumber::float('12345.230')->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->toLetters(); 91 | // "doce mil trescientos cuarenta y cinco con doscientos treinta" 92 | ``` -------------------------------------------------------------------------------- /docs/usage/numbers-to-money.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Numbers To Money 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Numbers To Money 8 | 9 | ## Method Value 10 | 11 | ### Integers 12 | 13 | It is very easy to use this solution for systems where you require the value more than in letters, with the specified currency structure. 14 | If you require that an integer have this transformation, you can do it in the following way. 15 | 16 | ```php 17 | SpellNumber::value(100)->locale('en')->currency('Dollars')->toMoney(); 18 | // "one hundred dollars" 19 | 20 | SpellNumber::value(100)->locale('es')->currency('Pesos')->toMoney(); 21 | // "cien pesos" 22 | 23 | SpellNumber::value(100)->locale('fa')->currency('تومان')->toMoney(); 24 | // "صد تومان" 25 | 26 | SpellNumber::value(100)->locale('hi')->currency('रूपये')->toMoney(); 27 | // "एक सौ रूपये" 28 | ``` 29 | 30 | ### Floating Point 31 | 32 | You can also pass a floating point number as an argument to convert it into words in currency format. Only the first two digits after the floating point will be taken. 33 | 34 | This method can be useful for invoices, receipts, and similar scenarios. Obtain the supplied value in currency format. 35 | 36 | ```php 37 | SpellNumber::value(100.12)->locale('en')->currency('Dollars')->fraction('Cents')->toMoney(); 38 | // "one hundred dollars and twelve cents" 39 | 40 | SpellNumber::value(100.12)->locale('es')->currency('Pesos')->fraction('Centavos')->toMoney(); 41 | // "cien pesos con doce centavos" 42 | 43 | SpellNumber::value(100.12)->locale('hi')->currency('रूपये')->fraction('पैसे')->toMoney(); 44 | // "एक सौ रूपये और बारह पैसे" 45 | 46 | SpellNumber::value(100.65)->locale('pl')->currency('złotych')->fraction('groszy')->toMoney(); 47 | // "sto złotych i sześćdziesiąt pięć groszy" 48 | ``` 49 | 50 | ## Method Integer 51 | 52 | You can also rely on the `Integer` method to define that the input is a new integer. 53 | Remember to ensure that the input value is of type `int`. 54 | 55 | ```php 56 | SpellNumber::integer(100)->locale('es')->currency('Pesos')->toMoney(); 57 | // "cien pesos" 58 | 59 | SpellNumber::integer(100)->locale('en')->currency('Dollars')->toMoney(); 60 | // "one hundred dollars" 61 | ``` 62 | 63 | ## Method Float 64 | 65 | Now, if you require it to be translated into letters of more than two decimal places, the solution may be to use the Float method. This method necessarily receives a string value that allows the library to read the complete value sent after the floating point. 66 | 67 | ```php 68 | SpellNumber::float('12345.230')->locale('es')->currency('Pesos')->fraction('Centavos')->toMoney(); 69 | // "doce mil trescientos cuarenta y cinco pesos con doscientos treinta centavos" 70 | 71 | SpellNumber::float('12345.230')->locale('en')->currency('Dollars')->fraction('Cents')->toMoney(); 72 | // "twelve thousand three hundred forty-five dollars and two hundred thirty cents" 73 | ``` 74 | 75 | ## Especific Locale 76 | 77 | If you want to use a specific locale, you should always use the constant `SpellNumber::SPECIFIC_LOCALE` 78 | 79 | ```php 80 | use Rmunate\Utilities\SpellNumber; 81 | 82 | SpellNumber::value(100)->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->currency('Pesos')->toMoney(); 83 | // "cien pesos" 84 | 85 | SpellNumber::integer(100)->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->currency('Pesos')->toMoney(); 86 | // "cien pesos" 87 | 88 | SpellNumber::float('12345.230')->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->currency('Pesos')->fraction('Centavos')->toMoney(); 89 | // "doce mil trescientos cuarenta y cinco pesos con doscientos treinta centavos" 90 | ``` -------------------------------------------------------------------------------- /docs/usage/numbers-to-ordinal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Numbers To Ordinal 3 | editLink: true 4 | outline: deep 5 | --- 6 | 7 | # Numbers To Ordinal 8 | 9 | You may need to list some data within your system, if so, this section is your solution. 10 | 11 | This package included the option to use ordinal numbers for this purpose. 12 | 13 | You can only use this method with integers, remember to ensure that the value meets this type, otherwise we may have unexpected responses. 14 | 15 | ## Method Value 16 | 17 | You can easily convert integers. If you do not define a locale, "en" (English) will be applied by default. Remember that if you export the provider configuration file, it will not require this definition. 18 | 19 | ```php 20 | use Rmunate\Utilities\SpellNumber; 21 | 22 | SpellNumber::value(2)->locale('en')->toOrdinal(); 23 | // "second" 24 | 25 | SpellNumber::value(2)->locale('en')->toOrdinal(SpellNumber::ORDINAL_DEFAULT); 26 | // "second" 27 | 28 | SpellNumber::value(2)->locale('es')->toOrdinal(SpellNumber::ORDINAL_MALE); 29 | // "segundo" 30 | 31 | SpellNumber::value(2)->locale('es')->toOrdinal(SpellNumber::ORDINAL_FEMALE); 32 | // "segunda" 33 | ``` 34 | 35 | ## Method Integer 36 | 37 | You can also rely on the `Integer` method to define that the input is a new integer. 38 | Remember to ensure that the input value is of type `int`. 39 | 40 | ```php 41 | use Rmunate\Utilities\SpellNumber; 42 | 43 | SpellNumber::integer(2)->locale('en')->toOrdinal(); 44 | // "second" 45 | 46 | SpellNumber::integer(2)->locale('en')->toOrdinal(SpellNumber::ORDINAL_DEFAULT); 47 | // "second" 48 | 49 | SpellNumber::integer(2)->locale('es')->toOrdinal(SpellNumber::ORDINAL_MALE); 50 | // "segundo" 51 | 52 | SpellNumber::integer(2)->locale('es')->toOrdinal(SpellNumber::ORDINAL_FEMALE); 53 | // "segunda" 54 | ``` 55 | 56 | ## Especific Locale 57 | 58 | If you want to use a specific locale, you should always use the constant `SpellNumber::SPECIFIC_LOCALE` 59 | 60 | ```php 61 | use Rmunate\Utilities\SpellNumber; 62 | 63 | SpellNumber::value(2)->locale('en_US', SpellNumber::SPECIFIC_LOCALE)->toOrdinal(); 64 | // "second" 65 | 66 | SpellNumber::integer(2)->locale('es_MX', SpellNumber::SPECIFIC_LOCALE)->toOrdinal(SpellNumber::ORDINAL_FEMALE) 67 | // "segunda" 68 | ``` -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SpellNumber", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "vitepress": "^1.0.0-rc.13" 9 | } 10 | }, 11 | "node_modules/@algolia/autocomplete-core": { 12 | "version": "1.9.3", 13 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", 14 | "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", 15 | "dev": true, 16 | "dependencies": { 17 | "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", 18 | "@algolia/autocomplete-shared": "1.9.3" 19 | } 20 | }, 21 | "node_modules/@algolia/autocomplete-plugin-algolia-insights": { 22 | "version": "1.9.3", 23 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", 24 | "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", 25 | "dev": true, 26 | "dependencies": { 27 | "@algolia/autocomplete-shared": "1.9.3" 28 | }, 29 | "peerDependencies": { 30 | "search-insights": ">= 1 < 3" 31 | } 32 | }, 33 | "node_modules/@algolia/autocomplete-preset-algolia": { 34 | "version": "1.9.3", 35 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", 36 | "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", 37 | "dev": true, 38 | "dependencies": { 39 | "@algolia/autocomplete-shared": "1.9.3" 40 | }, 41 | "peerDependencies": { 42 | "@algolia/client-search": ">= 4.9.1 < 6", 43 | "algoliasearch": ">= 4.9.1 < 6" 44 | } 45 | }, 46 | "node_modules/@algolia/autocomplete-shared": { 47 | "version": "1.9.3", 48 | "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", 49 | "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", 50 | "dev": true, 51 | "peerDependencies": { 52 | "@algolia/client-search": ">= 4.9.1 < 6", 53 | "algoliasearch": ">= 4.9.1 < 6" 54 | } 55 | }, 56 | "node_modules/@algolia/cache-browser-local-storage": { 57 | "version": "4.20.0", 58 | "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.20.0.tgz", 59 | "integrity": "sha512-uujahcBt4DxduBTvYdwO3sBfHuJvJokiC3BP1+O70fglmE1ShkH8lpXqZBac1rrU3FnNYSUs4pL9lBdTKeRPOQ==", 60 | "dev": true, 61 | "dependencies": { 62 | "@algolia/cache-common": "4.20.0" 63 | } 64 | }, 65 | "node_modules/@algolia/cache-common": { 66 | "version": "4.20.0", 67 | "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.20.0.tgz", 68 | "integrity": "sha512-vCfxauaZutL3NImzB2G9LjLt36vKAckc6DhMp05An14kVo8F1Yofb6SIl6U3SaEz8pG2QOB9ptwM5c+zGevwIQ==", 69 | "dev": true 70 | }, 71 | "node_modules/@algolia/cache-in-memory": { 72 | "version": "4.20.0", 73 | "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.20.0.tgz", 74 | "integrity": "sha512-Wm9ak/IaacAZXS4mB3+qF/KCoVSBV6aLgIGFEtQtJwjv64g4ePMapORGmCyulCFwfePaRAtcaTbMcJF+voc/bg==", 75 | "dev": true, 76 | "dependencies": { 77 | "@algolia/cache-common": "4.20.0" 78 | } 79 | }, 80 | "node_modules/@algolia/client-account": { 81 | "version": "4.20.0", 82 | "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.20.0.tgz", 83 | "integrity": "sha512-GGToLQvrwo7am4zVkZTnKa72pheQeez/16sURDWm7Seyz+HUxKi3BM6fthVVPUEBhtJ0reyVtuK9ArmnaKl10Q==", 84 | "dev": true, 85 | "dependencies": { 86 | "@algolia/client-common": "4.20.0", 87 | "@algolia/client-search": "4.20.0", 88 | "@algolia/transporter": "4.20.0" 89 | } 90 | }, 91 | "node_modules/@algolia/client-analytics": { 92 | "version": "4.20.0", 93 | "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.20.0.tgz", 94 | "integrity": "sha512-EIr+PdFMOallRdBTHHdKI3CstslgLORQG7844Mq84ib5oVFRVASuuPmG4bXBgiDbcsMLUeOC6zRVJhv1KWI0ug==", 95 | "dev": true, 96 | "dependencies": { 97 | "@algolia/client-common": "4.20.0", 98 | "@algolia/client-search": "4.20.0", 99 | "@algolia/requester-common": "4.20.0", 100 | "@algolia/transporter": "4.20.0" 101 | } 102 | }, 103 | "node_modules/@algolia/client-common": { 104 | "version": "4.20.0", 105 | "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.20.0.tgz", 106 | "integrity": "sha512-P3WgMdEss915p+knMMSd/fwiHRHKvDu4DYRrCRaBrsfFw7EQHon+EbRSm4QisS9NYdxbS04kcvNoavVGthyfqQ==", 107 | "dev": true, 108 | "dependencies": { 109 | "@algolia/requester-common": "4.20.0", 110 | "@algolia/transporter": "4.20.0" 111 | } 112 | }, 113 | "node_modules/@algolia/client-personalization": { 114 | "version": "4.20.0", 115 | "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.20.0.tgz", 116 | "integrity": "sha512-N9+zx0tWOQsLc3K4PVRDV8GUeOLAY0i445En79Pr3zWB+m67V+n/8w4Kw1C5LlbHDDJcyhMMIlqezh6BEk7xAQ==", 117 | "dev": true, 118 | "dependencies": { 119 | "@algolia/client-common": "4.20.0", 120 | "@algolia/requester-common": "4.20.0", 121 | "@algolia/transporter": "4.20.0" 122 | } 123 | }, 124 | "node_modules/@algolia/client-search": { 125 | "version": "4.20.0", 126 | "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.20.0.tgz", 127 | "integrity": "sha512-zgwqnMvhWLdpzKTpd3sGmMlr4c+iS7eyyLGiaO51zDZWGMkpgoNVmltkzdBwxOVXz0RsFMznIxB9zuarUv4TZg==", 128 | "dev": true, 129 | "dependencies": { 130 | "@algolia/client-common": "4.20.0", 131 | "@algolia/requester-common": "4.20.0", 132 | "@algolia/transporter": "4.20.0" 133 | } 134 | }, 135 | "node_modules/@algolia/logger-common": { 136 | "version": "4.20.0", 137 | "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.20.0.tgz", 138 | "integrity": "sha512-xouigCMB5WJYEwvoWW5XDv7Z9f0A8VoXJc3VKwlHJw/je+3p2RcDXfksLI4G4lIVncFUYMZx30tP/rsdlvvzHQ==", 139 | "dev": true 140 | }, 141 | "node_modules/@algolia/logger-console": { 142 | "version": "4.20.0", 143 | "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.20.0.tgz", 144 | "integrity": "sha512-THlIGG1g/FS63z0StQqDhT6bprUczBI8wnLT3JWvfAQDZX5P6fCg7dG+pIrUBpDIHGszgkqYEqECaKKsdNKOUA==", 145 | "dev": true, 146 | "dependencies": { 147 | "@algolia/logger-common": "4.20.0" 148 | } 149 | }, 150 | "node_modules/@algolia/requester-browser-xhr": { 151 | "version": "4.20.0", 152 | "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.20.0.tgz", 153 | "integrity": "sha512-HbzoSjcjuUmYOkcHECkVTwAelmvTlgs48N6Owt4FnTOQdwn0b8pdht9eMgishvk8+F8bal354nhx/xOoTfwiAw==", 154 | "dev": true, 155 | "dependencies": { 156 | "@algolia/requester-common": "4.20.0" 157 | } 158 | }, 159 | "node_modules/@algolia/requester-common": { 160 | "version": "4.20.0", 161 | "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.20.0.tgz", 162 | "integrity": "sha512-9h6ye6RY/BkfmeJp7Z8gyyeMrmmWsMOCRBXQDs4mZKKsyVlfIVICpcSibbeYcuUdurLhIlrOUkH3rQEgZzonng==", 163 | "dev": true 164 | }, 165 | "node_modules/@algolia/requester-node-http": { 166 | "version": "4.20.0", 167 | "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.20.0.tgz", 168 | "integrity": "sha512-ocJ66L60ABSSTRFnCHIEZpNHv6qTxsBwJEPfYaSBsLQodm0F9ptvalFkHMpvj5DfE22oZrcrLbOYM2bdPJRHng==", 169 | "dev": true, 170 | "dependencies": { 171 | "@algolia/requester-common": "4.20.0" 172 | } 173 | }, 174 | "node_modules/@algolia/transporter": { 175 | "version": "4.20.0", 176 | "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.20.0.tgz", 177 | "integrity": "sha512-Lsii1pGWOAISbzeyuf+r/GPhvHMPHSPrTDWNcIzOE1SG1inlJHICaVe2ikuoRjcpgxZNU54Jl+if15SUCsaTUg==", 178 | "dev": true, 179 | "dependencies": { 180 | "@algolia/cache-common": "4.20.0", 181 | "@algolia/logger-common": "4.20.0", 182 | "@algolia/requester-common": "4.20.0" 183 | } 184 | }, 185 | "node_modules/@babel/parser": { 186 | "version": "7.23.0", 187 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", 188 | "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", 189 | "dev": true, 190 | "bin": { 191 | "parser": "bin/babel-parser.js" 192 | }, 193 | "engines": { 194 | "node": ">=6.0.0" 195 | } 196 | }, 197 | "node_modules/@docsearch/css": { 198 | "version": "3.5.2", 199 | "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.2.tgz", 200 | "integrity": "sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==", 201 | "dev": true 202 | }, 203 | "node_modules/@docsearch/js": { 204 | "version": "3.5.2", 205 | "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.5.2.tgz", 206 | "integrity": "sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==", 207 | "dev": true, 208 | "dependencies": { 209 | "@docsearch/react": "3.5.2", 210 | "preact": "^10.0.0" 211 | } 212 | }, 213 | "node_modules/@docsearch/react": { 214 | "version": "3.5.2", 215 | "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.2.tgz", 216 | "integrity": "sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==", 217 | "dev": true, 218 | "dependencies": { 219 | "@algolia/autocomplete-core": "1.9.3", 220 | "@algolia/autocomplete-preset-algolia": "1.9.3", 221 | "@docsearch/css": "3.5.2", 222 | "algoliasearch": "^4.19.1" 223 | }, 224 | "peerDependencies": { 225 | "@types/react": ">= 16.8.0 < 19.0.0", 226 | "react": ">= 16.8.0 < 19.0.0", 227 | "react-dom": ">= 16.8.0 < 19.0.0", 228 | "search-insights": ">= 1 < 3" 229 | }, 230 | "peerDependenciesMeta": { 231 | "@types/react": { 232 | "optional": true 233 | }, 234 | "react": { 235 | "optional": true 236 | }, 237 | "react-dom": { 238 | "optional": true 239 | }, 240 | "search-insights": { 241 | "optional": true 242 | } 243 | } 244 | }, 245 | "node_modules/@esbuild/android-arm": { 246 | "version": "0.18.20", 247 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", 248 | "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", 249 | "cpu": [ 250 | "arm" 251 | ], 252 | "dev": true, 253 | "optional": true, 254 | "os": [ 255 | "android" 256 | ], 257 | "engines": { 258 | "node": ">=12" 259 | } 260 | }, 261 | "node_modules/@esbuild/android-arm64": { 262 | "version": "0.18.20", 263 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", 264 | "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", 265 | "cpu": [ 266 | "arm64" 267 | ], 268 | "dev": true, 269 | "optional": true, 270 | "os": [ 271 | "android" 272 | ], 273 | "engines": { 274 | "node": ">=12" 275 | } 276 | }, 277 | "node_modules/@esbuild/android-x64": { 278 | "version": "0.18.20", 279 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", 280 | "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", 281 | "cpu": [ 282 | "x64" 283 | ], 284 | "dev": true, 285 | "optional": true, 286 | "os": [ 287 | "android" 288 | ], 289 | "engines": { 290 | "node": ">=12" 291 | } 292 | }, 293 | "node_modules/@esbuild/darwin-arm64": { 294 | "version": "0.18.20", 295 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", 296 | "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", 297 | "cpu": [ 298 | "arm64" 299 | ], 300 | "dev": true, 301 | "optional": true, 302 | "os": [ 303 | "darwin" 304 | ], 305 | "engines": { 306 | "node": ">=12" 307 | } 308 | }, 309 | "node_modules/@esbuild/darwin-x64": { 310 | "version": "0.18.20", 311 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", 312 | "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", 313 | "cpu": [ 314 | "x64" 315 | ], 316 | "dev": true, 317 | "optional": true, 318 | "os": [ 319 | "darwin" 320 | ], 321 | "engines": { 322 | "node": ">=12" 323 | } 324 | }, 325 | "node_modules/@esbuild/freebsd-arm64": { 326 | "version": "0.18.20", 327 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", 328 | "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", 329 | "cpu": [ 330 | "arm64" 331 | ], 332 | "dev": true, 333 | "optional": true, 334 | "os": [ 335 | "freebsd" 336 | ], 337 | "engines": { 338 | "node": ">=12" 339 | } 340 | }, 341 | "node_modules/@esbuild/freebsd-x64": { 342 | "version": "0.18.20", 343 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", 344 | "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", 345 | "cpu": [ 346 | "x64" 347 | ], 348 | "dev": true, 349 | "optional": true, 350 | "os": [ 351 | "freebsd" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/@esbuild/linux-arm": { 358 | "version": "0.18.20", 359 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", 360 | "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", 361 | "cpu": [ 362 | "arm" 363 | ], 364 | "dev": true, 365 | "optional": true, 366 | "os": [ 367 | "linux" 368 | ], 369 | "engines": { 370 | "node": ">=12" 371 | } 372 | }, 373 | "node_modules/@esbuild/linux-arm64": { 374 | "version": "0.18.20", 375 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", 376 | "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", 377 | "cpu": [ 378 | "arm64" 379 | ], 380 | "dev": true, 381 | "optional": true, 382 | "os": [ 383 | "linux" 384 | ], 385 | "engines": { 386 | "node": ">=12" 387 | } 388 | }, 389 | "node_modules/@esbuild/linux-ia32": { 390 | "version": "0.18.20", 391 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", 392 | "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", 393 | "cpu": [ 394 | "ia32" 395 | ], 396 | "dev": true, 397 | "optional": true, 398 | "os": [ 399 | "linux" 400 | ], 401 | "engines": { 402 | "node": ">=12" 403 | } 404 | }, 405 | "node_modules/@esbuild/linux-loong64": { 406 | "version": "0.18.20", 407 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", 408 | "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", 409 | "cpu": [ 410 | "loong64" 411 | ], 412 | "dev": true, 413 | "optional": true, 414 | "os": [ 415 | "linux" 416 | ], 417 | "engines": { 418 | "node": ">=12" 419 | } 420 | }, 421 | "node_modules/@esbuild/linux-mips64el": { 422 | "version": "0.18.20", 423 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", 424 | "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", 425 | "cpu": [ 426 | "mips64el" 427 | ], 428 | "dev": true, 429 | "optional": true, 430 | "os": [ 431 | "linux" 432 | ], 433 | "engines": { 434 | "node": ">=12" 435 | } 436 | }, 437 | "node_modules/@esbuild/linux-ppc64": { 438 | "version": "0.18.20", 439 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", 440 | "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", 441 | "cpu": [ 442 | "ppc64" 443 | ], 444 | "dev": true, 445 | "optional": true, 446 | "os": [ 447 | "linux" 448 | ], 449 | "engines": { 450 | "node": ">=12" 451 | } 452 | }, 453 | "node_modules/@esbuild/linux-riscv64": { 454 | "version": "0.18.20", 455 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", 456 | "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", 457 | "cpu": [ 458 | "riscv64" 459 | ], 460 | "dev": true, 461 | "optional": true, 462 | "os": [ 463 | "linux" 464 | ], 465 | "engines": { 466 | "node": ">=12" 467 | } 468 | }, 469 | "node_modules/@esbuild/linux-s390x": { 470 | "version": "0.18.20", 471 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", 472 | "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", 473 | "cpu": [ 474 | "s390x" 475 | ], 476 | "dev": true, 477 | "optional": true, 478 | "os": [ 479 | "linux" 480 | ], 481 | "engines": { 482 | "node": ">=12" 483 | } 484 | }, 485 | "node_modules/@esbuild/linux-x64": { 486 | "version": "0.18.20", 487 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", 488 | "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", 489 | "cpu": [ 490 | "x64" 491 | ], 492 | "dev": true, 493 | "optional": true, 494 | "os": [ 495 | "linux" 496 | ], 497 | "engines": { 498 | "node": ">=12" 499 | } 500 | }, 501 | "node_modules/@esbuild/netbsd-x64": { 502 | "version": "0.18.20", 503 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", 504 | "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", 505 | "cpu": [ 506 | "x64" 507 | ], 508 | "dev": true, 509 | "optional": true, 510 | "os": [ 511 | "netbsd" 512 | ], 513 | "engines": { 514 | "node": ">=12" 515 | } 516 | }, 517 | "node_modules/@esbuild/openbsd-x64": { 518 | "version": "0.18.20", 519 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", 520 | "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", 521 | "cpu": [ 522 | "x64" 523 | ], 524 | "dev": true, 525 | "optional": true, 526 | "os": [ 527 | "openbsd" 528 | ], 529 | "engines": { 530 | "node": ">=12" 531 | } 532 | }, 533 | "node_modules/@esbuild/sunos-x64": { 534 | "version": "0.18.20", 535 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", 536 | "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", 537 | "cpu": [ 538 | "x64" 539 | ], 540 | "dev": true, 541 | "optional": true, 542 | "os": [ 543 | "sunos" 544 | ], 545 | "engines": { 546 | "node": ">=12" 547 | } 548 | }, 549 | "node_modules/@esbuild/win32-arm64": { 550 | "version": "0.18.20", 551 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", 552 | "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", 553 | "cpu": [ 554 | "arm64" 555 | ], 556 | "dev": true, 557 | "optional": true, 558 | "os": [ 559 | "win32" 560 | ], 561 | "engines": { 562 | "node": ">=12" 563 | } 564 | }, 565 | "node_modules/@esbuild/win32-ia32": { 566 | "version": "0.18.20", 567 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", 568 | "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", 569 | "cpu": [ 570 | "ia32" 571 | ], 572 | "dev": true, 573 | "optional": true, 574 | "os": [ 575 | "win32" 576 | ], 577 | "engines": { 578 | "node": ">=12" 579 | } 580 | }, 581 | "node_modules/@esbuild/win32-x64": { 582 | "version": "0.18.20", 583 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", 584 | "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", 585 | "cpu": [ 586 | "x64" 587 | ], 588 | "dev": true, 589 | "optional": true, 590 | "os": [ 591 | "win32" 592 | ], 593 | "engines": { 594 | "node": ">=12" 595 | } 596 | }, 597 | "node_modules/@jridgewell/sourcemap-codec": { 598 | "version": "1.4.15", 599 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 600 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 601 | "dev": true 602 | }, 603 | "node_modules/@types/linkify-it": { 604 | "version": "3.0.4", 605 | "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.4.tgz", 606 | "integrity": "sha512-hPpIeeHb/2UuCw06kSNAOVWgehBLXEo0/fUs0mw3W2qhqX89PI2yvok83MnuctYGCPrabGIoi0fFso4DQ+sNUQ==", 607 | "dev": true 608 | }, 609 | "node_modules/@types/markdown-it": { 610 | "version": "13.0.4", 611 | "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.4.tgz", 612 | "integrity": "sha512-FAIUdEXrCDnQmAAmJC+UeW/3p0eCI4QZ/+W0lX/h83VD3v78IgTFYftjnAeXS8H0g4PFQCgipc51cQDA8tjgLw==", 613 | "dev": true, 614 | "dependencies": { 615 | "@types/linkify-it": "*", 616 | "@types/mdurl": "*" 617 | } 618 | }, 619 | "node_modules/@types/mdurl": { 620 | "version": "1.0.4", 621 | "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.4.tgz", 622 | "integrity": "sha512-ARVxjAEX5TARFRzpDRVC6cEk0hUIXCCwaMhz8y7S1/PxU6zZS1UMjyobz7q4w/D/R552r4++EhwmXK1N2rAy0A==", 623 | "dev": true 624 | }, 625 | "node_modules/@types/web-bluetooth": { 626 | "version": "0.0.18", 627 | "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.18.tgz", 628 | "integrity": "sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==", 629 | "dev": true 630 | }, 631 | "node_modules/@vue/compiler-core": { 632 | "version": "3.3.4", 633 | "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz", 634 | "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==", 635 | "dev": true, 636 | "dependencies": { 637 | "@babel/parser": "^7.21.3", 638 | "@vue/shared": "3.3.4", 639 | "estree-walker": "^2.0.2", 640 | "source-map-js": "^1.0.2" 641 | } 642 | }, 643 | "node_modules/@vue/compiler-dom": { 644 | "version": "3.3.4", 645 | "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz", 646 | "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==", 647 | "dev": true, 648 | "dependencies": { 649 | "@vue/compiler-core": "3.3.4", 650 | "@vue/shared": "3.3.4" 651 | } 652 | }, 653 | "node_modules/@vue/compiler-sfc": { 654 | "version": "3.3.4", 655 | "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz", 656 | "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==", 657 | "dev": true, 658 | "dependencies": { 659 | "@babel/parser": "^7.20.15", 660 | "@vue/compiler-core": "3.3.4", 661 | "@vue/compiler-dom": "3.3.4", 662 | "@vue/compiler-ssr": "3.3.4", 663 | "@vue/reactivity-transform": "3.3.4", 664 | "@vue/shared": "3.3.4", 665 | "estree-walker": "^2.0.2", 666 | "magic-string": "^0.30.0", 667 | "postcss": "^8.1.10", 668 | "source-map-js": "^1.0.2" 669 | } 670 | }, 671 | "node_modules/@vue/compiler-ssr": { 672 | "version": "3.3.4", 673 | "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz", 674 | "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==", 675 | "dev": true, 676 | "dependencies": { 677 | "@vue/compiler-dom": "3.3.4", 678 | "@vue/shared": "3.3.4" 679 | } 680 | }, 681 | "node_modules/@vue/devtools-api": { 682 | "version": "6.5.1", 683 | "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", 684 | "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==", 685 | "dev": true 686 | }, 687 | "node_modules/@vue/reactivity": { 688 | "version": "3.3.4", 689 | "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz", 690 | "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==", 691 | "dev": true, 692 | "dependencies": { 693 | "@vue/shared": "3.3.4" 694 | } 695 | }, 696 | "node_modules/@vue/reactivity-transform": { 697 | "version": "3.3.4", 698 | "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz", 699 | "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==", 700 | "dev": true, 701 | "dependencies": { 702 | "@babel/parser": "^7.20.15", 703 | "@vue/compiler-core": "3.3.4", 704 | "@vue/shared": "3.3.4", 705 | "estree-walker": "^2.0.2", 706 | "magic-string": "^0.30.0" 707 | } 708 | }, 709 | "node_modules/@vue/runtime-core": { 710 | "version": "3.3.4", 711 | "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz", 712 | "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==", 713 | "dev": true, 714 | "dependencies": { 715 | "@vue/reactivity": "3.3.4", 716 | "@vue/shared": "3.3.4" 717 | } 718 | }, 719 | "node_modules/@vue/runtime-dom": { 720 | "version": "3.3.4", 721 | "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz", 722 | "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==", 723 | "dev": true, 724 | "dependencies": { 725 | "@vue/runtime-core": "3.3.4", 726 | "@vue/shared": "3.3.4", 727 | "csstype": "^3.1.1" 728 | } 729 | }, 730 | "node_modules/@vue/server-renderer": { 731 | "version": "3.3.4", 732 | "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz", 733 | "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==", 734 | "dev": true, 735 | "dependencies": { 736 | "@vue/compiler-ssr": "3.3.4", 737 | "@vue/shared": "3.3.4" 738 | }, 739 | "peerDependencies": { 740 | "vue": "3.3.4" 741 | } 742 | }, 743 | "node_modules/@vue/shared": { 744 | "version": "3.3.4", 745 | "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz", 746 | "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==", 747 | "dev": true 748 | }, 749 | "node_modules/@vueuse/core": { 750 | "version": "10.5.0", 751 | "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.5.0.tgz", 752 | "integrity": "sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==", 753 | "dev": true, 754 | "dependencies": { 755 | "@types/web-bluetooth": "^0.0.18", 756 | "@vueuse/metadata": "10.5.0", 757 | "@vueuse/shared": "10.5.0", 758 | "vue-demi": ">=0.14.6" 759 | }, 760 | "funding": { 761 | "url": "https://github.com/sponsors/antfu" 762 | } 763 | }, 764 | "node_modules/@vueuse/core/node_modules/vue-demi": { 765 | "version": "0.14.6", 766 | "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", 767 | "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", 768 | "dev": true, 769 | "hasInstallScript": true, 770 | "bin": { 771 | "vue-demi-fix": "bin/vue-demi-fix.js", 772 | "vue-demi-switch": "bin/vue-demi-switch.js" 773 | }, 774 | "engines": { 775 | "node": ">=12" 776 | }, 777 | "funding": { 778 | "url": "https://github.com/sponsors/antfu" 779 | }, 780 | "peerDependencies": { 781 | "@vue/composition-api": "^1.0.0-rc.1", 782 | "vue": "^3.0.0-0 || ^2.6.0" 783 | }, 784 | "peerDependenciesMeta": { 785 | "@vue/composition-api": { 786 | "optional": true 787 | } 788 | } 789 | }, 790 | "node_modules/@vueuse/integrations": { 791 | "version": "10.5.0", 792 | "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.5.0.tgz", 793 | "integrity": "sha512-fm5sXLCK0Ww3rRnzqnCQRmfjDURaI4xMsx+T+cec0ngQqHx/JgUtm8G0vRjwtonIeTBsH1Q8L3SucE+7K7upJQ==", 794 | "dev": true, 795 | "dependencies": { 796 | "@vueuse/core": "10.5.0", 797 | "@vueuse/shared": "10.5.0", 798 | "vue-demi": ">=0.14.6" 799 | }, 800 | "funding": { 801 | "url": "https://github.com/sponsors/antfu" 802 | }, 803 | "peerDependencies": { 804 | "async-validator": "*", 805 | "axios": "*", 806 | "change-case": "*", 807 | "drauu": "*", 808 | "focus-trap": "*", 809 | "fuse.js": "*", 810 | "idb-keyval": "*", 811 | "jwt-decode": "*", 812 | "nprogress": "*", 813 | "qrcode": "*", 814 | "sortablejs": "*", 815 | "universal-cookie": "*" 816 | }, 817 | "peerDependenciesMeta": { 818 | "async-validator": { 819 | "optional": true 820 | }, 821 | "axios": { 822 | "optional": true 823 | }, 824 | "change-case": { 825 | "optional": true 826 | }, 827 | "drauu": { 828 | "optional": true 829 | }, 830 | "focus-trap": { 831 | "optional": true 832 | }, 833 | "fuse.js": { 834 | "optional": true 835 | }, 836 | "idb-keyval": { 837 | "optional": true 838 | }, 839 | "jwt-decode": { 840 | "optional": true 841 | }, 842 | "nprogress": { 843 | "optional": true 844 | }, 845 | "qrcode": { 846 | "optional": true 847 | }, 848 | "sortablejs": { 849 | "optional": true 850 | }, 851 | "universal-cookie": { 852 | "optional": true 853 | } 854 | } 855 | }, 856 | "node_modules/@vueuse/integrations/node_modules/vue-demi": { 857 | "version": "0.14.6", 858 | "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", 859 | "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", 860 | "dev": true, 861 | "hasInstallScript": true, 862 | "bin": { 863 | "vue-demi-fix": "bin/vue-demi-fix.js", 864 | "vue-demi-switch": "bin/vue-demi-switch.js" 865 | }, 866 | "engines": { 867 | "node": ">=12" 868 | }, 869 | "funding": { 870 | "url": "https://github.com/sponsors/antfu" 871 | }, 872 | "peerDependencies": { 873 | "@vue/composition-api": "^1.0.0-rc.1", 874 | "vue": "^3.0.0-0 || ^2.6.0" 875 | }, 876 | "peerDependenciesMeta": { 877 | "@vue/composition-api": { 878 | "optional": true 879 | } 880 | } 881 | }, 882 | "node_modules/@vueuse/metadata": { 883 | "version": "10.5.0", 884 | "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.5.0.tgz", 885 | "integrity": "sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==", 886 | "dev": true, 887 | "funding": { 888 | "url": "https://github.com/sponsors/antfu" 889 | } 890 | }, 891 | "node_modules/@vueuse/shared": { 892 | "version": "10.5.0", 893 | "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.5.0.tgz", 894 | "integrity": "sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==", 895 | "dev": true, 896 | "dependencies": { 897 | "vue-demi": ">=0.14.6" 898 | }, 899 | "funding": { 900 | "url": "https://github.com/sponsors/antfu" 901 | } 902 | }, 903 | "node_modules/@vueuse/shared/node_modules/vue-demi": { 904 | "version": "0.14.6", 905 | "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", 906 | "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", 907 | "dev": true, 908 | "hasInstallScript": true, 909 | "bin": { 910 | "vue-demi-fix": "bin/vue-demi-fix.js", 911 | "vue-demi-switch": "bin/vue-demi-switch.js" 912 | }, 913 | "engines": { 914 | "node": ">=12" 915 | }, 916 | "funding": { 917 | "url": "https://github.com/sponsors/antfu" 918 | }, 919 | "peerDependencies": { 920 | "@vue/composition-api": "^1.0.0-rc.1", 921 | "vue": "^3.0.0-0 || ^2.6.0" 922 | }, 923 | "peerDependenciesMeta": { 924 | "@vue/composition-api": { 925 | "optional": true 926 | } 927 | } 928 | }, 929 | "node_modules/algoliasearch": { 930 | "version": "4.20.0", 931 | "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.20.0.tgz", 932 | "integrity": "sha512-y+UHEjnOItoNy0bYO+WWmLWBlPwDjKHW6mNHrPi0NkuhpQOOEbrkwQH/wgKFDLh7qlKjzoKeiRtlpewDPDG23g==", 933 | "dev": true, 934 | "dependencies": { 935 | "@algolia/cache-browser-local-storage": "4.20.0", 936 | "@algolia/cache-common": "4.20.0", 937 | "@algolia/cache-in-memory": "4.20.0", 938 | "@algolia/client-account": "4.20.0", 939 | "@algolia/client-analytics": "4.20.0", 940 | "@algolia/client-common": "4.20.0", 941 | "@algolia/client-personalization": "4.20.0", 942 | "@algolia/client-search": "4.20.0", 943 | "@algolia/logger-common": "4.20.0", 944 | "@algolia/logger-console": "4.20.0", 945 | "@algolia/requester-browser-xhr": "4.20.0", 946 | "@algolia/requester-common": "4.20.0", 947 | "@algolia/requester-node-http": "4.20.0", 948 | "@algolia/transporter": "4.20.0" 949 | } 950 | }, 951 | "node_modules/ansi-sequence-parser": { 952 | "version": "1.1.1", 953 | "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", 954 | "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", 955 | "dev": true 956 | }, 957 | "node_modules/csstype": { 958 | "version": "3.1.2", 959 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", 960 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", 961 | "dev": true 962 | }, 963 | "node_modules/esbuild": { 964 | "version": "0.18.20", 965 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", 966 | "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", 967 | "dev": true, 968 | "hasInstallScript": true, 969 | "bin": { 970 | "esbuild": "bin/esbuild" 971 | }, 972 | "engines": { 973 | "node": ">=12" 974 | }, 975 | "optionalDependencies": { 976 | "@esbuild/android-arm": "0.18.20", 977 | "@esbuild/android-arm64": "0.18.20", 978 | "@esbuild/android-x64": "0.18.20", 979 | "@esbuild/darwin-arm64": "0.18.20", 980 | "@esbuild/darwin-x64": "0.18.20", 981 | "@esbuild/freebsd-arm64": "0.18.20", 982 | "@esbuild/freebsd-x64": "0.18.20", 983 | "@esbuild/linux-arm": "0.18.20", 984 | "@esbuild/linux-arm64": "0.18.20", 985 | "@esbuild/linux-ia32": "0.18.20", 986 | "@esbuild/linux-loong64": "0.18.20", 987 | "@esbuild/linux-mips64el": "0.18.20", 988 | "@esbuild/linux-ppc64": "0.18.20", 989 | "@esbuild/linux-riscv64": "0.18.20", 990 | "@esbuild/linux-s390x": "0.18.20", 991 | "@esbuild/linux-x64": "0.18.20", 992 | "@esbuild/netbsd-x64": "0.18.20", 993 | "@esbuild/openbsd-x64": "0.18.20", 994 | "@esbuild/sunos-x64": "0.18.20", 995 | "@esbuild/win32-arm64": "0.18.20", 996 | "@esbuild/win32-ia32": "0.18.20", 997 | "@esbuild/win32-x64": "0.18.20" 998 | } 999 | }, 1000 | "node_modules/estree-walker": { 1001 | "version": "2.0.2", 1002 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 1003 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 1004 | "dev": true 1005 | }, 1006 | "node_modules/focus-trap": { 1007 | "version": "7.5.4", 1008 | "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", 1009 | "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", 1010 | "dev": true, 1011 | "dependencies": { 1012 | "tabbable": "^6.2.0" 1013 | } 1014 | }, 1015 | "node_modules/fsevents": { 1016 | "version": "2.3.3", 1017 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1018 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1019 | "dev": true, 1020 | "hasInstallScript": true, 1021 | "optional": true, 1022 | "os": [ 1023 | "darwin" 1024 | ], 1025 | "engines": { 1026 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1027 | } 1028 | }, 1029 | "node_modules/jsonc-parser": { 1030 | "version": "3.2.0", 1031 | "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", 1032 | "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", 1033 | "dev": true 1034 | }, 1035 | "node_modules/magic-string": { 1036 | "version": "0.30.5", 1037 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", 1038 | "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", 1039 | "dev": true, 1040 | "dependencies": { 1041 | "@jridgewell/sourcemap-codec": "^1.4.15" 1042 | }, 1043 | "engines": { 1044 | "node": ">=12" 1045 | } 1046 | }, 1047 | "node_modules/mark.js": { 1048 | "version": "8.11.1", 1049 | "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", 1050 | "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", 1051 | "dev": true 1052 | }, 1053 | "node_modules/minisearch": { 1054 | "version": "6.1.0", 1055 | "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.1.0.tgz", 1056 | "integrity": "sha512-PNxA/X8pWk+TiqPbsoIYH0GQ5Di7m6326/lwU/S4mlo4wGQddIcf/V//1f9TB0V4j59b57b+HZxt8h3iMROGvg==", 1057 | "dev": true 1058 | }, 1059 | "node_modules/nanoid": { 1060 | "version": "3.3.6", 1061 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 1062 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 1063 | "dev": true, 1064 | "funding": [ 1065 | { 1066 | "type": "github", 1067 | "url": "https://github.com/sponsors/ai" 1068 | } 1069 | ], 1070 | "bin": { 1071 | "nanoid": "bin/nanoid.cjs" 1072 | }, 1073 | "engines": { 1074 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1075 | } 1076 | }, 1077 | "node_modules/picocolors": { 1078 | "version": "1.0.0", 1079 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1080 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1081 | "dev": true 1082 | }, 1083 | "node_modules/postcss": { 1084 | "version": "8.4.31", 1085 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 1086 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 1087 | "dev": true, 1088 | "funding": [ 1089 | { 1090 | "type": "opencollective", 1091 | "url": "https://opencollective.com/postcss/" 1092 | }, 1093 | { 1094 | "type": "tidelift", 1095 | "url": "https://tidelift.com/funding/github/npm/postcss" 1096 | }, 1097 | { 1098 | "type": "github", 1099 | "url": "https://github.com/sponsors/ai" 1100 | } 1101 | ], 1102 | "dependencies": { 1103 | "nanoid": "^3.3.6", 1104 | "picocolors": "^1.0.0", 1105 | "source-map-js": "^1.0.2" 1106 | }, 1107 | "engines": { 1108 | "node": "^10 || ^12 || >=14" 1109 | } 1110 | }, 1111 | "node_modules/preact": { 1112 | "version": "10.18.1", 1113 | "resolved": "https://registry.npmjs.org/preact/-/preact-10.18.1.tgz", 1114 | "integrity": "sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==", 1115 | "dev": true, 1116 | "funding": { 1117 | "type": "opencollective", 1118 | "url": "https://opencollective.com/preact" 1119 | } 1120 | }, 1121 | "node_modules/rollup": { 1122 | "version": "3.29.4", 1123 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", 1124 | "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", 1125 | "dev": true, 1126 | "bin": { 1127 | "rollup": "dist/bin/rollup" 1128 | }, 1129 | "engines": { 1130 | "node": ">=14.18.0", 1131 | "npm": ">=8.0.0" 1132 | }, 1133 | "optionalDependencies": { 1134 | "fsevents": "~2.3.2" 1135 | } 1136 | }, 1137 | "node_modules/search-insights": { 1138 | "version": "2.9.0", 1139 | "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.9.0.tgz", 1140 | "integrity": "sha512-bkWW9nIHOFkLwjQ1xqVaMbjjO5vhP26ERsH9Y3pKr8imthofEFIxlnOabkmGcw6ksRj9jWidcI65vvjJH/nTGg==", 1141 | "dev": true, 1142 | "peer": true 1143 | }, 1144 | "node_modules/shiki": { 1145 | "version": "0.14.5", 1146 | "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", 1147 | "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", 1148 | "dev": true, 1149 | "dependencies": { 1150 | "ansi-sequence-parser": "^1.1.0", 1151 | "jsonc-parser": "^3.2.0", 1152 | "vscode-oniguruma": "^1.7.0", 1153 | "vscode-textmate": "^8.0.0" 1154 | } 1155 | }, 1156 | "node_modules/source-map-js": { 1157 | "version": "1.0.2", 1158 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1159 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1160 | "dev": true, 1161 | "engines": { 1162 | "node": ">=0.10.0" 1163 | } 1164 | }, 1165 | "node_modules/tabbable": { 1166 | "version": "6.2.0", 1167 | "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", 1168 | "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", 1169 | "dev": true 1170 | }, 1171 | "node_modules/vite": { 1172 | "version": "4.5.0", 1173 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", 1174 | "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", 1175 | "dev": true, 1176 | "dependencies": { 1177 | "esbuild": "^0.18.10", 1178 | "postcss": "^8.4.27", 1179 | "rollup": "^3.27.1" 1180 | }, 1181 | "bin": { 1182 | "vite": "bin/vite.js" 1183 | }, 1184 | "engines": { 1185 | "node": "^14.18.0 || >=16.0.0" 1186 | }, 1187 | "funding": { 1188 | "url": "https://github.com/vitejs/vite?sponsor=1" 1189 | }, 1190 | "optionalDependencies": { 1191 | "fsevents": "~2.3.2" 1192 | }, 1193 | "peerDependencies": { 1194 | "@types/node": ">= 14", 1195 | "less": "*", 1196 | "lightningcss": "^1.21.0", 1197 | "sass": "*", 1198 | "stylus": "*", 1199 | "sugarss": "*", 1200 | "terser": "^5.4.0" 1201 | }, 1202 | "peerDependenciesMeta": { 1203 | "@types/node": { 1204 | "optional": true 1205 | }, 1206 | "less": { 1207 | "optional": true 1208 | }, 1209 | "lightningcss": { 1210 | "optional": true 1211 | }, 1212 | "sass": { 1213 | "optional": true 1214 | }, 1215 | "stylus": { 1216 | "optional": true 1217 | }, 1218 | "sugarss": { 1219 | "optional": true 1220 | }, 1221 | "terser": { 1222 | "optional": true 1223 | } 1224 | } 1225 | }, 1226 | "node_modules/vitepress": { 1227 | "version": "1.0.0-rc.22", 1228 | "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.22.tgz", 1229 | "integrity": "sha512-n7le5iikCFgWMuX7sKfzDGJGlrsYQ5trG3S97BghNz2alOTr4Xp+GrB6ShwogUTX9gNgeNmrACjokhW55LNeBA==", 1230 | "dev": true, 1231 | "dependencies": { 1232 | "@docsearch/css": "^3.5.2", 1233 | "@docsearch/js": "^3.5.2", 1234 | "@types/markdown-it": "^13.0.2", 1235 | "@vue/devtools-api": "^6.5.1", 1236 | "@vueuse/core": "^10.5.0", 1237 | "@vueuse/integrations": "^10.5.0", 1238 | "focus-trap": "^7.5.4", 1239 | "mark.js": "8.11.1", 1240 | "minisearch": "^6.1.0", 1241 | "shiki": "^0.14.5", 1242 | "vite": "^4.4.11", 1243 | "vue": "^3.3.4" 1244 | }, 1245 | "bin": { 1246 | "vitepress": "bin/vitepress.js" 1247 | }, 1248 | "peerDependencies": { 1249 | "markdown-it-mathjax3": "^4.3.2", 1250 | "postcss": "^8.4.31" 1251 | }, 1252 | "peerDependenciesMeta": { 1253 | "markdown-it-mathjax3": { 1254 | "optional": true 1255 | }, 1256 | "postcss": { 1257 | "optional": true 1258 | } 1259 | } 1260 | }, 1261 | "node_modules/vscode-oniguruma": { 1262 | "version": "1.7.0", 1263 | "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", 1264 | "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", 1265 | "dev": true 1266 | }, 1267 | "node_modules/vscode-textmate": { 1268 | "version": "8.0.0", 1269 | "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", 1270 | "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", 1271 | "dev": true 1272 | }, 1273 | "node_modules/vue": { 1274 | "version": "3.3.4", 1275 | "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz", 1276 | "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==", 1277 | "dev": true, 1278 | "dependencies": { 1279 | "@vue/compiler-dom": "3.3.4", 1280 | "@vue/compiler-sfc": "3.3.4", 1281 | "@vue/runtime-dom": "3.3.4", 1282 | "@vue/server-renderer": "3.3.4", 1283 | "@vue/shared": "3.3.4" 1284 | } 1285 | } 1286 | } 1287 | } 1288 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "vitepress": "^1.0.0-rc.13" 4 | }, 5 | "scripts": { 6 | "docs:dev": "vitepress dev docs", 7 | "docs:build": "vitepress build docs", 8 | "docs:preview": "vitepress preview docs" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Bases/BaseSpellNumber.php: -------------------------------------------------------------------------------- 1 | result(); 23 | 24 | return new static($value, $type); 25 | } 26 | 27 | /** 28 | * Creates an instance of the child class with the provided integer value for conversion. 29 | * 30 | * @param int $value The integer value to convert to words. 31 | * 32 | * @throws SpellNumberExceptions When the Intl extension is not available or the value is not a valid integer. 33 | * 34 | * @return static An instance of the child class. 35 | */ 36 | public static function integer($value) 37 | { 38 | SpellNumberValidator::check('integer', $value)->result(); 39 | 40 | return new static($value, 'integer'); 41 | } 42 | 43 | /** 44 | * Creates an instance of the child class with the provided float value for conversion. 45 | * 46 | * @param float $value The float value to convert to words. 47 | * 48 | * @throws SpellNumberExceptions When the Intl extension is not available or the value is not a valid float. 49 | * 50 | * @return static An instance of the child class. 51 | */ 52 | public static function float($value) 53 | { 54 | SpellNumberValidator::check('double', $value)->result(); 55 | 56 | return new static($value, 'double'); 57 | } 58 | 59 | /** 60 | * Returns the list of available premises according to PHP. 61 | * 62 | * @return array An array containing all locales. 63 | */ 64 | public static function getAllLocales() 65 | { 66 | try { 67 | $availableLocales = \ResourceBundle::getLocales(''); 68 | 69 | return $availableLocales; 70 | } catch (\Throwable $th) { 71 | return array_keys(Langs::LOCALES_AVAILABLE); 72 | } 73 | } 74 | 75 | /** 76 | * Retrieves a list of all available locales supported by the NumberFormatter class. 77 | * 78 | * @return array An array containing all available locales. 79 | */ 80 | public static function getAvailableLocales() 81 | { 82 | return array_keys(Langs::LOCALES_AVAILABLE); 83 | } 84 | 85 | /** 86 | * Retrieves a list of all available TimeZones by the NumberFormatter class. 87 | * 88 | * @return array An array containing all available locales. 89 | */ 90 | public static function getAvailableLanguages() 91 | { 92 | return Langs::LOCALES_AVAILABLE; 93 | } 94 | 95 | /** 96 | * DEPRECATED - Delete Method Soon 97 | * Retrieves a list of all available locales supported by the NumberFormatter class. 98 | * 99 | * @return array An array containing all available locales. 100 | */ 101 | public static function getLocales() 102 | { 103 | return self::getAvailableLocales(); 104 | } 105 | 106 | /** 107 | * DEPRECATED - Delete Method Soon 108 | * Retrieves a list of all available TimeZones by the NumberFormatter class. 109 | * 110 | * @return array An array containing all available locales. 111 | */ 112 | public static function getLanguages() 113 | { 114 | return self::getAvailableLanguages(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Bases/BaseSpellNumberValidator.php: -------------------------------------------------------------------------------- 1 | method = $data['method'] ?? null; 25 | $this->type = $data['type'] ?? null; 26 | $this->value = $data['value'] ?? null; 27 | $this->words = $data['words'] ?? null; 28 | $this->lang = $data['lang'] ?? null; 29 | $this->locale = $data['locale'] ?? null; 30 | $this->mode = $data['mode'] ?? null; 31 | $this->currency = $data['currency'] ?? null; 32 | $this->fraction = $data['fraction'] ?? null; 33 | } 34 | 35 | /** 36 | * Get the method associated with the response. 37 | * 38 | * @return string The method name. 39 | */ 40 | public function getMethod() 41 | { 42 | return $this->method; 43 | } 44 | 45 | /** 46 | * Get the type associated with the response. 47 | * 48 | * @return string The response type. 49 | */ 50 | public function getType() 51 | { 52 | return $this->type; 53 | } 54 | 55 | /** 56 | * Get the value associated with the response. 57 | * 58 | * @return mixed The response value. 59 | */ 60 | public function getValue() 61 | { 62 | return $this->value; 63 | } 64 | 65 | /** 66 | * Get the words associated with the response. 67 | * 68 | * @return string The response words. 69 | */ 70 | public function getWords() 71 | { 72 | return $this->words; 73 | } 74 | 75 | /** 76 | * Get the language associated with the response. 77 | * 78 | * @return string The response language. 79 | */ 80 | public function getLang() 81 | { 82 | return $this->lang; 83 | } 84 | 85 | /** 86 | * Get the locale associated with the response. 87 | * 88 | * @return string The response locale. 89 | */ 90 | public function getLocale() 91 | { 92 | return $this->locale; 93 | } 94 | 95 | /** 96 | * Get the currency associated with the response. 97 | * 98 | * @return string The response currency. 99 | */ 100 | public function getCurrency() 101 | { 102 | return $this->currency; 103 | } 104 | 105 | /** 106 | * Get the fraction associated with the response. 107 | * 108 | * @return string The response fraction. 109 | */ 110 | public function getFraction() 111 | { 112 | return $this->fraction; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Exceptions/SpellNumberExceptions.php: -------------------------------------------------------------------------------- 1 | 'German', // German from Germany. 18 | 'en' => 'English', // English from the United States. 19 | 'es' => 'Spanish', // Spanish from Spain. 20 | 'fa' => 'Farsi', // Farsi from Iran. 21 | 'fr' => 'French', // French from France. 22 | 'hi' => 'Hindi', // Hindi from India. 23 | 'it' => 'Italian', // Italian from Italy. 24 | 'pl' => 'Polish', // Polish from Poland. 25 | 'pt' => 'Portuguese', // Portuguese from Portugal. 26 | 'ro' => 'Romanian', // Romanian from Romania. 27 | 'vi' => 'Vietnamese', // Vietnamese from Vietnam. 28 | ]; 29 | 30 | /** 31 | * Array containing connectors for each language. 32 | * 33 | * @var array 34 | */ 35 | public const LOCALES_CONNECTORS = [ 36 | 'de' => 'und', // German from Germany: "und" 37 | 'en' => 'and', // English from the United States: "and" 38 | 'es' => 'con', // Spanish from Spain: "con" 39 | 'fa' => 'ممیز', // Farsi from Iran: "ممیز" 40 | 'fr' => 'et', // French from France: "et" 41 | 'hi' => 'और', // Hindi from India: "और" 42 | 'it' => 'con', // Italian from Italy: "con" 43 | 'pl' => 'i', // Polish from Poland: "i" 44 | 'pt' => 'com', // Portuguese from Portugal: "com" 45 | 'ro' => 'cu', // Romanian from Romania: "cu" 46 | 'vi' => 'và', // Vietnamese from Vietnam: "và" 47 | ]; 48 | 49 | /** 50 | * Array containing connectors for each language used when representing money. 51 | * 52 | * @var array 53 | */ 54 | public const LOCALES_CONNECTORS_MONEY = [ 55 | 'de' => 'von', // German frm Germany: "von" 56 | 'en' => 'of', // English from the United States: "of" 57 | 'es' => 'de', // Spanish from Spain: "de" 58 | 'fa' => 'از', // Farsi from Iran: "از" 59 | 'fr' => 'de', // French from France: "de" 60 | 'hi' => 'का', // Hindi from India: "का" 61 | 'it' => 'di', // Italian from Italy: "di" 62 | 'pl' => 'i', // Polish from Poland: "i" 63 | 'pt' => 'de', // Portuguese from Portugal: "de" 64 | 'ro' => 'de', // Romanian from Romania: "de" 65 | 'vi' => 'của', // Vietnamese from Vietnam: "của" 66 | ]; 67 | } 68 | -------------------------------------------------------------------------------- /src/Langs/Replaces.php: -------------------------------------------------------------------------------- 1 | toMoney() 13 | * The primary value must be there, that is, "es" instead of "es_ES" or similar. 14 | */ 15 | public const TO_MONEY = [ 16 | 'de' => [ 17 | // 'illion' => 'illion Von', 18 | ], 19 | 'en' => [ 20 | 'illion' => 'illion of', 21 | ], 22 | 'es' => [ 23 | 'veintiuno' => 'veintiún', 24 | 'treinta Y Uno' => 'treintaiún', 25 | 'cuarenta y uno' => 'cuarentaiún', 26 | 'cincuenta y uno' => 'cincuentaiún', 27 | 'sesenta y uno' => 'sesentaiún', 28 | 'setenta y uno' => 'setentaiún', 29 | 'ochenta y uno' => 'ochentaiún', 30 | 'noventa y uno' => 'noventaiún', 31 | 'ciento uno' => 'ciento un', 32 | 'doscientos uno' => 'doscientos un', 33 | 'trescientos uno' => 'trescientos un', 34 | 'cuatrocientos uno' => 'cuatrocientos un', 35 | 'quinientos uno' => 'quinientos un', 36 | 'seiscientos uno' => 'seiscientos un', 37 | 'setecientos uno' => 'setecientos un', 38 | 'ochocientos uno' => 'ochocientos un', 39 | 'novecientos uno' => 'novecientos un', 40 | 'mil uno' => 'mil un', 41 | 'uno millón' => 'un millón', 42 | 'illón' => 'illón De', 43 | 'illones' => 'illones De', 44 | 'uno pesos' => 'un peso', 45 | 'uno soles' => 'un sol', 46 | 'uno euros' => 'un euro', 47 | 'uno bolívares' => 'un bolívar', 48 | 'uno bolivares' => 'un bolívar', 49 | 'uno quetzales' => 'un quetzal', 50 | 'uno lempiras' => 'un lempira', 51 | 'uno córdobas' => 'un córdoba', 52 | 'uno cordobas' => 'un córdoba', 53 | 'uno colónes' => 'un colón', 54 | 'uno colones' => 'un colón', 55 | 'uno balboas' => 'un balboa', 56 | 'uno centavos' => 'un centavo', 57 | 'uno céntimos' => 'un céntimo', 58 | 'uno centimos' => 'un céntimo', 59 | 'uno centimo' => 'un céntimo', 60 | ], 61 | 'fa' => [ 62 | 'ilion' => 'میلیون و', 63 | ], 64 | 'fr' => [ 65 | 'illion' => 'illion de', 66 | 'illions' => 'illions de', 67 | ], 68 | 'hi' => [ 69 | //... 70 | ], 71 | 'it' => [ 72 | 'ilione' => 'ilione di', 73 | 'ilioni' => 'ilioni di', 74 | ], 75 | 'pl' => [ 76 | //... 77 | ], 78 | 'pt' => [ 79 | 'ilhão' => 'ilhão de', 80 | 'ilhões' => 'ilhões de', 81 | ], 82 | 'ro' => [ 83 | 'ilion' => 'ilion de', 84 | 'ilioane' => 'ilioane de', 85 | ], 86 | 'vi' => [ 87 | 'illion' => 'illion của', 88 | ], 89 | ]; 90 | 91 | /** 92 | * ->toLetters() 93 | * The primary value must be there, that is, "es" instead of "es_ES" or similar. 94 | */ 95 | public const TO_LETTERS = [ 96 | 'de' => [ 97 | //... 98 | ], 99 | 'en' => [ 100 | //... 101 | ], 102 | 'es' => [ 103 | //... 104 | ], 105 | 'fa' => [ 106 | //... 107 | ], 108 | 'fr' => [ 109 | //... 110 | ], 111 | 'hi' => [ 112 | //... 113 | ], 114 | 'it' => [ 115 | //... 116 | ], 117 | 'pl' => [ 118 | //... 119 | ], 120 | 'pt' => [ 121 | //... 122 | ], 123 | 'ro' => [ 124 | //... 125 | ], 126 | 'vi' => [ 127 | //... 128 | ], 129 | ]; 130 | 131 | /** 132 | * ->toOrdinal() 133 | * The primary value must be there, that is, "es" instead of "es_ES" or similar. 134 | */ 135 | public const TO_ORDINAL = [ 136 | 'de' => [ 137 | //... 138 | ], 139 | 'en' => [ 140 | //... 141 | ], 142 | 'es' => [ 143 | //... 144 | ], 145 | 'fa' => [ 146 | //... 147 | ], 148 | 'fr' => [ 149 | //... 150 | ], 151 | 'hi' => [ 152 | //... 153 | ], 154 | 'it' => [ 155 | //... 156 | ], 157 | 'pl' => [ 158 | //... 159 | ], 160 | 'pt' => [ 161 | //... 162 | ], 163 | 'ro' => [ 164 | //... 165 | ], 166 | 'vi' => [ 167 | //... 168 | ], 169 | ]; 170 | } 171 | -------------------------------------------------------------------------------- /src/Miscellaneous/Utilities.php: -------------------------------------------------------------------------------- 1 | 1, 164 | '1' => 10, 165 | '02' => 2, 166 | '2' => 20, 167 | '03' => 3, 168 | '3' => 30, 169 | '04' => 4, 170 | '4' => 40, 171 | '05' => 5, 172 | '5' => 50, 173 | '06' => 6, 174 | '6' => 60, 175 | '07' => 7, 176 | '7' => 70, 177 | '08' => 8, 178 | '8' => 80, 179 | '09' => 9, 180 | '9' => 90, 181 | default => $value, 182 | }; 183 | 184 | return $result; 185 | } 186 | 187 | /** 188 | * Extracts the main value of the "Locale" example "es_MX" extracts "es". 189 | * 190 | * @param mixed $string 191 | * 192 | * @return string 193 | */ 194 | public static function extractPrimaryLocale($string) 195 | { 196 | return mb_strtolower(substr($string, 0, 2)); 197 | } 198 | 199 | /** 200 | * Determine the ordinal text mode based on the provided value. 201 | * 202 | * @param string|null $value The value indicating the ordinal text mode. 203 | * 204 | * @return string The corresponding rule for ordinal text formatting. 205 | */ 206 | public static function textOrdinalMode(?string $value) 207 | { 208 | return match ($value) { 209 | 'default' => '%spellout-ordinal', 210 | 'male' => '%spellout-ordinal-masculine', 211 | 'female' => '%spellout-ordinal-feminine', 212 | 'masculine' => '%spellout-ordinal-masculine', 213 | 'feminine' => '%spellout-ordinal-feminine', 214 | '%spellout-ordinal' => '%spellout-ordinal', 215 | '%spellout-ordinal-masculine' => '%spellout-ordinal-masculine', 216 | '%spellout-ordinal-feminine' => '%spellout-ordinal-feminine', 217 | 'ordinal' => '%spellout-ordinal', 218 | 'ordinal-masculine' => '%spellout-ordinal-masculine', 219 | 'ordinal-feminine' => '%spellout-ordinal-feminine', 220 | default => '%spellout-ordinal', 221 | }; 222 | } 223 | 224 | /** 225 | * Determine the human-readable ordinal text mode based on the provided value. 226 | * 227 | * @param string|null $value The value indicating the ordinal text mode. 228 | * 229 | * @return string The corresponding human-readable ordinal text mode. 230 | */ 231 | public static function textOrdinalModeHuman(?string $value) 232 | { 233 | return match ($value) { 234 | '%spellout-ordinal-masculine' => 'male', 235 | '%spellout-ordinal-feminine' => 'female', 236 | '%spellout-ordinal' => 'default', 237 | }; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/Miscellaneous/Words.php: -------------------------------------------------------------------------------- 1 | $replace) { 29 | $search = mb_strtolower($search); 30 | $replace = mb_strtolower($replace); 31 | $value = str_replace($search, $replace, $value); 32 | } 33 | 34 | // Return the adjusted text. 35 | return $value; 36 | } 37 | 38 | /** 39 | * Perform replacements in the given text based on the language and current currency from config file. 40 | * 41 | * @param string $value The original text string. 42 | * @param string $locale The language of the text. 43 | * 44 | * @return string The adjusted text with the replacements performed. 45 | */ 46 | public static function replaceFromConfig(string $value, string $locale) 47 | { 48 | // Primary Locale 49 | $locale = Utilities::extractPrimaryLocale($locale); 50 | 51 | //Value 52 | $value = mb_strtolower($value); 53 | 54 | //From Config. 55 | $replacesLocaleConfig = config("spell-number.replacements.$locale") ?? []; 56 | foreach ($replacesLocaleConfig as $search => $replace) { 57 | $search = mb_strtolower($search); 58 | $replace = mb_strtolower($replace); 59 | $value = str_replace($search, $replace, $value); 60 | } 61 | 62 | // Return the adjusted text. 63 | return $value; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Providers/SpellNumberProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__.'/../../config/spell-number.php' => config_path('spell-number.php'), 18 | ], 'config'); 19 | } 20 | 21 | /** 22 | * Register any package services. 23 | * 24 | * @return void 25 | */ 26 | public function register(): void 27 | { 28 | // Register any package-specific services here 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/SpellNumber.php: -------------------------------------------------------------------------------- 1 | value = $value; 49 | $this->type = $type; 50 | $this->locale = config('spell-number.default.lang') ?? config('spell-number.default.locale') ?? Langs::getLocaleLaravel(); 51 | $this->currency = config('spell-number.default.currency') ?? 'dollars'; 52 | $this->fraction = config('spell-number.default.fraction') ?? 'cents'; 53 | } 54 | 55 | /** 56 | * Set the locale for the conversion. 57 | * 58 | * @param string $locale The locale to use for the conversion. 59 | * 60 | * @throws SpellNumberExceptions If the provided locale is not valid. 61 | * 62 | * @return SpellNumber The SpellNumber instance with the updated locale. 63 | */ 64 | public function locale(string $locale, ?bool $specific_locale = null) 65 | { 66 | $valueConfig = config('spell-number.default.specific_locale'); 67 | 68 | $specific_locale = $specific_locale ?? $valueConfig ?? false; 69 | 70 | if ($specific_locale === false) { 71 | if (!Utilities::isValidLocale($locale)) { 72 | 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.'); 73 | } 74 | } 75 | 76 | $this->locale = $locale; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * Set the currency for the conversion. 83 | * 84 | * @param string $currency The currency to use for the conversion. 85 | * 86 | * @return SpellNumber The SpellNumber instance with the updated currency. 87 | */ 88 | public function currency(string $currency) 89 | { 90 | $this->currency = $currency; 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * Set the fraction for the conversion. 97 | * 98 | * @param string $fraction The fraction to use for the conversion. 99 | * 100 | * @return SpellNumber The SpellNumber instance with the updated fraction. 101 | */ 102 | public function fraction(string $fraction) 103 | { 104 | $this->fraction = $fraction; 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Convert the numeric value to words. 111 | * 112 | * @return string The textual representation of the numeric value. 113 | */ 114 | public function toLetters() 115 | { 116 | $callback = config('spell-number.callback_output'); 117 | 118 | if (!empty($callback) && is_callable($callback)) { 119 | $words = ($this->type == 'integer') ? $this->integerToLetters() : $this->doubleToLetters(); 120 | 121 | return $callback(new DataResponse([ 122 | 'method' => 'toLetters', 123 | 'type' => $this->type, 124 | 'value' => $this->value, 125 | 'words' => $words, 126 | 'lang' => Utilities::extractPrimaryLocale($this->locale), 127 | 'locale' => $this->locale, 128 | 'currency' => null, 129 | 'fraction' => null, 130 | ])); 131 | } 132 | 133 | return ($this->type == 'integer') ? $this->integerToLetters() : $this->doubleToLetters(); 134 | } 135 | 136 | /** 137 | * Convert the numeric value to a money representation. 138 | * 139 | * @return string The textual representation of the money value. 140 | */ 141 | public function toMoney() 142 | { 143 | $callback = config('spell-number.callback_output'); 144 | 145 | if (!empty($callback) && is_callable($callback)) { 146 | $words = ($this->type == 'integer') ? $this->integerToMoney() : $this->doubleToMoney(); 147 | 148 | return $callback(new DataResponse([ 149 | 'method' => 'toMoney', 150 | 'type' => $this->type, 151 | 'value' => $this->value, 152 | 'words' => $words, 153 | 'lang' => Utilities::extractPrimaryLocale($this->locale), 154 | 'locale' => $this->locale, 155 | 'currency' => $this->currency, 156 | 'fraction' => $this->fraction, 157 | ])); 158 | } 159 | 160 | return ($this->type == 'integer') ? $this->integerToMoney() : $this->doubleToMoney(); 161 | } 162 | 163 | /** 164 | * Convert the numeric value to ordinal representation. 165 | * 166 | * @return string The textual representation of the ordinal value. 167 | */ 168 | public function toOrdinal(?string $attr = null) 169 | { 170 | $valueConfig = config('spell-number.default.ordinal_output'); 171 | 172 | $attr = Utilities::textOrdinalMode($attr ?? $valueConfig); 173 | 174 | $callback = config('spell-number.callback_output'); 175 | 176 | if (!empty($callback) && is_callable($callback)) { 177 | $words = $this->integerToOrdinal($attr); 178 | 179 | return $callback(new DataResponse([ 180 | 'method' => 'toOrdinal', 181 | 'type' => $this->type, 182 | 'value' => $this->value, 183 | 'words' => $words, 184 | 'lang' => Utilities::extractPrimaryLocale($this->locale), 185 | 'locale' => $this->locale, 186 | 'mode' => Utilities::textOrdinalModeHuman($attr), 187 | ])); 188 | } 189 | 190 | return $this->integerToOrdinal($attr); 191 | } 192 | 193 | /** 194 | * Convert the integer numeric value to its ordinal textual representation. 195 | * 196 | * @return string The textual representation of the ordinal value. 197 | */ 198 | private function integerToOrdinal($attr) 199 | { 200 | if ($this->type == 'double') { 201 | throw SpellNumberExceptions::create('To convert to ordinal numbers, an integer value is required as input'); 202 | } 203 | 204 | $formatter = NumberFormatterWrapper::format($this->value, $this->locale, true, $attr); 205 | $formatter = Words::replaceLocale($formatter, $this->locale, self::TO_ORDINAL); 206 | 207 | return Words::replaceFromConfig($formatter, $this->locale); 208 | } 209 | 210 | /** 211 | * Convert the integer numeric value to its textual representation. 212 | * 213 | * @return string The textual representation of the integer numeric value. 214 | */ 215 | private function integerToLetters() 216 | { 217 | $formatter = NumberFormatterWrapper::format($this->value, $this->locale); 218 | $formatter = Words::replaceLocale($formatter, $this->locale, self::TO_LETTERS); 219 | 220 | return Words::replaceFromConfig($formatter, $this->locale); 221 | } 222 | 223 | /** 224 | * Convert the double numeric value to its textual representation. 225 | * 226 | * @return string The textual representation of the double numeric value. 227 | */ 228 | private function doubleToLetters() 229 | { 230 | $parts = explode('.', $this->value); 231 | 232 | if (!array_key_exists(1, $parts)) { 233 | return $this->integerToLetters(); 234 | } 235 | 236 | $parts[1] = Utilities::decimal($parts[1]); 237 | 238 | $letters1 = NumberFormatterWrapper::format($parts[0], $this->locale); 239 | $letters2 = NumberFormatterWrapper::format($parts[1], $this->locale); 240 | 241 | $output = sprintf('%s %s %s', $letters1, Utilities::connector($this->locale), $letters2); 242 | $output = Words::replaceLocale($output, $this->locale, self::TO_LETTERS); 243 | 244 | return Words::replaceFromConfig($output, $this->locale); 245 | } 246 | 247 | /** 248 | * Convert the integer numeric value to its money representation. 249 | * 250 | * @return string The money representation of the integer numeric value. 251 | */ 252 | private function integerToMoney() 253 | { 254 | $letters = NumberFormatterWrapper::format($this->value, $this->locale).' '.$this->currency; 255 | $letters = Words::replaceLocale($letters, $this->locale, self::TO_MONEY); 256 | 257 | return Words::replaceFromConfig($letters, $this->locale); 258 | } 259 | 260 | /** 261 | * Convert the double numeric value to its money representation. 262 | * 263 | * @return string The money representation of the double numeric value. 264 | */ 265 | private function doubleToMoney() 266 | { 267 | $parts = explode('.', $this->value); 268 | 269 | if (!array_key_exists(1, $parts)) { 270 | return $this->integerToMoney(); 271 | } 272 | 273 | $parts[1] = Utilities::decimal($parts[1]); 274 | 275 | $letters1 = NumberFormatterWrapper::format($parts[0], $this->locale).' '.$this->currency; 276 | $letters2 = NumberFormatterWrapper::format($parts[1], $this->locale).' '.$this->fraction; 277 | 278 | $output = $letters1.' '.Utilities::connector($this->locale).' '.$letters2; 279 | $output = Words::replaceLocale($output, $this->locale, self::TO_MONEY); 280 | 281 | return Words::replaceFromConfig($output, $this->locale); 282 | } 283 | 284 | /** 285 | * Perform a spell-related action based on the specified method and optional ordinal mode. 286 | * 287 | * @param string $method The method to perform (TO_LETTERS, TO_MONEY, or TO_ORDINAL). 288 | * @param string|null $modeOrdinal The optional ordinal mode when using TO_ORDINAL. 289 | * 290 | * @throws SpellNumberExceptions When the requested action is invalid. 291 | * 292 | * @return mixed The result of the specified action. 293 | */ 294 | private function spell(string $method, ?string $modeOrdinal = null) 295 | { 296 | return match ($method) { 297 | 'TO_LETTERS' => $this->toLetters(), 298 | 'TO_MONEY' => $this->toMoney(), 299 | 'TO_ORDINAL' => $this->toOrdinal($modeOrdinal), 300 | 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."), 301 | }; 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/Traits/Accesor.php: -------------------------------------------------------------------------------- 1 | value = $value; 29 | 30 | // Check if the intl extension is installed. 31 | $this->validateExtension(); 32 | 33 | // Validate that it is not a scientific notation. 34 | $this->validateScientificConnotation(); 35 | 36 | // Execute the appropriate validation based on the type. 37 | match ($type) { 38 | 'mixed' => $this->mixed(), 39 | 'integer' => $this->integer(), 40 | 'double' => $this->float() 41 | }; 42 | } 43 | 44 | /** 45 | * Validate when the type is "mixed". 46 | * 47 | * @throws SpellNumberExceptions 48 | * 49 | * @return $this 50 | */ 51 | public function mixed() 52 | { 53 | $this->validateNumeric(); 54 | $this->validateMaximum(); 55 | $this->response = $this->validateType(); 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * Validate when the type is "integer". 62 | * 63 | * @throws SpellNumberExceptions 64 | * 65 | * @return $this 66 | */ 67 | public function integer() 68 | { 69 | $this->validateInteger(); 70 | $this->response = $this->validateMaximum(); 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Validate when the type is "float". 77 | * 78 | * @throws SpellNumberExceptions 79 | * 80 | * @return $this 81 | */ 82 | public function float() 83 | { 84 | $this->validateString(); 85 | $this->response = $this->validateMaximum(); 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * Get the result of the validation. 92 | * 93 | * @return mixed The result of the validation. 94 | */ 95 | public function result() 96 | { 97 | return $this->response; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Validator/Traits/CommonValidate.php: -------------------------------------------------------------------------------- 1 | value)) { 18 | throw SpellNumberExceptions::create('The entered value exceeds the processable limits of the current version of PHP and has become a Scientific Connotation.'); 19 | } 20 | 21 | return true; 22 | } 23 | 24 | /** 25 | * Validate that the "Intl" extension is loaded. 26 | * 27 | * @throws SpellNumberExceptions If the "Intl" extension is not installed or not available. 28 | */ 29 | private function validateExtension() 30 | { 31 | if (!Utilities::validateExtension('Intl')) { 32 | throw SpellNumberExceptions::create('The INTL extension is not installed or not available. (https://www.php.net/manual/es/intl.installation.php)'); 33 | } 34 | 35 | return true; 36 | } 37 | 38 | /** 39 | * Validate if the value is numeric. 40 | * 41 | * @throws SpellNumberExceptions If the supplied value is not valid (not integer or float). 42 | */ 43 | private function validateNumeric() 44 | { 45 | if (!Utilities::isValidNumber($this->value)) { 46 | throw SpellNumberExceptions::create('The provided value is not valid. It must be of type integer or float (double). Additionally, scientific overtones generated when the maximum number available in the current version of PHP is exceeded cannot be processed.'); 47 | } 48 | 49 | return true; 50 | } 51 | 52 | /** 53 | * Validate if the value is an integer. 54 | * 55 | * @throws SpellNumberExceptions If the supplied value is not a valid integer. 56 | */ 57 | private function validateInteger() 58 | { 59 | if (!Utilities::isValidInteger($this->value)) { 60 | throw SpellNumberExceptions::create('The supplied value is not valid. It must be of type integer.'); 61 | } 62 | 63 | return true; 64 | } 65 | 66 | /** 67 | * Validate if the value is a string. 68 | * 69 | * @throws SpellNumberExceptions If the supplied value is not a valid string. 70 | */ 71 | private function validateString() 72 | { 73 | if (!Utilities::isValidString($this->value)) { 74 | throw SpellNumberExceptions::create('The supplied value is not valid. It must be of type String.'); 75 | } 76 | 77 | return true; 78 | } 79 | 80 | /** 81 | * Validate if the value does not exceed the maximum allowed value. 82 | * 83 | * @throws SpellNumberExceptions If the entered value exceeds the maximum allowed value. 84 | */ 85 | private function validateMaximum() 86 | { 87 | if (!Utilities::isNotExceedMax($this->value)) { 88 | throw SpellNumberExceptions::create('The value entered is too large and has been converted to scientific notation which prevents processing.'); 89 | } 90 | 91 | return true; 92 | } 93 | 94 | /** 95 | * Validate the type of the value. 96 | * 97 | * @return string Returns "integer" if the value is a valid integer, otherwise "double". 98 | */ 99 | private function validateType() 100 | { 101 | return Utilities::isValidInteger($this->value) ? 'integer' : 'double'; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Wrappers/NumberFormatterWrapper.php: -------------------------------------------------------------------------------- 1 | setTextAttribute(NumberFormatter::DEFAULT_RULESET, $attr); 28 | } 29 | 30 | // Format the given numeric value as a spelled-out string. 31 | $value = $numberFormatter->format($value); 32 | 33 | return str_replace(["\u{AD}", "\u{200B}"], '', $value); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/SpellNumberTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(SpellNumber::value(100)->locale('en')->toLetters(), 'One Hundred'); 13 | 14 | // Spanish locale 15 | $this->assertEquals(SpellNumber::value(100)->locale('es')->toLetters(), 'Cien'); 16 | 17 | // Farsi (Persian) locale 18 | $this->assertEquals(SpellNumber::value(100)->locale('fa')->toLetters(), 'صد'); 19 | 20 | // Hindi locale 21 | $this->assertEquals(SpellNumber::value(100)->locale('hi')->toLetters(), 'एक सौ'); 22 | 23 | // Test conversion of floating-point value to letters in English locale 24 | $this->assertEquals(SpellNumber::value(123456789.12)->locale('en')->toLetters(), 'One Hundred Twenty-Three Million Four Hundred Fifty-Six Thousand Seven Hundred Eighty-Nine And Twelve'); 25 | 26 | // Test conversion of integer to letters in English locale 27 | $this->assertEquals(SpellNumber::integer(100)->locale('en')->toLetters(), 'One Hundred'); 28 | 29 | // Test conversion of floating-point value to letters in English locale 30 | $this->assertEquals(SpellNumber::float('12345.230')->locale('en')->toLetters(), 'Twelve Thousand Three Hundred Forty-Five And Two Hundred Thirty'); 31 | } 32 | 33 | // Test conversion of numerical value to money representation in different locales 34 | public function testToMoney() 35 | { 36 | // English locale with 'Dollars' currency 37 | $this->assertEquals(SpellNumber::value(100)->locale('en')->currency('Dollars')->toMoney(), 'One Hundred Dollars'); 38 | 39 | // Spanish locale with 'Pesos' currency 40 | $this->assertEquals(SpellNumber::value(100)->locale('es')->currency('Pesos')->toMoney(), 'Cien Pesos'); 41 | 42 | // Hindi locale with 'रूपये' currency 43 | $this->assertEquals(SpellNumber::value(100)->locale('hi')->currency('रूपये')->toMoney(), 'एक सौ रूपये'); 44 | 45 | // Test conversion of integer to money representation in Spanish locale 46 | $this->assertEquals(SpellNumber::integer(100)->locale('es')->currency('Pesos')->toMoney(), 'Cien Pesos'); 47 | 48 | // Test conversion of floating-point value to money representation in Spanish locale 49 | $this->assertEquals(SpellNumber::float('12345.230')->locale('es')->currency('Pesos')->fraction('Centavos')->toMoney(), 'Doce Mil Trescientos Cuarenta Y Cinco Pesos Con Doscientos Treinta Centavos'); 50 | } 51 | } 52 | --------------------------------------------------------------------------------