Turning the Documentation Theme for Jekyll into a Multilanguage Theme
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.
Menus
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}}"> <span class="projectTitle">A{{site[page.lang]}}</span></a>
{% else %}
<a class="fa fa-home fa-lg navbar-brand" href="index.html"> <span class="projectTitle">{{site.en}}</span></a>
{% endif %}
{% else %}
<a class="fa fa-home fa-lg navbar-brand" href="index.html"> <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