Localized Tag and Category Navigation in Jekyll

Why Localized Navigation Matters

When building a multilingual site, it’s common to translate content. But what about navigation structures like categories and tags? If users switch to a different language, the taxonomy must follow. Showing English tags on a Spanish page creates confusion and breaks consistency.

In this guide, we’ll show how to create a tag-category navigation structure in Jekyll that automatically adapts based on the current language context. The solution works without JavaScript, external plugins, or third-party CMS—just plain Liquid, YAML, and data-driven design.

Site Structure for Multilingual Content

We’ll assume you have a language-aware directory structure:


├── _config.yml
├── _data
│   └── navigation.yml
├── _resources
│   ├── en
│   │   └── optimizing-includes.md
│   ├── es
│   │   └── optimizacion-includes.md

Each document contains language-specific lang, category, and tags metadata.

---
title: Optimizing Includes
lang: en
category: performance
tags: [includes,html]
---
---
title: Optimizando Includes
lang: es
category: rendimiento
tags: [includes,html]
---

Step 1: Localize Tags and Categories in _data

We create a centralized translation map:

_data/navigation.yml

en:
  categories:
    performance: Performance
    theming: Theming
  tags:
    includes: HTML Includes
    html: HTML

es:
  categories:
    rendimiento: Rendimiento
    tematica: Temática
  tags:
    includes: HTML Includes
    html: HTML

Step 2: Filter by Language in Your Template

We assume a layout variable page.lang exists. This allows filtering:

{% raw %}
{% assign lang = page.lang %}
{% assign localized_data = site.data.navigation[lang] %}
{% assign lang_items = site.resources | where: "lang", lang %}
{% assign categories = lang_items | map: "category" | uniq | sort_natural %}

{% for cat in categories %}
  

{{ localized_data.categories[cat] }}

{% assign cat_items = lang_items | where: "category", cat %} {% assign tags = cat_items | map: "tags" | join: "," | split: "," | uniq | sort_natural %} {% for tag in tags %}

{{ localized_data.tags[tag] }}

    {% for item in cat_items %} {% if item.tags contains tag %}
  • {{ item.title }}
  • {% endif %} {% endfor %}
{% endfor %} {% endfor %} {% endraw %}

Step 3: Language Switching UI (Optional)

If your pages support language toggles, ensure URLs follow the structure:

  • /en/resources/optimizing-includes/
  • /es/resources/optimizacion-includes/

Add a toggle at the top:



Styling Suggestions


.lang-switch {
  display: flex;
  gap: 1em;
  margin-bottom: 1.5em;
}
.lang-switch a {
  text-decoration: none;
  color: #007acc;
}

Key Advantages of This Approach

  • Language-specific navigation
  • No duplication of template code
  • Translation is centralized in one data file
  • Compatible with GitHub Pages, no plugin required

Common Pitfalls to Avoid

  • Using translated slugs in URLs without fallback links
  • Hardcoding translated titles in front matter
  • Ignoring plural and singular forms in category mapping

Bonus: Fallback to Default Language

If a tag or category translation is missing, display the raw key:

{% raw %}
{{ localized_data.categories[cat] | default: cat }}
{% endraw %}

Testing Your Localized Navigation

  1. Ensure both en and es resources exist with correct tags/categories
  2. Verify the YAML in navigation.yml is free of indentation errors
  3. Open /en/tags/ and /es/tags/ pages and validate output

Conclusion

Multilingual sites need more than just translated posts—they need translated structure. With Liquid logic and a data-driven YAML strategy, Jekyll allows you to create fully localized tag and category navigation without heavy frameworks or backend systems.

In the next article, we’ll explore building a custom multilingual search interface that respects language context while using client-side JavaScript and Lunr.js.