arcanedev/localization
Arcanedev Localization is a Laravel package for managing app translations with a built-in web interface. Edit, add, and delete language files from the browser, switch locales, and use middleware and helpers to set and persist the current language across requests.
Install and confirm discovery. composer require arcanedev/localization. The service provider (Arcanedev\Localization\LocalizationServiceProvider) is auto-discovered on Laravel 5.5+. Check composer.json of the package for the Laravel/illuminate version constraint before committing — the last tagged release is from Feb 2022, so validate against your framework version first.
Publish the config. php artisan vendor:publish --provider="Arcanedev\Localization\LocalizationServiceProvider" and open config/localization.php. Look at the locale, fallback_locale, and supported_locales keys. Every locale you want to offer must be declared here with its metadata (name, native, script, regional) — this array is the source of truth for the package, independent of Laravel's config/app.php.
Where to read first. src/Localization.php (the manager behind the localization() helper), src/Collections/Locales.php (the collection you iterate over), and src/Entities/Locale.php (per-locale accessors). Those three files answer 90% of "how do I get X" questions.
First use case: a language switcher. In a Blade layout:
@foreach (locales() as $code => $locale)
<a href="{{ url($code) }}"
@if ($code === localization()->getCurrentLocale()) aria-current="true" @endif>
{{ $locale['native'] }}
</a>
@endforeach
That single loop exercises the two helpers you'll use every day: locales() for the collection and localization() for the current/default/supported state.
Treat localization() as the read side, Laravel's translator as the write side. Use localization()->getCurrentLocale(), getDefaultLocale(), getFallbackLocale(), getSupportedLocales(), and getSupportedLocalesKeys() for anything in views, menus, or route generation. When you actually need to switch the active locale for translation lookups, call app()->setLocale($code) — the package reads state, it does not own Laravel's translator.
Locale collection iteration. locales() returns a Locales collection keyed by locale code. It supports all(), keys(), get($code), has(), contains(), and count(). Use ->keys() when you need a plain array for validation rules (Rule::in(localization()->getSupportedLocalesKeys())) and ->get($code)['name'] for display strings.
Per-locale route grouping. The idiomatic setup is a route group with an optional {locale} prefix plus URL::defaults() so route() helpers don't require you to pass the locale on every call:
Route::group([
'prefix' => localization()->getCurrentLocale(),
'middleware' => ['localization'],
], function () {
// your app routes
});
Then, in a middleware or service provider running early:
URL::defaults(['locale' => localization()->getCurrentLocale()]);
This is the pattern that makes route('posts.show', $post) "just work" without threading $locale through every controller.
Middleware for detection. Add the package middleware (or your own thin wrapper) to the web group so the current locale is resolved from the URL segment first, session second, Accept-Language third. Keep the detection order explicit and test all three branches — debugging "wrong language on refresh" almost always traces back to an ambiguous precedence rule.
Hreflang in layouts. For SEO, emit alternate links using the same source of truth:
@foreach (localization()->getSupportedLocalesKeys() as $code)
<link rel="alternate" hreflang="{{ $code }}" href="{{ url($code . request()->getPathInfo()) }}">
@endforeach
Deriving all alternate URLs from getSupportedLocalesKeys() guarantees the switcher and the hreflang block never drift apart.
Pair with a translation-storage package. arcanedev/localization is deliberately lightweight: it manages which locales exist and which one is active, not storing translated model attributes or UI strings. Use Laravel's JSON translation files for UI text and a model-level package (e.g. spatie/laravel-translatable) for content, and keep this package as the single authority on the locale list.
config/localization.php's locale and Laravel's config/app.php's locale are separate. Set them to the same value in your deployment config and be explicit about which one your bootstrapping reads.getCurrentLocaleNative(), getCurrentLocaleScript(), and getCurrentLocaleRegional() assume the corresponding key exists in supported_locales. If you add a locale with only 'name', calling ->native on it will break the switcher. Define all four keys for every entry, even if some are copy-paste placeholders.Locales is a plain array. It is an object with its own accessor contract. ->keys()->toArray() before passing it into in_array, Rule::in, or json_encode — otherwise you'll be serializing the collection wrapper and get surprising output.php artisan route:cache and config:cache work. If your detection middleware hits the database to look up locales, route:cache will still work but every request pays for a query — cache it or leave it in config.arcanedev/support compatibility before upgrading the framework. If the service provider fails to boot, the fix is usually a constraint bump or a locally forked dependency, not an application-code change. Pin the package version in composer.json rather than floating it./en/about) or unprefixed (/about). Mixing them produces duplicate content and broken canonical URLs. Pick one and encode it in the route group and the switcher together.app()->getLocale() after hitting a locale-prefixed URL rather than asserting on the package's internal state — that's the value that actually determines what users see. Use $this->get('/fr/…')->assertSee('…') to catch both routing and translation regressions in one test.localization as a singleton; if you need custom resolution (e.g. reading locale from a subdomain), bind your own implementation against the package's contract in a service provider rather than editing the vendor config. The Locales collection is also a natural place to hang app-specific helpers (flag emoji, RTL flag) via a small wrapper class.How can I help you explore Laravel packages today?