I would like to create application with many translated routes depending on selected language. I've once described it at 3 methods of creating URLs in multilingual websites.
In this case it should be the first method from mentioned topic so:
- I have one default language
- I can have many other languages
- Current language should be calculated only by URL (without cookies/sessions) to make it really friendly also for search engines
- For default language there should be no prefix in URL, for other languages should be language prefix after domain
- Each part of url should be translated according to the current language.
Let's assume I have set default language pl
and 2 other languages en
and fr
. I have only 3 pages - mainpage, contact page and about page.
Urls for site should look then this way:
/
/[about]
/[contact]
/en
/en/[about]
/en/[contact]
/fr
/fr/[about]
/fr/[contact]
whereas [about]
and [contact]
should be translated according to selected language, for example in English it should be left contact
but for Polish it should be kontakt
and so on.
How can it be done as simple as possible?
First step:
Go to
app/lang
directory and create here translations for your routes for each language. You need to create 3routes.php
files - each in separate language directory (pl/en/fr) because you want to use 3 languagesFor Polish:
For English:
For French:
Second step:
Go to
app/config/app.php
file.You should find line:
and change it into language that should be your primary site language (in your case Polish):
You also need to put into this file the following lines:
In
alt_langs
config you set alternative languages (in your caseen
andfr
) - they should be the same as file names from first step where you created files with translations.And
locale_prefix
is the prefix for your locale. You wanted no prefix for your default locale so it's set to empty string. This config will be modified in runtime if other language than default will be selected.Third step
Go to your
app/routes.php
file and put their content (that's the whole content ofapp/routes.php
file):As you see first you check if the first segment of url matches name of your languages - if yes, you change locale and current language prefix.
Then in tiny loop, you set requirements for your all route names (you mentioned that you want have
about
andcontact
translated in URL) so here you set them as the same as defined inroutes.php
file for current language.At last you create Route group that will have prefix as the same as your language (for default language it will be empty) and inside group you simply create paths but those parameters
about
andcontact
you treat asvariables
so you use{about}
and{contact}
syntax for them.You need to remember that in that case
{contact}
in all routes will be checked if it's the same as you defined it in first step for current language. If you don't want this effect and want to set up routes manually for each route using where, there's alternativeapp\routes.php
file without loop where you setcontact
andabout
separately for each route:Fourth step:
You haven't mentioned about it, but there's one extra thing you could consider. If someone will use url
/en/something
wheresomething
isn't correct Route, I think the best solution to make redirection. But you should make redirection not to/
because it's default language but to/en
.So now you can open
app/start/global.php
file and create here 301 redirection for unknown urls:What Marcin Nabiałek provided us with in his initial answer is a solid solution to the route localization problem.
The Minor Bugbear:
The only real downside with his solution is that we cannot use cached routes, which can sometimes be of great benefit as per
Laravel's
docs:Why can we not cache our routes?
Because Marcin Nabiałek's method generates new routes based on the
locale_prefix
dynamically, caching them would result in a404
error upon visiting any prefix not stored in thelocale_prefix
variable at the time of caching.What do we keep?
The foundation seems really solid and we can keep most of it!
We can certainly keep the various localization-specific route files:
We can also keep all the
app/config/app.php
variables:We will also need the bit of code that checks the route segments. But since the point of this is to utilize the cache we need to move it outside the
routes.php
file. That one will not be used anymore once we cache the routes. We can for the time being move it toapp/Providers/AppServiceProver.php
for example:Don't forget:
Setting up our routes:
Several changes will occur within our
app/Http/routes.php
file.Firstly we have to make a new array contain all of the
alt_langs
as well as the defaultlocale_prefix
, which would most likely be''
:In order to be able to cache all the various lang prefixes with translated route parameters we need to register them all. How can we do that?
*** Laravel aside 1: ***
Let's take a look at the definition of
Lang::get(..)
:The third parameter of that function is a
$locale
variable! Great - we can certainly use that to our advantage! This function actually let's us choose which locale we want to obtain the translation from!The next thing we are going to do is iterate over the
$all_langs
array and create a newRoute
group for each language prefix. Not only that, but we are also going to get rid of thewhere
chains andpatterns
that we previously needed, and only register the routes with their proper translations (others will throw404
without having to check for it anymore):Houston, we have a problem!
As you can see I prefer using named routes (most people do probably):
They can be very easily used inside your blade templates:
But there is an issue with my solution so far: Route names override each other. The
foreach
loop above would only register the last prefixed routes with their names.In other words only
example.com/
would be bound to thehome
route aslocale_perfix
was the last item in the$all_langs
array.We can get around this by prefixing route names with the language
$prefix
. For example:We will have to do this for each of the routes within our loop. This creates another small obstacle.
But my massive project is almost finished!
Well as you probably guessed you now have to go back to all of your files and prefix each
route
helper function call with the currentlocale_prefix
loaded from theapp
config.Except you don't!
*** Laravel aside 2: ***
Let's take a look at how Laravel implements it's
route
helper method.As you can see Laravel will first check if a
route
function exists already. It will register itsroute
function only if another one does not exist yet!Which means we can get around our problem very easily without having to rewrite every single
route
call made so far in ourBlade
templates.Let's make a
app/helpers.php
file real quick.Let's make sure Laravel loads the file before it loads its
helpers.php
by putting the following line inbootstrap/autoload.php
All we now have to do is make our own
route
function within ourapp/helpers.php
file. We will use the original implementation as the basis:That's it!
So what we have done essentially is registered all of the prefix groups available. Created each route translated and with it's name also prefixed. And then sort of overriden the Laravel
route
function to prefix all the route names (except some) with the currentlocale_prefix
so that appropriate urls are created in our blade templates without having to typeconfig('app.locale_prefix')
every single time.Oh yeah:
Caching routes should only really be done once you deploy your project as it is likely you will mess with them during devlopement. But you can always clear the cache:
Thanks again to Marcin Nabiałek for his original answer. It was really helpful to me.