Sp4ce.net rss

How to have language support with Jekyll

Jekyll doesn't provide any support for localization of posts and pages. This article explains how this website use jekyll and add a multilingual support for posts and pages. There is also a small code of php (even if I am not a big fan of the language) at the end.

Languages

The site is both in English and French. Jekyll doesn’t provide any support to have posts in two languages. It means that in your website meta data, the variable site.posts contains all your posts (both in English and French).

However, I wanted two versions of the home page, one version that contains only the English posts and the French only posts, and another version that contains only the French posts and the English only posts. So I am sure that every post is displayed, and available in the user favorite language. There is one home page called index.en.html and index.fr.html, both are laid out with _layout/index.html layout.

Categories

The site contains three categories of posts: site , computer and miscellaneous The _layout/index.html layout contains three widgets with the posts related to a category. Jekyll allows you to include part of pages in another using the include statemenent. So for a category (for example computer) it is:

<div class="panel">
   <h2>{{ page.computer_activities  }}</h2>
   <ul>
   {% for post in site.categories.computer  %}
      {% include category.html  %}
   {% endfor  %}
   </ul>
</div>

page.computer_activities contains the localized title of the category, specified in index.fr.thml (in French) and index.en.html (in English).
site.categories.computer is a list of all the posts in the computer category.

Then the file _includes/category.html is very simple:

{% if post.multilingual != true  %}
   <li>{% include post_link.html  %}
   {% if post.language == 'en'  %}
      {% include english_only.html  %}
   {% elsif post.language == 'fr'  %}
      {% include french_only.html  %}
   {% endif  %}
   </p></li>
{% elsif post.language == page.language  %}
   <li>{% include post_link.html  %}</p></li>
{% endif  %}

So if it is not a multilingual post, I display it with a small flag that explain that it is a French only or English only post, else I display only the posts which have the same language as the page. (_includes/post_link.html is a small code that displays a post link, and open a <p> tag, for those who notified the closing </p>)

Get the page language

I choose these conventions:

  • When a page is multilingual, it is named page_name.language.textile (language is fr or en)
  • When a page is only in one language, you don’t put the language extension, you set the language in the jekyll yaml header of the post.

So, in both case, you know the language of a page, by looking at the url, if it is a multilingual post, or by looking at the post meta data. Then I extend the Page and Post class using ruby monkey patching in order to make this information available in the post and the page meta data in both case. For example, in the _plugins/page.rb

require '_plugins/get_language'

module Jekyll
   class Page
      # Return the data of the page with additional
      # information if the page has a specific language.
      alias orig_to_liquid to_liquid
      def to_liquid
         # Gather all the basic information of the page.
         self.data = orig_to_liquid

         # Get the language of the page from the name of the post
         language = GetLanguage.get_language(self.url)
         # merge the language information with the page meta data.
         return GetLanguage.merge_data(self.data, language)
      end
   end
end

The method GetLanguage.merge_date looks like:

# Merge the data with the language values.
def GetLanguage.merge_data(data, language)
   if (language)
      title = data['title'] != nil ? data['title'] : ''
      data = data.deep_merge({
         'language' => language,
         'multilingual' => true,
         # remove the language extension of the title
         'title' => title.gsub(".#{language}",''),
      })
   end

   return data
end

So now, I know the language of each page, available in page.language or post.language, and I know also if it is multilingual page or not. Then I can display the small french or english flag at the bottom left corner when another language is available for a post. I also create two rss feeds atom.en.xml and atom.fr.xml

Get user favorite language

At the top level of the site, there is a small index.php page, that redirects the request to the correct index.fr.html or index.en.html. This is the only piece of php code I have on the site, because it was available on my hosting server, but you could do the same thing in JavaScript if you want. The goal is to get the user favorite language by reading this information in the browser.

<?php
// Définir ici les langues disponibles
$lang_list = array("fr","en");

$accept_lang = explode(",", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
reset($accept_lang);
while (list($index, $language) = @each($accept_lang)) {
   if (eregi( "^(.+);q=([0-9.]*)$", $language , $part)) {
      $accept_lang[$index] = $part[2] . ';' . $part[1];
   } else {
      $accept_lang[$index] = '1.0;' . $accept_lang[$index];
   }
}

arsort($accept_lang); //tri par ordre de préférence
reset($accept_lang);
while (list( , $a_lang) = @each($accept_lang) ) {
   reset($lang_list);
   while ( list( , $language) = @each($lang_list) ) {
      if (strpos($a_lang,$language)!=0) {
         $selected_language = $language;
         break 2;
      }
   }
}

unset($accept_lang, $a_lang, $language);

// langague par default (english)
if (!isset($selected_language)) {
   $selected_language = 'en';
}
?>
<html>
<head>
<meta http-equiv="Refresh" content="0; url=/index.<?php echo $selected_language ?>.html" />
</head>
<body>
</body>
</html>

I get this code from my old web site and I don’t remember if it really comes out of my mind. I am definitly not a php guru, so please be kind with my skills :p I am open to any improvement you may suggest.