Django I18N on Windows

django, i18n, programming

Concise run book on I18N to get things started

Prerequisite

Know the difference between locale name and language name:

  • Locale name: <language code>_<COUNTRY CODE> (examples: “en_US“, “fr_CA“), case sensitive.
  • Language name: <language subtag>-<range subtag> (examples: “en-us“, “fr-ca“), case insensitive.

They usually look alike EXCEPT locale names use the underscore (“_”) character to separate the language and country while the language names use the dash (“-“) to separate the subtags.

Creating a .po file for translation

  • Install the prerequisite gettext library. See gettext on Windows.
  • Create a “locale” subdirectory in the project’s base directory1.
  • Run python manage.py makemessages -l <locale name> [-l <locale name> ...] and provide locale names
    E.g.
    python manage.py makemessages -l en_US -l fr_CA

Translate each .po file

This can be done manually by editing the *.po file and fill in the msgstr values or by sending the file to a translation service.

Compile the translated .po file

  • Run python manage.py compilemessages
  • This will produce a .mo (e.g. django.mo) file next to the .po (e.g. django.po) file

Configure the Django settings file

Adding the LocaleMiddleware

Adding the middleware “django.middleware.locale.LocaleMiddleware” in the list MIDDLEWARE.

IMPORTANT: it must follow “django.contrib.sessions.middleware.SessionMiddleware” and precede “django.middleware.common.CommonMiddleware“.

For example:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',  # Order is important
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Set the languages supported

Add/edit the setting LANGUAGES, listing all the languages supported. The value should be a list of tuples (language name, description). For example:

LANGUAGES = [
    ('en', 'English'),
    ('es-419', 'Spanish Latin America'),  # NOTE: language name, not locale name
]

Set the path(s) to locale subdirs

Add the path to the “locale” subdirectory where the *.mo files can be found. For example:

LOCALE_PATHS = [
    BASE_DIR / "locale",
]

  1. NOTE that it is also possible to create additionallocale” subdirs under individual apps’ subdirs, as long as they are configured in setting’s LOCALE_PATHS.

Loading Resources from Python Packages

programming, Python

Start with importlib_resources

Start with the package importlib_resources.

Properly export modules

In order for a resource to be accessible, the module (or most likely the submodule) containing it needs to be properly exported. By properly exported I mean to adding the submodule containing the resource inside the setup.py.

In this case, I wanted to make a JSON file (answers.json) accessible from a submodule (v8ball.van) of the package vans-eightball:

v8ball
  van
    answers.json
    ...

To export the submodule, the packages property in the setup.py file needs to include “v8ball.van” in order for the resource answers.json to be exported and accessible:

setup(
name="vans-eightball",
version="0.0.2",
...
packages=["v8ball.van", "v8ball"],
include_package_data=True,
...
)

Accessing the resource

An example of accessing the resource:

Install the package

pip install vans-eightball

Accessing the resource

import json
import v8ball.van
from importlib_resources import files
resource_path = files(v8ball.van).joinpath('answers.json')
data = json.loads(resource_path.read_text())

Python Package Publishing Notes

programming, Python

Foundations

For the most part, the doc How to Publish an Open-Source Python Package to PyPI – Real Python is what I followed. However, had that been it, I wouldn’t need to write this page.

Refinements

Installing twine by itself isn’t enough; wheel is also required:

pip install wheel
pip install twine

If wheel is not installed, I get this error when trying to build:

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] …]
or: setup.py --help [cmd1 cmd2 …]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'bdist_wheel'

Test Publishing and Installing

To publish to the test repo:

twine upload --repository-url https://test.pypi.org/legacy/ dist/*

To test installing from the test repo:

pip install -i https://test.pypi.org/simple/ <package name>