laravel-lang/native-locale-names
Native locale names for Laravel: a lightweight package providing localized language names in their own scripts, ready to use for language switchers and locale lists.
Architecture fit. This is a leaf data provider, not a framework extension. It ships a static class (LaravelLang\NativeLocaleNames\LocaleNames) backed by generated PHP arrays mapping locale codes to endonyms — de → Deutsch, ja → 日本語, pt-BR → Português (Brasil). There is no service provider, no migrations, no HTTP client, no cache layer, no middleware. From a product standpoint that means: zero coupling to domain logic, zero schema change, zero runtime dependency on an external service. It lives entirely in the presentation layer — Blade, Livewire, Inertia shared props, JSON API resources — which is exactly the consistency win we are buying. The architectural question is not "does it fit" but "what do we wrap it in," because the public surface is deliberately minimal and the data underneath is machine-generated.
Integration feasibility. One Composer require, PHP 8.1+, Laravel 11–13. The only meaningful transitive dependency is laravel-lang/locale-list, which is the authoritative source for which locales exist — we do not control that list. Effort is measured in hours, not sprints. The historical trap: 2.x was a breaking reset. The namespace was renamed (Native → LocaleNames in 2.0.0), the locale source moved to locale-list, and sorting was disabled by default in 2.1.0. Any snippet dated before 2.0 is wrong. That is a documentation-hygiene risk for the team, not an engineering risk.
Technical risk. Blast radius is small; adoption signals are weak. Seven stars, zero dependents, one maintainer. The counterweight is real: it is published under the Laravel-Lang organization, whose locale packages are widely used across the Laravel ecosystem, so maintainer credibility runs higher than the star count suggests. The risks a TPM should carry on the register:
locale-list. If the product commits to a locale the list does not carry, we own the fallback.intl. Locale::getDisplayName() yields endonyms with no dependency, but couples output to the server's ICU version. This package is deterministic and ICU-independent — a genuine advantage we should document, or a future maintainer will "simplify" it away and silently regress labels.Key questions to close before commit.
pt-BR vs pt-PT, en-GB vs en-US)?null, exception, or raw code? Verify empirically and standardize inside our wrapper, never at call sites.intl present on every runtime we deploy to?Stack fit. Composer, PHP 8.1+, Laravel 11–13; require >=2.8.0 if Laravel 13 is on the roadmap. The package behaves identically across Blade, Livewire, Inertia, and API resources, which is the consistency argument for standardizing on one source rather than per-surface label maps.
Recommended wrapper — non-negotiable. Do not call the static entry point from views. Introduce a ~50-line internal abstraction:
config/locales.php — the product-owned allow-list of supported codes, versioned in git, reviewed like any other product decision.App\Support\LocaleNameRepository — merges allow-list with package labels, applies fallback contract, applies sort order, returns [code => label].This buys three things a TPM cares about: swap-out to intl or a fork touches one file; fallback behavior is enforced in one place; collation and ordering are ours to control rather than inherited.
Migration path. Replace hardcoded label arrays and any existing config('app.locales') name maps with the repository. Expect visible diffs on day one — region variants and script-native names will differ from whatever English-centric labels we currently ship. Treat that diff as a UX review artifact, not a regression. Budget a review pass with design/localization stakeholders before release; this is the step most likely to be skipped and most likely to generate user-visible surprise.
Compatibility. Pin with a caret inside major v2, and add a snapshot test asserting the rendered label set for every supported locale. That converts an automated data release from a production surprise into a failing CI check. The most likely install failure is a laravel-lang/locale-list major-version conflict if another Laravel-Lang package is already present — verify the resolved constraint in a throwaway branch before committing to a schedule.
Sequencing.
config/locales.php and the repository behind a feature flag; no view changes yet.Maintenance. Low and largely passive. No runtime infrastructure, no cron, no cache invalidation, no migrations. The maintenance burden is almost entirely version review: reading release notes to determine whether a bump is a refactor, a Laravel support bump, or a data refresh that changes what users see. That is a recurring 15-minute task per release, not a project.
Support. First-line support surface is the language switcher. The two predictable ticket classes are (a) "my language is missing" — a locale-list coverage issue, answered by the allow-list and a documented fallback — and (b) "this name looks wrong" — a data issue upstream, which we can override in the wrapper without forking. Document both resolutions now; otherwise each ticket becomes an investigation.
Scaling. Irrelevant in the usual sense — the data is a static PHP array, memory-resident, no I/O, no growth with user count or locale count. The scaling consideration that matters is surface scaling: every new place we render a locale name must go through the repository. One direct call to the package bypasses fallback and sorting and reintroduces the inconsistency we adopted this to eliminate. Enforce with a lint or code-review checklist item.
Failure modes.
locale-list major conflict at install → blocked at CI, not production.Ramp-up. Under a day for a mid-level Laravel engineer: one dependency, one config file, one small class, one test. The real ramp-up cost is organizational, not technical — aligning design, support, and whoever owns email templates on a single label source and a single fallback contract. Size the effort accordingly: the code is trivial, the review and sign-off are the actual schedule.
How can I help you explore Laravel packages today?