Other
Multilingual & Localization
Build multilingual sites with locale routing, translations, and localized content
Nimbu provides comprehensive multilingual support with automatic locale routing, translation management, and localized content delivery.
Locale Configuration
Locales are configured in your theme's config/locales.yml:
default: en
available:
- en
- nl
- fr
- deLocale Variables
locale
Current active locale:
{{ locale }}
<!-- Output: "en", "nl", "fr", etc. -->
{% if locale == 'nl' %}
<!-- Dutch-specific content -->
{% endif %}locale_url_prefix
URL prefix for current locale:
{{ locale_url_prefix }}
<!-- Output: "/nl" for Dutch, "" for default locale -->
<a href="{{ locale_url_prefix }}/about">About</a>config.locales
All available locales:
{% for locale_pair in config.locales %}
{{ locale_pair[0] }}: {{ locale_pair[1] }}
{% endfor %}Translation Helpers
{% translate %}
Fetch translations from your translation files:
{% translate 'menu.home' %}
<!-- Output: "Home" (en), "Accueil" (fr), "Home" (nl) -->
{% translate 'menu.about', default: 'About Us' %}
{% translate 'cart.items_count', count: cart.items.size %}With Variables:
{% translate 'welcome.message', name: customer.first_name %}
<!-- Translation: "Welcome back, {{name}}!" -->
<!-- Output: "Welcome back, John!" -->{% localized_path %}
Generate URL for specific locale:
{% localized_path 'nl' %}
<!-- Current page in Dutch -->
{% localized_path 'fr' %}
<!-- Current page in French -->
<a href="{% localized_path 'de' %}">Deutsch</a>Working with Translations
Page Translations
Pages have built-in translation support:
{{ page.translations }}
<!-- Array of available translations -->
{% for translation in page.translations %}
<a href="{{ translation.url }}" hreflang="{{ translation.locale }}">
{{ translation.localized_language }}
</a>
{% endfor %}Translation Properties:
translation.locale- Locale code (en, nl, fr)translation.url- Full URL to translated pagetranslation.localized_language- Language name in that language
Practical Examples
Language Switcher
<div class="language-switcher">
{% if page.translations.size > 1 %}
<!-- Page-specific translations -->
{% for translation in page.translations %}
<a
href="{{ translation.url }}"
class="lang-link {% if locale == translation.locale %}active{% endif %}"
hreflang="{{ translation.locale }}">
{{ translation.locale | upcase }}
</a>
{% endfor %}
{% elsif config.locales.size > 1 %}
<!-- Site-wide locale switching -->
{% for locale_option in config.locales %}
<a
href="{% localized_path locale_option[1] %}"
class="lang-link {% if locale == locale_option[1] %}active{% endif %}">
{{ locale_option[1] | upcase }}
</a>
{% endfor %}
{% endif %}
</div>Dropdown Language Selector
<div class="language-dropdown">
<button class="current-language" aria-haspopup="true">
{{ locale | upcase }}
<span class="icon-chevron">▼</span>
</button>
<ul class="language-menu" aria-label="{% translate 'language.select' %}">
{% for translation in page.translations %}
{% unless locale == translation.locale %}
<li>
<a
href="{{ translation.url }}"
hreflang="{{ translation.locale }}">
{{ translation.localized_language }}
</a>
</li>
{% endunless %}
{% endfor %}
</ul>
</div>Multilingual Navigation
<nav class="main-nav" aria-label="{% translate 'nav.main' %}">
<a href="{{ locale_url_prefix }}/">
{% translate 'menu.home' %}
</a>
<a href="{{ locale_url_prefix }}/about">
{% translate 'menu.about' %}
</a>
<a href="{{ locale_url_prefix }}/products">
{% translate 'menu.products' %}
</a>
<a href="{{ locale_url_prefix }}/contact">
{% translate 'menu.contact' %}
</a>
</nav>Localized Date Formatting
<article>
<h1>{{ article.title }}</h1>
<time datetime="{{ article.published_at | date: '%Y-%m-%d' }}">
{{ article.published_at | localized_date: "long" }}
</time>
<!-- English: "October 7, 2025" -->
<!-- Dutch: "7 oktober 2025" -->
<!-- French: "7 octobre 2025" -->
<p class="byline">
{% translate 'article.published_by', author: article.author.name %}
</p>
{{ article.content }}
</article>Multilingual Search
<form action="{{ locale_url_prefix }}/search" method="get">
<input
type="search"
name="q"
placeholder="{% translate 'search.placeholder' %}"
value="{{ params.q }}">
<button type="submit">
{% translate 'search.submit' %}
</button>
</form>
{% if params.q %}
<div class="search-results">
<h2>
{% translate 'search.results_for', query: params.q %}
</h2>
{% if results.size > 0 %}
{% for result in results %}
<div class="result">
<h3><a href="{{ result.url }}">{{ result.title }}</a></h3>
<p>{{ result.excerpt }}</p>
</div>
{% endfor %}
{% else %}
<p class="no-results">
{% translate 'search.no_results' %}
</p>
{% endif %}
</div>
{% endif %}Localized Product Catalog
<div class="product-list">
{% for product in products %}
<div class="product-card">
<a href="{{ locale_url_prefix }}{{ product.url }}">
<img
src="{{ product.images.first.url | filter, width: '400px' }}"
alt="{{ product.name }}">
</a>
<h3>
<a href="{{ locale_url_prefix }}{{ product.url }}">
{{ product.name }}
</a>
</h3>
<p class="price">
{{ product.price | money_with_currency }}
</p>
<a
href="{{ product.url }}"
class="btn">
{% translate 'product.view_details' %}
</a>
</div>
{% endfor %}
</div>Multilingual Contact Form
<div class="contact-page">
<h2>{% translate 'contact.title' %}</h2>
<p>{% translate 'contact.subtitle' %}</p>
{% form channels.contact, class: 'contact-form' %}
{% error_messages_for form_model %}
<div class="form-group">
{% input 'name', label: {% translate 'contact.name_label' %}, required: true %}
</div>
<div class="form-group">
{% input 'email', as: 'email', label: {% translate 'contact.email_label' %}, required: true %}
</div>
<div class="form-group">
{% text_area 'message', rows: 6, label: {% translate 'contact.message_label' %}, required: true %}
</div>
{% submit_tag {% translate 'contact.submit' %}, class: 'btn btn-primary' %}
{% endform %}
</div>Currency and Locale Selection
<div class="region-settings">
<div class="locale-selector">
<label>{% translate 'settings.language' %}:</label>
<select id="locale-select">
{% for translation in page.translations %}
<option
value="{{ translation.url }}"
{% if locale == translation.locale %}selected{% endif %}>
{{ translation.localized_language }}
</option>
{% endfor %}
</select>
</div>
<div class="currency-selector">
<label>{% translate 'settings.currency' %}:</label>
<select id="currency-select">
<option value="EUR" {% if site.currency == 'EUR' %}selected{% endif %}>EUR (€)</option>
<option value="USD" {% if site.currency == 'USD' %}selected{% endif %}>USD ($)</option>
<option value="GBP" {% if site.currency == 'GBP' %}selected{% endif %}>GBP (£)</option>
</select>
</div>
</div>
<script>
document.getElementById('locale-select').addEventListener('change', function() {
window.location.href = this.value;
});
</script>SEO: Alternate Language Tags
<head>
<!-- Language alternatives for SEO -->
{% for translation in page.translations %}
<link
rel="alternate"
hreflang="{{ translation.locale }}"
href="{{ translation.url }}">
{% endfor %}
<!-- Default language fallback -->
<link rel="alternate" hreflang="x-default" href="{{ page.url }}">
<!-- Current page language -->
<html lang="{{ locale }}">
</head>Translation File Structure
Translations are stored in config/translations/:
# config/translations/en.yml
en:
menu:
home: "Home"
about: "About Us"
products: "Products"
contact: "Contact"
cart:
title: "Shopping Cart"
items_count:
one: "1 item"
other: "{{count}} items"
empty: "Your cart is empty"
product:
view_details: "View Details"
add_to_cart: "Add to Cart"
out_of_stock: "Out of Stock"# config/translations/nl.yml
nl:
menu:
home: "Home"
about: "Over Ons"
products: "Producten"
contact: "Contact"
cart:
title: "Winkelwagen"
items_count:
one: "1 artikel"
other: "{{count}} artikelen"
empty: "Je winkelwagen is leeg"
product:
view_details: "Bekijk Details"
add_to_cart: "Toevoegen"
out_of_stock: "Niet op Voorraad"Best Practices
1. Use Translation Keys
<!-- ✅ Good: Use translation keys -->
<h1>{% translate 'page.title' %}</h1>
<!-- ❌ Bad: Hardcode text -->
<h1>Welcome</h1>2. Provide Defaults
<!-- ✅ Good: Fallback for missing translations -->
{% translate 'new.feature', default: 'New Feature' %}
<!-- ⚠️ Without default, shows key if missing -->
{% translate 'new.feature' %}3. Consistent URL Structure
<!-- ✅ Good: Use locale_url_prefix consistently -->
<a href="{{ locale_url_prefix }}/products">Products</a>
<!-- ❌ Bad: Hardcoded paths -->
<a href="/products">Products</a>4. SEO-Friendly Language Tags
<!-- ✅ Good: Complete alternate tags -->
{% for translation in page.translations %}
<link rel="alternate" hreflang="{{ translation.locale }}" href="{{ translation.url }}">
{% endfor %}Next Steps
- Navigation - Language switchers
- Global Variables - Translation drops
- Filters - Dates - Localized dates
- Forms - Multilingual forms
Build truly global sites with Nimbu's multilingual features!