Django has built-in support for multiple languages and countries. Adapting Django projects to multiple languages and countries requires diving into three major topics, as defined by the software industry in general [1] [2]:Internationalization, also known as i18n for short i(18 letters for 'nternationalizatio')n; localization, also known as l10n for short l(10 letters for 'ocalizatio')n; and time zones.

Django default i18n, L10n and time zone behavior

The good news about adapting a Django project to multiple languages and countries is Django projects are enabled by default to support this behavior, the bad news is that doing so still requires a fair amount of configuration and design choices, in addition to the major effort required to make the actual text translations.

Listing 13-1 illustrates the default configurations in settings.py designed to influence i18n, L10n and timezone behavior in Django projects.

Listing 13-1. Default multi-language and multi-country Django configurations in settings.py

USE_I18N = True
USE_L10N = True
LANGUAGE_CODE = 'en-us'

USE_TZ = True
TIME_ZONE = 'UTC'

The USE_I18N variable defines whether a Django project should use i18n. As you can see in listing 13-1, USE_I18N defaults to True. This ensures a Django project is enabled to look for certain i18n queues in a project's structure and files (e.g. formatted strings, translation bundles). It's possible to set USE_I18N to False to this disable i18n checkups with the purpose of getting a minimal -- often negligible -- performance boost.

The USE_L10N variable defines whether a Django project should format numbers and dates according to project's locale. Although a float number will be a float no matter where in the world a Django project is used, just as date will be a date, numbers and dates tend to vary how they're represented across the world. For example, the number 1,000.34 can also be represented as 1 000,34 -- note the space to separate thousands and comma to separate decimals -- depending on a region, similarly, the date December 31st 2020 can be represented as 12/31/2020 (month,day,year) in the United States, as well as 31/12/2020 (day/month/year) in France.

As you can see in listing 13-1, USE_L10N defaults to True to ensure the localization of dates and numbers is done by default, using the LANGUAGE_CODE variable to determine the formatting locale (e.g. with LANGUAGE_CODE='en-us', dates and numbers are formatted according to the English(en)-United States(us) locale). If you set USE_L10N to False, Django falls back to the underlying Python's installation locale to determine how to format dates and numbers. The section "Localize numbers and dates" has more details on this process.

The LANGUAGE_CODE variable in listing 13-1 define a project's language code per the IETF language tag standard[3] which is also used by Internet browsers. The first two characters of the LANGUAGE_CODE variable -- which are required -- represent a language based on the ISO 639-1 standard[4], the second two character separated by - represents a country based on the ISO 3166-1 standard[5]. The default LANGUAGE_CODE variable as you can see in listing 13-1 is en-us, where en represents English and us the United States, meaning this Django project uses English based on the United States dialect.

The reason behind the second two letters in the LANGUAGE_CODE is due to language variations from one country to another. For example, in English the word color can also be written as colour, just as center can be written as centre, the difference is one variation is the preferred spelling for English in the United States and the other for English in the United Kingdom. These same minor variations can happen in other languages like German (e.g. spelling for German in Germany & spelling for German in Switzerland), Spanish (e.g. spelling for Spanish in Spain & spelling for Spanish in Mexico) and Portuguese (e.g. spelling for Portuguese in Brazil, spelling for Portuguese in Portugal).

Because dealing with regional language variations can lead to additional -- and sometimes unnecessary work -- a two letter LANGUAGE_CODE value (e.g. en for English, es for Spanish) is perfectly valid, just as it's valid for browsers per the IETF language tag standard.

The LANGUAGE_CODE value influences three things:

The USE_TZ variable in listing 13-1 defaults to True to indicate a Django project should use time-zone aware date times. Time zone aware dates are a nice feature, particularly when you plan to operate in multiple countries where time zones can influence an application's logic. For example, midnight activity in America, is early morning in Europe and early mid-day in Asia, so how do you generate time-based reports for all regions ? Time zone aware date times can help you sort these types of issues. You can disable time-zone aware date times in Django setting the USE_TZ variable to False.

When the USE_TZ variable is set to True, unless a date time explicitly declares a time zone, Django uses the time zone defined in the TIME_ZONE variable. In listing 13-1, you can see the TIME_ZONE variable defaults to UTC -- or Coordinated Universal Time -- which is the primary reference time used by many clocks and computers.

Language codes, locales, languages and HTTP headers

Armed with a basic understanding of Django's default i18n, L10n and time zone behaviors, the following is another list of recurring generic concepts that are essential to understand when embarking on a project of this kind:

  1. https://en.wikipedia.org/wiki/Internationalization_and_localization     

  2. https://en.wikipedia.org/wiki/Tz_database     

  3. https://en.wikipedia.org/wiki/IETF_language_tag     

  4. https://en.wikipedia.org/wiki/ISO_639-1     

  5. https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2     

  6. https://github.com/django/django/blob/master/django/conf/global_settings.py#L51     

  7. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language