How Can You Build a Multilingual Jekyll Site Using Ruby and Data Files
Is It Possible to Build a Multilingual Static Site with Jekyll?
Yes — you can build a multilingual site in Jekyll by combining:
- YAML/JSON translation files in
_data/ - Per-language content in collections
- Ruby plugins to route and organize pages
This method allows total control over translations, SEO, and structure, without requiring external CMS or JavaScript.
Folder and URL Structure for Multilingual Content
/_data/translations/ en.yml id.yml fr.yml /_posts/ en/2025-01-01-welcome.md id/2025-01-01-selamat-datang.md /pages/ about.en.md about.id.md
This structure produces URLs like:
/en/about//id/about//fr/blog/
Step 1: Create Data Files for Translations
# _data/translations/en.yml
title: "Welcome"
nav:
home: "Home"
about: "About Us"
# _data/translations/id.yml
title: "Selamat Datang"
nav:
home: "Beranda"
about: "Tentang Kami"
Step 2: Write a Filter to Load Translation Keys
# _plugins/i18n_filter.rb
module Jekyll
module I18nFilter
def t(input, lang = "en")
site = @context.registers[:site]
translations = site.data["translations"][lang] || {}
keys = input.split(".")
keys.reduce(translations) { |memo, key| memo[key] if memo }
end
end
end
Liquid::Template.register_filter(Jekyll::I18nFilter)
Usage in Template:
{{ "nav.home" | t: page.lang }}
Step 3: Add Language in Front Matter
---
layout: default
title: About
lang: id
permalink: /id/about/
---
Jekyll will build this as /id/about/ and you can use page.lang for all conditionals.
Step 4: Optional Ruby Plugin for Auto-Permalink by Language
# _plugins/i18n_permalink.rb
Jekyll::Hooks.register :pages, :pre_render do |page|
if page.data["lang"] && page.data["i18n_slug"]
lang = page.data["lang"]
slug = page.data["i18n_slug"]
page.data["permalink"] = "/#{lang}/#{slug}/"
end
end
Then use in front matter:
lang: fr
i18n_slug: accueil
Step 5: Language Switcher Menu
You can now build a switcher like this:
<nav class="lang-switcher">
<a href="/en/{{ page.i18n_slug }}/">EN</a>
<a href="/id/{{ page.i18n_slug }}/">ID</a>
<a href="/fr/{{ page.i18n_slug }}/">FR</a>
</nav>
This assumes that your translated pages have matching i18n_slug keys per language.
Step 6: Shared Includes with Translations
# _includes/nav.html
<ul>
<li><a href="/{{ page.lang }}/">{{ "nav.home" | t: page.lang }}</a></li>
<li><a href="/{{ page.lang }}/about/">{{ "nav.about" | t: page.lang }}</a></li>
</ul>
This makes your navigation fully translatable.
Bonus: Language Detection from URL
You can extract language from URL in a hook:Jekyll::Hooks.register :pages, :pre_render do |page|
url = page.url
lang_code = url.split("/")[1]
page.data["lang"] ||= lang_code
end
Tips for SEO and Best Practices
- Use canonical URLs per language
- Add
hreflangtags for international search indexing - Don’t mix multiple languages in one file
- Use structured folders like
/en/,/id/,/fr/
Conclusion
Multilingual support in Jekyll doesn’t require JavaScript or dynamic CMS. With smart use of:
- YAML files for i18n keys
- Ruby plugins for slug control and fallback
- Liquid filters for translations
...you can build a multilingual, SEO-ready, fully static website — all while keeping build logic clean and transparent.
Next Steps
- Build pages in at least 2 languages
- Create a simple language switcher
- Extract all UI strings to YAML
In the next article, we’ll cover **building a multilingual blog with unified pagination**, ensuring posts in each language have their own archive, slugs, and feed.