Experimental project
This is a sandbox project, which contains experimental code for developer use only.
Status: pre-alpha sandbox project. APIs unstable. Active
development.
The module applies Drupal's well-known interface-translation pattern (the
locale module) to content fields. A source string is
extracted once, deduplicated by hash, translated once per target language, and
substituted at render time. The source entity is never duplicated.
Features
- Pluggable backend. Ships with an OpenAI-compatible adapter
that works with Ollama (local), Azure OpenAI, RunPod, Modal, vLLM, LM Studio,
Together AI, Groq, and any service exposing the OpenAI Chat Completions API.
Write your own adapter in ~150 lines of PHP. - Translation memory by default. The string "Read more" is
translated once and reused across every node and paragraph that contains it. You
pay the backend once, not 500 times. - No entity duplication. Your source content stays as a
single English entity. The French version of a page is the same entity rendered
with translated strings. Views, listings, Solr, and migrations are
untouched. - One backend, two surfaces. The same backend that
translates your content also translates Drupal interface strings (menus,
t()calls, config) via the existing locale storage. One config, one
prompt template, one glossary serves both. - Status tracking and retry. Per-language status enum
(untranslated, queued, in-flight, translated, outdated, manual override, failed)
with automatic retry on transient errors and editor-driven retry on
failures. - Editor sidebar widget. Per-entity translation progress
visible on the node edit form with one-click retry. - Admin UI at
/admin/config/regional/translate/contentextending the same UX as
Drupal's interface-translation admin page. - PO file import/export for handoff to external translators
using standard gettext tooling. - Per-language URL aliases via Pathauto and a custom token,
with redirect-module integration for graceful slug transitions when translations
update. - Drush commands for backfill, retry, status reporting, and
bulk formatter migration.
Post-Installation
- Enable the
languagecore module and configure URL language
negotiation (prefix mode) at
/admin/config/regional/language/detection. - Add the target languages you want at
/admin/config/regional/language. - Install a backend submodule (e.g.
content_locale_openai_compatible) and configure it at
/admin/config/regional/translate/content/backend— endpoint URL,
model, API key (via the Key module recommended), prompt template, and
glossary. - Flip the field formatters on the content types you want translatable:
drush content_locale:flip-formatters --bundle=article --display=default - Backfill existing content:
drush content_locale:extract-all - Monitor progress at
/admin/config/regional/translate/content
or via the sidebar widget on any entity edit form.
Importantly: do not enable the
content_translation core module — Content Locale is a deliberate
alternative that's incompatible with the per-entity translation approach.
Additional Requirements
- Drupal core ^11
- PHP 8.3 or later
- The
languageandlocalecore modules - A backend submodule (one ships with the project; you can write your
own)
Recommended modules /
libraries
- Pathauto — required
for per-language URL aliases via the custom token. - Key — store backend API
credentials outside of configuration. - Redirect — automatic
redirects when translation-driven alias changes update slugs. - Simple XML
Sitemap — per-language XML sitemap generation.
Similar projects
- Content Translation (Drupal core). Duplicates the source
entity per language with separate revisions, fields, and translations. Forces
Solr per-language indexes and Views/listings rework. Content Locale instead keeps
a single entity and substitutes translations at render time. - TMGMT (Translation
Management Tool). Provides a job-based workflow over Drupal's
per-entity content translation. Content Locale is deliberately job-less — strings
flow directly from extraction to backend to render — and works at the string
level rather than the entity level. - Locale (Drupal core). Same pattern as Content Locale, but
for code-levelt()strings only. Content Locale extends that pattern
to content fields and shares the backend so both surfaces translate through the
same service.
Community Documentation
Architecture and design captured in the project's OpenSpec change proposal
(link forthcoming once the sandbox is published). Issue queue is the canonical
place for questions during pre-alpha.
Project information
- Project categories: Multilingual
- Ecosystem: Drupal core
- Created by mnw on , updated
