Internationalization (i18n)

See also

Preferred packages for Internationalization (i18n)

Treat the source language as “developer” English, using the language code en_US. Then, translate the “developer” English to “proper” English, using the language code en.

Note

If using the source language as “proper” English, then replace en_US with en in the commands and templates below.

Reference:

Install dependencies

pip install -r docs/requirements.txt sphinx-intl
pip install -r requirements.txt
pip install ocdsextensionregistry[cli] sphinx-intl
pip install babel

If you use a translation tool:

Install the Crowdin CLI:

npm install -g @crowdin/cli

Set the Crowdin personal access token in the .crowdin.yml file in your home directory:

api_token: ...

Install the Transifex Client.

Initialize tool configuration

If you use a translation tool:

  1. Create the project on Crowdin.

    1. In the project’s Customize Tabs settings, disable all except Sources, Members and Activity.

    2. Add managers to the project.

  2. Find the project’s ID:

    crowdin project list -i 1
    
  3. Create the configuration file, using manage.py from data-support:

    path/to/manage.py update-crowdinyml-files --project-id PROJECT_ID
    
sphinx-intl create-txconfig

Mark strings to translate

When using Django, use its translation functions and template tags. Otherwise, use gettext.gettext().

Configure message extraction

When using Django, this step is already done. Otherwise, create a pyproject.toml file, for example:

[[tool.babel.mappings]]
method = "python"
pattern = "**.py"

[[tool.babel.mappings]]
method = "jinja2"
pattern = "**.html"

Extract strings to translate

Whenever text in the interface is added or updated, you must extract the strings to translate from the code files into PO files by running:

django-admin makemessages --no-obsolete -l en_US

If you are not using a translation tool, update the translation files. For example:

django-admin makemessages --no-obsolete -l en -l es
sphinx-build -nW --keep-going -q -b gettext docs/ docs/_build/gettext

If you are not using a translation tool, update the translation files:

sphinx-intl update -p docs/_build/gettext -d docs/locale
make extract

See its documentation.

If you are not using a translation tool, update the translation files:

sphinx-intl update -p build/locale -d locale
pybabel extract -F pyproject.toml -o messages.pot .
pybabel update -N -i messages.pot -d locale

Update tool configuration

If you use a translation tool, whenever a new PO file is added, you must update its configuration.

Using manage.py from data-support:

# Sphinx:
path/to/manage.py update-crowdinyml-files -p docs/_build/gettext -d docs/locale
# Standard or Extensions:
path/to/manage.py update-crowdinyml-files -p build/locale -d locale

Translate with a tool

Push the PO files:

crowdin push sources
tx push -s

If you made local changes to translations, push the translations. For example:

crowdin push translations --import-eq-suggestions -l es-ES
tx push -t -l es

When ready, pull the translations with:

crowdin pull translations
tx pull -f -a

Compile the PO files to MO files

python manage.py compilemessages
sphinx-intl build -d docs/locale

This step isn’t required.

sphinx-intl build -d locale
pybabel compile -f -d locale

Continuous integration

Repositories that support multiple locales should test that translation files are complete. To do that, the workflow extracts messages, updates catalogs, and then counts incomplete translations.

Note

These workflows only run on push for the main branch, so that feature branches don’t fail until a PR is created.

Create a .github/workflows/i18n.yml file.

For a Django application, reuse the i18n-django workflow. Change the python-version to match the version used to compile the requirements_dev.txt file.

name: Translations
on:
  pull_request: {}
  push:
    branches: [main]
jobs:
  i18n:
    uses: open-contracting/.github/.github/workflows/i18n-django.yml@main
    permissions:
      contents: read
    with:
      source: en_US
      python-version: '{{ cookiecutter.python_version }}'

For a Babel project, reuse the i18n-babel workflow. Change the command as needed:

name: Translations
on:
  pull_request: {}
  push:
    branches: [main]
jobs:
  i18n:
    uses: open-contracting/.github/.github/workflows/i18n-babel.yml@main
    permissions:
      contents: read
    with:
      command: |
        pybabel extract -F pyproject.toml -o messages.pot .
        pybabel update -N -i messages.pot -d locale

Warning

If your default branch is not named main, edit the push: key.

Note

If you use the jinja2 extractor, install jinja2 with pip.