Laravel Localization

Feb. 12, 2017, 12:01 p.m.

I decided to try to translate this site into French, given that I live in the French-speaking part of Switzerland. Laravel has a lot of great tools for localization built-in, but there were a few things sorely lacking. Laravel, by default, has localization files in /resources/lang/en. Each file is just an array with a key and the translated text as the value. If you want to add a new language you just copy the files over into a new directory, in this case /fr, and translate the text directly in there. In the views instead of typing in the text directly you call trans('file.key') and it pulls the text for 'key' out of the 'file.php' in the appropriate language directory. This couldn't be any easier.

The hard part was when I started trying to figure out how to set the language to be displayed automatically. Laravel pulls this value from config/app.php, and you can change this value easily, but because Laravel is RESTful it has to be done on every request. So I decided to stick the actual language in a session variable and then change the value in the config array if needed.

I tried to make a middleware to do this on each request, but this didn't work because it seemed as if the session either wasn't saving from middleware, or possibly wasn't initialized yet in the middleware. More on this below. So I abandoned the middleware route and added a function in my controller that sets the language thusly:

App::setLocale( session('locale') ? session('locale') : config('app.locale'));

This worked fine, I just need to make sure to call the function everytime a page may need to be translated. My next step was to try to add a subdomain 'fr.' that would automatically set the language to French. You can do this in the web.php routes file, but from what I can tell it needs to be called on every single route, which seemed like an awful lot of work for something that should be pretty easy. 

So I went back to the middleware and created a middleware called SetLanguage that I added to app\Http\Kernel.php so it runs on every request. The middleware is quite simply this:

$pieces = explode('.', $request->getHost());
if($pieces[0] == 'fr'){
    session(['locale' => 'fr']);
}
return $next($request);

And this works fine. I think the problems I had with the middleware the first time I tried it was that Laravel has changed how sessions are handled in new releases, and the Session facade no longer works or works differently. Instead you now use the session() helper function or call $request->session() to modify the session. I had been trying to use the Session facade.

One thing that seems a bit odd is that since the middleware runs on every request it should translate every page that is called from the fr. subdomain to French. In actuallity it initially sets the language to French, but if you change the language using the drop-down menu it keeps the selection. This doesn't seem right, but in this case the actual behavior makes more sense than the expected behavior, so I am ignoring this bug.

Once I got this figured out, the translation was a simple matter of replacing text in my views with references to the lang files, which went smoothly, although I did have to spend some time trying to figure out how to translate some technical terms into French, and still am not sure I have them all translated properly.  

Labels: coding , laravel , localization

No comments

Update on Localization

Feb. 12, 2017, 12:01 p.m.

After I had everything written and working I decided to go back and try to figure out why I couldn't run my function to get the language out of session and put it into the app config globally. It didn't make sense that I needed to cut and paste the same function into every single controller. So I tried it again as a helper function and this time it works perfectly. I have no idea why it didn't work before, but it's working now. I took the function out of the controllers and replaced it with a call to the helper, which is much better because I don't need to have the same exact code repeated in 10 different places, although it is a bit frustrating that I don't know why it didn't work at first.

I also added a call to setlocale() in the helper function which allows dates to be localized using strftime() instead of date(). I spent a while trying to get this working - I had to add the locales to the server using:

dpkg-reconfigure locales

And select the locales you want to use and then restart Apache. I wasn't able to get the date localization working on my local dev environment, for which I am using Homestead. I am still not sure why, the main difference between my production server and my dev environment is that the former uses Apache and the latter Nginx, so maybe it has something to do with that. As much as I hate not knowing why things don't work that should work, I'm not going to spend more time trying to figure it out since it is working here.

Labels: coding , localization

No comments

The Archive List on the Right Here

Oct. 28, 2016, 9:45 a.m.

It was a bit tricky for me to get the archive list on the right here working properly. It was easy to do in normal PHP, but I wanted to stay within a strict MVC model where all the processing is done in the controller and the view just displays the data. I was having a hard time figuring out how to properly group the items without setting variables in the view.

I ended up building an array with three nested levels in the controller. The array is as follows:

  • $year => [
    • $month =>[
      • 'title' => $title
      • 'slug' => $slug ]
    • ]

I start with my array with the $year as key and an empty array as the value. Then for each month I push on an array with $month as key and an empty array as the value. Then for each post I push on an array with two values - the post $title and $slug. This array is passed into the view.

To display it I just do three nested foreach loops:

  • foreach($array as $year => $months)
    • // output $year and the corresponding HTML for the collapse panels
    • foreach($months as $month => $posts)
      • // output $month and the HTML for the collapse panels
      • foreach($posts as $post)
        • // output $post['slug] and $post['title']

Simple, clean and easy! Much simpler than the other ways to do this I found online. 

Labels: coding

No comments

First Post

Oct. 28, 2016, 9:44 a.m.

I don't really have much to say, and I may not ever post here again. I'm just doing this to teach myself Laravel. But I can't just leave this page blank with nothing here, so I figured I should write something. 

Honestly, I don't much care for blogs. I don't really think my opinions are so great that everyone needs to read them and I certainly don't want to read everyone else's opinions about everything. I used to keep a blog on blogger where I would post random things I found, but I haven't updated it since 2009. 

But writing a little blog module seemed like a good way to learn so I made this. And it would be really silly if it was just a blank page with nothing here. 

Labels: test

4 comments

Archives