Number::spellOrdinal
Spells a number's ordinal form as words, turning 3 into third and 21 into twenty-first.
Number::spellOrdinal(int|float $number, ?string $locale = null): string- Added in Laravel 11.34.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$$number | int|float | The value to spell as an ordinal. |
$$localeoptional | ?string= null | ICU locale supplying the spell-out ruleset. Falls back to the Number class locale. |
Return value
stringThe ordinal written out in words, for example 'twenty-first'.
Errors
Throws RuntimeException when the intl PHP extension is not loaded.
Changelog
- PHP 11.34.0Added to the Number class.
Number::spellOrdinal() is the word form of Number::ordinal: 1 becomes first, 2 becomes second, 21 becomes twenty-first. Internally it creates a spell-out formatter and switches its default ruleset to %spellout-ordinal.
The ruleset is not universal
That ruleset name is the catch. Not every ICU locale defines %spellout-ordinal, and several that do define gendered variants such as %spellout-ordinal-masculine instead. When the ruleset is missing, NumberFormatter does not throw — it falls back, and you may get the cardinal spelling or an empty result depending on the ICU version bundled with your PHP. Test the specific locales you support rather than assuming parity with English.
No thresholds here
Unlike Number::spell, there are no after and until parameters. If your style guide says "first through ninth in words, 10th onward in digits", you have to branch yourself and call Number::ordinal() for the rest.
Fractions make no sense as ordinals; pass integers. And like every locale-aware method on this class, it needs the intl extension.
Example
When to use it
- Writing 'your third failed login attempt' in a security email
- Producing 'the twenty-first of the month' in a contract clause
- Labelling tournament placings in prose rather than as 1st, 2nd, 3rd
Frequently asked questions
What is the difference between ordinal and spellOrdinal?
ordinal() keeps the numeral and appends an indicator ('21st'); spellOrdinal() writes the whole thing as words ('twenty-first'). Use the first in tables and the second in running prose.
Which Laravel version added Number::spellOrdinal?
Laravel 11.34.0. On earlier versions you have to build a NumberFormatter yourself and set the DEFAULT_RULESET text attribute to %spellout-ordinal.
Can I spell ordinals only below a threshold like Number::spell does?
Not through this method — it takes no after or until parameters. Branch on the value yourself and fall back to Number::ordinal() above your cut-off.
More examples
Words below ten, digits above
Prints first, third, ninth, then 10th and 25th. spellOrdinal() has no after or until parameters, so the threshold has to be a plain comparison in your own code — this is the shape that mirrors what Number::spell(until: 10) does for cardinals.
<?php
use Illuminate\Support\Number;
function placing(int $n): string
{
return $n < 10 ? Number::spellOrdinal($n) : Number::ordinal($n);
}
foreach ([1, 3, 9, 10, 25] as $n) {
echo placing($n), PHP_EOL;
}A notification sentence
Reads 'This is the third failed sign-in attempt on your account today.' The spelled form sits inside a sentence far better than '3rd', which reads as machine output in email copy.
<?php
use Illuminate\Support\Number;
class FailedLoginNotification
{
public function __construct(private int $attempt) {}
public function toMail(): string
{
return sprintf(
'This is the %s failed sign-in attempt on your account today.',
Number::spellOrdinal($this->attempt)
);
}
}
echo (new FailedLoginNotification(3))->toMail(), PHP_EOL;Checking locale support before relying on it
Comparing the two columns is the quickest way to spot a locale where %spellout-ordinal is missing: if the ordinal column matches the cardinal one, ICU fell back rather than applying an ordinal ruleset, and you should not ship that string.
<?php
use Illuminate\Support\Number;
foreach (['en', 'fr', 'de', 'es'] as $locale) {
printf(
"%-3s cardinal: %-20s ordinal: %s\n",
$locale,
Number::spell(3, locale: $locale),
Number::spellOrdinal(3, locale: $locale)
);
}
Discussion