goodcat / laravel-l10n
An opinionated Laravel package for app localization
Installs: 13
Dependents: 0
Suggesters: 0
Security: 0
Stars: 6
Watchers: 0
Forks: 0
Open Issues: 1
pkg:composer/goodcat/laravel-l10n
Requires
- php: ^8.2
- illuminate/http: ^12.44.0
- illuminate/routing: ^12.44.0
- illuminate/support: ^12.44.0
Requires (Dev)
- larastan/larastan: ^3.5
- laravel/pint: ^1.22
- orchestra/testbench: ^10.4
- pestphp/pest: ^3.8
README
An opinionated Laravel package for app localization.
Quickstart
Get started with laravel-l10n in three steps.
- Download the package via Composer.
composer require goodcat/laravel-l10n
- Add the locale middlewares to your
bootstrap/app.phpfile.return Application::configure(basePath: dirname(__DIR__)) ->withMiddleware(function (Middleware $middleware): void { $middleware->web([ \Goodcat\L10n\Middleware\SetLocale::class, \Goodcat\L10n\Middleware\SetPreferredLocale::class, ]); });
- Define localized routes using the
lang()method.Route::get('/example', Controller::class) ->lang(['fr', 'de', 'it', 'es']);
That's it. You're all set to start using laravel-l10n.
Configuration
To customize the package behavior, publish the configuration file:
php artisan vendor:publish --tag=l10n-config
Add Locale Prefix
By default, this package adds the locale prefix to translated routes, except for the fallback locale.
This means a route like /example will be served by the clean URL /example for the default language (e.g. English), while other locales will include their prefix (e.g. /es/ejemplo, /it/esempio).
If you prefer to hide the locale prefix for all languages, set add_locale_prefix to false in your config/l10n.php file.
After this change, routes will use translated URIs without locale prefixes (e.g. /ejemplo instead of /es/ejemplo).
Route translations
Use the lang() method to define which locales a route should support. Route translations are managed via language files.
Translations via Language Files
Manage route translations in dedicated language files. This approach keeps your routes clean and centralizes your translations, making them easier to manage.
The expected file structure is as follows:
/lang ├── /es │ └── routes.php ├── /fr │ └── routes.php ├── /it │ └── routes.php
Inside your routes.php file, map the original route URI to a translated slug:
// lang/es/routes.php return [ 'example' => 'ejemplo', ];
Then define the route with the locales it should support:
Route::get('/example', Controller::class) ->lang(['es', 'fr', 'it']);
This will generate:
/example(fallback locale)/es/ejemplo(Spanish, translated via language file)/fr/example(French, no translation defined)/it/example(Italian, no translation defined)
Note
The key should be the route URI without the leading slash. For example, for Route::get('/example'), the key should be example.
Route groups
To avoid repetitive language definitions on every single route, you can use Route::lang()->group():
Route::lang(['es', 'it'])->group(function () { Route::get('/example', fn () => 'Hello, World!'); Route::get('/another', fn () => 'Another route'); });
All routes inside the group will inherit the locale definitions.
Route Matching
Use L10n::is() to check if the current route matches a given pattern, regardless of the locale:
<div> @if (L10n::is('example')) <p>Matches /example, /es/ejemplo, /it/esempio, etc.</p> @endif </div>
This is the localized equivalent of Route::is().
URL Generation
The package automatically replaces Laravel's default URL generator with LocalizedUrlGenerator, ensuring that the route() helper generates the correct URLs for the current locale without any extra configuration.
Note
If you need to use a custom URL generator, you can override it in your AppServiceProvider by aliasing your own implementation to the url service.
Using the route() and action() Helpers
Once the generator is registered, the route() helper will intelligently create URLs based on the current application locale.
- For the current locale: The helper automatically generates the correct URL based on the active language.
- For a specific locale: You can explicitly request a URL for a different language by passing the
langparameter to theroute()helper.
// Assuming the current locale is 'en' route('example'); // Returns "/example" // To generate a URL for a different locale route('example', ['lang' => 'fr']); // Returns "/fr/example" // If a translation exists for 'es' in lang/es/routes.php, the translated slug is used route('example', ['lang' => 'es']); // Returns "/es/ejemplo"
The action() helper works the same way:
action(Controller::class, ['lang' => 'es']); // Returns "/es/ejemplo"
Localized views
This package makes it easy to organize your views by language. The application's view loader is configured to automatically search for a localized version of a view before falling back to the generic one.
How it works
When you render a view, the system follows a specific search order based on the current application locale.
- Locale-specific path: The application first tries to find the view within a folder that matches the current locale. For example, if the locale is set to
it, it will look for theexampleview inresources/views/it/example.blade.php. - Generic path: If the view is not found in the locale-specific folder, it will then fall back to the generic
resources/views/example.blade.php.
This makes it straightforward to organize your views with a clean, language-based folder structure, like the one below.
/resources/views
├── example.blade.php
├── /it
│ └── example.blade.php
└── /es
└── example.blade.php
The example.blade.php file in the root views folder can serve as your default template, while the localized versions (it/example.blade.php, es/example.blade.php) contain language-specific content or layouts.
User Locale Preference
This package provides a robust mechanism for automatically detecting a user's preferred language.
It adds the app()->getPreferredLocale() and app()->setPreferredLocale() methods to your Laravel application.
The SetPreferredLocale middleware is responsible for populating the preferred locale. It does this by checking a series of configurable preferred locale resolvers.
By default, the package checks the following sources in order:
- SessionLocale: Checks if a locale was set in the session.
- UserLocale: Checks if the authenticated user has a preferred locale (the user model must implement a
preferredLocale()method). - BrowserLocale: Falls back to the browser's
Accept-Languageheader.
Customizing Resolvers
You can customize the resolvers by setting the static property on the L10n class:
use Goodcat\L10n\L10n; use Goodcat\L10n\Resolvers\BrowserLocale; L10n::$preferredLocaleResolvers = [ new BrowserLocale, ];
Checking Fallback Locale
The package also adds a helper method to check if a locale is the fallback locale:
app()->isFallbackLocale('en'); // true if 'en' is the fallback locale