Turning the Documentation Theme for Jekyll into a Multilanguage Theme

Concept

The purpose of this project is to turn the Documentation Theme for Jekyll by Tom Johnson into a multilanguage theme, while ensuring:

  • compatibility with the existing yml files
  • compatibility with existing pages in a website
  • flexibility and economy of localization mechanisms
  • English as the fallback language

One of the main resources for this project is Sylvain Durands post Making Jekyll multilingual.

This project has been based on Access Now Helpline’s Community Documentation website, which contains pages in several languages, but does not include posts.

Localized Parts

  • Pages: pages in pages/mydoc as well as the index page (Liquid syntax).

  • Menus: Sidebar and top/navbar (Liquid syntax).

  • Tags: description and links (Liquid syntax)

  • Other strings: summary, search bar, title of website, etc. (Liquid syntax, CSS).

Features

  • Support for RTL languages (JS)
  • Script for first-time visit of home page (JS)
  • Script for the language selector (JS)
  • Script to translate website title in home page (Liquid syntax)
  • Tag lists changing based on the selected language (Liquid syntax)
  • URLs in the sidebar changing based on the selected language (Liquid syntax)

Summary

Pages

The multilanguage mechanism for pages is based on the ref: and lang values in the YAML front matter, with:

  • one common identifier (ref:) in all localized pages
  • one language identifier (lang: followed by a 2-letter language code) for each different language

The mechanism for localizing the home page is identical, but the localized index files are placed withing the pages folder together with the other pages.

To localize titles in menus, the “multilanguage” value is assigned to the title value, followed, at the same indentation level, by a key and a value (key: value) where the key is the language code, and the value is the localized string, e.g.:

- title: multilanguage
  en: Email Templates
  it: Modelli di Email
  es: Plantillas de correo
  ru: Шаблоны писем
  fl: Mga Gabay sa Email
  de: E-mail-Vorlagen

For internal links in the sidebar, the language identifier will be used to create links to the localized corresponding file (e.g.: ar_title.html for Arabic, where title.html is linked).

For external links in the top navigation menu, the “multilanguage” value is assigned to the external_url key, followed by a m_externalurl child field, whose children are in turn language identifiers and strings, e.g.:

external_url: multilanguage
          m_external_url: 
            en: https://accessnow.org/help
            it: https://accessnow.org/help
            de: https://www.accessnow.org/help-deutsch/?ignorelocale
            fil: https://www.accessnow.org/helpline-tagalog/?ignorelocale

Tags

As for pages, also tags are localized by creating a tag page per language in pages/tags. Each localized file is named with the corresponding language code (e.g. ru_tag_harassment_templates.md will be the Russian localization for tag_harassment_templates.md), and includes the ref, lang and langNow in the YAML front matter.

Other Components

Other strings to be localized are located in _config.yml, _data/strings.yml, and css/customstyle.css.

The Scripts in Detail

Script for the Language Selector

(JS) in file community-documentation/_layouts/default.html

    var languages = ["ar", "de", "en", "es", "fl", "fr", "it", "pt", "ru"];

    var languageshomeredirect = ["ru","de","it"];
    //sostit with Liquid sintax?

    var written = new Array(9 + 1).join('0').split('');

    var langind = 0;

    var towrite = [];

    var i = 0;

    function listaWritten() {
        $("ul.multilang li").each(function(index) {
            var langex = $(this).text();
            written[index] = langex.trim();
        });
        //console.log(written);
    }

    function writeAllLang() {
        $("ul.multilang li").remove();
        for (a = 0; a < languages.length; a = a + 1) {
            var singlelang = towrite[a];
            $("ul.multilang").append(singlelang);
        };
    }

    function addMultilangLink() {
        listaWritten();
        for (i = 0; i < languages.length; i = i + 1) {

            if (languages[i].trim() == written[langind]) {
                var htmlwritten = $("ul.multilang li");
                //console.log("nothing to do" + htmlwritten[langind] + langind);
                var writenow = $(htmlwritten[langind]).html();
                //console.log("to do" + writenow);
                towrite.push("<li>" + writenow + "</li>");

                langind = langind + 1;
            } else {
                //$("ul.multilang").append('<a href="./how_translate_this_website.html" class="tradu">' + languages[i] + '</a>');
                towrite.push('<li><a href="#" class="tradu">' + languages[i] + '</a></li>');
            }
        };

        writeAllLang();
        //onclick appear #wannat
        $('.tradu').click(function() {
            $('#wannat').removeClass("hide")
        });

    }; /* cycles through each li of ul.multilang and checks if they have a class with a language; if the class exists, the script proceeds, else, if it does not exist, it appends a standard li.*/

Support for RTL Languages

(JS) in file community-documentation/_layouts/default.html

     var rtllang= [
"ar",  // Arabic
"dv",  // Divehi; Dhivehi; Maldivian
"he",  // Hebrew (modern)
"fa",  // Persian
"ps",  // Pashto; Pushto
"ur",  // Urdu
"yi",  // Yiddish
];
    function toggleMultilangRtl(){
        var languageset= $("html").attr("lang");
       //languageset= languageset.trim();
        for(i=0;i<rtllang.length;i++){
        if (languageset == rtllang[i]){
            $(".post-content").addClass("multilanguagertl");
            $(".post-header").addClass("multilanguagertl");
            return true;                
            }
    else{
          $(".post-content").removeClass("multilanguagertl");
        $(".post-header").removeClass("multilanguagertl");
        }
        };
    };
    
    $(function() {
       
        // multilang init
        addMultilangLink();
        // multilang rtl-ltr
        toggleMultilangRtl();
    });

Script for First-Time Visit of Home Page

(JS) in file community-documentation/_layouts/default.html

  function getBrowserLang(){
       var writelocallang=localStorage.getItem("languageid");
           
       if(writelocallang==null){
       var userlang = navigator.language || navigator.userLanguage; 
       
       // if local storage is empty, redirect, else don't do anything.
       userlang=userlang.slice(0, 2);
           
       localStorage.setItem("languageid", "");
           
           for(i=0;i<languageshomeredirect.length;i++){
               //console.log(languageshomeredirect);
               if (userlang == languageshomeredirect[i]){
               window.location.replace(userlang+"_index.html");
           };
           }           
       }
    //console.log("nothing");
   }
    $(function() {
        getBrowserLang();   
    });

Script to Translate Website Title in Home Page

(Liquid sintax) in file community-documentation/_includes/topnav.html

{% if site.topnav_title == "multilanguage"%}
    {% if site[page.lang] and [page.lang != "en"]%}
   <a class="fa fa-home fa-lg navbar-brand" href="{{[page.lang]_index.html}}">&nbsp;<span class="projectTitle">A{{site[page.lang]}}</span></a>
   {% else %}
   <a class="fa fa-home fa-lg navbar-brand" href="index.html">&nbsp;<span class="projectTitle">{{site.en}}</span></a>
   {% endif %}
   {% else %}
   <a class="fa fa-home fa-lg navbar-brand" href="index.html">&nbsp;<span class="projectTitle"> {{site.topnav_title}}</span></a>
{% endif %}      

TAG Logic

Language based selection (Liquid syntax) in file community-documentation/_includes/taglogic.html

   {% assign thisTag = page.tagName %}
   {% assign thisLang = page.langNow %}        
{% for page in site.pages %}
{% for tag in page.tags %}
  {% for lang in page.lang %}
    {% if tag == thisTag and lang == thisLang %} 

    <tr><td><a href="{{ page.url | remove: "/" }}">{{page.title}}</a></td>
        <td><span class="label label-default">Page</span></td>
      <td>{% if page.summary %} {{ page.summary | strip_html | strip_newlines | truncate: 160 }} {% else %} {{ page.content | truncatewords: 50 | strip_html }} {% endif %}</td>
    </tr>
    {% endif %}
   {% endfor %}
  {% endfor %}
  {% endfor %}

Side Panel

Links to URLs based on selected language are generated - (Liquid sintax) in file community-documentation/_includes/sidebar.html

{% assign thisLang = page.lang %}

<ul id="mysidebar" class="nav">
    {% if sidebar[0].product == "multilanguage" %}
            {% if sidebar[0][page.lang] %}  
<li class="sidebarTitle">{{sidebar[0][page.lang]}} {{sidebar[0].version}}</li>
            {% else %}
<li class="sidebarTitle">{{sidebar[0].en}} {{sidebar[0].version}}</li>
            {% endif %}
    {% else %}
<li class="sidebarTitle">{{sidebar[0].product}} {{sidebar[0].version}}</li>
    {% endif %}
{% for entry in sidebar %}
{% for folder in entry.folders %}
{% if folder.output contains "web" %}
<li>
    {% if folder.title == "multilanguage" %}
        {% if folder[page.lang] %}  
  <a href="#">{{ folder[page.lang] }}</a>
        {% else %}
  <a href="#">{{ folder.en }}</a>
        {% endif %}
    {% else %}
  <a href="#">{{folder.title}}</a>
    {% endif %}
  <ul>
      {% for folderitem in folder.folderitems %}
      {% if folderitem.output contains "web" %}
      {% if folderitem.external_url %}
      <li><a href="{{folderitem.external_url}}" target="_blank">{{folderitem.title}}</a></li>
      {% elsif page.url == folderitem.url %}
      <li class="active"><a href="{{folderitem.url | remove: "/"}}">{{folderitem.title}}</a></li>
      {% elsif folderitem.type == "empty" %}
      <li><a href="{{folderitem.url | remove: "/"}}">{{folderitem.title}}</a></li>

     {% elsif thisLang == "en" %} <!--english-->
                <li><a href="{{folderitem.url | remove: "/"}}">{{folderitem.title}}</a></li>

      {% elsif folderitem.lang contains thisLang %} <!--multilang-->
                <li><a href="{{thisLang}}_{{ folderitem.url | remove: "/"}}">{{folderitem.title}}</a></li>
      {% endif %}
      {% for subfolders in folderitem.subfolders %}
      {% if subfolders.output contains "web" %}
      <li class="subfolders">
        {% if subfolders.title == "multilanguage" %}
            {% if subfolders[page.lang] %}  
          <a href="#">{{ subfolders[page.lang] }}</a>
            {% else %}
          <a href="#">{{ subfolders.en }}</a>
            {% endif %}
        {% else %}
          <a href="#">{{subfolders.title}}</a>
    {% endif %}
          <ul>
              {% for subfolderitem in subfolders.subfolderitems %}
              {% if subfolderitem.output contains "web" %}
              {% if subfolderitem.external_url %}
              <li><a href="{{subfolderitem.external_url}}" target="_blank">{{subfolderitem.title}}</a></li>
              {% elsif page.url == subfolderitem.url %}
              <li class="active"><a href="{{subfolderitem.url | remove: "/"}}">{{subfolderitem.title}}</a></li>
              {% elsif thisLang == "en" %} <!--english-->
                                <li><a href="{{subfolderitem.url | remove: "/"}}">{{subfolderitem.title}}</a></li>
              {% elsif folderitem.lang contains thisLang %} <!--multilang-->
                                <li><a href="{{subfolderitem.url | remove: "/"}}">{{subfolderitem.title}}</a></li>
              {% endif %}
              {% endif %}
              {% endfor %}
          </ul>
      </li>
      {% endif %}
      {% endfor %}
      {% endif %}
      {% endfor %}
  </ul>
  </li>
    {% endif %}
  {% endfor %}
  {% endfor %}

Top Navigation Menu

Titles can be localized and connected to links that vary according to the selected language - (Liquid syntax) in file community-documentation/_layouts/topnav.html

{% assign topnav = site.data[page.topnav] %}
{% assign topnav_dropdowns = site.data[page.topnav].topnav_dropdowns %}
            {% for entry in topnav.topnav %}
            {% for item in entry.items %}
            {% if item.title == "multilanguage"%}
                    {% if item[page.lang] %}
                    <li><a href="{{item.url | remove: "/"}}">{{item[page.lang]}}</a></li>
                    {% else %}
                    <li><a href="{{item.url | remove: "/"}}">{{item.en}}</a></li>
                    {% endif %}
            {% elsif item.external_url %}
            <li><a href="{{item.external_url}}" target="_blank">{{item.title}}</a></li>
            {% elsif page.url contains item.url %}
            <li class="active"><a href="{{item.url | remove: "/"}}">{{item.title}}</a></li>
            {% else %}
            <li><a href="{{item.url | remove: "/"}}">{{item.title}}</a></li>
            {% endif %}
            {% endfor %}
            {% endfor %}
            <!-- entries with drop-downs appear here -->
            <!-- conditional logic to control which topnav appears for the audience defined in the configuration file.-->
            {% for entry in topnav_dropdowns %}
            {% for folder in entry.folders %}
            <li class="dropdown">
            {% if folder.title == "multilanguage"%}
                    {% if folder[page.lang] %}
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ folder[page.lang] }}<b class="caret"></b></a>
                    {% else %}
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ folder.en }}<b class="caret"></b></a>
                    {% endif %}
            {% else %}
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ folder.title }}<b class="caret"></b></a>
            {% endif %}
                <ul class="dropdown-menu">
                    {% for folderitem in folder.folderitems %}
                    {% if folderitem.title == "multilanguage"%}
                        {% if folderitem[page.lang] %}
                            {% if folderitem.external_url=="multilanguage"%}
                                {% if folderitem.m_external_url[page.lang]%}
                                        <li><a href="{{folderitem.m_external_url[page.lang]}}" target="_blank">{{folderitem[page.lang]}}</a></li>
                                {% else %}
                                        <li><a href="{{folderitem.m_external_url.en}}" target="_blank">{{folderitem.en}}</a></li>
                                {% endif%}
                             {% elsif page.url contains folderitem.url %}
                            <li class="dropdownActive"><a href="{{folderitem.url |  remove: "/"}}">{{folderitem[page.lang]}}</a></li>
                            {% elsif folderitem.url %}
                            <li><a href="{{folderitem.url | remove: "/"}}">{{folderitem[page.lang]}}</a></li>
                            {% elsif folderitem.external_url!="multilanguage" %}
                            <li><a href="{{folderitem.external_url}}" target="_blank">{{folderitem[page.lang]}}</a></li>                        {% endif %}
                    {% elsif folderitem.external_url=="multilanguage"%}
                                {% if folderitem.m_external_url[page.lang]%}
                                        <li><a href="{{folderitem.m_external_url[page.lang]}}" target="_blank">{{folderitem.en}}</a></li>
                                {% else %}
                                        <li><a href="{{folderitem.m_external_url.en}}" target="_blank">{{folderitem.en}}</a></li>
                                {% endif%}
                    {% elsif folderitem.external_url%}
                    <li><a href="{{folderitem.external_url}}" target="_blank">{{folderitem.en}}</a></li>
                    {% elsif page.url contains folderitem.url %}
                    <li class="dropdownActive"><a href="{{folderitem.url |  remove: "/"}}">{{folderitem.en}}</a></li>
                    {% else %}
                    <li><a href="{{folderitem.url | remove: "/"}}">{{folderitem.en}}</a></li>
                    {% endif %}
                    {% elsif folderitem.external_url %}
                    <li><a href="{{folderitem.external_url}}" target="_blank">{{folderitem.title}}</a></li>
                    {% elsif page.url contains folderitem.url %}
                    <li class="dropdownActive"><a href="{{folderitem.url |  remove: "/"}}">{{folderitem.title}}</a></li>
                    {% else %}
                    <li><a href="{{folderitem.url | remove: "/"}}">{{folderitem.title}}</a></li>
                    {% endif %}
                    {% endfor %}
                </ul>
            </li>
            {% endfor %}
            {% endfor %}

::::::::::::::::::::::::::::::::::::::

A Possible Todo List for the Future

  • Create localization mechanisms for posts

  • The default setting is now hard-coded as en - it should be left empty and determined by a key:value.

https://github.com/sylvaindurand/jekyll-multilingual/issues/4

  • A general improvement of the code

  • Automatic identification of a new language