i18n plugin

The i18n plugin is used to automatically redirect your website visitors from specific countries or with a specific browser language setting to a corresponding internationalized version of the URL. For example, a common practice is to have alternate translated versions of webpages with a langage code prefix, i.e. the Spanish version of /about/ would be located at /es/about/ and the French version at /fr/about/. The i18n plugin can be configured to redirect based on either the Accept-Language header or the country of the visitor based on a geo-ip lookup.

In case you aren’t familiar, the term i18n is shorthand for “internationalization”.

The url prefixes such as es/, fr/, etc. are called “locales”. While they commonly are based on ISO 639-1 two letter codes, you can use any strings you like.

File structure

The plugin assumes that the build process that generates your static site duplicates the site content in sub-folders corresponding to your locale names, in this case de, fr, and es. The pages not nested beneath one of the locale folders represents the defaultLocale.

├── 2018
│   └── 11
│       └── 04
│           └── welcome
│               └── index.html
├── about
│   └── index.html
├── de
│   ├── 2018
│   │   └── 11
│   │       └── 04
│   │           └── welcome
│   │               └── index.html
│   ├── about
│   │   └── index.html
│   └── index.html
├── es
│   ├── 2018
│   │   └── 11
│   │       └── 04
│   │           └── welcome
│   │               └── index.html
│   ├── about
│   │   └── index.html
│   └── index.html
├── fr
│   ├── 2018
│   │   └── 11
│   │       └── 04
│   │           └── welcome
│   │               └── index.html
│   ├── about
│   │   └── index.html
│   └── index.html
└── index.html

Static Site Generators

This plugin works extremely well in conjunction with the i18n capabilities of popular static site generators. All of the following emit a static site in the structure described above.

Check the documentation for your static generator of choice. Chances are it either supports i18n output natively, or there exists a plugin that enables it. Checkout our demo site which uses jekyll-polyglot.

Configuration

Declare the i18n plugin in your aerobatic.yml:

plugins:
  - name: i18n
    options:
      defaultLocale: en
      locales:
        # Redirect users whose Accept-Language header is Spanish "es" *OR* if they are
        # located in Mexico, Costa Rica, or Spain.
        es:
          languages: [es]
          countries: [mx, cr, es]
        de:
          languages: [de]
        fr:
          languages: [fr]
          countries: [fr]

  - name: webpage

Options

defaultLocale

The name of your site’s default locale. This locale is not represented in the url path.

locales

Dictionary of your alternative locales. The keys represent the locale prefixes that appear in your site’s locale specific urls, i.e. the es equivalent of https://yoursite.com/about would be https://yoursite.com/es/about.

locale.languages

The list of languages that should redirect to the locale URL. Each language must be a two letter ISO 639-1 code. Typically you will only have a single language specified, but there may be cases where you want two closely related languages to share the same content. If the Accept-Language header corresponds to any of the specified languages, they will be redirected to the corresponding locale URL.

locale.countries

The list of countries that should redirect to the locale URL. Each value must be an ISO 3166-1 alpha-2 country code. The country is based on a conversion of the incoming request IP address. If the request country matches any value in the list, the request is redirected to the locale specific URL.

Locale redirect logic

  1. When a request arrives, the browser language from the Accept-Language and the country identified by the IP address are extracted.
  2. Look for the first locale with a match in either the languages or countries list.
  3. If a match is found, determine if the locale in the request is different than the locale matching the request. If so, 302 redirect to the corresponding locale URL.
  4. If there is a cookie aerobatic-locale, it takes precedence over the HTTP headers. See allowing user to select locale

The following table depicts what would happen under different sceneario assuming the sample configuration show above is in effect and the incoming request is for homepage, i.e. https://www.site.com:

Country Accept-Language Result Reason
US en Render the homepage Country no language match a locale, so render the default
US es Redirect to https://site.com/es/ The language es matches one of the languages for the es locale.
ES en Redirect to https://site.com/es/ The country MX (Mexico) matches one of the countries for the es locale even though the language is en.
US de Redirect to https://site.com/de/ The language de (German) matches one of the languages for the de locale
DE en Render the default homepage The de locale is only configured to redirect based on the language, not the country. So even though this visitor is in Germany, they get the default version.
IT it Render the default homepage The plugin configuration does not specify any locale for the Italian language or the country of Italy

Although the examples on this page use English (en) as the default, that’s definitely not required. Your default locale could just as well be Chinese, Swedish, Russian, or Portugese.

Allowing user to select locale

So far we have only discussed automatically redirecting the user to a locale based on their request headers. However you might wish to allow the user to manually choose their locale and have this value override whatever their headers say. A typical UX for this is to provide some sort of language selector widget like so:

You can tack on a special querystring parameter ?__locale=es. This will cause the server to set a cookie called aerobatic-locale with the specified locale then redirect to the appropriate locale-specific URL. For example a request to https://site.com/about?__locale=es will redirect to https://site.com/es/about. Subsequent requests will continue to honor the value in the cookie rather than inspect the Accept-Language header and the origin country.

Our demo Jekyll site uses the following bit of JavaScript in it’s language selector widget to enable this:

// JavaScript for manually overriding the active locale
document.querySelectorAll("div.locale-selector button").forEach(function(elem) {
  elem.addEventListener("click", function() {
    var locale = elem.getAttribute("data-locale");
    location.replace(location.pathname + "?__locale=" + locale);
  });
});

Fallback to default locale

It might not be practical or desirable to provide a translated version of every single page on your site. If a locale specific URL is requested and a webpage does not exist the user is redirected to the default locale. For example https://site.com/fr/english-only-page will redirect to https://site.com/english-only-page. In this case a 301 permanant redirect is used.

Note that the static site generator may create a duplicate copy of the page in the default language at the locale specific path. This is how jekyll-polyglot works (see fallback language support). In this case just make sure that the <link rel="canonical"> points to the default locale URL to avoid duplicate content issues for SEO.

Other use cases

Using the i18n plugin for translations to different languages is the most common use case, however there are other use cases as well. For example, say you have a statically generated English language eCommerce site and you’d like to show localized currencies to users from a select set of countries. In this case the locale names would refer to countries rather than languages. Here’s how you could configure that:

plugins:
  - name: i18n
    options:
      defaultLocale: en
      locales:
        # Redirect Canadian visitors to /ca/*
        ca:
          countries: [ca]
        # Redirect users in Great Britian, Gibralter, and Zimbabe to /uk/*
        uk:
          countries: [gb, uk, gi, zw]
        # Redirect Chinese users to /cn/*
        cn:
          countries: [cn]

Testing

In order to test that the i18n plugin is behaving as expected, it is useful to be able to simulate HTTP requests with different languages and from different countries. Changing your browser language is straightforward using Postman or a browser extension such as Quick Language Switcher.

Simulating a different country is tricker since it entails proxying your request through an IP address in that country. To make this easier we provide a special querystring parameter __country that you can append to a URL that will override the IP based country logic. For example to simulate a request from China, just request http://yoursite.com?__country=ch.

Unlike the ?__locale querystring which you can use in your site’s locale selector, the ?__country querystring is only intended for your personal testing.