Não é difícil estruturar o seu site Jekyll para produzir conteúdos em várias línguas diferentes. Acabei de fazer isso com o meu site pessoal e vou explicar aqui nesse post as principais etapas que realizei.

Jekyll é um gerador de sites estáticos muito simples que facilita o desenvolvimento de páginas pessoais e blogs como este aqui. Recentemente eu estava muito interessado em produzir conteúdos não só em inglês, mas também em português, minha língua nativa. Então, pesquisei algumas formas de estender o Jekyll para suportar mais de uma língua. Meus principais objetivos foram:

  • Poder ter um blog separado em Português e outro em Inglês
  • Poder escrever artigos somente para um desses blogs (ex: um texto somente em português, tal como este aqui).
  • Poder escrever artigos que fossem equivalentes nas duas línguas. Esses artigos precisam ter links para suas traduções em outras línguas.
  • Separar o Feed RSS por língua.
  • Ter a tradução de outras páginas do site, como a minha página Sobre
  • Permitir ao usuário escolher a língua que ele deseja visualizar no site.

Apesar da existência de alguns plugins, a melhor abordagem que encontrei foi a descrita por DJ Walker nesse artigo, onde ele explora a simplicidade das estruturas de pastas do Jekyll e os mecanismos de Front Matter para construir um site internacionalizável onde fosse fácil construir novas páginas e referenciar páginas equivalentes traduzidas em outras línguas. Recomendo a leitura antes de seguir aqui, principalmente pois o texto dele discute maiores detalhes.

Sendo assim, resolvi focar nos passos cruciais e adicionar algumas etapas que ele não cobriu que podem ser úteis para outras pessoas também, tais como produzir feeds RSS separadas para cada língua e considerar paginação do seu blog.

Configuração

Antes de começar, eu instalei dois plugins:

Veja a documentação no site do Jekyll de como instalar plugins.

Adicione as seguintes configurações no arquivo _config.yml:

# Localization
lang: 'en' # Língua padrão
languages: ["en", "pt-br"] # Línguas disponíveis
languageNames:
  en: English
  pt-br: Português (BR)

# PAGINATION
pagination:
  enabled: true
  per_page: 5
  paginate_path: "/page/:num/"
  collection: 'posts'
  category: 'posts'
  sort_reverse: true

Estrutura

A primeira etapa consiste em separar as pastas do seu projeto por língua:

_includes/
_layouts/
en/
  _posts/
    2018-08-02-debcamp18.md
    2019-04-15-ruby-initialize-objects-with-a-hash.md
  index.html
  feed.xml
  about.md
pt-br/
  _posts/
    2019-04-15-ruby-initialize-objects-with-a-hash.md
  index.html
  feed.xml
  about.md
public/
_config.yml

Essa estrutura adiciona um namespace na URL das suas páginas que indica a língua do conteúdo. Por exemplo:

Metadados nas Páginas

Dentro de cada página nós iremos usar o Front Matter para adicionar metadados relacionados à língua que está sendo utilizada por ela. Por enquanto, a única chave adicionada será locale que indicará a língua do conteúdo da página. Ou seja, todas as páginas dentro da pasta en terão locale: en, enquanto as páginas dentro de pt-br terão locale: pt-br.

en/about.md

---
layout: profile
title: About
locale: en
---

pt-br/about.md

---
layout: profile
title: Sobre
locale: pt-br
---

Dados e Configuração

Crie uma pasta nova chamada _data:

mkdir _data

Dentro dela, vamos criar dois arquivos:

  • languages.yml - arquivo que irá conter alguns dados gerais sobre as línguas disponíveis no site que podemos referenciar nas páginas. Por exemplo, no meu caso, contém o nome da língua e um emoji unicode da bandeira referente ao país que fala aquela língua.

_data/languages.yml

en:
  label: English
  icon: 🇺🇸
pt-br:
  label: Português (BR)
  icon: 🇧🇷
  • translations.yml - arquivo que irá conter traduções de algumas strings que estarão presentes em arquivos genéricos que não estão inclusos dentro das pastas de línguas disponíveis, tais como os arquivos das pastas _includes e _layouts.

_data/translations.yml

related-posts:
  en: "Related Posts"
  pt-br: "Artigos Relacionados"
different-language:
  en: "Read this page in a different language"
  pt-br: "Leia esta página em um idioma diferente"

Com essa estrutura, os links passaram a ser relativos à língua que está sendo utilizada. Assim, precisamos modificar os links que existem nos arquivos da pasta _includes e _layouts. Por exemplo, eu tenho o arquivo _includes/navbar.html que contém a barra de navegação presente em todo o site composta por links para a homepage e as principais páginas.

Dado que marcamos as nossas páginas usando Front Matter, fica fácil modificar os links, uma vez que em todas as páginas temos acesso à variável page que referência a página atual.

<a href="{{ site.baseurl }}/{{ page.locale }}">
  Home
</a>

Antes:

<a href="">Home</a>

Depois:

<a href="/pt-br">Home</a>

Ou seja, os links do site passarão a direcionar para as páginas traduzidas na língua que o usuário está usando.

Artigos do Blog

Uma das principais vantagens de se permitir várias línguas em seu site está na possibilidade de ter um blog que atinge públicos diferentes. Você passa a ter a opção de publicar textos que interessam ao público de uma língua/região específica ou até mesmo distribuir o mesmo conteúdo em diversas línguas diferentes.

Para alcançar essa flexibilidade, nós vamos adicionar essas duas novas chaves nos metadados de cada post (Front Matter):

  • locale - língua em que o texto do artigo está escrito (en ou pt-br)
  • lang-ref - uma string que identifica unicamente um post para que possamos encontrar posts equivalentes traduzidos em outras línguas. Ou seja, um post em português que tiver o mesmo lang-ref de outro post em inglês serão textos equivalentes.

en/_posts/2019-04-15-ruby-initialize-objects-with-a-hash.md

layout: post
title: "Quick Note #1 - Initialize Ruby objects passing named arguments with a Hash"
lang-ref: ruby-initialize-objects-with-a-hash
locale: en

pt-br/_posts/2019-04-15-ruby-initialize-objects-with-a-hash.md

layout: post
title: "Nota Rápida #1 - Inicializar objetos em Ruby passando argumentos nomeados com um Hash"
lang-ref: ruby-initialize-objects-with-a-hash
locale: pt-br

Altere o layout do seus posts para que seja possível encontrar artigos equivalentes em outras línguas:

_layouts/post.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
layout: default
---
<article>
  <header id="main">
    <h1 id="{{ page.title | cgi_escape }}" class="title">{{ page.title }}</h1>
    <p class="meta">
    {{ page.date | date: "%B %-d, %Y" }}
    {% if page.author %} - {{ page.author }}{% endif %}
    </p>
  </header>
  <section class="post-content">
    <p class="center">
      {% assign posts=site.posts | where:"lang-ref", page.lang-ref | sort: 'locale' %}
      {% if posts.size > 1 %}
      <em><b>{{ site.data.translations['different-language'][page.locale] }}:</b></em>
        {% for t_post in posts %}
          {% if t_post.locale != page.locale %}
            <a href="{{ site.base-url }}{{ t_post.url }}">{{ site.data.languages[t_post.locale].icon }} {{ site.data.languages[t_post.locale].label }}</a>
          {% endif %}
        {% endfor %}
      {% endif %}
    </p>
    {{ content }}
  </section>
</article>

Algumas notas sobre o layout acima:

  • Na linha 14, nós procuramos outros posts que possuam a mesma lang-ref do post atual que está sendo renderizado.
  • Caso existam posts em outras línguas equivalentes ao atual (linha 15), nós apresentamos os links para as traduções disponíveis.
  • Repare que usamos na linha 16 a string different-language traduzida que definimos anteriormente em _data/translations.yml
  • Da mesma forma, na linha 19, utilizamos a o emoji e o nome das línguas que definimos em _data/languages.yml

Blog e Paginação

Com as modificiações feitas até agora temos praticamente dois blogs diferentes, um composto por artigos na língua portuguesa e outro com artigos em inglês. Por isso, dentro da pasta de cada língua teremos uma página específica para o blog que carregará somente os artigos daquela língua:

en/
  blog.html
pt-br/
  blog.html

pt-br/blog.html

---
layout: blog
locale: pt-br
pagination:
  enabled: true
  collection: posts
  locale: pt-br
---

en/blog.html

---
layout: blog
locale: en
pagination:
  enabled: true
  collection: posts
  locale: en
---

Os metadados que colocamos em pagination são usados pelo plugin jekyll-paginate-v2. Ele já consegue separar a paginação baseada no atributo locale das páginas. Dessa forma, o layout dos blogs fica da seguinte forma:

_layouts/blog.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div class="posts">
    {% for post in paginator.posts %}
    <div class="post-teaser">
      <span>
        <header>
          <h1>
            <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">
              {{ post.title }}
            </a>
          </h1>
          <p class="meta">
            {{ post.date | date: "%B %-d, %Y" }}
          </p>
        </header>
      </span>
    </div>
    {% endfor %}
  </div>

  {% if paginator.total_pages > 1 %}
    <div class="pagination">
      {% if paginator.previous_page %}
        <a href="{{ paginator.previous_page_path | prepend: site.baseurl | replace: '//', '/' }}" class="button" >
          <i class="fa fa-chevron-left"></i>
        </a>
      {% endif %}
      {% if paginator.next_page %}
        <a href="{{ paginator.next_page_path | prepend: site.baseurl | replace: '//', '/' }}" class="button" >
          <i class="fa fa-chevron-right"></i>
        </a>
      {% endif %}
    </div>
  {% endif %}
</div>

Algumas notas sobre o layout acima:

  • Na lina 2, percorremos os posts disponíveis no paginator, que já foi filtrado de acordo com os atributos do Front Matter definidos em en/blog.html e pt-br/blog.html
  • O bloco entre a linha 20 e a linha 33 adiciona os links para navagar entre as páginas disponíveis, caso tenha mais de uma página de artigos daquela língua.

Feed RSS

A construção do feed RSS segue a mesma lógica da página do blog, ou seja, teremos dois arquivos feed.xml, um em cada língua.

pt-br/feed.xml

---
layout: feed
locale: pt-br
---

en/feed.xml

---
layout: feed
locale: en
---

_layouts/feed.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ site.theme_settings.title | xml_escape }}</title>
    <atom:link href="{{ "/feed.xml" | relative_url }}" rel="self" type="application/rss+xml"/>
    <link>{{ site.url }}{{ site.baseurl }}/</link>
    <description>{{ site.theme_settings.description | xml_escape }}</description>
    <pubDate>{{ site.time | date_to_rfc822 }}</pubDate>
    {% assign posts=site.posts | where:"locale", page.locale | sort: 'locale' %}
    {% for post in posts limit:15 %}
    <item>
        <title>{{ post.title | xml_escape }}</title>
        <link>{{ post.url | relative_url }}</link>
        <guid isPermaLink="true">{{ post.url | relative_url }}</guid>
        <description>{{ post.content | xml_escape }}</description>
        <pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
    </item>
    {% endfor %}
  </channel>
</rss>

Esse layout possuí a estrutura do Feed RSS. A linha 9 é a mais importante desse arquivo, pois ela atribui à variável posts somente os artigos que compartilham o mesmo locale do feed que está sendo renderizado.

Redirecionando a Homepage

Com as modificações feitas até agora, todo o conteúdo do site estará sempre sob o namespace de uma das duas línguas suportadas, ou seja, as URLs das páginas seguem uma das duas estruturas abaixo:

  • /en/page
  • /pt-br/page

No meu caso, eu coloquei a língua inglesa como padrão para o meu site. Isso quer dizer que sempre que alguém acessar a raiz do site, eu quero redirecioná-lo para /en/

Para isso, usamos o plugin jekyll-redirect-from. Na raiz do projeto, criamos a página index.html e definimos para onde ela irá redirecionar:

---
redirect_to: /en/
---

Por fim, falta somente adicionar uma opção no menu de navegação que permita ao usuário mudar o idioma do site. No meu caso, eu acrescentei o seguinte trecho no arquivo _includes/navbar.html, que contém o menu de navegação que é renderizado em todas as páginas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Language Switcher -->
<div class="dropdown">
  <button class="dropbtn">{{ site.data.languages[page.locale].icon }} {{ site.languageNames[page.locale] }}</button>
  <div class="dropdown-content">
    {% for locale in site.languages %}
      {% if page.locale == locale %}
        {% assign class_name = 'active' %}
      {% else %}
        {% assign class_name = '' %}
      {% endif %}
      <a class="{{ class_name }}" href="{{ site.baseurl }}/{{ locale }}/">{{ site.languageNames[locale] }}</a>
    {% endfor %}
  </div>
</div>
  • Na linha 3, baseados na língua da página atual, usamos o ícone/emoji de bandeira que definimos em _data/languages.yml assim como o nome da língua que definimos em _config.yml. Esse botão terá a língua atual que está sendo visualizada pelo usuário.
  • Caso o usuário clique no botão, temos um dropdown com as opções de línguas disponíveis, que é definida pela configuração site.languages, conforme a linha 5
  • Cada língua disponível terá um link para que o usuário possa trocar a língua do site, como pode ser visto na linha 11 do código acima.

No caso, usei as classes do Bootstrap para criar o dropdown.

Conclusões

Com a estrutura apresentada aqui nesse artigo fica bem fácil produzir conteúdos para o seu site em diferentes línguas. Sinta-se livre para personalizar e ajustar os códigos apresentados para sua própria necessidade.

Aproveite para ler o conteúdo do meu site em Inglês ou em Português se você preferir.

Aproveite também para assinar o meu feed em uma das duas línguas:

Por favor, caso observe erros de escrita ou nos códigos, me deixe saber através dos comentários ou me envie um email.


Let’s get moving on! 😉