jobmetric/laravel-language
jobmetric/laravel-language is a Laravel package for managing application languages and localization. It offers language CRUD, activation and default-language handling, plus helpers for multilingual Eloquent models, locale switching, and translation storage in Laravel apps.
This release (2.1.1) is the first assessment for the package, so treat the notes as cumulative: much of what's listed is historical feature work consolidated into the current shape. Read the list bottom-up — the newest entries are at the top, and several of them are breaking renames rather than new capability.
Minimum path to a working install:
composer require panelify/language — note the hard requirement of PHP 8.2 and the peer packages panelify/core (^1.26) and panelify/multi-calendar (^1.2). Version drift here is the most common install failure.migrate.flags assets plus a hasAsset method on the service provider for checking availability without hitting the filesystem yourself.Language row. The calendar field is now part of the model and the admin list, and several features (the cast, the date rules) read from it — a null calendar silently disables them.Where to look first: the service provider, not the facade. The notes show the facade was rewritten to be bound by the provider, and the provider now owns class registration and asset lookup. Language facade calls resolve through that container binding, so if Language::... throws a binding error, the provider is the file to open.
First real use case — per-request locale and timezone:
// bootstrap/app.php (Laravel 11+) or Kernel $middlewareGroups
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\Panelify\Language\Http\Middleware\SetLanguageMiddleware::class,
\Panelify\Language\Http\Middleware\SetTimezoneMiddleware::class,
]);
})
Order matters: language first, then timezone, because the timezone middleware resolves the active language to decide what timezone applies. The helper method added for SetTimezoneMiddleware exists precisely so you can compute the target timezone outside the middleware (queues, console commands, tests) without duplicating that lookup.
Locale resolution via middleware. SetLanguageMiddleware was rewritten "for better solve program" — the current version handles the resolution chain internally and is no longer something you subclass to customise. If you previously overrode it to read a session key or Accept-Language header, move that logic into a listener or set the locale before the middleware runs. The provider-based registration means the middleware now resolves the language service from the container, which is what makes it testable — there's a dedicated middleware test in the release, so mirror its setup.
Calendar-aware date casting. DateBasedOnCalendarCast is the headline ergonomic win: cast a date attribute once and it renders according to the calendar stored on the language row (Gregorian, Jalali, Hijri, etc. — "some calendar type" entries in the enum).
protected function casts(): array
{
return [
'published_at' => \Panelify\Language\Casts\DateBasedOnCalendarCast::class,
];
}
The cast depends on the language service being resolvable at hydration time. In jobs and console commands, set the locale explicitly before touching models with this cast, or you'll get the default language's calendar rather than the intended one.
Validation rules belong in form requests. The three new rules are designed to be composed:
LanguageExistRule — validates a submitted language identifier against the language table.CheckLocaleRule — validates an actual locale string (format/known-locale check), distinct from LanguageExistRule, which only asks whether your table has it.CheckFutureDateRule — calendar-aware future-date check. Use this instead of after:today whenever the input is in a non-Gregorian calendar; a plain after comparison will be wrong.The notes mention translation keys were set for these rules, so publish the translation files if you need non-English messages — a missing key surfaces as the raw rule name in the error bag, which is an easy-to-miss bug.
Events over direct service calls. LanguageStoredEvent (renamed from LanguageAddEvent) and SetLocaleEvent (namespace moved) are the extension points. Listen to LanguageStoredEvent to warm caches, regenerate route lists, or sync to a search index. Listen to SetLocaleEvent to propagate locale to anything the middleware doesn't cover.
Asset lookups. Prefer $provider->hasAsset($code) over file_exists() when rendering flag markup; it encapsulates the published path and returns false cleanly for codes without flags.
Several renames in this line are breaking. Before upgrading, grep your codebase for:
LanguageAddEvent → LanguageStoredEventDateConverter — removed from this package and moved into panelify/multi-calendar. Any use of the old namespace will fatal. Update the import and add the dependency explicitly rather than relying on it being transitively present.SetLocaleEvent — namespace changed.addLanguageDate() → addLanguageData() on the language service.The facade file changed too, so if you extended or aliased it, re-check your binding.
Pin panelify/core deliberately. The notes show the core dependency being bumped for EnumToArray → EnumMacros and, in 2.1.1, to 1.26 for an upgraded DTO method. EnumMacros means the old EnumToArray trait/helper is gone — enums relying on it need to adopt the macro approach. This is the most likely cause of a "works in one project, breaks in another" situation, because a loose constraint resolves to different cores.
The query builder package bumped 5.7 → 6.3. If you have code that assumed the older builder API (particularly around custom query execution paths), run your full suite — this is the kind of transitive bump that fails at runtime, not at install.
Migration churn. The migration changed repeatedly across this line. On upgrade, do not assume php artisan migrate is a no-op. Compare the published file, and if the calendar column was added after your install, existing rows will have it null — backfill before enabling DateBasedOnCalendarCast on production data, or the cast will throw or fall back depending on your null handling.
Middleware debugging. If locale isn't sticking, check in this order: (1) is the provider registered and the facade binding resolvable, (2) is SetLanguageMiddleware actually in the stack for that route — it's often skipped on API routes, (3) is SetTimezoneMiddleware running after it, (4) is a later middleware or controller overwriting App::setLocale(). Both middlewares have tests in the release; reading them is faster than guessing.
"Code formatted" and "changed case file" entries. These look cosmetic but they mean file and class casing shifted. On case-sensitive filesystems (Linux, most CI) a stale autoload map will fail while your local macOS dev machine works. Run composer dump-autoload after upgrading and clear any cached config/discovery files.
Documentation is thin relative to surface area. The release adds "new document" but the volume of renames and middleware rewrites outpaces it. For anything not covered, the *Test.php files in the release (language service, both middlewares, DateConverter, DateBasedOnCalendarCast, each rule, helper methods) are effectively the specification. Start there when behaviour is ambiguous.
How can I help you explore Laravel packages today?