Product Decisions This Supports
- Feature: legally-required spelled-out amounts. Any product that emits invoices, receipts, quotes, payroll slips, cheques, contracts, or notarized documents in markets where the numeric amount must be duplicated in words. This is a compliance-shaped checkbox, not a delight feature — it blocks market entry until it exists.
- Roadmap: localization expansion. The library ships number transformers for ~40 locales and currency transformers for a large subset (English, Spanish, French, German, Portuguese BR/PT, Russian, Polish, Ukrainian, Turkish, Arabic, Georgian, Swahili, Yoruba, and more). That directly informs the question "can we launch in market X next quarter?" — the answer becomes a one-day integration instead of a multi-week language-rules project.
- Build vs. buy: strongly favors buy. The hard part of number-to-words is not the algorithm, it's the linguistic exceptions — gender agreement, plural inflection of millions/billions, "ein Euro" vs "eins Euro", Danish "og" connectors, Romanian ROL→RON. The 3.0.0 changelog is essentially a catalogue of the bugs you would have shipped yourself. Writing this in-house for one language is a day; for five languages it is a quarter, and it never stays fixed.
- Use cases beyond documents: accessibility (screen-reader-friendly text alternatives for figures), voice/IVR and telephony flows that read amounts aloud, e-commerce checkout reassurance ("fifty dollars ninety nine cents"), CRM/email templates with humanized deal values, and printed point-of-sale receipts.
- Architecture decision: thin domain wrapper, not a direct dependency. Wrap it behind an internal
AmountInWords service with your own tests and a locale allowlist. This keeps the "buy" reversible and gives you one place to swap implementations if a locale's output is wrong or the maintainer stalls.
- Version pinning is a product-visible risk decision. v3.x requires PHP 8.2+ (Laravel 11/12 territory). If you are still on Laravel 9/10 on PHP 8.0/8.1, you are adopting the 2.x line, which will not receive the 3.0 currency-coverage improvements. That trade-off belongs in the same conversation as your framework upgrade.
When to Consider This Package
Adopt when most of these are true:
- You need spelled-out amounts in two or more languages, or in a language whose rules you do not want to own (Slavic plural inflection, Arabic, Hungarian).
- Output is user-facing and legal/financial — errors are expensive and embarrassing, so a battle-tested implementation with ~120 locale regression tests beats your first draft.
- Your runtime is PHP 8.2+ (v3) or 8.0/8.1 (v2), and you are comfortable pinning a Composer version.
- You need currency phrasing (
toWords(5099, 'USD') → "fifty dollars ninety nine cents"), not just cardinals.
- Your integration surface is small — one or two call sites behind a service class.
Look elsewhere or build in-house when:
- You only need English.
NumberFormatter::SPELLOUT in ext-intl is already installed in many stacks; adding a dependency for one locale is hard to justify. (Caveat: intl spellout is inconsistent across locales, which is exactly why this package exists — re-evaluate as soon as you add language two.)
- You need a specific document format, e.g. cheque style "One hundred and 00/100 dollars", all-caps legal register, or fixed-width fields. This library gives you words, not templates — you will still write the formatter.
- You need decimals end-to-end. Currency transformers take integers only by design; you must multiply by 100 upstream and own the rounding policy. If your pipeline is float-heavy, that's a real correctness burden to plan for.
- Your target locale isn't in the table (or is listed with number support but a
- for currency). Community-contributed locales also vary in quality — treat them as "needs native-speaker review," not "done."
- You need vendor guarantees. Single-maintainer project, 427 stars, low visible downstream adoption, community PRs for most locales. There is no SLA, no security policy in the README, and locale bugs are fixed when a native speaker shows up. That's fine for a UI nicety; it's a governance question for a regulated document path.
- You need non-PHP output (client-side preview, PDF microservice in another language). This is a PHP library; your rendering boundary matters more than the library.
How to Pitch It (Stakeholders)
To executives (30 seconds): "We need to print amounts twice on financial documents — once as digits, once as words — in every market we sell into. Buying a mature, MIT-licensed PHP library that already covers ~40 languages turns a per-market localization blocker into a one-day integration. It's free, it has no framework lock-in, and it removes a multi-quarter in-house language-rules project from the roadmap. The risk is concentrated, not eliminated: one maintainer, and locale accuracy varies, so we validate the languages we actually ship in and wrap it so it's swappable."
To engineering (the build): "Standalone Composer package, MIT, no Laravel coupling — two classes in, NumberToWords::transformNumber('de', 5120) or a currency transformer out. Note the constraints: currency input must be integer minor units (multiply by 100 yourself and decide the rounding policy); v3 needs PHP 8.2, so on older Laravel we pin 2.x and lose the 3.0 currency-coverage work. Put it behind a single AmountInWords service with a locale allowlist and golden-file tests per locale, and pin the version in composer.json — do not call the static methods from controllers."
To finance/legal: "The library produces the words; it does not produce the document. Output for our shipping locales must be reviewed by a native speaker once, then frozen with tests, because these strings appear on invoices and cannot drift silently on a dependency update."
Recommendation: Adopt for multi-locale document and UI use, behind a wrapper, with a pinned version and a per-locale acceptance test. Contribute locale fixes upstream rather than forking — the MIT license makes the fork a fallback, not a strategy.