Skip to main content
Documentation Configuration

Internationalization (I18n)

Internationalization, or i18n as it’s commonly known, is the process of defining content and templates for your site in such a way as to support multiple languages or locales. Whether that’s French vs. Chinese, or American English vs. British English, it gives you the ability reach more diverse audiences and constituencies.

Starting in Bridgetown 1.1, you can configure multiple locales for your website and set which particular locale should be considered “the default”. There are a couple of different routing options available:

  1. The default locale URLs start at the root of the site (/), and other locales start at a locale-specific prefix (/es, /de), etc.
  2. All locales start at a locale-specific prefix, and the root (/) redirects to the “default” locale at its designated prefix.

Routing options not currently supported by Bridgetown at present are the subdomain config (www.mysite.com, es.mysite.com, zh.mysite.com, etc.) and the international domains config (www.mysite.com, www.mysite.co.uk, etc.). However, if you’re able to work within a prefix-style config (MDN is a good example of this type of approach out in the wild), Bridgetown is here for you.

Bridgetown uses the Ruby I18n gem to aid in storing and accessing translations, the same library used by Ruby on Rails. Thus many of the same conventions will apply if you’re already familar with i18n in Rails.

Table of Contents #

Setup & Translations #

First, you’ll want to define your locales in bridgetown.config.yml. There are three configuration options, which by default are:

available_locales: [en]
default_locale: en
prefix_default_locale: false
  • available_locales: This is an array of locales you wish to support on your site. They can be simple language codes (es for Spanish, th for Thai, etc.), or they can also include regional differences known as subtags (pt-BR for Brazilian Portuguese, pt-PT for Portuguese as spoken in Portugal, etc.). You can look up various languages and subtags here. An example value for English, French, and German would be: [en, fr, de].
  • default_locale: This the locale you wish to consider the “default” for your site (aka the locale a visitor would first encounter before specifically choosing a locale). The default is English: en.
  • prefix_default_locale: As mentioned above, you can either have default locale URLs live within the root of your site, or you can set this to true to have the root direct to the default locale’s prefix.

Once you’ve completed your intial configuration, create a src/_locales folder and add files in YAML, JSON, or Ruby hash format for your locale translations. The first key of the data structure should be the locale, with various hierarchies of subkeys as you deem fit. Here’s an example of a en.yml file:

en:
  site:
    title: Local Listings
    tagline: The best homes you'll find anywhere in the area.
  welcome:
    intro: Welcome to Local Listings! Enjoy our fine selection.

And here’s a example of a es.rb file:

{
  es: {
    site: {
      title: "Listados Locales",
      tagline: "Las mejores casas que encontrará en cualquier lugar de la zona."
    },
    welcome: {
      intro: "¡Bienvenido a los listados locales! Disfruta de nuestra fina selección."
    }
  }
}

Within your templates, you’ll now be able to use the t Liquid tag or filter, or the t Ruby helper to reference these keys. For example, in Liquid:

{% t site.title %} <!-- tag style -->
{{ "site.tagline" | t }} <!-- filter style -->

and in ERB:

<%= t("welcome.intro") %>

The Ruby helper in particular also supports some additional functionality.

Variable Interpolation

If you store a translation like this:

en:
  products:
    price: "$%{price}"

Then you can pass that variable to the t helper:

<%= t("products.price", price: resource.data.price) %>

Relative page keys

If you start your translation key starts with a period, we’ll automatically scope the key to the page. For example, if the page is about.html:

<%= t(".foo") %>

Will retrieve the key from:

en:
  about:
    foo: Foo

Or if the page is contact/about.html:

en:
  contact:
    about:
      foo: Foo

Automatic HTML key safety

If your translation key ends with _html or is html, it will automatically be marked html_safe.

en:
  products:
    tagline_html: The <storng>best</strong> product!

There are many other useful features of the i18n gem, so feel free to peruse the Rails Guide to Internationalization for additional documentation.

In Ruby, the t helper is shorthand for I18n.t, so if you find yourself in a context where t is not available—perhaps in a plugin—you can write I18n.t directly.

Localizing dates #

To ensure dates are displayed using the active locale, you’ll need to localize them using I18n.l, or use the provided shortcut: l.

By adding gem "rails-i18n" to your Gemfile, you’ll get localized month and day names, along with standard date and time formats in each locale supported by that gem. You can also define your own localization for date and time.

In Ruby, the l helper is shorthand for I18n.l, so if you find yourself in a context where l is not available—perhaps in a plugin—you can write I18n.l directly.

Localizing Your Resources and Templates #

Beyond using simple translated strings, you will want to ensure you have translated variants of your content and that you can switch freely between the locale variants. There are two ways to do this:

Separate Files #

Create a separate resource file for each locale. You can either use the locale front matter key to set the locale of a file, or you can include the locale in the filename itself. For example: about.en.md, about.fr.md, about.it.md, etc. You can do this for any type resource, whether it’s a page, blog post, or some other custom collection.

Multi-Locale Files #

If the same resource should be available to multiple locales, use a single resource file in “multi locale” mode and use special front matter and template syntax to include translated content. You can switch to this mode by setting locale: multi in your front matter or using the .multi extension within your file name. For example: about.multi.md. This will generate resources in all of your site.config.available_locales.

If you want to only output a limited set of locales, then use the locales front matter key to include only the locales that should be written.

---
title: My Title
locale: multi
locales:
  - en
  - es
  - de
---

If you want to override front matter values per-locale, then you use the locale_overrides front matter key to include keys which will overwrite the default keys for all locales other than the default. Here’s an example:

---
title: My Title
locale_overrides:
  es:
    title: Mi Título
  de:
    title: Mein Titel
---

Then in the body of the resource, you can use conditional template syntax to check the value of the site.locale variable. (And of course this works in any layout template as well.) Using ERB syntax:

<% if site.locale == :en %>

Here's my content in **English**.

<% elsif site.locale == :es %>

Aquí está mi contenido en **Español**.

<% elseif site.locale == :de %>

Hier sind meine Inhalte auf **Deutsch**.

<% end %>

Switching Between Locales #

You can use a resource’s all_locales method to get a list of all matching translated resources. This is perfect for a section of your navbar or site footer which could allow the reader to switch to their preferred locale. Using Liquid:

{% for local_resource in resource.all_locales %}
  <a href="{{ local_resource.relative_url }}">{{ local_resource.data.locale | t }}</a>
{% endfor %}

Creating Localized Paths & Filtering Collections #

The in_locale filter/helper can help you link to another part of the site within the currently rendering locale, such as in navbars, sidebars, footers, etc.

<a href="{{ '/posts' | in_locale | relative_url }}">{% t nav.posts %}</a>

In addition, if you’re accessing and looping through a collection directly, you can use the in_locale filter/helper there as well to filter out those resources not in the current locale.

{% assign posts = collections.posts.resources | in_locale %}
{% for post in posts %}
  <li>
    <a href="{{ post.relative_url }}">{{ post.data.title }}</a>
  </li>
{% endfor %}

Pagination and Prototype Pages #

Whether you use one-file-per-locale or multi-locale files technique, your paginated pages and prototype pages will similarly filter out any resources not in the current locale whenever you access paginator.resources.

Updating <head> and other places #

Localize any other string with t there as well, such as the site title or tagline.

We Value Your Feedback #

I18n is hard to get right, and there can be confusing or unexpected edge cases to work through. We value your questions and your suggestions on how to make Bridgetown a great platform for multi-locale websites and apps.