Skip to content

Architecture

How it works

django-traduire sits between django-modeltranslation and a translation API:

Your Django models
       |
django-modeltranslation (creates title_fr, title_de, title_en, ...)
       |
django-traduire (decides what to translate, writes the columns back)
       |
   chunking + richtext + htmltokens (makes the text fit, keeps the markup)
       |
Translation backend (Google free / DeepL / Google Cloud / OpenAI / yours)

Core flow

  1. Discover fields — asks modeltranslation.translator which fields are registered on the model
  2. Read source — reads the source language column (e.g. title_fr)
  3. Check targets — skips target columns that already have content (unless overwrite=True)
  4. Decide the mode — is this field rich text? (HTML_FIELDS, field class, then content)
  5. Group — one group per target language and mode, so one request carries as much as the provider allows
  6. Fit — plain text is chunked on natural boundaries; a rich-text document is planned instead of cut: structural markup is set aside and only the prose inside each block — a heading, a list item, a table cell — becomes a request, so every request fits the budget whatever the document's shape
  7. Translate — calls the backend's translate_raw() once per request
  8. Reassemble — answers are checked before they are trusted, pieces are glued back, markup restored, whitespace preserved
  9. Write back — measures the column, then saves with update_fields

Steps 6 and 8 are the whole point of the split between translate_batch() and translate_raw(): a backend author never writes them.

Pluggable backends

A backend implements one method and declares three limits:

class MyBackend(BaseBackend):
    max_chars = 5000
    max_texts = 20
    supports_html = False

    def translate_raw(self, texts, source, target, is_html=False) -> list[str]: ...

This design means:

  • Batch efficiency — as few requests as the provider allows, not one per field
  • Easy to extend — one method to add a new translation service
  • Testable — swap in a fake backend, and assert on what it received

Modules

Module Responsibility
translator what to translate, and writing it back safely
conf reading and validating the TRADUIRE settings
chunking cutting long text on natural boundaries, losslessly
htmltokens reading a document as tokens and writing it back byte for byte
richtext planning a document: what is structure, what is prose to send
backends.base request budgeting, grouping, and the two rich-text strategies
backends.* one provider each: build a request, read an answer
exceptions TraduireError, ConfigurationError, BackendError

Settings resolution

Setting Default Fallback
SOURCE_LANGUAGE None LANGUAGE_CODE (stripped of region)
TARGET_LANGUAGES None All LANGUAGES except source
BACKEND google_free.GoogleFreeBackend —
AUTO_TRANSLATE False —
HTML_MODE "auto" —
MAX_CHARS None the backend's max_chars
ON_TOO_LONG "truncate" —
FAIL_SILENTLY True —