Internationalization (i18n)#
Treat the source language as “developer” English, using the language code en_US
. Then, use Transifex to translate the “developer” English to “proper” English, using the language code en
.
Note
If you are using the source language as “proper” English, then replace en_US
with en
in the commands and templates below.
Reference: Non-English as a Source Language
See also
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 babel.cfg file, for example:
[python: **.py]
[jinja2: **.html]
Add the Transifex mapping#
Note
For reference, the equivalent with the old Python Transifex Client, replacing TXPROJECT
and APP
, was:
tx config mapping -r TXPROJECT.django -f APP/locale/en_US/LC_MESSAGES/django.po -s en_US -t PO 'APP/locale/<lang>/LC_MESSAGES/django.po'
Translate with Transifex#
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 -l en_US --no-obsolete
pybabel extract -F babel.cfg -o messages.pot .
pybabel update -i messages.pot -d locale
Then, push the PO files to Transifex with:
tx push -s
If you made local changes to translations, push the translations to Transifex. For example:
tx push -t -l en
When ready, pull the translations from Transifex with:
tx pull -f -a
Then, compile the PO files to MO files with:
python manage.py compilemessages
pybabel compile -f -d locale
Reference: Django Translation
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, use the following. 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:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '{{ cookiecutter.python_version }}'
cache: pip
cache-dependency-path: '**/requirements*.txt'
- name: Install translate-toolkit
run: |
sudo apt update
sudo apt install gettext translate-toolkit
- run: pip install -r requirements.txt
- run: python manage.py makemessages -a
- name: Count incomplete translations
shell: bash
run: |
output=$(find . -name LC_MESSAGES -not -path "*/en_US/*" -exec pocount --incomplete --short "{}" +)
echo $output
[ "$output" = "" ]
For a Babel project, adapt:
name: Translations
on:
pull_request: {}
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install translate-toolkit
run: |
sudo apt update
sudo apt install translate-toolkit
- run: pip install babel
- name: Update catalogs
run: |
pybabel extract -F babel.cfg -o locale/sphinx.pot -k '_ l_ lazy_gettext' .
pybabel update -i locale/sphinx.pot -d locale -D sphinx
- name: Count incomplete translations
shell: bash
run: |
output=$(find . -name LC_MESSAGES -exec pocount --incomplete --short "{}" +)
echo $output
[ "$output" = "" ]
Warning
If your default branch is not named main
, edit the push:
key.
Note
If you use the jinja2
extractor, install jinja2
with pip
.