Skip to main content

djangocms-deepl-translations

Allows to translate Django CMS 5+ content using the DeepL API.

Pipeline badge Coverage badge Release badge Django Packages

Installation

  1. Install the module from PyPI:

    python3 -m pip install djangocms-deepl-translations
    

    Or, if you're using poetry :

    poetry add djangocms-deepl-translations
    
  2. Optionally, install the versioning extension from PyPI:

    python3 -m pip install djangocms-deepl-translations[versioning]
    

    Or, if you're using poetry :

    poetry add djangocms-deepl-translations[versioning]
    
  3. Add it in your INSTALLED_APPS:

      "djangocms_deepl_translations",
      "djangocms_transfer",
    

    Important — Order when using contribs: If you use optional contribs (e.g. djangocms_deepl_translations.contrib.djangocms_stories), list them before djangocms_deepl_translations. The contrib’s ready() must run before the main app’s admin registers its URLs, so that the contrib can register its “translate to” view in the registry. Otherwise the admin will not expose the contrib’s translation URL.

    Example with the Stories contrib:

      "djangocms_deepl_translations.contrib.djangocms_stories",
      "djangocms_deepl_translations",
      "djangocms_transfer",
    
  4. Run the migrations:

    python manage.py migrate
    

Configuration

Required settings

  • DJANGOCMS_DEEPL_TRANSLATIONS_API_KEY: set in your Django settings.

Optional settings

  • DJANGOCMS_DEEPL_TRANSLATIONS_LANGUAGES: a list of tuples (e.g. [("fr", "French"), ("it", "Italian"), ("de", "Deutsche"), ("es", "Spanish")]) of languages codes and names that are available for translation. If unset, settings.LANGUAGES is used.

  • DJANGOCMS_DEEPL_TRANSLATIONS_API_LANGUAGE_MAPPING: a dict mapping your project’s language codes to DeepL API language codes (e.g. "en" → "EN-GB", "fr" → "FR"). If unset, the default mapping is used (fr → "FR", en → "EN-GB", es → "ES", de → "DE", it → "IT", pt → "PT-PT", nl → "NL"). Regional variants (e.g. EN-GB, PT-PT) are used as target languages only; for source languages the module automatically strips the suffix (EN-GB → EN), as required by the DeepL API.

  • DJANGOCMS_DEEPL_TRANSLATIONS_WARN_UNCONFIGURED_PLUGINS: when True (default), Django's system check warns when installed CMS plugin types have no translation config. Set to False to disable these warnings.

  • DJANGOCMS_DEEPL_TRANSLATIONS_BULK_TRANSLATE_DEFAULT_USER_ID: optional integer user id used by bulk_translate_pages (and related bulk commands) when --user is not passed; falls back to the first superuser if unset.

Plugin translation config: DJANGOCMS_DEEPL_TRANSLATIONS_PLUGIN_CONFIG

This setting defines, for each CMS plugin type, which model fields are sent to the DeepL API and how embedded plugins (e.g. links inside text) are handled. Entries override or extend the built-in defaults (TextPlugin from djangocms-text, LinkPlugin from djangocms-link).

Each key is a plugin type name (the class __name__, e.g. "TextPlugin", "LinkPlugin"). Each value is a dict with the following optional parameters:

Parameter Description
translatable_fields Tuple of field paths whose values are sent to the DeepL API. Each item is a field name (e.g. "name") or a nested path using /: e.g. "config/accordion_item_header"
no_translation Boolean. If True, this plugin type is not translated (no API call). Use for structural or non-text plugins. Cannot be combined with translatable_fields.

Example (override default, add a custom plugin):

DJANGOCMS_DEEPL_TRANSLATIONS_PLUGIN_CONFIG = {
    # Override TextPlugin
    "TextPlugin": {
        "translatable_fields": ("body",),
    },
    # Add config for a plugin that has no translatable content
    "StructuralPlugin": {
        "no_translation": True,
    },
    # Add config for a custom plugin with two translatable fields
    "MyCustomPlugin": {
        "translatable_fields": ("title", "description"),
    },
    # Nested path: value at plugin_data["data"]["config"]["accordion_item_header"]
    "AccordionItemPlugin": {
        "translatable_fields": ("config/accordion_item_header",),
    },
}

When DJANGOCMS_DEEPL_TRANSLATIONS_WARN_UNCONFIGURED_PLUGINS is True (default), Django's system check (e.g. manage.py check) warns with a single message listing all installed plugin types that have no entry in this config (or in the built-in defaults), so you can add one and avoid incomplete translations. The message also mentions the suggest_deepl_plugin_config command to generate a base config.

Management command: suggest_deepl_plugin_config

The command suggest_deepl_plugin_config lists all installed CMS plugin types that do not yet have a translation config (neither in the built-in defaults nor in DJANGOCMS_DEEPL_TRANSLATIONS_PLUGIN_CONFIG). It prints on stdout a ready-to-paste Python snippet that adds an entry for each of them with no_translation: True, so that the system check no longer warns and translation is explicitly disabled until you configure them.

When to use it: When you first set up the module or after installing new CMS plugins, to quickly get a full config and then replace no_translation: True with real options (e.g. translatable_fields) only for the plugins you want to translate.

Usage:

python manage.py suggest_deepl_plugin_config

Example output (when some plugins are unconfigured):

# Add this to your settings to disable translation for plugins not yet configured.
# Then replace no_translation=True with translatable_fields (etc.) as needed.
DJANGOCMS_DEEPL_TRANSLATIONS_PLUGIN_CONFIG = {
    "PlaceholderPlugin": {
        "no_translation": True,
    },
    "URLFieldPlugin": {
        "no_translation": True,
    },
}

Copy the printed block into your settings file. If every installed plugin already has a config, the command prints a success message and no snippet.

Management command: bulk_translate_pages

Bulk-translate published CMS pages from a source language to a target language using the same pipeline as the admin “Translate to” action (PageTranslator + DeepL API). Only pages that have a published PageContent in the source language (via djangocms-versioning) are processed.

Two-phase run (default, without --create-only):

  1. Phase 1 — For each page: create and publish target-language page content with empty placeholders (titles/slugs/meta still translated). This makes target pages exist so links in content can resolve correctly on the next pass.
  2. Phase 2 — Full translation: export placeholders, translate plugins, fill target, publish.

Typical workflow: Run the full command once (both phases). If you maintain many pages and need targets to exist before a second pass, you can run with --create-only first, then run again without it to execute phase 2 only on the pages that completed phase 1.

Usage:

python manage.py bulk_translate_pages --source-language fr --target-language en
Option Description
--source-language Required. Django/CMS language code (e.g. fr, en).
--target-language Required. Target language code.
--page-ids Optional. One or more page primary keys. If set, only those pages are processed (each must still be published in the source language). Example: --page-ids 12 34 56.
--create-only Only phase 1: create/publish empty target contents; no placeholder translation.
--fail-silently On error, log to stderr and continue. Default: stop and re-raise on first error (full traceback).
--user User ID used for versioning publish actions. Default: DJANGOCMS_DEEPL_TRANSLATIONS_BULK_TRANSLATE_DEFAULT_USER_ID if set, otherwise the first superuser.

See also Optional settings for DJANGOCMS_DEEPL_TRANSLATIONS_BULK_TRANSLATE_DEFAULT_USER_ID. The command name is bulk_translate_pages (python manage.py bulk_translate_pages).

Optional contribs

Optional integrations (e.g. for translating articles from djangocms-stories) live under djangocms_deepl_translations.contrib.*. Add the contrib app to INSTALLED_APPS before djangocms_deepl_translations (see Installation above), so that the contrib’s ready() runs first and registers its “translate to” view in the admin.

Contrib App name Purpose
Stories (posts/articles) djangocms_deepl_translations.contrib.djangocms_stories DeepL translation for djangocms-stories posts; toolbar “Translate to” on post detail views.
Page meta (TitleMeta) djangocms_deepl_translations.contrib.djangocms_page_meta DeepL translation for djangocms-page-meta TitleMeta SEO fields when translating CMS pages (PageTranslator, including bulk_translate_pages). Requires djangocms_page_meta in INSTALLED_APPS.

When the Stories contrib is installed, you can bulk-translate posts (published source PostContent only) with:

python manage.py bulk_translate_stories_posts --source-language fr --target-language en

Options mirror bulk_translate_pages above: --post-ids, --create-only, --fail-silently, --user (with post primary keys instead of page ids).

Extracting / updating translations (i18n_tools, Poe, makemessages, translate_messages, compilemessages)

Prerequisites

  1. Install project dependencies with Poetry from the repository root (see Installation for runtime deps):

    poetry install
    
  2. Install Poethepoet so the poe tasks are available (it is listed under the dev dependency group):

    poetry install --with dev
    
  3. Optional — auto-translation of .po files uses django-deep-translator via the translate_messages management command. Install the i18n_tools optional group:

    poetry install --with i18n_tools
    

    You can combine groups, e.g. poetry install --with dev,i18n_tools.

The i18n_tools package

The i18n_tools/ directory holds a small Django entrypoint (i18n_tools/manage.py) and settings (i18n_tools/settings.py) used only to run:

  • makemessages — extract/update translatable strings into .po files under djangocms_deepl_translations/locale/ and contrib locale/ trees.
  • translate_messages — fill empty/fuzzy entries in those .po files using machine translation (requires --with i18n_tools).
  • compilemessages — compile .po to .mo for use at runtime.

These commands are wired as Poe tasks in pyproject.toml ([tool.poe.tasks.*]).

Poe tasks

After poetry install --with dev (and --with i18n_tools when using translate_messages), run tasks as poe <task> or, from the repo root:

poetry makemessages --language fr
poetry translate_messages -l fr
poetry compilemessages
Task Purpose
poetry makemessages Regenerate .po for one locale (-l / --language, default fr). Run for each language you maintain, e.g. poetry makemessages --language de and poetry makemessages --language fr.
poetry translate_messages Auto-translate untranslated/fuzzy strings in .po for the target locale (-l) from the source locale (-s / --source-language, default en).
poe compilemessages Compile .po → .mo for one locale (-l, default en). Run per locale or invoke poetry run python3 i18n_tools/manage.py compilemessages with no -l to compile every catalog Django finds.

Example: after makemessages, refresh bothFrench and German catalogs from English:

Make messages for French and German:

# Generate .po files for French and German
poetry makemessages -l fr
poetry makemessages -l de

Fill .po using English as source (default -s is en):

# Fill .po using English as source (default -s is en)
poetry translate_messages -l fr
poetry translate_messages -l de

Then compile both locales for runtime:

poe compilemessages

Note: Machine translation depends on django-deep-translator’s backend configuration (e.g. API keys if applicable). Untranslated strings only are updated when using -u (already set in the Poe task).

Contributing

Feel free to send feedbacks on the module using it's Home page. See CONTRIBUTING.md for contributions.

Tests

Tests are located in the tests directory.

Tests are written with pytest. They are based on a testapp models :

To run the tests, you must launch the command ./run_tests.sh

A coverage report can be generated using the command ./run_coverage.sh

Cross-version matrix (tox)

CI runs tox (see tox.ini) against the Python, Django, and django CMS versions declared as PyPI classifiers in pyproject.toml: django CMS 5.0.x with Django 4.2 / 5.0 / 5.1 / 5.2 / 6.0, and Python 3.10–3.14 (Django 6.x envs use Python ≥ 3.12 only). Interpreters missing on the machine are skipped (skip_missing_interpreters).

Local examples:

pip install "tox>=4.21"
tox run -e py312-django52-cms50    # one environment
# Full matrix: use run-parallel (not `run -p`) — tox 4 puts `-p` on the parallel subcommand.
tox run-parallel                   # parallel envs; default -p = auto (all logical CPUs)
tox p                              # short alias for run-parallel
tox run-parallel -p 4              # at most 4 envs at once (less RAM / less load)
tox run -e coverage                # coverage + `coverage.xml` (default `python3`, Django 5.2, CMS 5)

Parallelism: tox run executes each environment sequentially. For several envs at once, use tox run-parallel (or tox p); -p auto / -p N applies to that subcommand, not to tox run. Optional pytest -n auto (pytest-xdist) inside one env: tox run -e py312-django52-cms50 -- tests -n auto (Django DB settings may need tweaks); prefer run-parallel for the matrix first.

pyenv

tox does not read ~/.pyenv/versions by itself: it looks for python3.10, python3.11, … on your PATH. That usually means the pyenv shims (~/.pyenv/shims), after init.

  1. Load pyenv in the shell you use to run tox (bash example):
    export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}"
    export PATH="$PYENV_ROOT/bin:$PATH"
    eval "$(pyenv init - bash)"   # zsh: eval "$(pyenv init - zsh)"
    
  2. Install the runtimes you need, e.g. pyenv install 3.10.13 (repeat for 3.11, 3.12, …).
  3. Expose every version to shims in this repo (often required when tox is installed via pipx, which otherwise sees a minimal PATH):
    cd /path/to/djangocms-deepl-translations
    pyenv local 3.14 3.12 3.11 3.10   # use your exact `pyenv versions` names
    
    This creates/updates .python-version so python3.10, python3.11, … resolve correctly.
  4. Check before tox: command -v python3.10 && python3.10 -V.

The deprecated tox-pyenv plugin targeted tox 3; with tox 4, pyenv local + shims on PATH is the usual approach (reference).

If your IDE terminal does not load ~/.bashrc / ~/.zshrc, pyenv may never be on PATH — run the eval "$(pyenv init …)" block in that terminal or use a login shell.

License

This project is licensed under the GNU GPLv3 License - see the LICENSE file for details.

Metadata

Release files for djangocms-deepl-translations 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for djangocms-deepl-translations 0.5.0
File Size Uploaded
djangocms_deepl_translations-0.5.0.tar.gz 82.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for djangocms-deepl-translations 0.5.0
File Interpreter ABI Platform
djangocms_deepl_translations-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 206.0 kB

Release files / djangocms_deepl_translations-0.5.0.tar.gz

Download URL djangocms_deepl_translations-0.5.0.tar.gz
Size 82.7 kB
Tags Source
SHA-256 checksum
How to use checksums
8ecadf1862058b338809f9ad3f642bd7f10a4ff87ae074de3918e17ae2948933
BLAKE2b-256 checksum
How to use checksums
b383c3374a00f37398b013dbc2ade4d24fb46e1b6a197f049d122619443f590a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / djangocms_deepl_translations-0.5.0-py3-none-any.whl

Download URL djangocms_deepl_translations-0.5.0-py3-none-any.whl
Size 123.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e28e0386267df7b2fa99032d2f58a7ac10c68d6d8121d57fd1259de186f43807
BLAKE2b-256 checksum
How to use checksums
9edea0a4e764d6d6f321674e367cf63aa005758e7d75d26a078270a675ebc75f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release history Release notifications | RSS feed

0.5.2

2 release files

0.5.1

2 release files

This release

0.5.0 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page