Loading ...

What's your function in life?

Ours is writing random blog articles about web development, TYPO3 and more.

Format numbers and currency locale aware

With TYPO3 v9 the locale settings can be done directly in site configuration. These may be used to determine the correct values for thousand separators and decimal points. However, the fluid default viewhelpers expect the thousand separator and decimal points as argument instead of using the set locale, which is cumbersome in multi-language environments. Additionally, the LC_NUMERIC locale is not set by TYPO3 to avoid problems when calculating with floats. The following two view helpers can be used for locale aware numbers and currency formatting.

NumberViewHelper:

 

<?php

namespace Your\NameSpace\ViewHelpers;
/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;

/**
 * Formats a number with custom precision, based on the current locale
 *
 * @see www.php.net/manual/en/function.number-format.php
 *
 * = Examples =
 *
 * <code title="Defaults">
 * <base:format.number>423423.234</f:format.number>
 * </code>
 * <output>
 * 423,423.20
 * </output>
 *
 * <code title="With all parameters">
 * <base:format.number decimals="1">423423.234</f:format.number>
 * </code>
 * <output>
 * 423.423,2
 * </output>
 */
class NumberViewHelper extends AbstractViewHelper
{
    use CompileWithRenderStatic;

    /**
     * Output is escaped already. We must not escape children, to avoid double encoding.
     *
     * @var bool
     */
    protected $escapeChildren = false;


    /**
     * Initialize arguments.
     *
     * @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
     */
    public function initializeArguments()
    {
        $this->registerArgument('decimals', 'int', 'The number of digits after the decimal point', false, '2');
    }

    /**
     * Format the numeric value as a number with grouped thousands, decimal point and
     * precision.
     *
     * @param array $arguments
     * @param \Closure $renderChildrenClosure
     * @param RenderingContextInterface $renderingContext
     *
     * @return string The formatted number
     */
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
    {
        // numeric locale is not set by the core because of problems with floats, set it here manually for number format:
        $currentLocale = setlocale(LC_MONETARY, 0);
        $oldLocale = setlocale(LC_NUMERIC, 0);
        setlocale(LC_NUMERIC, $currentLocale);
        $localeInfo = localeconv();
        setlocale(LC_NUMERIC, $oldLocale);
        $decimals = $arguments['decimals'];
        $stringToFormat = $renderChildrenClosure();
        return number_format($stringToFormat, $decimals, $localeInfo['decimal_point'], $localeInfo['thousands_sep']);
    }
}

 

 

CurrencyViewhelper:

 

<?php

namespace Your\NameSpace\ViewHelpers\Format;

/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;

/**
 * Formats a given float to a currency representation.
 *
 * = Examples =
 *
 * <code title="Defaults">
 * <f:format.currency>123.456</f:format.currency>
 * </code>
 * <output>
 * 123,46
 * </output>
 *
 * <code title="All parameters">
 * <f:format.currency currencySign="$" prependCurrency="true" separateCurrency="false" decimals="2">54321</f:format.currency>
 * </code>
 * <output>
 * $54,321.00
 * </output>
 *
 * <code title="Inline notation">
 * {someNumber -> f:format.currency(currencySign: '€')}
 * </code>
 * <output>
 * 54,321,00 €
 * (depending on the value of {someNumber})
 * </output>
 *
 * <code title="use dash for decimals without value">
 * <f:format.currency useDash="true">123.00</f:format.currency>
 * </code>
 * <output>
 * 123,-
 * </output>
 */
class CurrencyViewHelper extends AbstractViewHelper
{
    use CompileWithRenderStatic;

    /**
     * Output is escaped already. We must not escape children, to avoid double encoding.
     *
     * @var bool
     */
    protected $escapeChildren = false;

    /**
     * Initialize arguments.
     *
     * @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
     */
    public function initializeArguments()
    {
        $this->registerArgument('currencySign', 'string', 'The currency sign, eg $ or €.', false, '');
        $this->registerArgument('prependCurrency', 'bool', 'Select if the currency sign should be prepended', false, false);
        $this->registerArgument('separateCurrency', 'bool', 'Separate the currency sign from the number by a single space, defaults to true due to backwards compatibility', false, true);
        $this->registerArgument('decimals', 'int', 'Set decimals places.', false, 2);
        $this->registerArgument('useDash', 'bool', 'Use the dash instead of decimal 00', false, false);
    }

    /**
     * Formats a float to currency formatting
     *
     * @param array $arguments
     * @param \Closure $renderChildrenClosure
     * @param RenderingContextInterface $renderingContext
     *
     * @return string the formatted amount
     */
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
    {
        $localeInfo = localeconv();
        $currencySign = $arguments['currencySign'];
        $decimalSeparator = $localeInfo['mon_decimal_point'];
        $thousandsSeparator = $localeInfo['mon_thousands_sep'];
        $prependCurrency = $arguments['prependCurrency'];
        $separateCurrency = $arguments['separateCurrency'];
        $decimals = $arguments['decimals'];
        $useDash = $arguments['useDash'];

        $floatToFormat = $renderChildrenClosure();
        if (empty($floatToFormat)) {
            $floatToFormat = 0.0;
        } else {
            $floatToFormat = (float)$floatToFormat;
        }
        $output = number_format($floatToFormat, $decimals, $decimalSeparator, $thousandsSeparator);

        if ($useDash && $floatToFormat === floor($floatToFormat)) {
            $output = explode($decimalSeparator, $output)[0] . $decimalSeparator . '—';
        }

        if ($currencySign !== '') {
            $currencySeparator = $separateCurrency ? ' ' : '';
            if ($prependCurrency === true) {
                $output = $currencySign . $currencySeparator . $output;
            } else {
                $output = $output . $currencySeparator . $currencySign;
            }
        }
        return $output;
    }
}

 

 

Comments

No comments

Write comment

* These fields are required