Details
A year ago this was a talk about an idea. In June 2026 the translation corpus from Project Polyglot was merged into Nick — 118 files, contributed through the Plone release team. Four further pull requests against Volto are still open, and why they are open is one of the more interesting things I have to report.
Nick is not the demo target here — it is what I run. Nick is the backend for everything described below: the knowledge base, the quality system, and a multi-tenant fleet of live sites I depend on. Over the past year that has produced eleven upstream reports, several of which Rob fixed and one of which was taken upstream verbatim. The multilingual side of Nick gets tested rather than merely designed, because when it breaks it breaks for me first.
Why the rigour, and why it is not fastidiousness. Project Polyglot aims at translations good enough to be used in accredited systems for regulatory compliance. EU regulation — MDR, IVDR, the Machinery Regulation, the General Product Safety Regulation — requires that safety-critical information such as instructions for use, labels, and safety notices be supplied in the official language of every member state where a product is placed on the market. Up to 23 languages, each carrying legal liability. A mistranslated safety warning is not a cosmetic defect, and a mistranslated submission is rejected by the notified body.
Interface strings and regulatory content are not the same material, but they run through the same machinery, and building that machinery to the stricter standard produced everything else in this talk. It also explains why the priority language list is the 23 EU official languages rather than the ones with the most downloads.
The problem, in numbers anyone can check. Plone's interface messages are context-poor by construction, and unevenly so. The current plone.pot carries 3,279 messages but only 1,412 "#. Default" comments — 43.1 per cent. For the rest, a translator works from the message identifier alone, and that identifier is often a slug such as help_event_start rather than English text. Volto's catalogue is 100 per cent defaulted; volto-slate's is 0 per cent. Same gettext toolchain, three entirely different translatability situations, and nothing in the file format tells you which one you are holding. Getting it wrong produces no error — it quietly translates the lookup key.
The bug a spellchecker cannot see. Plone's German catalogue currently renders "Determines if your Plone is optimized for small or large sites…" as "Optimiert Plone für große Websites…" — "optimises Plone for large sites". The qualifier "small or" is gone and "determines if" has become "optimises", so the sentence asserts something the original does not. Every structural check passes it: placeholder counts match, length ratio normal, script correct, not a duplicate, not untranslated. A human reading it caught it. It is still live upstream, and the same defect repeats in the neighbouring string. I will bring the fix rather than only the complaint.
Why a database rather than passing files through a model. I will explain why .pot, .po, and JSON stopped working as the substrate, and what it takes to bring backend and frontend messages into one model without losing the gettext concepts that matter — domains, contexts, source references, stable identifiers, defaults, plurals, locale metadata. The corpus holds 5,222 canonical messages and 358,627 translations across seven platforms and 71 languages, with 23,740 preserved terms and 9,428 agreed terminology entries.
Where the AI actually runs, and why that is an architectural rule. The language models in this project run on local hardware and never on the production server. That is not a preference or a cost decision — it is a written architectural rule, because enabling AI endpoints on a regulated production system is classified as a significant change requiring security re-audit and notified-body notification. The same reasoning puts source control on self-hosted infrastructure inside the EU: repositories holding compliance records, supplier relationships and audit findings are themselves sensitive material. Sovereignty is not a feature bolted on at the end; it decides where the compute lives, and therefore what the architecture can look like.
Working with an AI as a team member, with the same discipline as one. The project's governing principle is that AI is held to the same standard as a human developer: everything auditable, every change documented before deployment. In practice that meant discovering, the expensive way, which controls are load-bearing. One batch import shuffled 3,687 translations against their message identifiers across 43 languages; all 3,687 were purged and 3,373 verified replacements re-imported. An attempt to have a language model grade translation quality was paid for, evaluated, and abandoned — a model's opinion of a translation turns out not to be evidence of anything. The instruction file that governs the collaboration is now long, and every rule in it is traceable to a specific failure, because an AI's memory is whatever someone wrote down for it.
The instrument built in response. Structural validation cannot assess meaning, so meaning needed its own measurement. The check now in production embeds source and translation with a local multilingual model and scores each translation's distance against a per-language robust median — geometry rather than judgement, ranked candidates rather than verdicts. It was gated on fifteen deliberately constructed examples before being allowed near real data; version one failed that gate and the threshold was retuned. The first full scan returned 304 findings, and I will give an honest account of how many were false positives.
Where this meets the community, which is the part I want to discuss rather than present. Plone and Volto already have an active human translation workflow on Weblate; plone.app.locales carries 69 languages and most of its recent commits come from Weblate contributors. A database-backed, AI-assisted corpus has to find its place alongside that, not on top of it. The open Volto pull requests are stalled on exactly this question — whether bulk language contributions should arrive as one pull request or one per language so each can be reviewed properly. I think the reviewers asking for the latter are right, and I would like to use part of this session to work out a contribution route reviewers can sustain.
On Plone itself the project has run a deliberate tiered policy: full coverage for the user-facing interfaces, best effort for the Plone backend, on the reasoning that it is administrator-facing, heavy in technical jargon, and 3,599 strings deep. That was a resourcing decision, not a judgement about its worth. If there is interest in the community, I am glad to put more into Plone's languages — and I would rather hear what is wanted than guess.
What attendees will take away: how backend and frontend i18n resources can share one data model; where the "#. Default" versus msgid distinction will bite and how to detect it; a working method for catching meaning drift that structural QC cannot see; why running your own models locally is an architectural decision rather than a preference; what it takes to work with an AI on regulated material without the audit trail becoming fiction; and what changes when translations have to carry legal weight.

