Internationalization and localization, or i18n and l10n as they are often abbreviated (hint for newbies: count the missing letters) are personally some of my favorite topics in web development. Fortunately, LocomotiveCMS has a powerful set of tools for dealing with these issues.
Technically, i18n and l10n aren't the same thing, but for this tutorial I'm just going to use one term, localization, which I will use to mean adapting your site for use in multiple languages and regions of the world.
Some sites don't need to be localized. If you're working on such a site that only needs to be displayed in a single language, feel free to skip this section for now. If you decide to localize the site later or start working on a different project that needs localization, you can always come back.
Setting Localization
Let's start localizing our blog. In addition to the present English localization, I am going to create a Japanese localization of the site. I chose Japanese because it's the only other language I speak, but feel free to follow along with a different language of your choice.
The first thing we need to do is add the Japanese locale to config/site.yml
.
locales: [en, ja]
The line above specifies that we have two localizations for our site: English and Japanese. The localization listed first is always the default localization. So, in this case English is the default localization. You can specify languages using their ISO code. By default, LocomotiveCMS supports:
- English (en)
- German (de)
- French (fr)
- Polish (pl)
- Brazilian Portugese (pt-BR)
- Italian (it)
- Dutch (nl)
- Norwegian (nb)
- Spanish (es)
- Russian (ru)
- Estonian (et)
- Japanese (ja)
- Simplified Chinese (zh-CN)
While we're here in site/config.yml
, let's also localize the SEO variables. These settings, like many settings in Wagon can be localized using the following format.
foo_bar:
en: baz
ja: バズ
Change the SEO variables to the values shown below.
seo_title:
en: "Travel Tips for the Adventurous!"
ja: "冒険の一歩を踏み出しましょう"
meta_keywords:
en: "travel backpacking budget adventure"
ja: "旅行 バックパッキング 安い旅行 冒険"
meta_description:
en: "Learn about cool places to see abroad, interesting things to eat, and little travel tips you won't believe you lived without."
ja: "おすすめスポット、素敵な料理、観光アドバイスなどの現地旅行情報を配信中!"
One thing to note: the name
property in config/site.yml
is one of the very few values which cannot be localized. Not to worry, we will soon encounter localization strings with which we can easily localize our site's title. Actually, for this site I won't be localizing the title as it's quite usual for Japanese websites to use English titles, but I've included this as a warning for those localizing their own sites.
Template Localization
Now that we've enabled localization, preview the site by visiting http://localhost:3333.
Everything is the same so far. Let's try with a localized URL: http://localhost:3333/en.
Still the same. Whichever language comes first in site.yml
's locale property is the default language, making our default locale English. When the URL doesn't specify a locale, the default locale is used. So, for our site, /
is the same as /en
.
Let's try another page, http://localhost:3333/en/fdsafas.
I just type in some nonsense from the home row and we've been brought to our trusty 404 page. Let's try this with the Japanese locale, http://localhost:3333/ja/fdsafas.
Nothing. In LocomotiveCMS, you need to have a separate template for each locale. Create a new 404 template for the Japanese localization named app/views/pages/404.ja.liquid
and paste in the following contents.
---
title: お探しのページが見つかりません
published: true
---
{% extends 'index' %}
{% block 'main' %}
<p>
申し訳ありません。そのページは存在しません。
</p>
{% endblock %}
Let's give that page another try.
Better, but there is still some English on this page (such as the navigation menu and title) that the template is inheriting from the index.liquid
template.
Let's localize index.liquid
, too. Create another new file, app/views/pages/index.ja.liquid
and paste in the contents shown below.
---
slug: index
title: ホーム
---
Take a look.
The template was blank, but the page has been rendered just like the English version. When you leave a localized template's body blank, the template will inherit the body from the corresponding template in the default locale.
But if we inherit the default locale's body, how do we change the text to reflect the current locale's language? There are several ways, but we will start with translation strings.
Translation strings
We have a lot of stuff that needs translating on the home page, but let's start small. Open index.liquid
, find the Home link in the navigation menu and replace it with the code below.
<a href="/{{ locale }}">{{ 'home_page' | translate }}</a>
The code above changes two things. First, the target URL is no longer the root path, but the root path plus the current locale, which is stored in the global locale
variable. Second, Home has been replaced with some liquid code that runs the string 'home_page' through the translate filter.
If you preview the site now, you will see an error where Home should be:
[unknown translation key: home]
Open config/translations.yml
and add the following code to the file.
home_page:
en: Home
ja: ホーム
If you visit the Japanese localization of the page, you will see the string has changed.
It also still displays correctly in English.
I can think of one other place where we need a translation string. Create a Japanese localization of the posts template, app/views/pages/posts.ja.liquid
, and paste in the following code.
---
slug: posts
title: 記事
published: true
position: 1
---
Then in the default posts template (posts.liquid
), change the Read More link to code below, which uses a translation string and changes the link to be locale sensitive.
<a href="/{{ locale }}/posts/{{ post._slug }}" class="btn btn-primary">
{{ 'read_more' | translate }}
</a>
Finally, add the read_more
translation string to translations.yml
.
read_more:
en: Read More
ja: もっと見る
Let's preview the page.
Looking good. You can also see that as we localize our templates, the {% nav %}
tag updates our navigation menu with the appropriate localized titles for us.
Now here's something to note about translation strings: like content entries and editable element values, these can be edited from the back-office. That means that what we put in Wagon is just for testing. The actual string values will be managed from the back-office once we push our site to an engine.
Back-office editable elements
Next up, let's look at how we can localize editable elements when developing our site with Wagon. The values of editable elements will of course later be managed through the back-office area, but it's nice to localize these values during testing (especially with a visually distinct language like Japanese).
Let's start by localizing the jumbotron text on the home page. Open app/views/pages/index.ja.liquid
and paste the following YAML into the header.
editable_elements:
'main/jumbotron_title': しばらく旅に出ませんか?
'main/jumbotron_message': おすすめスポット、素敵な料理、観光アドバイスなどの現地旅行情報を配信中!
'main/jumbotron_button': 記事に進む
On the English pages we set the editable element default values by putting the text between the opening and closing editable tags. Another method of setting editable values is to do so in the header, as shown above. We could have also chosen to use this method on the English pages if we had wanted to.
The values above are specified in the following format:
editable_elements:
'block_name/element_name': Value
Let's preview the changes in your browser.
Looks good. Though the button still links to the post page without specifying the locale. Let's change it to specify the locale, as we did with the home link above.
<p>
<a href="/en/posts" class="btn btn-primary btn-lg">
</a>
</p>
Let's move onto localizing the About page. Create a new file, app/views/pages/about.ja.liquid
, and paste in the following code.
---
slug: about
title: サイトについて
published: true
position: 2
editable_elements:
'main/caption': ソールの景福宮
'main/about_body': <p>ウィズダム・フォア・ワンダラーへようこそ。このサイトは世界中の面白い場所や旅行中に体験できることを共有する場として作られました。本サイトではおすすめの観光スポットだけではなく、便利な「旅行術」も紹介しています。 普通の旅行に飽きてしまった人、何か新しいことに挑戦してみたい人、ウィズダム・フォア・ワンダラーを利用して冒険の一歩を踏み出しましょう。ボン・ボヤージュ!</p>
---
And take another look.
Looking good!
Only one problem. If you look again at the English version of the home page or about page you will notice that the editable areas are now blank! This is a bit of a strange behavior, we might even call it a bug, and hopefully LocomotiveCMS will fix this in future versions. But for the moment, defining the values of editable elements through the YAML header of a page or any localized version of that page will cause the default value of an editable element tag not to be displayed.
So let's get our home and about pages working again by adding the editable element values to their headers. I've provided the resulting headers below so you can quickly paste them in.
Header for the home page:
---
title: Home
published: true
editable_elements:
'main/jumbotron_title': Life's to short not to get away for awhile!
'main/jumbotron_message': Learn about cool places to see abroad, interesting things to eat, and little travel tips you won't believe you lived without.
'main/jumbotron_button': Browse our articles
---
And the header for the about page:
---
title: About
listed: true
published: true
position: 2
editable_elements:
'main/caption': Gyeongbokgung, Seoul
'main/about_body': "<p>Thanks for visiting Wisdom for Wanderers! This blog aims to introduce readers to cool things to see, eat, and do all over the world. And along the way, I'll share some handy travel tips that can make wandering the globe easier and more fun than ever. The blog tends to focus on sights and places a little off the tourist trail, so unadventurous beware.</p>
<p>I hope you enjoy the site and bon voyage!</p>"
---
If you go to the English version of the site in your browser, things should look better now.
Locale Switcher
Last thing we will add to this lesson is a locale switcher to be displayed in the footer, so that site visitors can easily change the site to be displayed in their native language. Open app/views/snippets/footer.liquid
and replace with the copyright paragraph with the code shown below.
<p>
© {{ site.name | escape }} {{ now | localized_date: '%Y' }}
{% for locale_string in locales %}
{% unless locale_string == locale %}
| <a href="/{{ locale_string }}">{{ locale_string | translate }}</a>
{% endunless %}
{% endfor %}
</p>
The above code uses a new global variable, locales
, which is an array of the site's locales. The code loops through each locale and, as long as it is not the current locale, puts a link to that locale, separating each link with a pipe character. Right now there are only English and Japanese versions of the site, but assuming we added more localizations later, this code would still work.
To complete the locale switcher we must add two more translation strings. Insert the below entries into config/translations.yml
.
en:
ja: English
ja:
en: 日本語
These entries probably look backwards at first, but that's what we want. If I'm an English speaker who has wound up on the Japanese version of the site, English is easier to spot than 英語。 So how does our switcher look?
When viewing the English version of the site:
And when viewing the Japanese version of the site:
Not too bad.
Finishing Up
Today was a whirlwind! We covered how you can localize templates, strings, editable elements, and even the site's meta data. To top it off we implemented a locale switcher in the site's footer so that visitors can easily change the site's language.
I also would like to briefly point out an important feature of LocomotiveCMS's localization: its flexibility! There are a wide range of localization needs and one size doesn't fit all. When dealing with two similar languages, sometimes all that's necessary is to change the language of the text.
But when dealing with languages that have different character sets, it is not uncommon that page styles and even page layouts need to vary by locale. Then there are cultural differences. For example, while a marketing photo that features a happily kissing couple might be fine for the U.S., it's probably not a good idea to use that photo in Malaysia.
Finally, there is the fact that sometimes sites need to be translated not only linguistically. Sometimes different regions need a different pages, different content, or even a different design than the main site.
LocomotiveCMS has the flexibility to accommodate all of these situations. When you're localizing a page that changes a lot depending on the locale, you can completely override the default locale's template. When you just need to change some text here and there, you can inherit the default locale's template by leaving the template's body blank, and change the content's language with translations strings. Further, you can mix and match the above strategies, using each where needed on a page by page and locale by locale basis, so you can override only where you need to, and inherit the rest of the time.
Ok, let'scommit the changes to the source repository.
$ git add app/views/pages
$ git add app/views/snippets/footer.liquid
$ git add config/site.yml config/translations.yml
$ git commit -m "Added Japanese localization for all pages"
The next lesson continues with localization, focusing on how to localize content types to round off our site's Japanese localization.
Next: content type localization