4

How can I get the current number format (decimal separator, thousands seperator) according to the store-front's locale in a subscriber of the ProductListingCriteriaEvent event in Shopware 6 ?

Goal is to parse strings to float which are given in the current locale (1.000,00 in DE and 1,000.00 in EN).

I looked at the event's context, but did not find the locale information.

I did the same logic in Twig before (which seems a bit crazy):

{% set thousands_separator = 1000.1|format_number(locale=app.locale)|replace({'0':'','1':''})|slice(-2,1) %}
{% set decimals_seperator = 1000.1|format_number(locale=app.locale)|replace({'0':'','1':''})|slice(-1,1) %}
{% set floatValue = stringValue | replace({thousands_separator:'', decimals_seperator:'.'}) %}

EDIT:

There is \Shopware\Core\Framework\App\AppLocaleProvider::getLocaleFromContext but it provides only the locale code, not the number format information.

Alex
  • 32,506
  • 16
  • 106
  • 171

2 Answers2

2

You can use the service Shopware\Core\System\Locale\LanguageLocaleCodeProvider to retrieve the locale by the ID of the language. Then just use the \NumberFormatter to parse as float:

$locale = $this->languageLocaleProvider->getLocaleForLanguageId($context->getLanguageId());
$formatter = new \NumberFormatter($locale, \NumberFormatter::DECIMAL);
$float = $formatter->parse('123,456.789');
var_dump($float);
// float(123456.789)

If you need this as a twig filter, you could register your own twig filter:

<service id="MyPlugin\Core\Framework\Adapter\Twig\Filter\ToFloatFilter">
    <argument type="service" id="Shopware\Core\System\Locale\LanguageLocaleCodeProvider"/>
    <tag name="twig.extension"/>
</service>
class ToFloatFilter extends AbstractExtension
{
    private LanguageLocaleCodeProvider $languageLocaleCodeProvider;

    public function __construct(LanguageLocaleCodeProvider $languageLocaleProvider)
    {
        $this->languageLocaleProvider = $languageLocaleProvider;
    }

    public function getFilters()
    {
        return [
            new TwigFilter('to_float', [$this, 'toFloat'], ['needs_context' => true]),
        ];
    }

    public function toFloat($twigContext, $value)
    {
        $locale = $this->languageLocaleProvider->getLocaleForLanguageId($twigContext['context']->getLanguageId());
        $formatter = new \NumberFormatter($locale, \NumberFormatter::DECIMAL);

        if (!$value) {
            return null;
        }

        return $formatter->parse($value);
    }
}
{{ "123,456.789"|to_float }}
{# 123456.789 #}
dneustadt
  • 12,015
  • 1
  • 12
  • 18
0

Disclaimer: Not a nice solution - see other answers!

I almost literally ported the Twig solution, but I am wondering if there is a more elegant solution for this:

private function parseLocalNumber(string $number, Context $context = null): float
{
    $number = str_replace($this->thousandsSeparator, '', $number);
    $number = str_replace($this->decimalSeparator, '.', $number);

    return floatval($number);
}

private function resolveNumberFormat(Context $context)
{
    $locale = $this->appLocaleProvider->getLocaleFromContext($context);
    $formattedNumber = (new IntlExtension())->formatNumber(1000.1, [], 'decimal','default', $locale);
    $punctuationOnly = strtr($formattedNumber, ['0' => '', '1' => '']);
    $this->thousandsSeparator = substr($punctuationOnly, -2, 1);
    $this->decimalSeparator = substr($punctuationOnly, -2, 1);
}
Alex
  • 32,506
  • 16
  • 106
  • 171