Problem/Motivation
The domain_config module declares drupal:language as a hard dependency, and three of its four override classes extends/implements classes from the language module at PHP-declaration level. By transitive dependency, domain_config_ui also requires language to be enabled. As a result, monolingual sites that only need domain-specific config overrides (without language-aware variants) are forced to install and maintain the language module — even though they have no use for translations.
The two feature sets bundled in domain_config and domain_config_ui today are conceptually independent:
- Domain-only config overrides — different
system.site,system.theme, etc. per domain. Self-contained, no need for the language module. - Domain × language config overrides — different overrides per (domain, language) pair on multilingual sites. Fundamentally needs Drupal core's language collection machinery, which lives in the
languagemodule.
Splitting each of the two modules into a base module and a language-aware companion would let monolingual sites use the domain-only feature set without ever installing language, while keeping the multilingual feature set fully supported for sites that need it.
Proposed resolution
Split the two modules into four:
| Module | Contents | Depends on |
|---|---|---|
domain_config |
Domain-only override services and helpers (no language coupling) | domain:domain |
domain_config_language (new) |
Domain × language override services, language manager decorator | domain:domain_config, drupal:language |
domain_config_ui |
UI for domain-only overrides | domain:domain_config |
domain_config_language_ui (new) |
UI additions for language-aware overrides (admin column, language-aware delete logic, decorator) | domain:domain_config_ui, domain:domain_config_language |
Files that move (domain_config → domain_config_language)
src/Config/DomainLanguageConfigFactoryOverride.php+ interfacesrc/Config/DomainLanguageConfigOverride.phpsrc/Config/DomainLanguageConfigCollectionNameTrait.phpsrc/DomainConfigLanguageManager.php- Services
domain.language.config_factory_overrideanddomain.language_managerfromdomain_config.services.yml - Tests under
tests/that installlanguage
Files that move (domain_config_ui → domain_config_language_ui)
src/DomainConfigUILanguageManager.php(extendsDomainConfigLanguageManager)- Service
domain.language_managerdecorator declaration
Refactors needed inside the UI module
Two classes today mix language-aware and language-agnostic logic and need extension points so the language UI can live in a separate submodule:
Config/DomainConfigFactory.php— the inline language-override delete loop (~lines 269–273) becomes an event subscriber indomain_config_language_ui, listening toDomainConfigOverrideEvents::DELETE_OVERRIDE. Leans on the project's existing event infrastructure.Controller/DomainConfigUIController.php— the "Languages" column rendering (~lines 132–192) moves behind a hook (e.g.hook_domain_config_ui_listing_columns_alter) so the language UI submodule can add its own column without a hard dep from the base UI module.
Service IDs
Service string IDs stay stable across the split (domain.language.config_factory_override, domain.language_manager, etc.). Only the declaring module changes. Code calling \Drupal::service('domain.language.config_factory_override') keeps working unchanged.
PHP namespaces
The language-aware classes move from Drupal\domain_config\… to Drupal\domain_config_language\… (and from Drupal\domain_config_ui\… to Drupal\domain_config_language_ui\…). Since these classes are marked @internal following #3584261, the public API contract is the service IDs, not the FQCNs. No deprecated alias classes — those would reintroduce the language coupling we're trying to remove.
Update hooks
One domain_config_update_1000X() that auto-installs domain_config_language when language is enabled, plus an equivalent domain_config_ui_update_1000X() for domain_config_language_ui:
if (\Drupal::moduleHandler()->moduleExists('language')) { \Drupal::service('module_installer')->install(['domain_config_language']); }
Every existing 3.x site already has language enabled (it was a hard dep), so this preserves their current behavior automatically.
Data migration
None needed. The config-collection storage format (domain.{id} and domain.{id}.language.{lang}) is collection-name driven, not module-owned. Existing overrides on disk continue to be served by the new module without any rename or move. The 2.x → 3.x migration in domain_config_update_10001 stays in place.
Remaining tasks
- Create the two new submodules (
domain_config_language,domain_config_language_ui) with info.yml, services.yml, install file. - Move the language-aware PHP classes and update namespaces.
- Refactor
DomainConfigFactory's inline language delete to a subscribed event. - Refactor
DomainConfigUIController's language column to an alter hook. - Move language-aware tests; update the shared
DomainConfigTestBaseas needed. - Add the two update hooks for auto-installation when
languageis enabled. - Update GitLab CI matrix to also run
domain_configwithlanguagedisabled (the new supported configuration) anddomain_config_uistandalone. - Update
mkdocs.yml, thedocs/domain_config/pages and the CHANGELOG.
User interface changes
None for sites that currently have both domain_config_ui and language enabled. The update hook auto-enables domain_config_language_ui, so the admin listing keeps the "Languages" column and language-aware editing features.
For new sites (or sites that uninstall the language UI submodule), the admin listing and forms render only the domain-level overrides — cleaner UI for monolingual use cases.
API changes
- The
@internalclassesDrupal\domain_config\Config\DomainLanguage*(i.e.DomainLanguageConfigFactoryOverride,DomainLanguageConfigFactoryOverrideInterface,DomainLanguageConfigOverride,DomainLanguageConfigCollectionNameTrait) andDrupal\domain_config\DomainConfigLanguageManagermove toDrupal\domain_config_language\…. Custom code typehinting these would need ausestatement update. - Service IDs are unchanged.
- New event subscriber contract:
DomainConfigOverrideEvents::DELETE_OVERRIDEnow drives language-override cleanup. Anyone who already subscribes to that event keeps working. - New alter hook:
hook_domain_config_ui_listing_columns_alter(or similar — exact name TBD during review).
Data model changes
None. Config collection naming is unchanged.
Effort estimate
~3–5 days of focused work. The domain_config half is mostly mechanical file moves; the domain_config_ui half is the bulk of the work because it involves designing two small extension points.
Release strategy
Ship as 3.1.0 with a clear release-notes call-out. The breaking change is limited to @internal namespace moves, which the project's API stability guarantees do not cover. Existing sites are migrated automatically via the update hooks.
Related issues
- #3586509 — original report that surfaced the coupling problem (closed without code change after analysis showed the user's site had a misinstalled
languagemodule). - #3060758 — introduced the new
Config/class hierarchy in 3.x that made the cross-module coupling load-bearing at compile time. - #3584261 — marked the affected classes as
@internal, which is what makes the namespace move feasible without a public-API breaking change.
Issue fork domain-3588828
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
mably commentedComment #4
mably commentedComment #6
mably commentedComment #11
mably commented