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:
- Number and date output formats, if
USE_L10N
is set toTrue
, as described in the previous section. - The language used for internal Django constructs, like the Django admin and error messages.
- The fall back language to use in case a user's requested language isn't available or supported by a Django application, the next section Django language selection workflow contains more details on the role end users have in determining a Django application's language.
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:
- Language code.- A reference for a <two_lowercase_letter_language>-<two_lowercase_letter_country> or <two_lowercase_letter_language> (e.g.
en-us
anden
are both language codes). Note Django normalizes language codes to lowercase letters, in other contexts (e.g. browsers and other commercial software) the Djangoen-us
language code is usually represented with the country code in uppercase (e.g.en-US
). - Locale.- Loosely speaking a locale is the same thing as a language code in Django, with a syntax variation: the two letter country code is converted to uppercase and the dash is converted to an underscore (e.g. the
en-us
language code corresponds to theen_US
locale, similarly theen
language code corresponds to theUS
locale). Strictly speaking though -- in other software -- a locale requires to be a <two_letter_language>-<two_letter_country>, otherwise a <two_letter_language> is simply a language, but Django doesn't use this subtle distinction, just as Django also doesn't use the standard locale syntax of &l;ttwo_lowercase_letter_language>-<two_uppercase_letter_country> (e.g.en-US
). - Languages.- Django also defines close to 100 internally supported languages using the same language code syntax (e.g.
en
anden-gb
are both Django languages). By internally supported languages it means languages for which Django is equipped to operate in (e.g. present the Django admin, messages and errors in a given language). Django defines its default internal languages in theLANGUAGES
variable[6] insettings.py
, which can be overridden like any other configuration parameter. - HTTP Accept-Language header.- Nearly all browsers that visit a Django application will include the HTTP
Accept-Language
header[7] with a list languages or locales (i.e. a list of <two_lower_letter_language> or <two_lowercase_letter_language>-<two_uppercase_letter_country> values). This list of languages or locales is a user's way of indicating "I prefer these languages", therefore Django can take these values into account to determine which language/locale to use for a particular user.