Goal
- Support multi-language, mainly in English and supplemented by Chinese;
- When visiting pages begin with
/, e.g./about.html,/posts/hello-world, English pages would be shown; - When visiting pages began with
/zh/, e.g./zh/about.html,/zh/posts/hello-world, Chinese pages would be shown; - When visiting
../hello-worldposts, corresponding langugage posts would be jumped via the link; - Posts which are specified language would only be shown in
archiveandindexpages.
Defect
- As
404.htmlpage was not supported for separate language file, two langugage content would be shown on it at the same time. - The
jekyll-paginate-v2, a plugin supports pagination for multi-language, is not supported by GitHub Pages. So the blog has not been supported pagination so far. See details below. - It is a little bit tricky to show date in multi-language way. Some string formatting work should be taken as it is able to have done that but is not a better way. Please see Jekyll Date Formatting.
Setup
Firstly, I found a i18n plugin Jekyll Multiple Languages Plugin.
The number of stars in this plugin is the most among multi-language plugins, and there are many other related multi-language plugins listed. You can use it without hesitate if you don’t want to setup custom configuration.
Currently for simplistic and customize, and also omitting unnecessary features in the plugin, I choose to use custom rules to support multi-language.
1. Single Post Setting
There are two properties needed to added for every posts and pages
uidfor unique post whose English and Chinese are the sameuid;- Note:
uidcan be declared in any other name, e.gdocument_id.
- Note:
localefor language symbol which specify what pages should be shown.
So add configuration in Front Matter of every posts
---
uid: hello-world
locale: zh
---
2. For Posts
It is very redundant that locale has to be added in Front Matter every posts at every time. Do not repeat yourself. I am going to use different folders to distinguish posts in different languages and set a default value to locale in config file.
Create zh folder to store Chinese posts in _posts directory, which origin _posts folder is used to store English posts. When visiting /zh/posts/some-posts Chinese pages are shown.
(Of course, follow below to set another path to use different directory)
defaults:
- values: #1
locale: en
- scope: #2
path: _posts/zh/**
type: posts
values:
locale: zh
- #1 not set for
scopemeans global default: all posts and pages are default toen; - #2 set for
scopemeans in the path of_posts/zh/**: all posts are default tozh.
3. For Common String
The common string is such as Archive and About from menu or Next Page.
By the feature of site.data, create locales folder in /_data directory, and create en.yaml and zh.yaml for storing English and Chinese string.
# in /_data/locales/en.yml
menu:
about: About
archive: Archive
# in /_data/locales/zh.yml
menu:
about: 关于
archive: 归档
Replace the old words specified by locale
<nav class="menu-content">
<a href="/about">{{ site.data.locales[page.locale].menu.about }}</a>
</nav>
4. For Pages
The pages are archive.html and about.html such page type in Jekyll.
As use of collection, _zh folder need to be created independently in root directory.
collections:
zh:
output: true
And create corresponding files in _zh folder
/menu/archive.html/menu/about.html
5. Language Selector
The selector is a button for switching pages or posts between different locale.
Create a new file named language-selector.html in _includes folder.
Finding corresponding language page from posts, pages and collections.
{% if page.uid %} <!-- #1 -->
<div class="language-selector">
{% assign postsOrPages = site.posts <!-- #2 -->
| concat: site.pages
| concat: site.zh
| where: "uid", page.uid
| sort: "locale" %}
{% for item in postsOrPages %}
{% unless item.url contains '/page/' %} <!-- #3 -->
<a href="{{ item.url }}" class="{{ item.locale }}">
{{ site.data.locales[item.locale].name }} <!-- #4 -->
</a>
{% endunless %}
{% endfor %}
</div>
{% endif %}
- #1 Selector is shown only on
uidexisted; - #2 Concat the list of
posts,pagesandzhto find the sameuidpages; - #3 Filter the pages not in
/pages/folder; - #4 Get the common string of language symbol form
locale
6. For Archive Page
The posts should be filtered in specified locale language manually in archive.html.
There is a convenient method to get the next post by post.next which dose not guarantee the same locale posts shown in one page. Should do an alternative way
<!-- archive.html -->
<!-- use the index of posts instead of post.next -->
{% assign posts = site.posts | where: "locale", page.locale %}
{% for index in (0..posts.size) limit: posts.size %}
{% assign post = posts[index] %}
{% assign prevIndex = index | minus: 1 %}
{% assign prev = posts[prevIndex] %}
{% capture year %}{{ post.date | date: '%Y' }}{% endcapture %}
{% capture prevYear %}{{ prev.date | date: '%Y' }}{% endcapture %}
{% if year != prevYear or index == 0 %}
<h3>{{ year }}</h3>
{% endif %}
<li itemscope>
<a href="{{ post.url }}">{{ post.title }}</a>
<p class="post-date"><span>{{ post.date | date: "%B %-d" }}</span></p>
</li>
{% endfor %}
The key point is that using the index of posts after filtered by locale instead of post.next.
7. Multi-Language SEO
Create a new page named language-seo.html in _included folder to specify corresponding language pages by hreflang.
{% assign posts = site.posts | where:"uid", page.uid | sort: 'locale' %}
{% for post in posts %}
<link rel="alternate" hreflang="{{ post.locale }}" href="{{ post.url }}" />
{% endfor %}
As well as include in head.html.
<!-- Language SEO -->
{% include language-seo.html %}
Lastly
Some unimportant and regular configuration are omitted, please to see the Source: lexcao.github.io if you want to learn the full setup.
References
- Jekyll Multiple Language Plugin
- Making Jekyll Multilingual
- Multi Languages with Jekyll
- Deploy A Multi-Language Jekyll Site
- Jekyll-Paginate-V2
One More Thing
Support Pagination
The old pagination plugin jekyll-pagination-v1 is no longer suitable for the short of filtering by the criteria. It is not able to show the one language list of posts on specified language.
Fortunately, jekyll-pagination-v2 announced to support pagination on multi-language way. As I mentioned above, the permitted plugins provided by GitHub Pages does not contain this plugin. Which you need to deploy automated manually if you want to use. Here are setup jekyll-pagination-v2.
Gemfile install
$ gem 'jekyll-paginate-v2'
_config.yaml setup
Note: the configuration of v2 and v1 is not compatible.
plugins:
- jekyll-paginate-v2
pagination:
enabled: true
per_page: 5
permalink: /page/:num/
sort_reverse: true
pages configs
You need add more configuration in Front Matter of index.html to paginate.
# /index.html
---
pagination:
enabled: true # yes, enable is needed here
locale: en
---
pagination.locale is to specify the criteria of the language according to locale of the pages. I use locale for my custom rules for multi-language so that the pagination use the same filed for reusable.
Correspondly, create index.html in _zh folder.
# /_zh/index.html
---
permalink: /zh/ # I don't like suffix of .html so do this
pagination:
enabled: true
locale: zh
---
Note: I came cross a problem here that the pagination was not working after I put the index.html into _zh folder, which has not been solved yet.
The temporary solution was to put zh/index.html file to the root directory, and renamed to index_zh.html.
paginating
The use of paginating is as same as the v1 does. Please see Pagination.