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
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); // هزار و پانصد
Publish the config to set your default locale:
php artisan vendor:publish --tag=numera-config
// config/numera.php
return [
'default_locale' => 'en',
'fallback_locale' => 'en',
];
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)
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
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'
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); // یک و نیم
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); // هزار و چهارصد و دو
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
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)
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
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'); // دویست و یک
Real-world use cases
Invoice systems — convert totals to words for legal documents:
echo Numera::init('fa')->toCurrency(15750000, 'IRR');
// پانزده میلیون و هفتصد و پنجاه هزار ریال
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
Financial reports — ordinals for ranking, fractions for ratios:
echo Numera::init('en')->n2o(1); // first
echo Numera::init('en')->toFraction(0.5); // one half
Forms and CRMs — read user-entered phone numbers back:
echo Numera::init('en')->toPhone('+44 20 7946 0958');
Links
- GitHub: github.com/pinoox/numera
- Packagist: packagist.org/packages/pinoox/numera
- Laravel bridge: packagist.org/packages/pinoox/numera-laravel
- Translation Guide: TranslationGuide.md
- CHANGELOG: CHANGELOG.md
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)