Preface

When I found myself coding a multilingual site in Jekyll, I stumbled on a lot of useful resources while surfing the Web, but I struggled quite a bit to digest and replicate their approaches due to the lack of a concrete, working example to look at.

At first, I tried to replicate their approaches directly in the site I was working on, but this quickly backfired because it proved to be too big of a bite to chew for a designer who codes.

Not giving up, I then decided to create a basic site from scratch to focus on learning how to handle multiple languages in Jekyll without any extra complexity in the picture.

That very same basic site is hosted in this repository, which I gladly share with the world as an example project, hoping to help anyone interested in coding a multilingual site using Jekyll.

Foreword

A few words before starting. Sites built with the approach illustrated in this series of articles:

  • can support as many languages as needed
  • can serve pages or posts that may not need to be translated into all supported languages
  • have a language switch that can either direct web surfers to view the current page or post in the selected language, if available, or direct them to an alternative fallback page
  • do not require you to install custom plugins
  • leverage the basics of Jekyll and thus making them relatively future-proof (famous last words)
  • can be published as GitHub Pages sites

Specifically, the basic site hosted in this repository and used as a concrete example to illustrate my approach:

  • is visually quite crude, as the focus is on illustrating a structural (not visual) approach to building multilingual sites
  • supports English and Italian as example languages

Directory structure

The directory structure of the basic site looks like this:

.
├── _data
│   └── snippets.yml
├── _includes
│   ├── head.html
│   ├── header.html
│   ├── localizations.html
│   └── references.html
├── _layouts
│   ├── base.html
│   ├── index.html
│   ├── page.html
│   └── post.html
├── _posts
│   ├── en
│   │   ├── YYYY-MM-DD-title.markdown
│   │   ├── …
│   │   └── YYYY-MM-DD-title.markdown
│   └── it
│       ├── YYYY-MM-DD-titolo.markdown
│       ├── …
│       └── YYYY-MM-DD-titolo.markdown
├── 404.html
├── config.yml
├── en
│   ├── drafts.html
│   ├── feed.xml
│   ├── postface.html
│   ├── preface.html
│   ├── sitemap.xml
│   └── stories.html
├── index.html
├── it
│   ├── bozze.html
│   ├── feed.xml
│   ├── prefazione.html
│   ├── sitemap.xml
│   └── storie.html
└── sitemap.xml

Pages

We organize the pages into as many subdirectories as the languages we plan to support, naming them using ISO language codes. The basic site has two subdirectories, one named en for grouping the English pages and one named it for grouping the Italian pages.

├── …
├── en
│   ├── drafts.html
│   ├── feed.xml
│   ├── postface.html
│   ├── preface.html
│   ├── sitemap.xml
│   └── stories.html
├── …
├── it
│   ├── bozze.html
│   ├── feed.xml
│   ├── prefazione.html
│   ├── sitemap.xml
│   └── storie.html
├── …

After Jekyll has built the site, we can reach, for example, the English page stories.html and the Italian page storie.html at the URLs www.site.ext/en/stories.html and www.site.ext/it/storie.html, respectively.

Exceptions

But, of course, there are exceptions. We place the pages 404.html, index.html, and sitemap.html in the root directory of the site. Why?

404.html and index.html are unique pages. Jekyll builds and serves automatically one and only one of them at a time.

sitemap.xml is a Sitemap index which points to other localized sitemaps in the respective language subfolders (read the section Multilingual sitemaps for more details).

Posts

We organize the posts following a similar logic. The basic site has two subdirectories in the folder named _posts, one named en for grouping the English posts and one named it for grouping the Italian posts.

├── …
├── _posts
│   ├── en
│   │   ├── YYYY-MM-DD-title.markdown
│   │   ├── …
│   │   └── YYYY-MM-DD-title.markdown
│   └── it
│       ├── YYYY-MM-DD-titolo.markdown
│       ├── …
│       └── YYYY-MM-DD-titolo.markdown
├── …

Configuration

We then add the following configuration options in the _config.yml file placed in the site’s root directory:

defaults:
-
  scope:
    path: '_posts/en'
    type: 'posts'
  values:
    permalink: 'en/story/:title'
    language: en
-
  scope:
    path: '_posts/it'
    type: 'posts'
  values:
    permalink: 'it/storia/:title'
    language: it

By setting global permalinks for posts, we can reach, for example, the English post named 2021-01-01-hello-world.markdown and the Italian post named 2021-01-01-ciao-mondo.markdown at the URLs www.site.ext/en/story/hello-world.html and www.site.ext/it/storia/ciao-mondo.html, respectively.