From e61a72400b479a4ddb011353dc0d7c415e812235 Mon Sep 17 00:00:00 2001 From: Ignace Nyamagana Butera Date: Sat, 23 Jul 2022 16:38:07 +0200 Subject: [PATCH] Adding Laravel specific package to monorepo --- .gitattributes | 1 + composer.json | 10 + phpstan.neon | 1 + src/Laravel/.gitattributes | 9 + src/Laravel/.github/FUNDING.yml | 1 + src/Laravel/.github/workflows/build.yml | 55 +++ src/Laravel/CHANGELOG.md | 37 ++ src/Laravel/Factory.php | 85 +++++ src/Laravel/FactoryTest.php | 44 +++ src/Laravel/HelpersTest.php | 233 +++++++++++++ src/Laravel/IntlFactory.php | 15 + src/Laravel/IntlFormatter.php | 16 + src/Laravel/Provider.php | 41 +++ src/Laravel/README.md | 315 ++++++++++++++++++ src/Laravel/composer.json | 67 ++++ src/Laravel/config/bakame-intl-formatter.php | 129 +++++++ src/Laravel/helpers.php | 197 +++++++++++ src/Laravel/test_files/country_name.blade.php | 6 + .../test_files/country_timezones.blade.php | 3 + .../test_files/currency_name.blade.php | 6 + .../test_files/currency_symbol.blade.php | 4 + .../test_files/format_currency.blade.php | 6 + src/Laravel/test_files/format_date.blade.php | 10 + .../test_files/format_number.blade.php | 11 + .../test_files/language_name.blade.php | 7 + src/Laravel/test_files/locale_name.blade.php | 7 + .../test_files/timezone_name.blade.php | 5 + 27 files changed, 1321 insertions(+) create mode 100644 src/Laravel/.gitattributes create mode 100644 src/Laravel/.github/FUNDING.yml create mode 100644 src/Laravel/.github/workflows/build.yml create mode 100644 src/Laravel/CHANGELOG.md create mode 100644 src/Laravel/Factory.php create mode 100644 src/Laravel/FactoryTest.php create mode 100644 src/Laravel/HelpersTest.php create mode 100644 src/Laravel/IntlFactory.php create mode 100644 src/Laravel/IntlFormatter.php create mode 100644 src/Laravel/Provider.php create mode 100644 src/Laravel/README.md create mode 100644 src/Laravel/composer.json create mode 100644 src/Laravel/config/bakame-intl-formatter.php create mode 100644 src/Laravel/helpers.php create mode 100644 src/Laravel/test_files/country_name.blade.php create mode 100644 src/Laravel/test_files/country_timezones.blade.php create mode 100644 src/Laravel/test_files/currency_name.blade.php create mode 100644 src/Laravel/test_files/currency_symbol.blade.php create mode 100644 src/Laravel/test_files/format_currency.blade.php create mode 100644 src/Laravel/test_files/format_date.blade.php create mode 100644 src/Laravel/test_files/format_number.blade.php create mode 100644 src/Laravel/test_files/language_name.blade.php create mode 100644 src/Laravel/test_files/locale_name.blade.php create mode 100644 src/Laravel/test_files/timezone_name.blade.php diff --git a/.gitattributes b/.gitattributes index 7226cd8..19126aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,6 +7,7 @@ /.php-cs-fixer.php export-ignore /docs export-ignore /build export-ignore +/src/Laravel export-ignore /test_files export-ignore /phpstan.neon export-ignore /phpunit.xml export-ignore diff --git a/composer.json b/composer.json index 6f240b4..11e6f10 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,11 @@ "src/Test**" ] }, + "autoload-dev": { + "files": [ + "src/Laravel/helpers.php" + ] + }, "require": { "ext-json": "*", "php": "^8.0", @@ -40,6 +45,11 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.8", + "illuminate/config": "^8.0 || ^9.0", + "illuminate/support": "^8.0 || ^9.0", + "moneyphp/money": "^3.0 || ^4.0", + "nunomaduro/larastan": "^1.0.0 || ^2.1", + "orchestra/testbench": "^6.6.0 || ^7.5", "phpstan/phpstan": "^1.7", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.1", diff --git a/phpstan.neon b/phpstan.neon index 317c185..e3d3622 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,4 +1,5 @@ includes: + - vendor/nunomaduro/larastan/extension.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon diff --git a/src/Laravel/.gitattributes b/src/Laravel/.gitattributes new file mode 100644 index 0000000..63b1c36 --- /dev/null +++ b/src/Laravel/.gitattributes @@ -0,0 +1,9 @@ +* text=auto + +/.gitattributes export-ignore +/.github export-ignore +/test_files export-ignore +/CHANGELOG.md export-ignore +/README.md export-ignore +/**/*Test.php export-ignore +/**/Test** export-ignore diff --git a/src/Laravel/.github/FUNDING.yml b/src/Laravel/.github/FUNDING.yml new file mode 100644 index 0000000..70fdf4b --- /dev/null +++ b/src/Laravel/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [nyamsprod] diff --git a/src/Laravel/.github/workflows/build.yml b/src/Laravel/.github/workflows/build.yml new file mode 100644 index 0000000..1a43c58 --- /dev/null +++ b/src/Laravel/.github/workflows/build.yml @@ -0,0 +1,55 @@ +--- +name: build +on: + push: ~ + pull_request: ~ + +jobs: + linux_tests: + name: PHP on ${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.composer-flags }} + runs-on: ubuntu-20.04 + strategy: + matrix: + php: ['7.4', '8.0', '8.1'] + stability: [prefer-stable] + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + tools: composer:v2 + - name: Check PHP Version + run: php -v + + - name: Validate composer files + run: composer validate --strict + + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + - uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.stability }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-${{ matrix.stability }}- + ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress ${{ matrix.flags }} + + - name: Run Unit tests with coverage + run: composer phpunit + + - name: Run static analysis + run: composer phpstan + if: ${{ matrix.php == '8.1' && matrix.stability == 'prefer-stable'}} + + - name: Run Coding style rules + run: composer phpcs:fix + if: ${{ matrix.php == '8.1' && matrix.stability == 'prefer-stable'}} diff --git a/src/Laravel/CHANGELOG.md b/src/Laravel/CHANGELOG.md new file mode 100644 index 0000000..e7ddb5a --- /dev/null +++ b/src/Laravel/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +All Notable changes to `bakame/laravel-intl-formatter` will be documented in this file + +## [0.3.0] - 2022-06-14 + +### Added + +- Support for `Money` package. +- Added `IntlFactory` facade. +- `NumberFactory` and `DateFactory` are now accessible as readonly property of the `Factory` class. + +### Fixed + +- **[BC Break]** Renamed `Factory::newInstance` in `Factory::newFormatter` +- **[BC Break]** Keys used for container registration are updated. + +### Deprecated + +- None + +### Removed + +**[BC Break]** `Factory::newInstance` replaced by `Factory::newFormatter` + +## [0.2.0] - 2022-06-06 + +** Dependencies fixes + +## [0.1.0] - 2022-06-04 + +**Initial release!** + +[Next]: https://github.com/bakame-php/laravel-intl-formatter/compare/0.3.0...main +[Next]: https://github.com/bakame-php/laravel-intl-formatter/compare/0.2.0...0.3.0 +[0.2.0]: https://github.com/bakame-php/laravel-intl-formatter/compare/0.1.0...0.2.0 +[0.1.0]: https://github.com/bakame-php/laravel-intl-formatter/releases/tag/0.1.0 diff --git a/src/Laravel/Factory.php b/src/Laravel/Factory.php new file mode 100644 index 0000000..0f050ef --- /dev/null +++ b/src/Laravel/Factory.php @@ -0,0 +1,85 @@ +dateFactory = $dateFactory; + $this->numberFactory = $numberFactory; + } + + /** + * @param array{ + * date:array{ + * dateFormat:key-of, + * timeFormat:key-of, + * calendar:key-of, + * pattern?:?string, + * }, + * number:array{ + * style:key-of, + * pattern?:?string, + * attributes?:array, int|float|key-of|key-of>, + * textAttributes?:array, string>, + * symbolAttributes?:array, string> + * } + * } $settings + */ + public static function fromAssociative(array $settings): self + { + return new self( + DateFactory::fromAssociative($settings['date']), + NumberFactory::fromAssociative($settings['number']) + ); + } + + public function newFormatter(DateResolver $dateResolver): Formatter + { + return new Formatter($this->dateFactory, $this->numberFactory, $dateResolver); + } + + /** + * @param StyleFormat|key-of|null $style + * @param array, int|float|key-of|key-of> $attrs + */ + public function newIntlMoneyFormatter(?string $locale = null, StyleFormat|string $style = null, array $attrs = []): IntlMoneyFormatter + { + $locale = $locale ?? Locale::getDefault(); + $hash = $locale.'|'.json_encode($style).'|'.json_encode($attrs); + static $instances = []; + if (!isset($instances[$hash])) { + $instances[$hash] = new IntlMoneyFormatter( + $this->numberFactory->createNumberFormatter($locale, $style, $attrs), + new ISOCurrencies() + ); + } + + return $instances[$hash]; + } +} diff --git a/src/Laravel/FactoryTest.php b/src/Laravel/FactoryTest.php new file mode 100644 index 0000000..a07d0b8 --- /dev/null +++ b/src/Laravel/FactoryTest.php @@ -0,0 +1,44 @@ + + */ + protected function getPackageProviders($app): array + { + return [ + Provider::class, + ]; + } + + /** @test */ + public function it_can_format_a_money_object(): void + { + $money = Money::EUR(250); + + self::assertSame('2,50 €', IntlFactory::newIntlMoneyFormatter('fr', 'currency')->format($money)); + self::assertSame('2,50 €', format_currency($money, null, 'fr')); + + self::assertSame('deux virgule cinq', IntlFactory::newIntlMoneyFormatter('fr', 'spellout')->format($money)); + self::assertSame('deux virgule cinq', format_number($money, 'fr', 'default', [], 'spellout')); + } +} diff --git a/src/Laravel/HelpersTest.php b/src/Laravel/HelpersTest.php new file mode 100644 index 0000000..6dd3428 --- /dev/null +++ b/src/Laravel/HelpersTest.php @@ -0,0 +1,233 @@ +set('view.paths', [__DIR__.'/test_files']); + } + + /** + * @param array $withParameters + * + * @throws BindingResolutionException + * + * @return array|string + */ + public function renderView(string $viewName, array $withParameters = []) + { + return app(ViewFactory::class)->make($viewName, $withParameters)->render(); + } + + /** + * @param Application $app + * + * @return array + */ + protected function getPackageProviders($app): array + { + return [ + Provider::class, + ]; + } + + /** @test */ + public function it_can_format_a_country_name(): void + { + /** @var string $content */ + $content = $this->renderView('country_name'); + $expected = <<renderView('country_timezones'); + $expected = <<renderView('currency_name'); + $expected = <<renderView('currency_symbol'); + $expected = <<renderView('format_currency'); + $expected = <<renderView('format_date'); + $expected = <<renderView('format_number'); + $expected = <<renderView('language_name'); + $expected = <<renderView('locale_name'); + $expected = <<renderView('timezone_name'); + $expected = <<expectException(FailedFormatting::class); + + format_currency(300, null, 'nl_NL'); + } +} diff --git a/src/Laravel/IntlFactory.php b/src/Laravel/IntlFactory.php new file mode 100644 index 0000000..9190f01 --- /dev/null +++ b/src/Laravel/IntlFactory.php @@ -0,0 +1,15 @@ +app->runningInConsole()) { + $this->publishes([ + BKM_INTL_FORMATTER.'/config/bakame-intl-formatter.php' => config_path('bakame-intl-formatter.php'), + ], 'config'); + } + } + + public function register(): void + { + parent::register(); + + if (! defined('BKM_INTL_FORMATTER')) { + define('BKM_INTL_FORMATTER', realpath(__DIR__)); + } + + $this->mergeConfigFrom(BKM_INTL_FORMATTER.'/config/bakame-intl-formatter.php', 'bakame.intl.laravel.configuration'); + + $this->app->singleton(DateResolver::class, fn (): DateResolver => DateResolver::fromTimeZone(now()->getTimezone())); + $this->app->singleton(Factory::class, fn ($app): Factory => Factory::fromAssociative( + $app->make('config')->get('bakame.intl.laravel.configuration') + )); + $this->app->singleton( + Formatter::class, + fn ($app): Formatter => $app->make(Factory::class)->newFormatter($app->make(DateResolver::class)) + ); + } +} diff --git a/src/Laravel/README.md b/src/Laravel/README.md new file mode 100644 index 0000000..7ffabe1 --- /dev/null +++ b/src/Laravel/README.md @@ -0,0 +1,315 @@ +Laravel Intl Formatter +======================================= + +[![Author](http://img.shields.io/badge/author-@nyamsprod-blue.svg?style=flat-square)](https://twitter.com/nyamsprod) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) +[![Build](https://github.com/bakame-php/laravel-intl-formatter/workflows/build/badge.svg)](https://github.com/bakame-php/laravel-intl-formatter/actions?query=workflow%3A%22build%22) +[![Latest Version](https://img.shields.io/github/release/bakame-php/laravel-intl-formatter.svg?style=flat-square)](https://github.com/bakame-php/laravel-intl-formatter/releases) +[![Total Downloads](https://img.shields.io/packagist/dt/bakame/laravel-intl-formatter.svg?style=flat-square)](https://packagist.org/packages/bakame/laravel-intl-formatter) +[![Sponsor development of this project](https://img.shields.io/badge/sponsor%20this%20package-%E2%9D%A4-ff69b4.svg?style=flat-square)](https://github.com/sponsors/nyamsprod) + +This is a Laravel port of the [Twig Intl Extension](https://github.com/twigphp/intl-extra) package. + +The package can be used in any Laravel based application to quickly handle internationalization +by providing helper functions in Blade templates or Laravel codebase. + +System Requirements +------- + +- Laravel 8 and/or 9 +- Symfony Intl component + +Installation +------------ + +Use composer: + +``` +composer require bakame/laravel-intl-formatter +``` + +Configuration +------------ + +In order to edit the default configuration you need to publish the package configuration to your application config directory: + +```bash +php artisan vendor:publish --provider="Bakame\Laravel\Intl" --tag=config +``` + +The configuration file will be published to `config/bakame-intl-formatter.php` in your application directory. + +Please refer to the config file for an overview of the available options. + +Documentation +------------ + +Once installed the package provides the following global helper functions: + +### Country Name + +Returns the country name given its two-letter/five-letter code; + +```blade +country name: {{ country_name($country, $locale) }} +``` + +```php +echo view($templatePath, ['country' => 'FR', 'locale' => 'NL'])->render(); +// country name: Frankrijk +``` + +### Currency Name + +Returns the currency name given its three-letter code; + +```blade +currency name: {{ currency_name($currency, $locale) }} +``` + +```php +echo view($templatePath, ['currency' => 'JPY', 'locale' => 'PT'])->render(); +// currency name: Iene japonês +``` + +### Currency Symbol + +Returns the currency symbol given its three-letter code; + +```blade +currency symbol: {{ currency_symbol($currency, $locale) }} +``` + +```php +echo view($templatePath, ['currency' => 'JPY', 'locale' => 'PT'])->render(); +// currency symbol: JP¥ +``` + +### Language name + +Returns the currency symbol given its three-letter code; + +```blade +language name: {{ language_name($language, $locale) }} +``` + +```php +echo view($templatePath, ['language' => 'it', 'locale' => 'nl'])->render(); +// language name: Italiaans +``` + +### Locale name + +Returns the currency symbol given its three-letter code; + +```blade +locale name: {{ locale_name($data, $locale) }} +``` + +```php +echo view($templatePath, ['data' => 'sw', 'locale' => 'nl'])->render(); +// locale name: Swahili +``` + +### Timezone name + +Returns the timezone name given its identifier; + +```blade +timezone name: {{ locale_name($data, $locale) }} +``` + +```php +echo view($templatePath, ['timezone' => 'Asia/Tokyo', 'locale' => 'es'])->render(); +// timezone name: hora de Japón (Tokio) +``` + +### Country Timezones + +Returns the timezone identifiers of the given country code; + +```blade +country timezones: {{ implde(", ", country_timezones($country)) }} +``` + +```php +$content = view($templatePath, ['country' => 'CD', 'locale' => 'es'])->render(); +echo $content, PHP_EOL; // country timezones: Africa/Kinshasa, Africa/Lubumbashi +``` + +### Format Currency + +Formats a number as a currency; + +```blade +format currency: {{ format_currency($amount, $currency, $attrs, $locale) }} +``` + +```php +$templateData = [ + 'amount' => 100.356, + 'currency' => 'USD', + 'locale' => 'ES', + 'attrs' => [ + 'fraction_digit' => 1, + 'rounding_mode' => 'floor', + ] +]; +echo view($templatePath, $templateData)->render(); +// format currency: 100,3 US$ +``` + +### Format Number + +Formats a number; + +```blade +format number: {{ format_number($number, $locale, $attrs) }} +``` + +```php +$templateData = [ + 'number' => 100.356, + 'locale' => 'nl', + 'style' => 'spellout', + 'type' => 'double', + 'attrs' => [ + 'fraction_digit' => 1, + 'rounding_mode' => 'floor', + ] +]; +echo view($templatePath, $templateData)->render(); +// format number: honderd komma drie +``` + +### Format DateTime + +Formats a date and time; + +```blade +format datetime: {{ format_datetime($date, $locale, $timezone, $dateFormat, $timeFormat, $pattern, $calendar) }} +``` + +```php +$templateData = [ + 'date' => 'yesterday', + 'dateFormat' => 'full', + 'timeFormat' => 'full', + 'pattern' => '' , + 'timezone' => 'Africa/Lubumbashi', + 'calendar' => 'gregorian' , + 'locale' => 'sw', +]; +echo view($templatePath, $templateData)->render(); +// format datetime: Alhamisi, 2 Juni 2022 00:00:00 Saa za Afrika ya Kati +``` + +### Format Date + +Formats a the date portion of a datetime; + +```blade +format date: {{ format_date($date, $locale, $timezone, $dateFormat, $pattern, $calendar) }} +``` + +```php +$templateData = [ + 'date' => 'yesterday', + 'dateFormat' => 'long', + 'pattern' => '' , + 'timezone' => 'Africa/Lubumbashi', + 'calendar' => 'gregorian' , + 'locale' => 'sw', +]; +echo view($templatePath, $templateData)->render(); +// format date: 2 Juni 2022 +``` + +### Format Time + +Formats the time portion of a datetime; + +```blade +format time: {{ format_time($date, $locale, $timezone, $timeFormat, $pattern, $calendar) }} +``` + +```php +$templateData = [ + 'date' => 'yesterday', + 'dateFormat' => 'full', + 'pattern' => '' , + 'timezone' => 'Africa/Lubumbashi', + 'calendar' => 'gregorian' , + 'locale' => 'sw', +]; +echo view($templatePath, $templateData)->render(); +// format time: 00:00:00 Saa za Afrika ya Kati +``` + +Each function uses the same arguments in the same order as the Twig Extra package filters/functions. + +### Locale specification + +If no `locale` is specified in function calls, the function will use the result of `Illuminate\Support\Facades\App::currentLocale()` +as the locale value to use. + +### functions signature + +In PHP8+, you can use named parameters to improve functions usages as they tend to have a lot of arguments: + +**In PHP7.4** + +```php + [ + /* + |-------------------------------------------------------------------------- + | Date Type + |-------------------------------------------------------------------------- + | + | Date type to use (none, short, medium, long, full). This is one of the IntlDateFormatter constants. + | In PHP8+ you can add (relative_short, relative_medium, relative_long, relative_full) + | + | Refer to the docs for more information: https://www.php.net/manual/en/intldateformatter.create.php + | + | Refer to Bakame\Intl\Options\DateType class + | + | Expected: string + */ + 'dateFormat' => 'medium', + /* + |-------------------------------------------------------------------------- + | Time Type + |-------------------------------------------------------------------------- + | + | Time type to use (none, short, medium, long, full). This is one of the IntlDateFormatter constants. + | + | Refer to the docs for more information: https://www.php.net/manual/en/intldateformatter.create.php + | + | Refer to Bakame\Intl\Options\TimeType class + | + | Expected: string + */ + 'timeFormat' => 'medium', + /* + |-------------------------------------------------------------------------- + | Date Pattern + |-------------------------------------------------------------------------- + | + | Optional pattern to use when formatting or parsing. + | + | Refer to the docs for more information: https://unicode-org.github.io/icu/userguide/format_parse/datetime/ + | + | Expected: ?string + */ + 'pattern' => null, + /* + |-------------------------------------------------------------------------- + | Calendar + |-------------------------------------------------------------------------- + | + | Calendar to use (gregorian, traditional) + */ + 'calendar' => 'gregorian', + ], + 'number' => [ + /* + |-------------------------------------------------------------------------- + | NumberFormatter Style + |-------------------------------------------------------------------------- + | + | Style of the formatting, one of the format style constants. If NumberFormatter::PATTERN_DECIMAL or + | NumberFormatter::PATTERN_RULEBASED is passed then the number format is opened using the given pattern, + | which must conform to the syntax described in » ICU DecimalFormat documentation or » + | ICU RuleBasedNumberFormat documentation, respectively. + | + | Expected: string + | + | Refer to Bakame\Intl\Options\NumberStyle class + | + */ + 'style' => 'decimal', + /* + |-------------------------------------------------------------------------- + | Number Pattern + |-------------------------------------------------------------------------- + | + | Pattern string if the chosen style requires a pattern. + | + | Expected: ?string + */ + 'pattern' => null, + /* + |-------------------------------------------------------------------------- + | Number Attributes + |-------------------------------------------------------------------------- + | + | Set a numeric attribute associated with the formatter. + | An example of a numeric attribute is the number of integer digits the formatter will produce. + | + | Expected: array + | + | Refer to the docs for more information: https://www.php.net/manual/en/numberformatter.setattribute.php + | Refer to Bakame\Intl\Options\NumberAttribute class + */ + 'attributes' => [], + /* + |-------------------------------------------------------------------------- + | Number Text Attributes + |-------------------------------------------------------------------------- + | + | Set a text attribute associated with the formatter. An example of a text attribute is the suffix for + | positive numbers. If the formatter does not understand the attribute, U_UNSUPPORTED_ERROR error is + | produced. + | + | Rule-based formatters only understand NumberFormatter::DEFAULT_RULESET and NumberFormatter::PUBLIC_RULESETS. + | + | Expected: array + | + | Refer to the docs for more information: https://www.php.net/manual/en/numberformatter.settextattribute.php + | Refer to Bakame\Intl\Options\TextAttribute class + */ + 'textAttributes' => [], + /* + |-------------------------------------------------------------------------- + | Symbol Attributes + |-------------------------------------------------------------------------- + | + | Set a symbol associated with the formatter. The formatter uses symbols to represent the special + | locale-dependent characters in a number, for example the percent sign. This API is not supported + | for rule-based formatters. + | + | Expected: array + | + | Refer to the docs for more information: https://www.php.net/manual/en/numberformatter.setsymbol.php + | Refer to Bakame\Intl\Options\SymbolAttribute class + */ + 'symbolAttributes' => [], + ], +]; diff --git a/src/Laravel/helpers.php b/src/Laravel/helpers.php new file mode 100644 index 0000000..4fb5826 --- /dev/null +++ b/src/Laravel/helpers.php @@ -0,0 +1,197 @@ + + */ + function country_timezones(string $country): array + { + return IntlFormatter::getCountryTimezones($country); + } +} + +if (! function_exists('format_currency')) { + /** + * @param int|float|Money $amount + * @param array, int|float|key-of|key-of> $attrs + */ + function format_currency( + $amount, + ?string $currency = null, + ?string $locale = null, + array $attrs = [] + ): string { + if ($amount instanceof Money) { + return IntlFactory::newIntlMoneyFormatter($locale, 'currency', $attrs)->format($amount); + } + + if (null === $currency) { + throw new FailedFormatting('The currency value is missing.'); + } + + return IntlFormatter::formatCurrency($amount, $currency, $locale ?? App::currentLocale(), $attrs); + } +} + +if (! function_exists('format_number')) { + /** + * @param key-of $type + * @param int|float|Money $number + * @param array, int|float|key-of|key-of> $attrs + * @param key-of|null $style + */ + function format_number( + $number, + ?string $locale = null, + string $type = 'default', + array $attrs = [], + ?string $style = null + ): string { + if ($number instanceof Money) { + return IntlFactory::newIntlMoneyFormatter($locale, $style, $attrs)->format($number); + } + + return IntlFormatter::formatNumber($number, $locale ?? App::currentLocale(), $type, $attrs, $style); + } +} + +if (! function_exists('format_datetime')) { + /** + * @param DateTimeInterface|string|int|null $date A date or null to use the current time + * @param DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + * @param key-of|null $dateFormat + * @param key-of|null $timeFormat + * @param key-of|null $calendar + * + * @throws FailedFormatting + */ + function format_datetime( + $date, + ?string $locale = null, + $timezone = null, + ?string $dateFormat = null, + ?string $timeFormat = null, + ?string $pattern = null, + ?string $calendar = null + ): string { + return IntlFormatter::formatDateTime( + $date, + $locale ?? App::currentLocale(), + $timezone, + $dateFormat, + $timeFormat, + $pattern, + $calendar + ); + } +} + +if (! function_exists('format_date')) { + /** + * @param DateTimeInterface|string|int|null $date A date or null to use the current time + * @param DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + * @param key-of|null $dateFormat + * @param key-of|null $calendar + */ + function format_date( + $date, + ?string $locale = null, + $timezone = null, + ?string $dateFormat = null, + ?string $pattern = null, + ?string $calendar = null + ): string { + return IntlFormatter::formatDate( + $date, + $locale ?? App::currentLocale(), + $timezone, + $dateFormat, + $pattern, + $calendar + ); + } +} + +if (! function_exists('format_time')) { + /** + * @param DateTimeInterface|string|int|null $date A date or null to use the current time + * @param DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + * @param key-of|null $timeFormat + * @param key-of|null $calendar + */ + function format_time( + $date, + ?string $locale = null, + $timezone = null, + ?string $timeFormat = null, + ?string $pattern = null, + ?string $calendar = null + ): string { + return IntlFormatter::formatTime( + $date, + $locale ?? App::currentLocale(), + $timezone, + $timeFormat, + $pattern, + $calendar + ); + } +} diff --git a/src/Laravel/test_files/country_name.blade.php b/src/Laravel/test_files/country_name.blade.php new file mode 100644 index 0000000..ec72512 --- /dev/null +++ b/src/Laravel/test_files/country_name.blade.php @@ -0,0 +1,6 @@ +{{ country_name('UNKNOWN') }} +{{ country_name(null) }} +{{ country_name('FR') }} +{{ country_name('US') }} +{{ country_name('US', 'fr') }} +{{ country_name('CH', 'fr_CA') }} diff --git a/src/Laravel/test_files/country_timezones.blade.php b/src/Laravel/test_files/country_timezones.blade.php new file mode 100644 index 0000000..28c3539 --- /dev/null +++ b/src/Laravel/test_files/country_timezones.blade.php @@ -0,0 +1,3 @@ +{{ count(country_timezones('UNKNOWN')) }} +{{ implode(', ', country_timezones('FR')) }} +{{ implode(', ', country_timezones('US')) }} diff --git a/src/Laravel/test_files/currency_name.blade.php b/src/Laravel/test_files/currency_name.blade.php new file mode 100644 index 0000000..3dab767 --- /dev/null +++ b/src/Laravel/test_files/currency_name.blade.php @@ -0,0 +1,6 @@ +{{ currency_name('UNKNOWN') }} +{{ currency_name(null) }} +{{ currency_name('EUR') }} +{{ currency_name('JPY') }} +{{ currency_name('EUR', 'fr') }} +{{ currency_name('JPY', 'fr_FR') }} diff --git a/src/Laravel/test_files/currency_symbol.blade.php b/src/Laravel/test_files/currency_symbol.blade.php new file mode 100644 index 0000000..c4c7369 --- /dev/null +++ b/src/Laravel/test_files/currency_symbol.blade.php @@ -0,0 +1,4 @@ +{{ currency_symbol('UNKNOWN') }} +{{ currency_symbol(null) }} +{{ currency_symbol('EUR') }} +{{ currency_symbol('JPY') }} diff --git a/src/Laravel/test_files/format_currency.blade.php b/src/Laravel/test_files/format_currency.blade.php new file mode 100644 index 0000000..0cf5e64 --- /dev/null +++ b/src/Laravel/test_files/format_currency.blade.php @@ -0,0 +1,6 @@ +{{ format_currency(1000000, 'EUR') }} +{{ format_currency(1000000, 'EUR', 'de', []) }} +{{ format_currency(1000000, 'EUR', null, ['fraction_digit' => 2]) }} +{{ format_currency(12.345, 'EUR', null, ['rounding_mode' => 'floor']) }} +{{ format_currency(125000, 'YEN') }} +{{ format_currency(\Money\Money::EUR(1234), null, null, ['rounding_mode' => 'floor']) }} diff --git a/src/Laravel/test_files/format_date.blade.php b/src/Laravel/test_files/format_date.blade.php new file mode 100644 index 0000000..cc511f9 --- /dev/null +++ b/src/Laravel/test_files/format_date.blade.php @@ -0,0 +1,10 @@ +{{ format_datetime('2019-08-07 23:39:12') }} +{{ format_datetime('2019-08-07 23:39:12', 'fr') }} +{{ format_datetime('2019-08-07 23:39:12', 'fr', null, 'none', 'short') }} +{{ format_datetime('2019-08-07 23:39:12', 'fr', null, 'short', 'none') }} +{{ format_datetime('2019-08-07 23:39:12', 'fr', null, 'full', 'full') }} +{{ format_datetime('2019-08-07 23:39:12', null, null, 'medium', 'medium', "hh 'oclock' a, zzzz") }} + +{{ format_date('2019-08-07 23:39:12') }} +{{ format_date('2019-08-07 23:39:12', 'fr', null, 'medium') }} +{{ format_time('2019-08-07 23:39:12') }} diff --git a/src/Laravel/test_files/format_number.blade.php b/src/Laravel/test_files/format_number.blade.php new file mode 100644 index 0000000..73d3d82 --- /dev/null +++ b/src/Laravel/test_files/format_number.blade.php @@ -0,0 +1,11 @@ +{{ format_number(12.345) }} +{{ format_number(12.345, 'fr', 'default', [], 'decimal') }} +{{ format_number(12.345, null, 'default', [], 'percent') }} +{{ format_number(12.345, null, 'default', [], 'spellout') }} +{{ format_number(12.345, null, 'default', [], 'percent') }} +{{ format_number(12.345, null, 'default', [], 'spellout') }} +{{ format_number(80.345, 'fr_FR', 'default', [], 'spellout') }} +{{ format_number(80.345, 'fr_CH', 'default', [], 'spellout') }} +{{ format_number(12, null, 'default', [], 'duration') }} +{{ format_number(0.12, null, 'default', ['fraction_digit' => 1], 'percent') }} +{{ format_number(0.12345, null, 'default', ['rounding_mode' => 'ceiling'], 'percent') }} diff --git a/src/Laravel/test_files/language_name.blade.php b/src/Laravel/test_files/language_name.blade.php new file mode 100644 index 0000000..0e3e42f --- /dev/null +++ b/src/Laravel/test_files/language_name.blade.php @@ -0,0 +1,7 @@ +{{ language_name('UNKNOWN') }} +{{ language_name(null) }} +{{ language_name('de') }} +{{ language_name('fr') }} +{{ language_name('de', 'fr') }} +{{ language_name('fr', 'fr_FR') }} +{{ language_name('fr_CA', 'fr_FR') }} diff --git a/src/Laravel/test_files/locale_name.blade.php b/src/Laravel/test_files/locale_name.blade.php new file mode 100644 index 0000000..4153702 --- /dev/null +++ b/src/Laravel/test_files/locale_name.blade.php @@ -0,0 +1,7 @@ +{{ locale_name('UNKNOWN') }} +{{ locale_name(null) }} +{{ locale_name('de') }} +{{ locale_name('fr') }} +{{ locale_name('de', 'fr') }} +{{ locale_name('fr', 'fr_FR') }} +{{ locale_name('fr_CA', 'fr_FR') }} diff --git a/src/Laravel/test_files/timezone_name.blade.php b/src/Laravel/test_files/timezone_name.blade.php new file mode 100644 index 0000000..36ecc85 --- /dev/null +++ b/src/Laravel/test_files/timezone_name.blade.php @@ -0,0 +1,5 @@ +{{ timezone_name('UNKNOWN') }} +{{ timezone_name(null) }} +{{ timezone_name('Europe/Paris') }} +{{ timezone_name('America/Los_Angeles') }} +{{ timezone_name('America/Los_Angeles', 'fr') }}