Multilingual & Localization
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:
yaml
default: en
available:
- en
- nl
- fr
- deLocale Variables
locale
Current active locale:
liquid
{{ locale }}
<!-- Output: "en", "nl", "fr", etc. -->
{% if locale == 'nl' %}
<!-- Dutch-specific content -->
{% endif %}locale_url_prefix
URL prefix for current locale:
liquid
{{ locale_url_prefix }}
<!-- Output: "/nl" for Dutch, "" for default locale -->
<a href="{{ locale_url_prefix }}/about">About</a>config.locales
All available locales:
liquid
{% for locale_pair in config.locales %}
{{ locale_pair[0] }}: {{ locale_pair[1] }}
{% endfor %}Translation Helpers
{% translate %}
Fetch translations from your translation files:
liquid
{% 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:
liquid
{% translate 'welcome.message', name: customer.first_name %}
<!-- Translation: "Welcome back, {{name}}!" -->
<!-- Output: "Welcome back, John!" -->{% localized_path %}
Generate URL for specific locale:
liquid
{% 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:
liquid
{{ 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
liquid
<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
liquid
<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
liquid
<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
liquid
<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
liquid
<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
liquid
<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
liquid
<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
liquid
<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
liquid
<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/:
yaml
# 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"yaml
# 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
liquid
<!-- ✅ Good: Use translation keys -->
<h1>{% translate 'page.title' %}</h1>
<!-- ❌ Bad: Hardcode text -->
<h1>Welcome</h1>2. Provide Defaults
liquid
<!-- ✅ Good: Fallback for missing translations -->
{% translate 'new.feature', default: 'New Feature' %}
<!-- ⚠️ Without default, shows key if missing -->
{% translate 'new.feature' %}3. Consistent URL Structure
liquid
<!-- ✅ 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
liquid
<!-- ✅ 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!