akaunting/laravel-language
A database-driven language and translation manager for Laravel. Add, edit, and switch languages at runtime, store translation lines in the database, and set the active locale per request with built-in middleware. Includes models, routes, and a ready-to-use service provider.
This package is a feature-flag layer for Laravel. With 2.0.0 it supports Laravel 8 through 12, so the same integration steps apply across the supported range.
Install the package:
composer require <vendor>/<package>
Publish the config and any migrations:
php artisan vendor:publish --provider="Vendor\\Package\\PackageServiceProvider"
php artisan migrate
Define your first flag in the published config. Most feature-flag packages use a central definition file:
// config/features.php
return [
'new-checkout' => [
'enabled' => false,
'description' => 'Gradual rollout of the new checkout flow',
],
];
Check the flag in application code:
use Vendor\Package\Facades\Feature;
if (Feature::active('new-checkout')) {
return view('checkout.new');
}
return view('checkout.legacy');
For per-user or per-team behavior, use the override API added in 2.0.0:
Feature::for($user)->activate('new-checkout');
Feature::for($user)->deactivate('new-checkout');
The first practical use case is gating one new screen or endpoint behind a flag, then enabling it for internal users or a small cohort before a full rollout. Look first at the published config file for flag definitions, then at the package’s facade or helper for the check API.
Keep flag names and defaults in config rather than scattering string literals. This makes discovery and cleanup easier:
// config/features.php
return [
'billing.invoices-v2' => [
'enabled' => false,
'description' => 'New invoice layout and PDF generation',
'owner' => 'billing-team',
],
];
Use the package’s Blade directive or facade in views to avoid leaking flag logic into controllers:
@feature('new-checkout')
<x-checkout.new />
@else
<x-checkout.legacy />
@endfeature
For whole routes, wrap them in middleware or conditionally register them in a service provider:
Route::middleware('feature:new-checkout')->group(function () {
Route::get('/checkout', NewCheckoutController::class);
});
The 2.0.0 override feature is the main workflow addition. Use it for:
A common pattern is to resolve the flag subject consistently:
$subject = $user->currentTeam ?? $user;
if (Feature::for($subject)->active('new-checkout')) {
// ...
}
Be explicit about whether a flag is evaluated against the user, the team, or globally. Mixing subjects across call sites is a common source of confusing rollout behavior.
Use the package’s percentage or cohort support for staged releases. A practical workflow:
Feature flags should not make tests nondeterministic. Explicitly activate or deactivate flags in test setup:
protected function setUp(): void
{
parent::setUp();
Feature::activate('new-checkout');
}
public function test_new_checkout_flow(): void
{
$this->actingAs($user)
->get('/checkout')
->assertSee('New checkout');
}
If the package stores overrides in the database, consider using an in-memory or array store in tests to avoid seeding flag tables.
Flags evaluated in queued jobs run in a different lifecycle from the web request. Activate overrides explicitly when dispatching test jobs, and be aware that a flag can change between dispatch and execution. For long-running workers, avoid caching flag state indefinitely.
With flag overrides added in 2.0.0, confirm the precedence order in the package documentation: typically global config, then segment or percentage rules, then per-subject overrides. A user override that does not appear to work is often being superseded by a higher-precedence rule, or the evaluation subject is not the one you expect.
After changing flag definitions, run:
php artisan config:clear
In production, remember that php artisan config:cache will freeze config-based flags until the cache is rebuilt. If flags are expected to change without deployment, store them in the database or another runtime source instead of config.
Many feature-flag packages cache flag state for performance. If you activate an override and do not see the change immediately, check:
For Laravel Octane, long-lived workers may retain resolved flag state. Restart workers after deploying flag changes that are expected to take effect immediately.
2.0.0 supports Laravel 8 through 12. If you are upgrading from a release that supported fewer versions, check your composer.json constraints and run the test suite on your actual Laravel version. Laravel 8 is end-of-life upstream, so pinning to it should be a deliberate decision rather than an accident.
Feature flags are temporary release tools. Treat flag removal as part of the rollout checklist:
A useful convention is to name flags with a cleanup date or ticket reference in the description.
When a flag behaves unexpectedly, log the evaluation inputs rather than only the result:
logger()->debug('Feature flag evaluation', [
'flag' => 'new-checkout',
'subject' => $subject->getKey(),
'result' => Feature::for($subject)->active('new-checkout'),
]);
This quickly reveals whether the issue is the subject, the override store, caching, or the flag definition itself.
If the package exposes a flag resolver or driver contract, use it to centralize custom logic such as tenant-based rollout or external flag providers. Keep custom resolvers small and covered by tests, because they become a critical path for every flagged request.
How can I help you explore Laravel packages today?