pharaonic/laravel-readable
Laravel Readable adds human-friendly formatting helpers to your Laravel app—turn numbers into 1.2K, dates into "3 days ago", bytes into file sizes, and more, with simple Blade-ready functions.
Architecture fit — Strong (isolated presentation concern). pharaonic/laravel-readable is a stateless formatting utility: human-readable numbers, dates, relative times, durations, and file sizes exposed as static methods, global helpers, and Blade directives. It touches no database, queue, cache, or storage layer. That means it slots into the presentation layer only — Blade views, mail templates, Nova/Filament columns, API resource transformers at the outermost boundary. It should be treated as a view concern and explicitly not used in domain logic, money arithmetic, or persisted values, because these helpers are lossy by design (rounding, locale-dependent strings).
The package is a thin façade over Carbon (diffForHumans, localization) and PHP's intl/NumberFormatter for number and size formatting. Functionally, most teams already have this capability; the package's value is consistency and reduced boilerplate, not new capability. Integration surface is small: one service provider with auto-discovery, an optional publishable config, and a set of global helpers/directives.
Integration feasibility — Easy, under a day. composer require + auto-discovery is the whole install. Two things need verification before we commit:
ext-intl dependency — number/size formatting almost certainly requires intl. If it's a hard requirement rather than a soft fallback, our Docker/CI images and any shared-hosting-adjacent environments must add it.Technical risk — Low blast radius, elevated provenance risk. The code is read-only formatting; a defect produces wrong-looking strings, not corrupted data or downtime. The real risks are supply chain and abandonment:
NumberFormatter, so a vendored internal App\Support\Format class is a realistic exit.Key questions for the maintainer / due diligence
ext-intl a hard composer.json requirement? What is the fallback behavior when it's absent — exception, English-only, or silent garbage?app()->getLocale(), Carbon::getLocale(), a per-call argument, or a config default? Do the numbers and dates share a locale source?Stack fit. Compatible with any modern Laravel application (PHP 8.x, Laravel 9–11 expected, to be confirmed). No conflict with Livewire, Inertia, or API-first architectures — but note that in an Inertia/SPA or mobile-client architecture, server-side formatting is the wrong layer: ship ISO-8601 timestamps and raw byte counts and format client-side with Intl.NumberFormat/Intl.RelativeTimeFormat. This package is only a fit for server-rendered Blade and email.
Migration path. Nothing to migrate from unless we already have ad-hoc formatting scattered across views — in which case the migration is the point: inventory existing number_format(), Carbon::diffForHumans(), and hand-rolled byte formatters, and consolidate them behind this package (or behind our own wrapper around it).
Recommended adoption pattern — wrap, don't sprinkle.
composer require pharaonic/laravel-readable pinned to an exact version (not a caret range), with composer.lock committed.App\Support\Format — that delegates to the package. All views call our adapter. This gives us a one-file swap to a native Carbon/NumberFormatter implementation if the package is abandoned, and a single place to enforce escaping and locale policy.Compatibility checks before merge.
ext-intl in composer.json and add it to the Dockerfile and CI matrix.APP_LOCALE values we actually ship (e.g., en, de, fr, ar) — pluralization and relative-time phrasing are the usual failure points.php artisan view:cache and don't break under Octane's long-lived workers (static state is the thing to check).Sequencing.
intl delegation. If it fails, vendor or reimplement (est. 1 day).App\Support\Format wrapper with unit tests over the golden fixtures.Maintenance — Near zero, but ownership is the question. Runtime maintenance is trivial: no migrations, no cron, no config drift, no data. The maintenance burden is organizational — tracking an unverified-source, single-maintainer dependency in our SBOM and re-reviewing it at each Laravel major upgrade. Budget ~2 hours per Laravel major for compatibility validation.
Support — Weak upstream, strong internal fallback. With 23 stars and an unverifiable repository, there is effectively no community support channel and no expectation of a timely CVE response. Our mitigation is structural, not contractual: the adapter boundary plus a documented 1-day reimplementation path means our support story doesn't depend on the maintainer. Do not adopt without that boundary in place.
Scaling — Non-issue. Pure CPU string formatting, microsecond-scale, no I/O, no shared state, no allocation pressure beyond the return strings. Safe under Octane and in high-fan-out Blade rendering. Only caveat: avoid calling relative-time helpers in tight loops over large collections where a single "now" reference would do — otherwise the clock can advance mid-render and produce inconsistent output within one page.
Failure modes and mitigations
| Failure | Impact | Detection | Mitigation |
|---|---|---|---|
ext-intl missing in an environment |
Fatal error or silent English-only output | CI matrix + container smoke test | Pin extension in image; assert on startup |
| Package abandoned / Laravel major incompatibility | Blocks framework upgrade | Dependency upgrade dry-run | Adapter lets us swap in ~1 day; vendoring removes the risk |
| Unverified source / compromised artifact | Supply-chain | Manual provenance review | Pin exact version, commit lockfile, vendor if unresolved, monitor SBOM |
| Locale mismatch (dates localized, numbers not) | Inconsistent UI, i18n bugs | Golden-file tests across locales | Single locale source enforced inside the adapter |
| Timezone drift in relative times | "in 2 hours" that's actually 3 | Tests with fixed clock | Resolve against config('app.timezone'); inject a test clock |
| File-size boundary rounding (1000 KB vs 1 MB) | Cosmetic inconsistency across surfaces | Fixture tests at boundaries | Lock base and precision in published config |
| Blade directive output unescaped | XSS if fed user input | Code review | Adapter returns escaped strings; forbid directives on user-supplied data |
| Global helper collision | Autoload/override surprises | Static analysis in CI | Prefer static methods or adapter over helpers |
Ramp-up — Under a day for any Laravel developer. The API is self-describing and mirrors concepts engineers already know from Carbon and NumberFormatter. The only real onboarding item is the team convention: always call App\Support\Format, never the package directly, never in domain code.
Recommendation. Conditional go. Technically this is a low-risk, low-effort, low-ceiling convenience package that fits cleanly into the Blade layer. But its ~19/100 composite score, 23 stars, and unverified repository mean the decision hinges on a half-day provenance review, not on engineering merit. If upstream can be verified, adopt behind an adapter and pin the version. If it can't, reimplement the handful of formatters natively over Carbon and NumberFormatter — the cost is roughly one engineer-day, and it eliminates the dependency risk entirely while preserving 100% of the value.
How can I help you explore Laravel packages today?