Skip to content

Commit

Permalink
Add localization support for the Number::forHumans helper
Browse files Browse the repository at this point in the history
This has been requested by a large number of people after laravel#48845. My attempt at implementing localization support for this method is using translation helpers.

```php
// Example language setup

'sv' => [
    'thousand' => 'tusen',
    'million' => 'miljon',
    'billion' => 'miljard',
    'trillion' => 'biljon',
    'quadrillion' => 'biljard',
],
```
  • Loading branch information
caendesilva committed Nov 23, 2023
1 parent 99d21f3 commit c3b7ba1
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 9 deletions.
18 changes: 9 additions & 9 deletions src/Illuminate/Support/Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,30 +141,30 @@ public static function fileSize(int|float $bytes, int $precision = 0, ?int $maxP
* @param int|null $maxPrecision
* @return string
*/
public static function forHumans(int|float $number, int $precision = 0, ?int $maxPrecision = null)
public static function forHumans(int|float $number, int $precision = 0, ?int $maxPrecision = null, ?string $locale = null)
{
$units = [
3 => 'thousand',
6 => 'million',
9 => 'billion',
12 => 'trillion',
15 => 'quadrillion',
3 => __('thousand', locale: $locale ?? static::$locale),
6 => __('million', locale: $locale ?? static::$locale),
9 => __('billion', locale: $locale ?? static::$locale),
12 => __('trillion', locale: $locale ?? static::$locale),
15 => __('quadrillion', locale: $locale ?? static::$locale),
];

switch (true) {
case $number === 0:
return '0';
case $number < 0:
return sprintf('-%s', static::forHumans(abs($number), $precision, $maxPrecision));
return sprintf('-%s', static::forHumans(abs($number), $precision, $maxPrecision, $locale));
case $number >= 1e15:
return sprintf('%s quadrillion', static::forHumans($number / 1e15, $precision, $maxPrecision));
return sprintf('%s %s', static::forHumans($number / 1e15, $precision, $maxPrecision, $locale), __('quadrillion', locale: $locale ?? static::$locale));
}

$numberExponent = floor(log10($number));
$displayExponent = $numberExponent - ($numberExponent % 3);
$number /= pow(10, $displayExponent);

return trim(sprintf('%s %s', static::format($number, $precision, $maxPrecision), $units[$displayExponent] ?? ''));
return trim(sprintf('%s %s', static::format($number, $precision, $maxPrecision, $locale), $units[$displayExponent] ?? ''));
}

/**
Expand Down
44 changes: 44 additions & 0 deletions tests/Support/SupportNumberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Illuminate\Tests\Support;

use Illuminate\Support\Number;
use Illuminate\Translation\ArrayLoader;
use Illuminate\Translation\Translator;
use PHPUnit\Framework\TestCase;

class SupportNumberTest extends TestCase
Expand Down Expand Up @@ -157,6 +159,8 @@ public function testBytesToHuman()

public function testToHuman()
{
$this->mockTranslator();

$this->assertSame('1', Number::forHumans(1));
$this->assertSame('1.00', Number::forHumans(1, precision: 2));
$this->assertSame('10', Number::forHumans(10));
Expand Down Expand Up @@ -208,6 +212,46 @@ public function testToHuman()
$this->assertSame('-1 thousand quadrillion', Number::forHumans(-1000000000000000000));
}

public function testToHumansWithLocalization()
{
$this->mockTranslator();

app('translator')->setLoaded([
'*' => [
'*' => [
'sv' => [
'thousand' => 'tusen',
'million' => 'miljon',
'billion' => 'miljard',
'trillion' => 'biljon',
'quadrillion' => 'biljard',
],
],
],
]);

$this->assertSame('1 tusen', Number::forHumans(1000, locale: 'sv'));
$this->assertSame('10 tusen', Number::forHumans(10000, locale: 'sv'));
$this->assertSame('1 miljon', Number::forHumans(1000000, locale: 'sv'));
$this->assertSame('1 miljard', Number::forHumans(1000000000, locale: 'sv'));
$this->assertSame('1 biljon', Number::forHumans(1000000000000, locale: 'sv'));
$this->assertSame('1 biljard', Number::forHumans(1000000000000000, locale: 'sv'));
$this->assertSame('1 tusen biljard', Number::forHumans(1000000000000000000, locale: 'sv'));

$this->assertSame('-1 tusen', Number::forHumans(-1000, locale: 'sv'));
$this->assertSame('-1 miljon', Number::forHumans(-1000000, locale: 'sv'));
$this->assertSame('-1 miljard', Number::forHumans(-1000000000, locale: 'sv'));

$this->assertSame('1,23 tusen', Number::forHumans(1234, locale: 'sv', precision: 2));
$this->assertSame('1,2 tusen', Number::forHumans(1234, locale: 'sv', maxPrecision: 1));
$this->assertSame('1,23 miljon', Number::forHumans(1234567, locale: 'sv', precision: 2));
}

protected function mockTranslator(): void
{
app()->singleton('translator', fn () => new Translator(new ArrayLoader(), 'en'));
}

protected function needsIntlExtension()
{
if (! extension_loaded('intl')) {
Expand Down

0 comments on commit c3b7ba1

Please sign in to comment.