تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Localization Overview0:00

Let's take a look at localization within a Laravel application. Laravel has a bunch of useful methods and tools to work with localization, but there are a lot of things you have to consider. For example, you can have text coming from different sources, whether it's hardcoded in a Blade file, validation messages, or coming from the database. We also have to consider how we can change the locale for every request the application makes. So let's take a look at an example of how we can approach this problem. So I have a demo app prepared as always, it's a typical blog application. So we can list our posts here, we can view a single post, and we can create a post using

PHP Key Translations0:26

So I have a demo app prepared as always, it's a typical blog application. So we can list our posts here, we can view a single post, and we can create a post using this form here. Now if you take a look at the documentation, you'll see that there are two ways to define translation strings. You can use short keys, or you can use translation strings as keys. So let's start with using short keys here. So the idea is to make use of a PHP array, provide a key, and then have different folders corresponding to your different locales. So let's take a look at this way.

corresponding to your different locales. So let's take a look at this way. So if we go to the dashboard here, let's make this string here translatable. So that would be in dashboard.blade.php right here. So let me just wrap this in a div. And we want to make use of the __ method to make this string translatable. So instead of hardcoding it, we would wrap it in curly braces like this, and then use the __ method for our translatable string. Now for short keys, we have to provide a key here. So instead of the actual string we want to show, we provide a key.

Now for short keys, we have to provide a key here. So instead of the actual string we want to show, we provide a key. So how about we put it in a file named messages, we'll name it welcome. So we have to make a file named messages.php, and we have to provide a key named welcome. The value will be the actual translation string. So let's save that. And if this doesn't exist, then Blade will just render out this key here. So if we go back here, you'll see it renders out messages.welcome. And if you take a look, default Laravel already comes with a bunch of translation strings in PHP format for your validation messages.

And if you take a look, default Laravel already comes with a bunch of translation strings in PHP format for your validation messages. So if you look here, you'll see English translations for validation messages. But for this new string, let's create a new file within the English folder. And I said it was messages.php, it's just going to be an array, sorry, I have to open PHP here. And this return, I believe the key was welcome. And the value will be the rendered string for English. So say you are logged in. And if I did that correctly, it should show that in the browser now.

So say you are logged in. And if I did that correctly, it should show that in the browser now. And it does. Now if you want to translate this to a different language, then we have to make sure this file exists in a different folder corresponding to that locale. So we can basically duplicate the entire EN folder. So let's duplicate. Let's make it French here. So now we have English and French translations. And within French, let's go to messages.

So now we have English and French translations. And within French, let's go to messages. The key is going to be the same, but let's just say fr for French translation. And let me change some of the other validation messages as well so we can see that working. So within validation, how about we update the required validation message. So I'll just say fr field is required. Now our app is still in English, so it's not going to update here. And we'll take a look at how to programmatically change the locale. But for now, let's force it in our config/app.php. There should be a section here for locale right here.

But for now, let's force it in our config/app.php. There should be a section here for locale right here. And let's change it to French. And let's see if this changes. And it does. Cool. Let's also check out the validation message. So if I go to createPost and hit the createPost button here without filling out the forms, we do get the French translation here. And for the success message after we create a post, that should be within our PostController.

we do get the French translation here. And for the success message after we create a Post, that should be within our PostController store method right here. I'm hard coding it right here. So instead of hard coding it, we can do the same thing. So let me just cut this out. We can make use of the __ method again to say this string should be translatable. And let's make a file named posts. And let's define a key named created. And we have to make sure this exists in both English and French.

And let's define a key named created. And we have to make sure this exists in both English and French. So let's go ahead and create that. So let me just copy this within messages. Let's create a new one named posts, posts.php, paste that in. It was posts.created. And we can paste the message in here. And we have to make sure this is in French as well. So let's grab this. Let's make a new file for French.

So let's grab this. Let's make a new file for French. Let's copy this again, paste that in, and let's say French. Okay. So now if I did this correctly, the app is still in French locale. So we should have that French translation as we create a post. And we're getting this error here. Looks like it's conflicting with this posts translation with this posts.php. So I'm just going to rename this to posts_messages. And same with this one here.

So I'm just going to rename this to postsMessages. And same with this one here. And now we have to update our PostsController to use that instead. So back here, this should be postsMessages created. Okay. So let's try that one more time. Okay. Again, we are in French locale. So let's see if we get the French translation after we create a post, a new post, postBody here.

JSON Translation Files5:29

So let's see if we get the French translation after we create a Post, a new Post, postBody here. Okay. So we do get the French translation. Now the other way to provide translations is to make use of the default language as a key. And then we can make use of JSON files instead of PHP arrays. So you can see here, thinking of a name for a key can be quite cumbersome. So you might prefer translating your strings this way. So for example, let's go back to our dashboard.

So you might prefer translating your strings this way. So for example, let's go back to our dashboard. Instead of keys here, let's just leave this one in here, let's make a new one. We would provide the default language in here and say, Hey, you are logged in. So this would show, go back to our dashboard. But if you wanted to support other languages, then you would define a JSON file for that other language using this as a key. So let's go back to our lang folder. Instead of PHP and arrays, we can make use of JSON files. So we don't need English because that's the default locale.

Instead of php and arrays, we can make use of JSON files. So we don't need English because that's the default locale. So we just have to define JSON files for our additional locales. So in this case, let's make one for French, fr.json. And we can define a JSON object here. The key is English. And then the value is the translation for French. So let me just paste this in again and add fr to the front. Let's save that. Again, our app should still be in French.

Translating Database Fields7:08

then hunting down the translation file, we can just see it directly in here. And if you need translations for all of the default ones provided in Laravel, then you can make use of this package here. I believe it does support both the php syntax and also the JSON syntax. So be sure to check this out. Now what if we need to translate the strings that are stored in our database, for example, our list of posts here. So by default, this is not translatable. So we have to make use of a package. And a good one to use is Spotty's Laravel translatable package.

So we have to make use of a package. And a good one to use is Spotty's Laravel Translatable package. So it's pretty straightforward to use. Let's take a look at how we can do this. It's first composer require, as always. Let me just rerun npm run dev here. And to make our model translatable, in this case, our Post model, it says first we have to add the hasTranslations straight on that model. So let's go to our Post model. Let's add hasTranslations here.

So let's go to our Post model. Let's add hasTranslations here. Make sure to import that, okay. Next is to create a public property named translatable, which holds an array of all of the attributes we want to make translatable. So for example, like this. So in this case, our Post has a title and a body, and we want both of those to be translatable. So let's add those fields within here, title and body. Okay, let me save this. And the last step is to make sure that all translatable attributes are set to JSON in

Okay, let me save this. And the last step is to make sure that all translatable attributes are set to JSON in your database. So let's go to our migration. So create_posts_table. So title and body are translatable, but we have to make them JSON columns. So let's duplicate this. Let's comment this out. And let's change these to JSON. So title is now JSON and body is now JSON, okay.

And let's change these to JSON. So title is now JSON and body is now JSON, okay. And I'm going to change the locale back to English for now. So say EN, let's save that. And if you take a look at the database here, and before we re-migrate the database, take a note of how the structure looks for our post table. Nothing special here. We just have strings for our title and body. But after we migrate again, it's almost the same, but now it's a JSON object, which will have the locale for each translation.

But after we migrate again, it's almost the same, but now it's a JSON object, which will have the locale for each translation. So this will be a JSON object that has EN and then this value for English. So let's take a look at that. So again, I'm going to stop this, I am going to php artisan migrate:fresh --seed. This is my alias. So now they should be JSON format. And by default, it should add those strings in the default locale. So again, let me run npm run dev. Let's refresh this.

So again, let me run npm run dev. Let's refresh this. And you can see we do have a JSON object with EN as the key and the actual string as the value. So our app will remain the same if we refresh this. So again, this is the English translation. And this is post four. But if I were to add a French translation here, so that would be four here. Let's add French here. And there should be no trailing comma on JSON.

And you can see it is French now. And the way this package works, if you don't specify a locale, it will take the current one. So right now we're in French, if we create a new Post, let's say Bonjour. Say test here. This should be saved as French. So create Post, we get our French translation here, you'll see it shows Bonjour here. And if you take a look at the database, it is French. But something you might want to do in terms of UI for translating things in the database is to default to English.

Locale Switching Middleware11:45

And it does show. Cool. And of course, we want to be able to change the locale on each request. So the end goal would be to have some sort of language switcher within here. So when we're on a page, we have the option to switch to a different locale. So let's see how we can do that. So let me just switch the locale back to English. And you can see there's even a fallback locale here, which we can also make use of. So if we take a look at the docs, we can make use of the setLocale. There's also getLocale, or currentLocale is the same thing.

So if we take a look at the docs, we can make use of the setLocale. There's also getLocale, or currentLocale is the same thing. And we can also check if the app is in a certain locale. So we can make use of these methods. However, they're only set for a given request. So let's go to our routes file. Right now the locale is English. And let's make another one here, just to test things out. So let's say Route::call, let's say test. And I'll make use of the app global helper here.

So let's say route call, let's say test. And I'll make use of the app global helper here. So we don't have to import anything. And we'll say getLocale, or currentLocale works as well. And let's just dndump this. This should be English. Let's go to our test endpoint. Okay, let's set it before we dndump it. So we'll say app::setLocale. Let's change it to French.

So we'll say app setLocale. Let's change it to French. And now it should dump out FR. And it does. But like I said, this only happens for the current request. So if I were to dump it out in a different request, say for example, the homepage, then we'll still get whatever is defined in the config. So in this case, it'll be English again, because it's a different request. So let's go to the homepage here. And it's back to English.

So let's go to the homepage here. And it's back to English. So we want to be able to set the locale and have it stay in that locale until we change it again. So I'm going to do this in the URL and also make use of middleware. So let me just remove this. And we don't need this anymore. Let me save this and go back here, or back to our dashboard. So what I want to do is have the first URL param to always be the locale. So for example, the locale for this URL will be en-dashboard.

So what I want to do is have the first URL param to always be the locale. So for example, the locale for this URL will be en-dashboard. And the same for any other route. For example, for posts, it'll be en-posts. For a specific post, it should be slash en slash posts slash for. And you saw here that it's blank, because there is no English translation for this post. So let me just update that in our database. That should be this one here. And then we just add the English translation here. Okay.

Okay. So how can we force a locale to be the first param in the URL? So let's go to our RouteServiceProvider. And you can see within here, it scaffolds out our routes. So down here, you can see it adds the middleware of api for our API routes and also a prefix of api. And for our web, you can see it's sending our middleware here and loading our routes web here. So what we can do is grab the locale. And like I said, that's going to be the first URL param.

So what we can do is grab the locale. And like I said, that's going to be the first URL param. So let's start with that. Let's say locale equals, we can make use of the request global helper. So request::segment to grab the first URL param, and it's the first one. And let's just dd(locale); So before I do that, actually, let's go to this page. So in this case, the first segment will be /posts. So let's just dd(locale), save that.

So in this case, the first segment will be /posts. So let's just dindump locale, save that. And let's refresh this. And you can see it is posts. And if we go to dashboard, that should be dashboard. Okay. So we can add a prefix here in our routes/web.php. So right here, we can say prefix. And the prefix is going to be that locale. So let me just remove this dindump.

And the prefix is going to be that locale. So let me just remove this dump. And let's see what we get here. So let's refresh this page. And you can see it's now going to the homepage because of our new prefix. So actually, let me put the dump back so you can see it. Let's dump the locale, but not stop the app. You can see the locale is now the first param, in this case, it's dashboard. And since we have a prefix, it's matching this endpoint here and going to the welcome blade.

And since we have a prefix, it's matching this endpoint here and going to the welcome Blade. Now, if I wanted to go to the actual dashboard, we can manually set the locale here. Let's say EN. And now it goes to the dashboard. So what we have to do here is force this first param to be a locale that we support. And if it's not, then we can just redirect back to the fallback locale. So I'm going to create a middleware here to enforce this rule. So let's start with that. And I'll stop this php artisan make:middleware.

So let's start with that. And I'll stop this php artisan make:middleware. Let's name it SetLanguage, or SetLocaleLanguage is fine. Let's make sure to run npm run dev again. And we want to make sure to run this middleware for our web routes. So let's go back to RouteServiceProvider, let me get rid of this dump here. And let's go to our middleware. So SetLanguage. So down here, again, let's just dump something, let's say settingLanguage. And let's make sure this runs on our web middleware.

So down here, again, let's just dump something, let's say settingLanguage. And let's make sure this runs on our web middleware. So within our app/Http/Kernel.php, we can add this to our web middleware. So we'll add it right here. Let me just grab this one. We can say app/Http/ and this one is called setLanguage. Okay. So this should run on every request. So we should get that dump, refresh, and we do for each of our routes here, posts, create.

So this should run on every request. So we should get that dump, refresh, and we do for each of our routes here, posts, createPost and so on. Cool. So again, we have to make sure this first param is a locale we support. So we have to store that somewhere. So within our config/app.php, if we want, we can make this an array, or in this case, I'm just going to make another variable called locales, which is an array of locales that we support. So this will be an array. In our case, it's just English and French.

So this will be an array. In our case, it's just English and French. Okay, let's save that. And now within our middleware, we can check if it's within that array. So let's say if, so if it's not in that array, so if not in array, let's grab the locale here. So again, locale equals, this time we have access to the request, and we can say segment 1. So if the locale is not within that array, so again, the locale comes from the URL, and we can compare it against that array of locales we just made.

So if the locale is not within that array, so again, the locale comes from the URL, and we can compare it against that array of locales we just made. So config app.locales. Okay. And we have to close this out. And this parenthesis is in the incorrect place. Okay. So what do we want to do here, if it's not in the array? So we want to redirect back to the current page, but with the fallback URL. So let's say redirect to current page with fallback locale.

So we want to redirect back to the current page, but with the fallback URL. So let's say redirect to current page with fallback locale. And we'll do that in a second. But for now, there's just abort, let's say 404. And we'll say locale not supported, just for now. And if the locale is supported, then we can actually set the locale. So we can say app.setLocale to the locale. Okay. And hopefully that should work. So let's get rid of this dump here.

And it does, cool. You can see the translation changed here. So everything is working correctly. And if the locale is not supported, say for example, we put DE in here. So DE, we do get a 404. But we will make it redirect back to the fallback locale in a second. So it should redirect back to the page we were on, but for English. Actually, let's handle that case right now, because we need it for our language switcher as well. So instead of aborting, let's redirect.

as well. So instead of aborting, let's redirect. So return, redirect. And we'll use the URL helper. And we'll make a new method here. Let's call it getCurrentUrl with locale. And the locale will come from the config, and we have that fallback. So app.fallbackLocale. Okay. And we need the same functionality within our language switcher.

Okay. And we need the same functionality within our language switcher. So that's why I'm defining it here. So we can get rid of this. We have to define this method somewhere, and I'll put it within a helpers.php file. So let me copy this. We'll save this. Let's make a helpers.php file. So helpers.php. Let's open up php.

So helpers.php. Let's open up php. Let's say function space.in(). This takes in a $locale. Let's actually type in the $parameter to a string. And what we want to do here. So we want to take the current URL, but replace this part with whatever is passed in. So if the $locale is not supported, in this case, we'll default to English, but we can also make use of this for our language switcher. So if we were to click on a locale that we do support, then we just want to redirect.

also make use of this for our language switcher. So if we were to click on a locale that we do support, then we just want to redirect back to this page with that specific locale. So let's grab all of the URL params with the segments method. So let's say $requestSegments. Okay. And let's just dd that. So dd $segments, and let's see what we get. So let's change this to, again, something that's not supported. Get current URL with locale.

So let's change this to, again, something that's not supported. Get current URL with locale. So we have to autoload this helpers file within our composer.json. So say composer.json, and within our autoload key, you have to specify files, and we want to autoload that file. So that would be app/helpers.php. Okay. Let's save that. We have to stop this. We have to run composer dump-autoload.

We have to stop this. We have to run composer dump-autoload. Let's run npm run dev again, and let's see if it works now. And it does, cool. So we have our segments here. So we want to replace this segment with whatever is being passed in. So let's do that. So back to our helpers, let's update the first item in the array. So segments[0] equals the locale being passed in. And then we want to redirect back to that new URL.

So segments $zero equals the locale being passed in. And then we want to redirect back to that new URL. So in this case, it'll be /en/posts, because that's the URL we're on. So we can use the implode method to change it back to a string, and the separator will be a /. So let's dd that, say implode, and the separator is a /, and we are imploding the segments method, or segments array. So let's see what we get here. Let's refresh this, and that is correct. And if we wrap this in the URL helper, which is what we're doing within here, then we should

Let's refresh this, and that is correct. And if we wrap this in the url helper, which is what we're doing within here, then we should get a full URL. So let me just do this. And we do get the full endpoint here, but we don't need that because we're doing that within our middleware. So let's get rid of that. And we can just return this. Okay. So let's save this, go back here, and this should work now.

Building Language Switcher24:13

So now with that method in place, we should now be able to implement our language switcher. So let's start off with something simple. Let's go to our navigation.blade.php, and let's put it up here, right by our dropdown. So this is the dropdown for logging out, right here. So we'll put it right here for now. And let me just put some space in between these items. So we'll say space-x for, and let's see if this is the correct spot. So say langSwitcher. Okay, so that's fine. So we can loop over our locales here, or our supportedLocales.

Okay, so that's fine. So we can loop over our locales here, or our supported locales. So we can say foreach, let's say config('app.locales') as locale. Then we can make a link here to switch to that language. So we can say <a href=", and we can use that method we just created. So it was called getCurrentURLWithLocale. So it's pretty much the same as this. So let's grab this whole thing, okay? And let's paste that in here. I think I have too many brackets here, and this should be locale.

And let's paste that in here. I think I have too many brackets here, and this should be locale. Actually, no, that was the right amount of brackets. And this should be locale as well. Okay, let's try this out. Save this. Back to the browser. locale must be of type string. And this should be locale without the config. Okay, let's try that one more time.

So we can just reuse the dropdown that's already in here. So let me just duplicate this one. Duplicate this. So instead of the username, it should be now the locale. So we can say app()->getLocale(). I believe this is the chevron down, which we can keep. And this is the contents of the dropdown. So we don't need this anymore. So how about we make a div here? APX4, PY2, let's say item one and item two, just to see if it's in the correct spot.

Save that. Okay, now it's here. Now we can do the same thing, and just loop over our locales here. So we can grab what we have up here, and we just comment this out, or copy it and then comment it out. Let's use it down here. And how about we add some sort of indicator to check off the current locale? So let's just reformat this. Let's put this within a span. So let's wrap this in a span.

Let's put this within a span. So let's wrap this in a span. And we can make a conditional here. So let's say if we can make use of isLocale, so if app is locale, the current locale, then we can put some sort of check mark here. I'll just put an X, so span, and let's just put an X here. Okay, let's see if this works. Let's refresh. It's currently French. And this has to be a block level element.

You can see the X because French is selected. And English is now selected. The translations do change, and it is selected here. And I've added a few more translations within our fr.json file, so we can see the language updating. So I'm going to refresh this. As we change to French, you'll see more of these menu items change. And they do. Cool. And even within our welcome.blade.php, there should be translations there as well.

Cool. And even within our welcome.blade.php, there should be translations there as well. So this language switcher doesn't exist in the welcome.blade.php, but we can just force the locale to fr within here. So let's say fr, and that's not updating because we're not using the __ method within our welcome.blade.php. So let's update a few things here about the documentation, the login, and the register. Let's see if login actually works. So the translation is working here, but I'm probably not using the __ method within here and here.

So the translation is working here, but I'm probably not using the __ method within here and here. So let's just change it for a few things in here, and then we'll call it a day. So within our welcome.blade.php, let's look for login. Yeah, so we have to make use of the __ method within here. So I'll just do that behind the scenes. Okay, so I'm making use of the __ method now, and let's refresh this. And you can see the French translations here. Cool. So as always, let's make a commit here.

Cool. So as always, let's make a commit here. Again, this is a separate project from the main one we've been working on. Let's git add, git commit. This is episode 11. Let's name it Laravel localization.

Defining Translation StringsTranslations in the DatabaseLanguage Switcher

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟