versioning and multilingual contribution in jekyll without complexity
Why Support Multiple Languages and Versions?
As your documentation or knowledge base grows, you may need to support multiple languages or different product versions. But traditional methods like plugin-heavy setups can introduce build errors and high maintenance—especially for beginners.
This article shows you how to manage multilingual and versioned documentation in a clean and scalable way using native Jekyll features, collections, and GitHub folders.
Fundamental Principles
Our strategy is built on three key principles:
- Folder-based language and version separation to avoid complex logic.
- Simple Liquid includes for navigation and language switching.
- Independent markdown files per version/language for full control.
File Structure for Language and Version Separation
Instead of using plugins like jekyll-multiple-languages-plugin (not supported on GitHub Pages), we use folders:
_docs/
en/
v1/
getting-started.md
features.md
v2/
getting-started.md
features.md
id/
v1/
getting-started.md
features.md
This structure gives you full control and allows you to mix languages and versions as needed.
Front Matter for Metadata
Each Markdown file should define its version and language in the front matter:
---
title: "Getting Started"
lang: en
version: v2
layout: doc
---
Use this metadata to group or filter content as needed in navigation menus or breadcrumbs.
Language and Version Switchers with Liquid
In your layout or include file, you can detect language/version and offer switchers:
{% assign base = page.path | split: '/' %}
{% assign current_lang = base[1] %}
{% assign current_version = base[2] %}
<div class="switcher">
<a href="/docs/en/{{ current_version }}/{{ base[3] }}">English</a> |
<a href="/docs/id/{{ current_version }}/{{ base[3] }}">Bahasa</a>
</div>
This gives a simple UI to move between translations without JS or plugin dependencies.
Navigation by Language and Version
Use custom data files per language-version combination to generate sidebars.
_data/
nav-en-v1.yml
nav-en-v2.yml
nav-id-v1.yml
Sample navigation file:
# nav-en-v2.yml
- title: Getting Started
url: /docs/en/v2/getting-started
- title: Features
url: /docs/en/v2/features
In layout:
{% capture nav_file %}nav-{{ page.lang }}-{{ page.version }}.yml{% endcapture %}
{% assign items = site.data[nav_file] %}
<ul>
{% for item in items %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
How Contributors Can Help with Translations
Create a simple workflow:
- Each translated file corresponds 1:1 with its English source.
- Track translation progress using GitHub Issues or a checklist in README.
- Use GitHub Labels like
translation-neededorneeds-update.
In CONTRIBUTING.md, explain how contributors can copy and translate files:
Please translate files inside `_docs/en/v2/` into `_docs/id/v2/` keeping the same filename.
Use the same front matter but change `lang: en` to `lang: id`.
Version Management Best Practices
New versions should only be created when major changes happen. Don’t clone everything unless necessary.
Only fork new docs into v2/ if breaking changes were introduced.
Otherwise, reuse v1/ files or override selectively.
This minimizes maintenance and keeps content DRY (Don’t Repeat Yourself).
Optional: Flag Outdated Translations
To help users identify stale translations, add a “last updated” date in front matter:
last_updated: 2024-10-01
In layout:
<p class="updated">Last updated: {{ page.last_updated }}</p>
You can also compare this date against the source language to warn users if they’re viewing outdated content.
Automatic Checks for Translation Coverage
Use GitHub Actions to detect if all expected translations exist:
# .github/workflows/check-translations.yml
name: Check Translation Coverage
on:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify ID translations for v2
run: |
missing=0
for f in _docs/en/v2/*.md; do
t="_docs/id/v2/$(basename "$f")"
if [ ! -f "$t" ]; then
echo "Missing: $t"
missing=1
fi
done
exit $missing
This prevents incomplete translation releases from being merged.
Conclusion
Multilingual and versioned documentation doesn't have to be complex. By separating content into folders, using simple front matter and Liquid logic, and guiding contributors with good structure, even beginners can manage sophisticated setups.
In the next article, we’ll explore how to integrate search across multiple languages and versions using client-side JavaScript search libraries like Lunr.js or Elasticlunr.