This release is a pre-release and may not be stable for production use.
collective.translators
This package extends plone.app.multilingual with pluggable external translation utilities for automatic content translation in Plone. It integrates several translation providers, so you can configure DeepL, AWS Translate, LibreTranslate, DeepSeek, Ollama, or ChatGPT and use them to translate your content.
This add-on requires plone.app.multilingual PR #468.
No released version of plone.app.multilingual provides the IExternalTranslationService interface yet.
Translation services
Each service registers a named utility that provides IExternalTranslationService.
The utilities share the same interface for translating content and for reporting the languages they support, so you can switch between providers without changing anything else.
Configure each service from its own control panel.
Google Translate is not part of this package.
plone.app.multilingual already ships it, and you configure it with the Google API key in the Languages site setup.
| Service | Utility name | Factory |
|---|---|---|
| AWS Translate | aws_translate |
AWSTranslatorFactory |
| ChatGPT | chatgpt_translate |
ChatGPTFactory |
| DeepL | deepl_translate |
DeeplTranslatorFactory |
| DeepSeek | deepseek |
DeepSeekFactory |
| LibreTranslate | libretranslate_translate |
LibreTranslateTranslatorFactory |
| Ollama | ollama |
OllamaFactory |
AWS Translate
Uses Amazon AWS Translate. Reads the credentials and the region from the Plone registry. Falls back to language autodetection when the source language is unknown.
ChatGPT
Uses the OpenAI API. Reads the credentials from the Plone registry.
DeepL
Uses the DeepL API, on either the Free or the Pro endpoint. Reads the API key from the Plone registry. Detects the source language on request, and translates both text and HTML.
DeepSeek
Uses DeepSeek, a translation API backed by a large language model. Reads the API key from the Plone registry. Translates through chat completions.
LibreTranslate
Uses an open source LibreTranslate server. You set the server URL and the API key. Detects the source language on request, and translates both text and HTML.
Ollama
Uses an Ollama server, so the models run on your own hardware. You set the server URL and the model. Use this service for private or offline translation.
Installation
Add collective.translators to the dependencies of your project.
LibreTranslate works out of the box, because it only needs HTTP requests. The other services need a client library, which you pull in through an extra:
collective.translators[deepl]
collective.translators[deepl,aws]
Add-ons control panel
Every translation service ships its own install and uninstall profile, and appears as a separate entry in the add-ons control panel.
| Add-on | Extra it needs |
|---|---|
| Collective Translators: AWS Translate | collective.translators[aws] |
| Collective Translators: ChatGPT | collective.translators[chatgpt] |
| Collective Translators: DeepL | collective.translators[deepl] |
| Collective Translators: DeepSeek | collective.translators[deepseek] |
| Collective Translators: LibreTranslate | none |
| Collective Translators: Ollama | collective.translators[ollama] |
An entry appears only when the library it needs is importable. A control panel therefore never points at a service that your site cannot use.
Installing any service also installs the shared collective.translators:default profile as a dependency.
That profile holds no configuration of its own, so it stays out of the list while it has nothing to offer.
It appears as Collective Translators: shared base only while an upgrade of the add-on is waiting to run, because the control panel reaches the upgrade steps of a product only through a profile it lists.
Upgrade from 1.0.0a2 or older
Older versions installed every service from a single profile.
To migrate a site, open /prefs_install_products_form and run the upgrade that Collective Translators offers there.
The same step is also available from /portal_setup/manage_upgrades, under the collective.translators:default profile.
The upgrade keeps the settings of the services whose library you installed, API keys included. It removes the leftover registry records and configlets of the other services. Install those services again from the add-ons control panel once you add their library.
Add a new service
Contribute a new translation service in either of two ways:
- Open a pull request against this package, following the structure below.
- Publish a separate Plone add-on that provides an external translation utility with the same interface and the same registration pattern.
Read the code of an existing service, such as DeepL or LibreTranslate, for a concrete example of every file below.
1. Implement and register the utility
Your utility class must implement IExternalTranslationService from plone.app.multilingual.interfaces, with at least these methods:
is_available()returnsTruewhen the service is enabled and ready.available_languages()returns the supported language codes, or the supported source and target pairs.translate_content(content, source_language, target_language)translates the content and returns the translated text.
Register a module level instance of the class in mytool/configure.zcml:
<utility
provides="plone.app.multilingual.interfaces.IExternalTranslationService"
name="your_tool_name"
component=".utility.YourTranslator"
/>
2. Include the package
Add the include to src/collective/translators/configure.zcml.
Guard it with a zcml:condition when your service needs a client library:
<include
package=".mytool"
zcml:condition="installed yourlibrary"
/>
3. Add a control panel
This step is optional. Skip it when your service needs no configuration.
Write the registry schema and the control panel form in mytool/controlpanel.py, then register the browser page and the plone.restapi adapter in mytool/configure.zcml.
Give the adapter a configlet_id that matches the action_id of the configlet, otherwise plone.restapi leaves your panel out of @controlpanels.
Declare a browser layer that extends collective.translators.interfaces.IBrowserLayer in mytool/interfaces.py, and bind the browser page to that layer.
4. Ship an install and an uninstall profile
Register both profiles in mytool/profiles.zcml, and include that file from mytool/configure.zcml.
The profiles then exist only when the ZCML of your service loads, which is what hides the add-on when the library is missing.
<genericsetup:registerProfile
name="default"
title="Collective Translators: My Tool"
provides="Products.GenericSetup.interfaces.EXTENSION"
directory="profiles/default"
/>
Put metadata.xml, browserlayer.xml, registry.xml, and controlpanel.xml in mytool/profiles/default/.
Make metadata.xml depend on profile-collective.translators:default.
Put browserlayer.xml, registry.xml, and controlpanel.xml in mytool/profiles/uninstall/, each one with remove="true".
Add the name of your service to collective.translators.setuphandlers.SERVICES, so that its uninstall profile stays hidden from the add-ons control panel.
5. Test the service
Restart your site. Install your add-on from the add-ons control panel. Open the control panel of your service, enter your API key or your settings, and translate a page.
Contribute
License
GPL version 2.
Changelog
1.0.0a2 (2026-10-01)
Breaking changes
- Remove the Google Translate service, with its profile and control panel. The 1002 upgrade step cleans its registry records and configlet from existing sites.
- Split the single GenericSetup profile into one install and one uninstall profile per translation service. Each service is now a separate entry in the add-ons control panel, shown only when the library it needs is importable. The shared
collective.translators:defaultprofile is hidden and installed as a dependency, and it no longer registers a browser layer of its own: every service registers a layer extendingcollective.translators.interfaces.IBrowserLayer. Existing sites are migrated by the upgrade step to profile version 1001, which keeps the settings of the available services and removes the leftovers of the others. [mamico]
Bug fixes
- Align
configlet_idwith theaction_idof the configlet for AWS Translate, ChatGPT, DeepL, DeepSeek, LibreTranslate and Ollama, so the panels are listed by the@controlpanelsendpoint ofplone.restapi. [mamico] - Register the missing control panel configlet for Ollama. [mamico]
- Show the
collective.translators:defaultprofile in the add-ons control panel while it has upgrade steps to run, and hide it the rest of the time. The control panel drops a hidden profile before it builds the entry of its product, so a permanently hidden base profile kept its upgrade steps out of/prefs_install_products_form, and those steps are the migration path of the sites installed with the single profile of 1.0.0a2 and older. Keying the decision on pending upgrades rather than on one version number keeps later migrations reachable as well. The uninstall profiles stay hidden at all times. [mamico]
Internal
- Move package metadata from
setup.pytopyproject.toml@plone - Require
Manage portalfor every control panel view. DeepSeek, Google Translate and LibreTranslate used to requireplone.app.controlpanel.Language, while their configlet already requiredManage portal. [mamico]
Tests
- Run the test suite with pytest instead of zope-testrunner, and declare
pytest,pytest-covandpytest-plonein thetestextra. Add coverage of the per service profiles and of the upgrade step. [mamico] - Run the tests in CI against the
plone.app.multilingualbranch that providesIExternalTranslationService, checked out by mxdev fromtox -e init, and move the constraints to the Plone 6.2 set the test matrix already names. Without it the ZCML of the add-on cannot be loaded at all against a releasedplone.app.multilingual. [mamico]
1.0.0a1 (2026-08-24)
Bug fixes:
- Fixed the issue related to not yet installed package. [mamico]
1.0.0a0 (2026-08-17)
Internal:
- Update configuration files @plone
100.0.0 (2025-05-11)
No significant changes.
1.0.0 (2025-05-11)
No significant changes.
Metadata
Release files for collective.translators 1.0.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| collective_translators-1.0.0a2.tar.gz | 53.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| collective_translators-1.0.0a2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 125.3 kB
Release files / collective_translators-1.0.0a2.tar.gz
| Download URL | collective_translators-1.0.0a2.tar.gz |
|---|---|
| Size | 53.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c78ec765075cb68a8ccf45e83a8760f38e7f754c9378d4e494889ef69879317
|
|
BLAKE2b-256 checksum How to use checksums |
ac33dc52f10fb85c466a24018234781957cc52aeeb9be5612f33372f508affc3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.14
|
Release files / collective_translators-1.0.0a2-py3-none-any.whl
| Download URL | collective_translators-1.0.0a2-py3-none-any.whl |
|---|---|
| Size | 72.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0dbea4edb5e1b5666a25fc628d45a459e62193cb72824a797a8a08696dfda4f6
|
|
BLAKE2b-256 checksum How to use checksums |
4c3f6791c42f1cb37c63126c056f3ba93e58acced1483ef6aee0dbf8f698024e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.14
|