DEV Community

yoosef alipour
yoosef alipour

Posted on

Numera 2.1: Laravel Support, 184 Languages, and a Whole Lot More

A follow-up to Numera: A PHP Library for Number to Words Conversion


When I published the first article about Numera back in 2024, it was a simple library with one goal: convert numbers to words in PHP. The response was encouraging, and the community feedback (including the great note about en vs en-US) pushed me to think bigger.

Today, Numera 2.1 is out — and it's a different beast. Same simple API, but now with 184 language packs, a Laravel bridge, GitHub Actions CI, and a set of new reading modes that cover real-world use cases I kept running into.

Let me walk you through what's new.


Install

# Plain PHP
composer require pinoox/numera:^2.1

# Laravel 9 / 10 / 11
composer require pinoox/numera-laravel
Enter fullscreen mode Exit fullscreen mode

What's new in 2.1

1. Laravel integration — first-class

The most-requested feature. Install pinoox/numera-laravel and you get auto-discovery, a Facade, and a publishable config — nothing to wire up manually.

use Pinoox\Numera\Laravel\Facades\Numera;

echo Numera::n2w(2024);               // two thousand twenty-four
echo Numera::toCurrency(1500, 'USD'); // one thousand, five hundred dollars

Numera::setLocale('fa');
echo Numera::n2w(1500); // هزار و پانصد
Enter fullscreen mode Exit fullscreen mode

Publish the config to set your default locale:

php artisan vendor:publish --tag=numera-config
Enter fullscreen mode Exit fullscreen mode
// config/numera.php
return [
    'default_locale'  => 'en',
    'fallback_locale' => 'en',
];
Enter fullscreen mode Exit fullscreen mode

All public methods work on the Facade — n2w, w2n, toOrdinal, toCurrency, withUnit, toWeekday, translateTo, and all the new ones below.


2. 184 language packs with regional variants

Numera now ships with 184 ISO 639-1 language packs plus regional variants like en-US, en-GB, fa-IR, and de-AT. Regional files only declare overrides; everything else is inherited from the parent locale.

Numera::init('en-GB')->toCurrency(3.01, 'GBP'); // three pounds and one penny
Numera::init('fa-IR')->getWeekdays();            // starts Saturday
Numera::init('de-AT')->getStrategyName();        // german (inherited from de)
Enter fullscreen mode Exit fullscreen mode

Languages with special compounding rules use a strategy system:

Strategy Locales Why
german de, de-* Inverted tens: einundzwanzig
french fr, fr-* Vigesimal 70–99: quatre-vingts
multiplier_tens id, ms, vi, … 2×10+3, not 2+10+3
default Most others English-style place value

3. Smart input formats

Different regions format numbers differently. Numera now handles them all automatically.

$n = Numera::init('en');

// European format (dot as thousands, comma as decimal)
echo $n->n2w('1.234.567,89');

// Swiss format (apostrophe as thousands)
echo $n->n2w("1'234'567.89");

// Persian/Arabic digits
echo $n->n2w('۱۲۳۴'); // converted to 1234 automatically

// Underscore separator (PHP/Python style)
echo $n->n2w('1_000_000'); // one million
Enter fullscreen mode Exit fullscreen mode

You can also detect the format explicitly:

Numera::detectFormat('1.234,56');   // 'european'
Numera::detectFormat("1'000");      // 'swiss'
Numera::detectFormat('۱۲۳');       // 'persian'
Numera::detectFormat('1_000_000'); // 'underscore'
Enter fullscreen mode Exit fullscreen mode

4. Fraction reading

Common fractions are now read naturally instead of as raw decimals.

$n = Numera::init('en');

echo $n->toFraction(0.5);   // one half
echo $n->toFraction(0.25);  // one quarter
echo $n->toFraction(0.75);  // three quarters
echo $n->toFraction(1.5);   // one and a half
echo $n->toFraction(2.333); // two and one third

// Persian
echo Numera::init('fa')->toFraction(0.5); // نیم
echo Numera::init('fa')->toFraction(1.5); // یک و نیم
Enter fullscreen mode Exit fullscreen mode

Short alias: n2f()


5. Year reading

Years are spoken differently from cardinals — "nineteen ninety-nine", not "one thousand nine hundred ninety-nine".

$n = Numera::init('en');

echo $n->toYear(1999); // nineteen ninety-nine
echo $n->toYear(2000); // two thousand
echo $n->toYear(2005); // two thousand and five
echo $n->toYear(2024); // twenty twenty-four

// Persian falls back to standard cardinal
echo Numera::init('fa')->toYear(1402); // هزار و چهارصد و دو
Enter fullscreen mode Exit fullscreen mode

Short alias: n2y()


6. Phone number reading

Read phone numbers digit-by-digit, preserving the grouping from the input format.

$n = Numera::init('en');

echo $n->toPhone('+1 415 555 0172');
// plus one, four one five, five five five, zero one seven two

echo $n->toPhone('021-8834-1100');
// zero two one, eight eight three four, one one zero zero

// Persian digits work too
echo $n->toPhone('۰۲۱-۸۸۳۴');
// zero two one, eight eight three four
Enter fullscreen mode Exit fullscreen mode

Short alias: n2p()


7. Roman numerals

$n = Numera::init('en');

echo $n->toRoman(2024);   // MMXXIV
echo $n->toRoman(1999);   // MCMXCIX
echo $n->fromRoman('XIV'); // 14
echo $n->fromRoman('xl');  // 40 (case-insensitive)
Enter fullscreen mode Exit fullscreen mode

Range: 1–3999. Throws InvalidArgumentException outside that range.

Short alias: n2r()


8. IP address and version string reading

$n = Numera::init('en');

echo $n->toIp('192.168.1.1');
// one ninety-two dot one sixty-eight dot one dot one

echo $n->toVersion('2.14.0');
// two point fourteen point zero

echo $n->toVersion('1.0.0-beta');
// one point zero point zero dash beta
Enter fullscreen mode Exit fullscreen mode

Short aliases: n2ip(), n2v()


9. GitHub Actions CI

Numera now runs a full test matrix on every push and PR — PHP 8.0, 8.1, 8.2, and 8.3. No more "works on my machine" surprises.


What still carries over from 2.0

Everything from the previous release still works exactly the same:

// Cardinals
Numera::init('en')->n2w(4454545156);
// four billion, four hundred fifty-four million, ...

// Ordinals
Numera::init('en')->n2o(21); // twenty-first
Numera::init('fa')->n2o(21); // بیست و یکم

// Currency
Numera::init('en')->toCurrency(1250.50, 'USD');
// one thousand, two hundred fifty dollars and fifty cents

// Units
Numera::init('en')->withUnit(5, 'hour'); // five hours
Numera::init('fa')->withUnit(1, 'day');  // یک روز

// Weekdays
Numera::init('fa')->toWeekday(6); // شنبه

// Cross-locale translation
Numera::init('fa')->translateTo('en', 'دویست و یک'); // two hundred one
Numera::init('en')->translateTo('fa', 'two hundred one'); // دویست و یک
Enter fullscreen mode Exit fullscreen mode

Real-world use cases

Invoice systems — convert totals to words for legal documents:

echo Numera::init('fa')->toCurrency(15750000, 'IRR');
// پانزده میلیون و هفتصد و پنجاه هزار ریال
Enter fullscreen mode Exit fullscreen mode

Accessibility — read version numbers and IPs aloud in screen-reader friendly text:

echo Numera::init('en')->toVersion('3.2.1'); // three point two point one
Enter fullscreen mode Exit fullscreen mode

Financial reports — ordinals for ranking, fractions for ratios:

echo Numera::init('en')->n2o(1);         // first
echo Numera::init('en')->toFraction(0.5); // one half
Enter fullscreen mode Exit fullscreen mode

Forms and CRMs — read user-entered phone numbers back:

echo Numera::init('en')->toPhone('+44 20 7946 0958');
Enter fullscreen mode Exit fullscreen mode

Links

Contributions are welcome — especially language packs. If you speak a language that isn't fully covered yet, the Translation Guide walks you through adding one in minutes.


Tags: #php #laravel #composer #opensource

Top comments (0)