# Multilingual Sites URL: /docs/content/i18n/ Section: content Tags: i18n, multilingual, internationalization, localization -------------------------------------------------------------------------------- Multilingual Sites Bengal supports full internationalization (i18n) with content routing, UI translations, and SEO-friendly hreflang tags. Quick Start 1. Configure Languages # config/_default/i18n.yaml i18n: strategy: "prefix" # URL prefix strategy (/en/, /fr/) default_language: "en" default_in_subdir: false # Default lang at root, others in subdirs languages: - code: "en" name: "English" weight: 1 - code: "fr" name: "Français" weight: 2 - code: "es" name: "Español" weight: 3 2. Organize Content by Language content/ ├── en/ │ ├── _index.md │ └── docs/ │ ├── _index.md │ └── getting-started.md ├── fr/ │ ├── _index.md │ └── docs/ │ ├── _index.md │ └── getting-started.md └── es/ ├── _index.md └── docs/ └── getting-started.md 3. Add UI Translations # i18n/en.yaml nav: home: "Home" docs: "Documentation" search: "Search" footer: copyright: "© 2026 My Project" content: read_more: "Read more" minutes_read: "{minutes} min read" # i18n/fr.yaml nav: home: "Accueil" docs: "Documentation" search: "Rechercher" footer: copyright: "© 2026 Mon Projet" content: read_more: "Lire la suite" minutes_read: "{minutes} min de lecture" 4. Build and Verify bengal build Output: public/ ├── index.html # English (default at root) ├── docs/ │ └── getting-started/ ├── fr/ │ ├── index.html │ └── docs/ │ └── getting-started/ └── es/ ├── index.html └── docs/ └── getting-started/ Configuration Reference Strategy Options Strategy Description URL Pattern "none" Single language (default) /docs/page/ "prefix" Language in URL path /en/docs/page/, /fr/docs/page/ Full Configuration i18n: # URL strategy strategy: "prefix" # Default language code default_language: "en" # Put default language in subdir too? (false = root) default_in_subdir: false # Language list with metadata languages: - code: "en" name: "English" hreflang: "en" # For SEO (usually same as code) weight: 1 # Sort order in language switchers - code: "fr" name: "Français" hreflang: "fr" weight: 2 Template Functions t() — Translate UI Strings {# Basic translation #} {{ t('nav.home') }} {# With parameters #} {{ t('content.minutes_read', {'minutes': page.reading_time}) }} {# Force specific language #} {{ t('nav.home', {}, 'fr') }} {# With default fallback text #} {{ t('custom.key', {}, None, 'Fallback text') }} Signature: t(key, params={}, lang=None, default=None) If a translation key is missing, Bengal automatically falls back to the default language. If still not found, returns the provided default or the key itself. current_lang() — Get Current Language {% let lang = current_lang() %} <html lang="{{ lang }}"> {% if current_lang() == 'fr' %} {# French-specific content #} {% end %} languages() — List All Languages {# Language switcher #} <nav class="language-switcher"> {% for lang in languages() %} <a href="/{{ lang.code }}/" {% if lang.code == current_lang() %}class="active"{% end %}> {{ lang.name }} </a> {% end %} </nav> Returns: List of language objects with: code — Language code (e.g., "en") name — Display name (e.g., "English") hreflang — SEO attribute weight — Sort order alternate_links() — Generate hreflang Tags {# In <head> for SEO #} {% for alt in alternate_links(page) %} <link rel="alternate" hreflang="{{ alt.hreflang }}" href="{{ alt.href }}"> {% end %} Output: <link rel="alternate" hreflang="en" href="/docs/getting-started/"> <link rel="alternate" hreflang="fr" href="/fr/docs/getting-started/"> <link rel="alternate" hreflang="x-default" href="/docs/getting-started/"> locale_date() — Localized Dates {# Format with locale-aware output #} {{ locale_date(page.date, 'medium') }} {# → "Dec 19, 2025" (English) or "19 déc. 2025" (French) #} {{ locale_date(page.date, 'long') }} {# → "December 19, 2025" or "19 décembre 2025" #} {# Force specific locale #} {{ locale_date(page.date, 'medium', lang='fr') }} Format options: short, medium, long Tip Tip For full date formatting, install Babel: pip install babel Content Linking Across Languages Link Translations with translation_key Pages with the same translation_key are linked as translations: # content/en/docs/getting-started.md --- title: Getting Started translation_key: getting-started --- # content/fr/docs/getting-started.md --- title: Démarrage translation_key: getting-started --- Bengal uses translation_key to: Generate alternate_links() for SEO Enable language switchers to link to the same page in other languages Per-Page Language Override Override the directory-based language: --- title: Page in French lang: fr --- Translation File Formats Bengal supports multiple formats for translation files: i18n/ ├── en.yaml # YAML (recommended) ├── fr.yaml ├── es.json # JSON also works └── de.toml # TOML also works Nested Keys # i18n/en.yaml nav: home: "Home" docs: title: "Documentation" description: "Learn how to use..." errors: 404: title: "Page Not Found" message: "The page you're looking for doesn't exist." Access with dot notation: {{ t('nav.docs.title') }} {{ t('errors.404.message') }} Integration with Other Features Menus Use get_menu_lang() to get language-aware menu items: {% for item in get_menu_lang('main', current_lang()) %} <a href="{{ item.url }}">{{ item.name }}</a> {% end %} For the default menu without language awareness: {% for item in get_menu('main') %} <a href="{{ item.url }}">{{ item.name }}</a> {% end %} RSS Feeds RSS feeds are generated per-language when using prefix strategy: /rss.xml — Default language /fr/rss.xml — French /es/rss.xml — Spanish Search Search indexes are built per-language for accurate results: /index.json — Default language /fr/index.json — French Example: Complete Language Switcher {# templates/partials/language-switcher.html #} <div class="language-switcher"> <button aria-label="Select language"> {{ current_lang() | upper }} </button> <ul class="dropdown"> {% for lang in languages() %} {% let is_current = lang.code == current_lang() %} <li> <a href="/{{ lang.code }}/" {% if is_current %}aria-current="true"{% end %} lang="{{ lang.code }}"> {{ lang.name }} </a> </li> {% end %} </ul> </div> Troubleshooting Translations Not Loading Check file exists: i18n/<lang>.yaml Verify YAML syntax is valid Check key path matches: t('nav.home') needs nav.home: in file Wrong Language Displayed Verify lang frontmatter or directory structure Check i18n.strategy is set to "prefix" Ensure content is in correct language directory Missing hreflang Tags Add translation_key to linked pages Verify pages exist in both languages Check alternate_links(page) is in <head> Info Seealso Template Functions Reference Configuration Reference Templating Guide -------------------------------------------------------------------------------- Metadata: - Author: lbliii - Word Count: 912 - Reading Time: 5 minutes