Turning your al-folio into a dual-language website

This post is part of a series of posts that explain how to set up your own site based on the al-folio theme and add support for a second language:


al-folio is a great theme for Jekyll websites. It is very customizable and easy to use, with support for blogging, repository information, projects, and more (see demo). However, it does not support multiple languages out of the box. This post will show you how to add support for another language in your al-folio. I’ll add support for pt-BR, since it is my mother language. I’ll assume you have already cloned your copy of the al-folio repository and have it running locally.

Installing dependencies

We will do this with the help of the Jekyll Multiple Languages Plugin. It adds i18n support for Jekyll. To install it, add the following line to your Gemfile under the group :jekyll_plugins do:

gem 'jekyll-multiple-languages-plugin'

Also, add the following line to your _config.yml under plugins::

- jekyll-multiple-languages-plugin

and the following lines after it (outside plugins), for example, before the Jekyll Minifier section:

# multi language settings
languages: ["en", "pt-br"]
default_locale_in_subfolder: false

Setting default_locale_in_subfolder to false will make your main language be the root of your website, instead of being in a subfolder. For example, instead of https://george-gca.github.io/en/, it will be https://george-gca.github.io/. This is the default behavior of al-folio, so we will keep it. The first language in the list will be the default language, English in this case. Then, run bundle install to install the plugin.

Creating translation files

Create a folder called _i18n and add sub-folders for each language, using the same names used on the languages setting on the _config.yml we just added. Also, create a yml file for each language. For example, for pt-br, create a folder called _i18n/pt-br. Then, create a file called _i18n/pt-br.yml. Our directory structure should look like this:

  • _i18n/en.yml
  • _i18n/pt-br.yml
  • _i18n/en/
  • _i18n/pt-br/

Adding language toggle

Now, we need to add a language toggle to our website. We will add it to the navigation bar. Open the file _includes/header.html and add the following code before the Toogle theme mode area:

<!-- Toogle language -->
<li class="nav-item active">
  {% if site.lang == "en" %}
  <a class="nav-link" href="{{site.baseurl_root}}/pt-br{{page.url}}"> PT-BR </a>
  {% elsif site.lang == "pt-br" %}
  <a class="nav-link" href="{{site.baseurl_root}}{{page.url}}"> EN </a>
  {% endif %}
</li>

This will add a link to the other language. The page.url will keep the current page, so the user will not be redirected to the home page. Note that site.baseurl_root is a variable introduced by the Jekyll Multiple Languages Plugin, and it points to the root of the page without the language path. More information about the newly added variables can be found here.

Header with language toggle.

Adding translated titles

Until now, we added everything we needed to support the translation, but haven’t done the translation per se. We will start that now. Open the file _i18n/en.yml and add the following lines:

titles:
  about: about
  blog: blog
  cv: cv
  news: news
  projects: projects
  publications: publications
  repositories: repositories
  teaching: teaching
  submenus: submenus
  unk: page not found

This will add the titles for the pages. Now, open the file _i18n/pt-br.yml and add the following lines:

titles:
  about: sobre
  blog: blog
  cv: cv
  news: novidades
  projects: projetos
  publications: publicações
  repositories: repositórios
  teaching: ensino
  submenus: submenus
  unk: página não encontrada

This will add the titles for the pages in Portuguese. Now that we have the translated titles, we have to tell the pages to use these instead of the hardcoded ones. To do so, open all the pages under the _pages folder and change their title to use the correct titles variable. For example, the new title for the about.md page should look like this:

title: titles.about

If you run your website now, you’ll see that the titles are shown as titles.about instead of just about as it was supposed to, since its default is in English. We still need to tell the html templates to select the correct translated version of these variables. To do so, open the file _includes/header.html and change all the title variables to use the t function. The t function, or its longer version translate, will ensure that it will select the correct version from the current language yml file. More specifically, do the following changes:

<!-- <a class="nav-link" href="{{ '/' | relative_url }}">{{ about_title }} -->
<a class="nav-link" href="{{ '/' | relative_url }}"
  >{% t about_title %}

  <!-- <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ p.title }} -->
  <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
    >{% t p.title %}

    <!-- <a class="dropdown-item" href="{{ child.permalink | relative_url }}">{{ child.title }}</a> -->
    <a class="dropdown-item" href="{{ child.permalink | relative_url }}">{% t child.title %}</a>

    <!-- <a class="nav-link" href="{{ p.url | relative_url }}">{{ p.title }} -->
    <a class="nav-link" href="{{ p.url | relative_url }}">{% t p.title %}</a></a
  ></a
>

Now run your website again and you’ll see that the titles have the correct values. You can even change the language on the toggle and see that the titles change accordingly.

Header in Portuguese.

Fixing translated navigation

Everything seems to work fine, except not. If you click in the PT-BR toggle in the header, then click in another page (e.g. repositories), it will change again the titles to English. To handle this kind of situation, we have to make sure that when the user clicks the header links, it will keep the language. Lets do this first for the about page. To do so, open the file _includes/header.html and change the link to the about page to use the site.baseurl variable instead of the relative_url. More specifically, do the following changes:

<!-- <a class="nav-link" href="{{ '/' | relative_url }}">{% t about_title %} -->
<a class="nav-link" href="{{ '/' | prepend: site.baseurl}}">{% t about_title %}</a>

Now, if you are in another page, for example, the repositories page, and click in the PT-BR toggle, then click in the about page, it will keep the language in the titles. Now, let’s enable this for all the other pages. To do so, open the file _includes/header.html again and do the following changes:

<!-- <a class="nav-link" href="{{ '/blog/' | relative_url }}">{{ site.blog_nav_title }} -->
<a class="nav-link" href="{{ '/blog/' | prepend: site.baseurl }}"
  >{{ site.blog_nav_title }}

  <!-- <a class="dropdown-item" href="{{ child.permalink | relative_url }}">{% t child.title %}</a> -->
  <a class="dropdown-item" href="{{ child.permalink | prepend: site.baseurl }}">{% t child.title %}</a>

  <!-- <a class="nav-link" href="{{ p.url | relative_url }}">{% t p.title %} -->
  <a class="nav-link" href="{{ p.url | prepend: site.baseurl }}">{% t p.title %}</a></a
>

Adding translated titles inside the pages

Now everything is working fine! The sun is rising, and the world is colorful again :rainbow:. But wait, there is still stuff missing. If you click in the PT-BR toggle, then click in the publicações page, you’ll see that the title of the page is still titles.publications.

What should be a translated title.

Oh crap, will this ever end someday? :scream: Well, yes, it will. We just need to tell the pages to use the correct translated title. To do so, let’s see which templates do the pages use. The page publications for example, which is located in file _pages/publications.md, uses the template page, as can be seen by the following line in the beginning of the file:

layout: page

So, lets open the file _layouts/page.html and change the title to use the t function. More specifically, do the following changes:

<!-- <h1 class="post-title">{{ page.title }}</h1> -->
<h1 class="post-title">{% t page.title %}</h1>

Now, if you run your website, it will not work. This happens because the t function is now trying to translate a variable that is not defined. But where? If you do a little search, you’ll notice that the publications page is not the only one that uses the layout: page. All pages that use it are:

  • 404.html
  • news.html
  • _pages/dropdown.md
  • _pages/projects.md
  • _pages/publications.md
  • _pages/repositories.md
  • _pages/teaching.md
  • _projects/1_project.md
  • _projects/2_project.md
  • _projects/3_project.md
  • _projects/4_project.md
  • _projects/5_project.md
  • _projects/6_project.md

You need to change the title for ALL these pages. We already did it for the pages inside _pages/, so let’s do it for the rest. Open the file 404.html and change its title: "Page not found" to title: titles.unk, since we already defined titles.unk in both _i18n/en.yml and _i18n/pt-br.yml. To keep the projects section more organized, let’s create new attributes for it inside each of the translation files. So, the new _i18n/en.yml and _i18n/pt-br.yml will look like this, respectively:

titles:
  about: about
  blog: blog
  cv: cv
  news: news
  projects: projects
  publications: publications
  repositories: repositories
  teaching: teaching
  submenus: submenus
  unk: page not found
projects:
  titles:
    project1: Project 1
    project2: Project 2
    project3: Project 3
    project4: Project 4
    project5: Project 5
    project6: Project 6
titles:
  about: sobre
  blog: blog
  cv: cv
  news: novidades
  projects: projetos
  publications: publicações
  repositories: repositórios
  teaching: ensino
  submenus: submenus
  unk: página não encontrada
projects:
  titles:
    project1: Projeto 1
    project2: Projeto 2
    project3: Projeto 3
    project4: Projeto 4
    project5: Projeto 5
    project6: Projeto 6

Now, all we have to do is open all the files inside _projects/ and change its titles. For the file _projects/1_project.md, for example, will look like this:

title: projects.titles.project1

Finally, we can run our website again and it will work as expected. :tada:.

Now THIS is a translated title.

Don’t forget to do the same for the other pages that use different layouts, like cv. Open the file _layouts/cv.html and modify:

<!-- <h1 class="post-title">{{ page.title }} {% if page.cv_pdf %}<a href="{{ page.cv_pdf | prepend: 'assets/pdf/' | relative_url}}" target="_blank" rel="noopener noreferrer" class="float-right"><i class="fas fa-file-pdf"></i></a>{% endif %}</h1> -->
<h1 class="post-title">
  {% t page.title %} {% if page.cv_pdf %}<a
    href="{{ page.cv_pdf | prepend: 'assets/pdf/' | relative_url}}"
    target="_blank"
    rel="noopener noreferrer"
    class="float-right"
    ><i class="fas fa-file-pdf"></i></a
  >{% endif %}
</h1>

Adding translated content

Now, let’s see how to add translated content. For example, let’s say that we want to translate the biography in the about page. The best way to achieve this is to create translated parts of the pages for each language, and then import the correct version. For this, cut the whole biography part of the about page (_pages/about.md), leaving only the header. Then, create a new file _i18n/en/pages/about.md and paste the content there. Do the same for the file _i18n/pt-br/pages/about.md, but adding the translated version. Now, in the file _pages/about.md, add the translate_file function, pointing to the new about files. The final _pages/about.md will look like this:

---
layout: about
title: titles.about
subtitle: <a href='#'>Affiliations</a>. Address. Contacts. Moto. Etc.
permalink: /

profile:
  align: right
  image: prof_pic.jpg
  image_circular: false # crops the image to make it circular
  address: >
    <p>555 your office number</p>
    <p>123 your address street</p>
    <p>Your City, State 12345</p>

news: true # includes a list of news items
selected_papers: true # includes a list of papers marked as "selected={true}"
social: true # includes social icons at the bottom of the page
---

{% translate_file pages/about.md %}
Biography in Portuguese.

Fixing page title in the browser

Currently, when you open a section of your site, in the browser tab the section name is not translated. To solve this, open the file _includes/metadata.html and change the following code:

{% if page.url == '/blog/index.html' %}
  {{ site.blog_nav_title }} | {{ title }}
{%- elsif page.title != 'blank' and page.url != '/' -%}
  {{ page.title }} | {{ title }}
{%- else -%}
  {{ title }}
{%- endif -%}

for this code:

{% if page.url == '/blog/index.html' %}
  {{ site.blog_nav_title }} | {{ title }}
{% elsif page.url contains '/blog/' %}
  {{ page.title }} | {{ title }}
{%- elsif page.title contains 'Announcement' -%}
  {{ title }}
{%- elsif page.title != 'blank' and page.url != '/' -%}
  {% t page.title %} | {{ title }}
{%- else -%}
  {{ title }}
{%- endif -%}

This will trigger another problem, caused by the title of the second news in the about page. We need to add a translated title for this new. Now, do the following changes:

File _news/announcement_2.md:

# title: A long announcement with details
title: news.titles.news2

File _i18n/en.yml:

news:
  titles:
    news2: A long announcement with details

File _i18n/pt-br.yml:

news:
  titles:
    news2: Um anúncio longo com detalhes

Summing up

Adding a translated version of your site is not hard, but it is kind of annoying. Even with the help of this great plugin, there are some little details that, if missed, will take a lot of time and patience to master (at least took me). There are a few things that can be done also, like translating the description of the pages and more of their content, but since these can be done based on the same concepts that I showed here, I’ll leave it as a homework :laughing:. I will detail how to create translated versions of your cv and blog in other posts, since this one is already really long. I hope this post was useful to you. If you have any questions, feel free to ask in the comments.