Search by

numero2 / contao-deepl

numero2

DeepL powered translations in the Contao Backend.

Package info

github.com/numero2/contao-deepl

Type:contao-bundle

pkg:composer/numero2/contao-deepl

Statistics

Installs: 1 643

Dependents: 1

Suggesters: 1

Stars: 5

Open Issues: 4

1.2.0 2026-09-30 10:16 UTC

This package is auto-updated.

Last update: 2026-09-30 10:25:55 UTC


README

About

This extension allows you to translate individual fields within a DCA (Data Container Array) with just one click, leveraging the DeepL API for accurate translations. It also includes caching of previously translated texts to optimize performance and minimize API calls.

System requirements

Installation & Configuration

  • Install the extension via Contao Manager or Composer (composer require numero2/contao-deepl)
  • Add your DeepL API Key to your .env

    DEEPL_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

  • Alternatively, you can add the API key to your config/config.yaml
deepl:
    api_key: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx'
  • A preferred mapping can be created for language variants (e.g. British English en-GB, Brazilian Portuguese pt-BR, etc.). The exact language code to be used can be found at Deepl.com.
deepl:
    api_key: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx'
    pref_lang:
      en:en-GB
      pt:pt-BR

Glossaries

A glossary keeps your terminology out of the translation: product names, brand terms, wording that must stay as it is. Create and maintain the glossaries in the DeepL web app and reference their IDs per language pair, written in base language codes:

deepl:
    api_key: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx'
    source_lang: 'de'
    glossaries:
      de-en: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
      de-fr: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

DeepL only accepts a glossary when the source language is explicit, so a source language has to be known. It is determined in this order:

  1. The fallback language of the site the record belongs to. In the usual multilingual setup — one language tree per language, one of them the fallback — that is the language you translate from, and it is derived per site, so an installation hosting several sites in different main languages gets the right answer for each. Nothing to configure.
  2. source_lang, when the first yields nothing. That happens when a root page is its own fallback, which is the normal shape of a one-domain-per- language setup.

If neither yields a language, the text is translated without a glossary rather than with a guessed source. A wrong source language means DeepL silently finds no glossary for the pair, and nobody notices that the terminology was not applied.

Glossaries exist for base languages only (en, not en-US), so the regional variant is stripped for the lookup while the translation itself keeps the full target code.

Usage

After installation, each field that can be translated will display a small DeepL translation icon DeepL Logo next to its label. Once clicked, DeepL will automatically translate the text in the field to match the language of your current page settings.

Contao Backend showing the DeepL translation button

💡 Hint: You can also translate all fields at once by pressing ALT+T on Windows or Option+T on Mac.

Which fields are translated

A button needs a target language first. It is determined by a language resolver from the page tree the record belongs to. The bundled resolvers cover pages, articles, forms, news and events, including their content elements. Records of other extensions that have no link to a page (a store locator, for instance) get no button at all until a resolver for them is registered: a service implementing LanguageResolverInterface, picked up automatically.

Within a table that has a resolver, whether a field gets a translation button is derived from its DCA, so fields of other extensions are handled the same way as core fields. The first rule that applies wins: c

deepl:
    fields:
        tl_content.embed: false       # never translate
        tl_form_field.value: true     # translate, although excluded by default
        '*.subheadline': false        # never translate, in any table

If you maintain an extension, mark a field in its DCA instead:

$GLOBALS['TL_DCA']['tl_my_table']['fields']['token']['translate'] = false;
$GLOBALS['TL_DCA']['tl_my_table']['fields']['label']['translate'] = true;

To see what is offered for translation and why, run:

vendor/bin/contao-console debug:deepl-fields tl_content

Without a table name it lists all tables supported out of the box; --all also shows the fields skipped for their input type.

The command first checks whether a language resolver handles the table. If none does, it warns instead of listing fields, since no button would appear anyway. For tl_content it also lists every parent table found in the database and whether content below it can be resolved. To make your own resolver show up there, also implement TableAwareResolverInterface and declare its tables in supportsTable(); resolvers without it are reported as "unknown".

Supported bundles

This extension supports the following Contao bundles: