How do I add multilingual support to the Mediumish theme

This guide shows how to extend the Mediumish theme to support multiple languages. It is written for beginners and focuses on practical, evergreen techniques that work for static site generators like Jekyll and hosting on GitHub Pages or Netlify. You will get clear options, code snippets, a language switcher example, SEO recommendations, and a short comparison table to help you choose the best approach for your site.

Table of contents

Why add multilingual support

Adding multilingual support expands your audience, improves user experience, and can increase traffic from search engines that target different languages. For a Mediumish-based blog, multilingual support means your templates, navigation, and posts are aware of the current language and display the correct labels, translated menus, and content versions.

There are two key goals when implementing multilingual support:

  1. Present content in the visitor's preferred language in a clear and consistent URL structure.
  2. Keep content discoverable by search engines using proper SEO tags and structured data.

Common strategies explained

There are several ways to implement multilingual support. Each has tradeoffs in complexity, maintainability, and SEO. Choose based on site size, number of languages, and how you manage translations.

Single repo, language folders

Create language subfolders at the top level like /en/ and /id/. Each language contains translated posts and localized templates. This approach is simple and works well with Jekyll by leveraging permalinks and collections.

Single repo, language front matter

Use a single post file per language pair and mark language in front matter. Example front matter keys: lang, translation_of. Your templates filter posts by language. This reduces file duplication but requires careful linking between translations.

Multiple repos or branches

Maintain a separate repository or branch per language. This isolates translations but adds overhead for synchronization and maintenance. Not recommended for small blogs.

Client side translations

Load translations with JavaScript and keep a single source of content. This is fast for small UI text changes but not ideal for full post translations since search engines may not index dynamically injected post content.

Recommended file structure

This structure is practical for Mediumish extended with multilingual support. It keeps posts and page translations organized.

# example site structure
/
├─ _config.yml
├─ _data/
│  └─ locales.yml
├─ _includes/
│  └─ lang-switcher.html
├─ _layouts/
│  └─ default.html
├─ _posts/
│  ├─ en/
│  │  └─ 2025-01-12-welcome-en.md
│  └─ id/
│     └─ 2025-01-12-welcome-id.md
├─ index.html
└─ assets/

Using _posts/en and _posts/id keeps translations grouped. Alternatively you may keep all posts in _posts with front matter key lang. Use whichever fits your workflow.

Config and data examples

Keep language metadata in a data file so templates can loop languages and labels are centralized. Example _data/locales.yml:

# _data/locales.yml
en:
  code: en
  name: English
  path: /en
  date_format: "%B %d, %Y"
  site_title: "My Blog"
  nav:
    home: "Home"
    about: "About"
id:
  code: id
  name: "Bahasa Indonesia"
  path: /id
  date_format: "%d %B %Y"
  site_title: "Blog Saya"
  nav:
    home: "Beranda"
    about: "Tentang"

Then add minimal config to _config.yml:

# _config.yml
title: "My Mediumish Site"
baseurl: ""
url: "https://example.com"
languages:
  - en
  - id
default_lang: en

The languages array ensures you can iterate languages in templates when generating language switchers and hreflang tags.

Liquid language switcher

Add an include that renders language links. It reads the locales.yml data and current page language to build links. Save this as _includes/lang-switcher.html.

{% raw %}{% assign current_lang = page.lang | default: site.default_lang %}
{% endraw %}

Note about the snippet above: it shows intent how to build URLs. You may need to adjust translated_url logic depending on whether posts are in language-specific folders or marked by front matter. If you use language folders as in the recommended structure, build links using post slugs and replace language segment accordingly.

Translating posts and content workflow

There are two common approaches to post translations:

  • Parallel posts Create a translated post file for each language under language folders. Keep the date and slug consistent so the posts appear as translations of one another.
  • Single master with translation references Keep one canonical post and add translation meta in front matter that links to other language versions. Your templates use the links to provide translated versions.

Example front matter for a translated post using parallel files:

---
title: "Welcome to the blog"
lang: en
translation_key: welcome-2025
---
---
title: "Selamat datang di blog"
lang: id
translation_key: welcome-2025
---

Using a consistent translation_key allows templates to find sibling translations across languages. Example search in Liquid:

{% raw %}{% assign siblings = site.posts | where: "translation_key", page.translation_key %}
{% for p in siblings %}
  {% if p.lang != page.lang %}
    {{ p.title }} ({{ p.lang }})
  {% endif %}
{% endfor %}{% endraw %}

SEO and hreflang best practices

Search engines expect consistent URL structures and hreflang annotations to identify language variants. Implement the following:

  • Unique URL per language, e.g. https://example.com/en/my-post and https://example.com/id/my-post.
  • Use rel="alternate" hreflang="xx" links in the <head> for all language versions and a hreflang="x-default" fallback if needed.
  • Include lang attributes on the <html> tag for each language layout.
  • Translate meta title and description and include language in structured data where relevant.

Liquid example to generate hreflang tags from languages array (place inside your default layout head):

{% raw %}{% for code in site.languages %}
  {% assign locale = site.data.locales[code] %}
  
{% endfor %}
{% endraw %}

Remember to test hreflang with Google Search Console after deployment and ensure each language page returns a 200 status code with correct canonical tags.

Automation and build tips

For sites with many posts and translators, automation helps. Use these tips:

  • Store translated strings for UI in _data/locales.yml so you only edit text in one place.
  • Use scripts to generate translated post skeletons. For example a small Node or Ruby script reads existing posts and creates translated markdown files with the translation_key set. This saves repetitive work.
  • Use continuous integration on Netlify or GitHub Actions to validate that translations are linked and that no broken links exist after builds.

Practical code example

Below is a compact example showing one way to set up a translated post and a simple language-aware post layout.

--- 
# file: _posts/en/2025-01-12-example-en.md
title: "Example post in English"
lang: en
translation_key: example-2025
---

This is the English version.

--- 
# file: _posts/id/2025-01-12-example-id.md
title: "Contoh tulisan dalam Bahasa"
lang: id
translation_key: example-2025
---

Ini versi Bahasa Indonesia.

Now an excerpt of a language-aware post.html layout using Liquid:

{% raw %}


  
  {{ page.title }}
  
  {% include seo-hreflang.html %}


  
{% include lang-switcher.html %}

{{ page.title }}

{{ page.date | date: site.data.locales[page.lang].date_format }}

{{ content }}

© {{ site.time | date: "%Y" }} {{ site.data.locales[page.lang].site_title }}

{% endraw %}

Adjust the include names to your theme and ensure includes exist for seo-hreflang.html and lang-switcher.html.

Strategy comparison table

Strategy Pros Cons Best for
Language folders Clear URLs, SEO friendly, straightforward Duplicate structure, more files Small to medium blogs
Single files with lang key Less duplication, centralized Templates must filter, linking translations is manual Sites with few posts and many UI strings
Client side translations Easy UI localization, less build complexity Poor SEO for post content, requires JS UI strings only, not full posts

FAQ

How should I handle date formats per language

Keep date formats in your locale data file and use Liquid date formatting per page language. This keeps dates natural for readers and consistent across templates.

How do I avoid duplicate content penalties

Use hreflang tags, unique translated meta descriptions, and correct canonical tags. Search engines recognize language variants when properly annotated with hreflang and language-specific URLs.

Can I keep Mediumish styles while adding languages

Yes. Keep the theme layouts and add language-aware includes and data. Avoid heavy inline styles. The layout logic changes are minimal: load labels from locale data and resolve translated content files.

Finally, a few practical deployment reminders:

  • Test language switcher links locally before pushing to production.
  • Use a staging site to verify hreflang and robots handling for each language.
  • Keep translators or translation memory tools organized so updates are easy to track.

Extending Mediumish for multiple languages is mostly about planning your content structure, centralizing UI strings, and adding a few Liquid helpers for switching languages and generating hreflang tags. The code examples here are intentionally simple so you can adapt them to your exact Mediumish setup.

Comments