Skip to content

A convenient way to set up, manage and use localized routes in a Laravel app.

License

Notifications You must be signed in to change notification settings

lada/laravel-localized-routes

 
 

Repository files navigation

Laravel Localized Routes

GitHub release License Build Status Code Coverage Scrutinizer Code Quality Total Downloads

ko-fi

A convenient way to set up, manage and use localized routes in a Laravel app.

✅ Requirements

  • PHP >= 7.1
  • Laravel >= 5.6

📦 Install

composer require codezero/laravel-localized-routes

Laravel will automatically register the ServiceProvider.

⚙️ Configure

☑️ Publish Configuration File

php artisan vendor:publish --provider="CodeZero\LocalizedRoutes\LocalizedRoutesServiceProvider" --tag="config"

You will now find a localized-routes.php file in the config folder.

☑️ Supported Locales

Using Slugs

Add any locales you wish to support to your published config/localized-routes.php file:

'supported-locales' => ['en', 'nl', 'fr'],

This will automically prepend a slug to your localized routes. More on this below.

Using Domains

Alternatively, you can use a different domain or subdomain for each locale by adding them to the supported-locales like this:

'supported-locales' => [
  'en' => 'example.com',
  'nl' => 'nl.example.com',
  'fr' => 'fr.example.com',
],

☑️ Omit Slug for Main Locale

Specify your main locale if you want to omit its slug from the URL:

'omit_url_prefix_for_locale' => null

Setting this option to 'en' will result, for example, in URL's like this:

  • English: /some-url instead of the default /en/some-url
  • Dutch: /nl/some-url as usual
  • French: /fr/some-url as usual

This option has no effect if you use domains instead of slugs.

Automatically Set Locale for Localized Routes

To automatically set the locale when a localized route is active via a middleware simply set the option to true:

'use_locale_middleware' => true

Alternatively, you can omit it completely or specify it for a specific route or route group:

Route::localized(function () {

    Route::get('about', AboutController::class.'@index')
        ->name('about')
        ->middleware(\CodeZero\LocalizedRoutes\Middleware\LocalizedRouteLocaleHandler::class);

    Route::group(
        [
            'as' => 'admin.',
            'middleware' => [\CodeZero\LocalizedRoutes\Middleware\LocalizedRouteLocaleHandler::class],
        ],
        function () {
            Route::get('admin/reports', ReportsController::class.'@index')
                ->name('reports.index');
        });

});

☑️ Set Options for the Current Localized Route Group

To set an option for one localized route group only, you can specify it as the second parameter of the localized route macro:

Route::localized(function () {

    Route::get('about', AboutController::class.'@index')
        ->name('about');

}, [
    'supported-locales' => ['en', 'nl', 'fr'],
    'omit_url_prefix_for_locale' => null,
    'use_locale_middleware' => false,
]);

🚗 Register Routes

Example:

// Not localized
Route::get('home', HomeController::class.'@index')
    ->name('home');

// Localized
Route::localized(function () {

    Route::get('about', AboutController::class.'@index')
        ->name('about');

    Route::name('admin.')->group(function () {
        Route::get('admin/reports', ReportsController::class.'@index')
            ->name('reports.index');
    });

});

In the above example there are 5 routes being registered. The routes defined in the Route::localized closure are automatically registered for each configured locale. This will prepend the locale to the route's URI and name. If you configured custom domains, it will use those instead of the slugs.

URI Name
/home home
/en/about en.about
/nl/about nl.about
/en/admin/reports en.admin.reports.index
/nl/admin/reports nl.admin.reports.index

If you set omit_url_prefix_for_locale to 'en' in the configuration file, the resulting routes look like this:

URI Name
/home home
/about en.about
/nl/about nl.about
/admin/reports en.admin.reports.index
/nl/admin/reports nl.admin.reports.index

⚠️ Beware that you don't register the same URL twice when omitting the locale. You can't have a localized /about route and also register a non-localized /about route in this case. The same idea applies to the / (root) route! Also note that the route names still have the locale prefix.

🚕 Generate Route URL's

You can get the URL of your named routes as usual, using the route() helper.

👎 The ugly way...

Normally you would have to include the locale whenever you want to generate a URL:

$url = route(app()->getLocale().'.admin.reports.index');
👍 A much nicer way...

Because the former is rather ugly, this package overwrites the route() function and the underlying UrlGenerator class with an additional, optional $locale argument and takes care of the locale prefix for you. If you don't specify a locale, either a normal, non-localized route or a route in the current locale is returned.

route($name, $parameters = [], $absolute = true, $locale = null)

A few examples:

app()->setLocale('en');
app()->getLocale(); // 'en'

$url = route('home'); // /home (normal routes have priority)
$url = route('about'); // /en/about (current locale)

// Get specific locales...
// This is most useful if you want to generate a URL to switch language.
$url = route('about', [], true, 'en'); // /en/about
$url = route('about', [], true, 'nl'); // /nl/about

// You could also do this, but it kinda defeats the purpose...
$url = route('en.about'); // /en/about
$url = route('en.about', [], true, 'nl'); // /nl/about

Note: in a most practical scenario you would register a route either localized or non-localized, but not both. If you do, you will always need to specify a locale to get the URL, because non-localized routes always have priority when using the route() function.

🚌 Redirect to Routes

Laravel's Redirector uses the same UrlGenerator as the route() function behind the scenes. Because we are overriding this class, you can easily redirect to your routes.

return redirect()->route('home'); // redirects to /home
return redirect()->route('about'); // redirects to /en/about (current locale)

You can't redirect to URL's in a specific locale this way, but if you need to, you can of course just use the route() function.

return redirect(route('about', [], true, 'nl')); // redirects to /nl/about

✍🏻 Generate Signed Route URL's

Generating a signed route URL is just as easy.

Pass it the route name, the nescessary parameters and you will get the URL for the current locale.

$signedUrl = URL::signedRoute('reset.password', ['user' => $id], now()->addMinutes(30));

You can also generate a signed URL for a specific locale:

$signedUrl = URL::signedRoute($name, $parameters, $expiration, true, 'nl');

Check out the Laravel docs for more info on signed routes.

🌎 Translate Routes

If you want to translate the segments of your URI's, create a routes.php language file for each locale you configured:

resources
 └── lang
      ├── en
      │    └── routes.php
      └── nl
           └── routes.php

In these files, add a translation for each segment.

// lang/nl/routes.php
return [
    'about' => 'over',
    'us' => 'ons',
];

Now you can use our Lang::uri() macro during route registration:

Route::localized(function () {

    Route::get(Lang::uri('about/us'), AboutController::class.'@index')
        ->name('about.us');

});

The above will generate:

  • /en/about/us
  • /nl/over/ons

If a translation is not found, the original segment is used.

🚏 Route Placeholders

Placeholders are not translated via language files. These are values you would provide via the route() function. The Lang::uri() macro will skip any placeholder segment.

If you have a model that uses a route key that is translated in the current locale, then you can still simply pass the model to the route() function to get translated URL's.

An example...

Given we have a model like this:

class Post extends \Illuminate\Database\Eloquent\Model
{
    public function getRouteKey()
    {
        $slugs = [
            'en' => 'en-slug',
            'nl' => 'nl-slug',
        ];

        return $slugs[app()->getLocale()];
    }
}

TIP: checkout spatie/laravel-translatable for translatable models.

If we have a localized route like this:

Route::localized(function () {

    Route::get('posts/{post}', PostsController::class.'@show')
        ->name('posts.show');

});

We can now get the URL with the appropriate slug:

app()->setLocale('en');
app()->getLocale(); // 'en'

$post = new Post;

$url = route('posts.show', $post); // /en/posts/en-slug
$url = route('posts.show', $post, true, 'nl'); // /nl/posts/nl-slug

🗃 Cache Routes

In production you can safely cache your routes per usual.

php artisan route:cache

🚧 Testing

composer test

☕️ Credits

🔓 Security

If you discover any security related issues, please e-mail me instead of using the issue tracker.

📑 Changelog

See a list of important changes in the changelog.

📜 License

The MIT License (MIT). Please see License File for more information.

About

A convenient way to set up, manage and use localized routes in a Laravel app.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • PHP 100.0%