5 min to read
Jekyll i18n
Jekyll & multi-language sites
How are you guys doing? I’ve been building this blog to make it multilingual and I wanted to share with you how the process went in case you ever need or are tempted to build something like this with Jekyll.
There is quite a lot of material going around, with and without plugins, but there wasn’t much clarity, among several other aspects that I tried and didn’t work as I expected, plus some disadvantages of using plugins for this kind of functionality that then don’t behave as expected once deployed. So here we go with the implementation without plugins!
To keep in mind the stack I use is #Jekyll, #Liquid for templating, #RubyGems for packages and everything running on the #Ruby terminal.
Small intro to Jekyll
To set up a blog or static site you will need to install Ruby.
Once installed, run the following commands to configure Jekyll fresh:
$ gem install bundler jekyll
~ $ $ jekyll new my-awesome-site
~ $ cd my-awesome-site
~/my-awesome-site $ bundle exec jekyll serve
# => Now browse to http://localhost:4000
For more documentation and details here!
What is i18n?
“It is common practice in the English language (especially in the computer field) to abbreviate internationalization with the numeronym “i18n”. This is because there are 18 letters between the first i and the last ene of the word. The same applies to localization, which is abbreviated to “L10n””.
In short, whenever we mention i18n or internationalization, we are referring to the fact of translating our content into N languages.
Jekyll
We start with skaffolding, very similar to what Jekyll already provides from scratch, the idea of organizing the divisions by language is subjective and my suggestion is the following, assuming a Spanish and English case:
_posts
en
post1.md
post2.md
es
post1.md
post2.md
_config.yml
We set these new paths and configure them in our config as follows:
defaults:
- scope:
path: '_posts/en
type: 'posts
values:
'permalink: 'en'
language: en
- scope:
path: '_posts/en'
type: 'posts'
values:
permalink: 'en'
language: en
- scope:
path: '_posts/en'
type: 'posts'
values:
permalink: 'en/:title'
language: en
- scope:
path: '_posts/en'
type: 'posts'
values:
permalink: 'en/:title'
language: en
As we can see above, the idea is to configure the paths relative to the languages that respect the structure we set up. The most important points to note are the following:
- Path will refer to the actual location of the folder where the posts and their language subfolder are located.
- Permalink will refer to the url that we need to build based on our structure. In this case, :title is a placeholder to dynamically point the urls to the names of each post automatically.
- And last but not least, at the beginning in defaults, something that I did not find in other blogs and I find important, is to be able to set the root url so that it can also be translated, that is to say, in the path of the domain to have for example /en or /es to be able to point the home to each language respectively, without having selected a post to read.
In addition to this, in our index.html and index.markdown files:
---
layout: home
permalink: en
layout: home
permalink: es
---
Liquid: i18n inside templates
Now here it comes the fun part to manage these languages on our views. Using Liquid, which is an open source templating engine from Shopify. If you used any engine like Twig or Smarty you’ll be friends in no time.
Language selector
{% assign url_language = page.url | split: "/" %}
<div class="languages">
<a class="languages__link {% if url_language[1] == 'en' %} highlight-language {% endif %}" href="/en">English</a>
<a class="languages__link {% if url_language[1] == 'es' %} highlight-language {% endif %}" href="/es">Español</a>
</div>
Filtered posts
Here we are going to filter the posts we display, so that only those that meet the language condition according to the url are displayed:
{% assign url_language = page.url | replace:’/’,’’ %} {% assign posts = site.posts | where_exp:”post”,”post.language == url_language “ %} {% for post in posts %} … {% endfor %}
And in the case of other filters that exist for other conditions, we can add the language filter:
{% assign next_posts = site.posts | where_exp:”post”,”post.is_generated != true” | where_exp:”post”,”post.path != page.path” %} {% assign url_language = page.url | split: “/” %} {% assign locale_next_posts = next_posts | where_exp:”post”,”post.language == url_language[1] “ %} {% assign shuffled_array = locale_next_posts | shuffle %} {% for post in shuffled_array limit:3 %} … {% endfor %}
Bonus!
Translations.yml
In case you want to translate not only posts, but also .yml configurations for texts that cross the site, it is also possible! Example:
translations:
text:
new_post: "New Post"
see_also: "See also"
search: "Search"
author: "Author"
share: "Share"
comments: "Comments"
button:
read_now: "Read Now"
share_on_twitter: "Share on Twitter"
share_on_facebook: "Share on Facebook"
pagination:
page: "Page"
of: "of"
next_page: "Next Page"
next_post: "Previous Page"
Redirects
There are several ways to redirect your index to one of these default languages, one of the simplest (that also works with Github pages) is to install the gem ‘jekyll-redirect-from’ more info here
We add in _config.yml the new plugin and add it to the whitelist:
plugins:
- jekyll-redirect-from
whitelist:
- jekyll-redirect-from
Finally we create an index.md where we are going to set the paths that we want to redirect to our home:
title: home
redirect_from:
- /
References of a blog that finally helped me to achieve these changes without the need of plugins here!