This release is a pre-release and may not be stable for production use.
cs.linguacopier
This products adds an action to copy contents to a selected language.
We have faced many times the work to create the contents of a site in one language and then recreate it in another language to let the customer or translators translate it.
This products provides an action with several options, which allows the content editor to recreate the contents of one section of the site in one or more languages, easing the work of the content editor.
Disclaimer: check the documentation to learn how this product can help you on effectively translating the content.
Translation
If a translation service is configured in Plone — for example Google Translate or DeepL — a copy can optionally translate the copied text as it goes.
- Tick Translate the copied content? in the classic UI form, or send
"translate": trueover REST. - Page titles, descriptions, and rich text are translated with the configured service, preserving rich-text markup.
- Fields marked language-independent (for example a shared relation) are left to
plone.app.multilingual; this add-on does not copy or translate them. - When nothing can translate a value, the original is kept and the copy is not aborted.
- The copy report shows, per object, whether it was translated, partially translated, or left as-is.
Translation is optional. Without a configured service — or without the (merged but unreleased) plone.app.multilingual external-translation API — the copy behaves exactly like a plain copy.
Installation
Install cs.linguacopier by adding cs.linguacopier it to your project's dependencies (either buildout, pyproject.toml, requirements.txt, uv or whatever you use to manage your Plone project's dependencies).
REST API
When plone.restapi is installed, the copier is also available over REST, so a Volto front end can use it:
POST /<content>/@copy-content-to
with a JSON body such as:
{
"target_languages": ["es", "ca"],
"include_context": true,
"include_children": true,
"translate": true
}
The endpoint requires the Manage portal permission and always answers 200 with a per-object result, including the translation outcome of each copied object. See the documentation for the full request and response contract.
Background copy jobs
A large translated copy can be run in the background instead of inside the request. A copy is queued as a job when it is sent with mode set to background, or when the automatic mode decides the copy is big enough to defer. Queued jobs are processed by a worker you run as a separate command:
bin/instance run src/cs/linguacopier/worker.py
Schedule that command with cron or a systemd timer. The worker records a heartbeat and the status of every job, shown in the Site Setup → Content copier panels, where jobs can also be cancelled, retried or deleted. See the documentation for the settings and examples.
Contribute
Support
If you are having issues, please let us know using the Github Issue Tracker: https://github.com/codesyntax/cs.linguacopier/issues
License
The project is licensed under the GPLv2.
Contributors
- Mikel Larreategi, mlarreategi@codesyntax.com
Changelog
2.4.0a1 (2026-10-15)
New features
- A
linguacopier-workerconsole script now runs the background job runner from the instance'szope.conf, so it can be scheduled by command name without knowing where the package is installed (drained bylinguacopier-worker /path/to/etc/zope.conf, which suits containers and cron). - Add a background mode to the content copier for large translated copies. A copy
request with
mode=backgroundon@copy-content-tois stored as a persistent job and returns202 Acceptedwith the job, instead of running inside the request. A separate worker command drains the queued jobs and records each job's status, progress and errors;GET @copy-jobslists the jobs andGET @copy-jobs/<id>reports one. - Background copy jobs are now processed in chunks, so a large copy commits its
progress progressively instead of in one transaction, and a job interrupted by a
worker restart resumes where it stopped. A job can be cancelled with
DELETE @copy-jobs/<id>: a queued job stops at once, a running one at the next chunk boundary. - Site Setup gains two Content copier panels managed with
cmf.ManagePortal. A settings form configures the copy mode, size threshold, chunk size, retries, worker user and job retention. A jobs list shows each background copy job's status, progress, timestamps and errors, with actions to cancel, retry or delete it, and a warning when no worker is running. - The background copy worker now prunes finished jobs according to the retention settings on every run, and how to run the worker — manually, with cron or a systemd timer — together with its heartbeat and retention settings, is documented.
- The classic-UI
@@copy-content-toform gains a copy-mode choice (automatic, directly, or in the background), preselected from the site's default mode. A copy sent to the background is queued and the editor is taken to the copy jobs panel; the direct and automatic choices still render the copy report as before. - The copier now chooses automatically whether to run a copy directly or in the
background. A copy with nothing to translate runs directly; a translated copy
runs directly when the estimated work (objects times target languages) fits the
configured limit and in the background otherwise. An explicit
modeon the request (auto,directorbackground) overrides the choice.
Internal
- Store background copy jobs — and their ordering and per-job records — in BTrees instead of the monolithic persistent mapping and list objects, so editing the job store rewrites only the affected buckets rather than re-serialising a whole structure into the ZODB on every change.
2.3 (2026-10-15)
New features
- Data grid field values are no longer copied verbatim: their free-text columns are translated row by row (using the same free-text-only rule), while numbers, choices and other non-text columns are copied as is.
Bug fixes
- Only free-text field values are now sent to the translation service. Controlled tokens (vocabulary/taxonomy choices), numbers, booleans, dates and times, e-mail addresses, URLs, passwords, relations, JSON, and file/image blobs are copied verbatim, so missing translation calls are avoided and their values are never sent to a translator.
- Ownership metadata fields (creators, contributors and rights) are copied verbatim and no longer sent to the translation service.
- Related items are now remapped to the corresponding translated objects when copying, for single relation choices as well as relation lists, and for relation fields provided by a behavior and not just those on the object's own schema. An unused, Archetypes-era helper that duplicated this for UID lists is removed.
Internal
- Update configuration files @plone
2.2 (2026-10-08)
New features
- When a copy translates content, the copied object's id (its URL segment) is now derived from its translated title, normalized with the target language's rules, so a translated page has a URL in the target language instead of the original wording. It applies only when a translation is first created, keeps the default id when the title is empty, and makes colliding ids unique the Plone way.
Bug fixes
- A copied subtree now keeps the source's sibling order. The copier enumerates each folder's direct children in their position order and, once the copy is done, sets each target folder's order to match its source, so a translated section reads in the same sequence as the original (including nested folders and existing translations).
- Stop translating fields whose value is a controlled token — a vocabulary term, a taxonomy value, or any other enumerated value. Such fields are now copied verbatim, so a translated copy never stores an invalid value that can break the page that renders it; free text is still translated.
2.1 (2026-10-05)
New features
- Add a
translateoption to the content copier: a checkbox in the@@copy-content-toform and atranslateboolean on the@copy-content-toREST request. When set, the copied text field values are translated with the external translation service configured in Plone, keeping the original value when nothing can translate it. Fields marked as language-independent are left to plone.app.multilingual, which shares and keeps them in sync, so the copier does not copy them. The copy report shows which items were translated in full, in part, or not at all.
Documentation
- Document the optional translation of copied content: the translate option in the classic UI form and the REST API, the three-valued translation outcome in the copy report, the field selection and fallbacks, and the external translation service it relies on.
2.0 (2026-10-02)
Breaking changes
- Replace
pkg_resourcesnamespace with PEP 420 native namespace. Support only Plone 6.2 and Python 3.10+. #3928 - Rename the
@@copy-content-toform fields toinclude_contextandinclude_children, matching the new REST API parameter names.
New features
- Add a
POST /<content>/@copy-content-toREST service so that a Volto front end can copy content and its subobjects into target languages. The endpoint is available whenplone.restapiis installed. - Show the outcome of a copy in the classic-UI
@@copy-content-toform: a per-object report (created, updated, skipped, failed) with the reasons for the failures, exportable as CSV. - Stop offering the content's own language as a copy target: the
@@copy-content-toform lists only the other supported languages (and is hidden when there is none), and the REST endpoint rejects a request that names the content's own language.
Bug fixes
- Fix
copy_other_thingsadapter lookup and updating of existing properties incopy_other_properties.
Internal
- Update configuration files @plone
Tests
- Add unit and integration tests for the content copier.
1.3 (2025-08-28)
Internal
- Repackage @erral
1.2 (2024-07-23)
- Remove includeDependencies for Plone 6 compatibility. @erral
1.1 (2021-03-22)
- add my name @libargutxi
- RelationList @libargutxi
- RelationList fields @libargutxi
1.0 (2019-07-11)
- Initial release. @erral
Metadata
Release files for cs.linguacopier 2.4.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cs_linguacopier-2.4.0a1.tar.gz | 148.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cs_linguacopier-2.4.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 230.1 kB
Release files / cs_linguacopier-2.4.0a1.tar.gz
| Download URL | cs_linguacopier-2.4.0a1.tar.gz |
|---|---|
| Size | 148.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c9d21ccb9efbcadc77dad28337b4bc4b1007ecaef69887e4f94fcbcfe8121579
|
|
BLAKE2b-256 checksum How to use checksums |
a8f2295083a368d69875af85cab6ff2a917571f1ec6249ea88ae665c62ce8516
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.2
|
Release files / cs_linguacopier-2.4.0a1-py3-none-any.whl
| Download URL | cs_linguacopier-2.4.0a1-py3-none-any.whl |
|---|---|
| Size | 81.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ec3acce516869c5334c5a9bd5f6d1adf2f1189f80fd4239fd16104c90669b69d
|
|
BLAKE2b-256 checksum How to use checksums |
c731c86762127f72779ba2fb6b85e50ecc19fb44c1b2f979fd1e5cfa8090c571
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.2
|