Skip to content

Domain Config Language

The Domain Config Language module adds language-aware per-domain configuration overrides on top of Domain Config. Enable it on multilingual sites where you need overrides that vary by both domain and language (e.g. a different site name on one.example.com in English versus French).

It is the natural companion to Domain Config: by itself, Domain Config provides one override layer per domain; with Domain Config Language you also get a second layer keyed by (domain, langcode).

When to enable it

  • You have the language module enabled.
  • You want different configuration values per domain and per language.

If you have a monolingual site, do not install this module — Domain Config on its own already covers domain-only overrides without pulling the language module into your install.

What it provides

  • Drupal\domain_config_language\Config\DomainLanguageConfigFactoryOverride — the config.factory.override service registered as domain.language.config_factory_override (priority -252).
  • Drupal\domain_config_language\Config\DomainLanguageConfigOverride — the per-domain, per-language editable override object.
  • Drupal\domain_config_language\DomainConfigLanguageManager — a decorator on the core language_manager service that resolves config overrides through the per-domain language configuration factory at runtime.

These classes are marked @internal. The stable contract is the service ID domain.language.config_factory_override.

Storage layout

Overrides are stored as Drupal config collections named:

domain.{domain_id}.language.{langcode}

The collection name is built by Drupal\domain_config_language\Config\DomainLanguageConfigCollectionUtils::createDomainLanguageConfigCollectionName(). Within a collection, the configuration object keeps its original name -- the site name override for one_example_com in French is stored as system.site inside the domain.one_example_com.language.fr collection.

This format predates the module split and is unchanged. Existing 3.x sites upgrade transparently — domain_config_update_10002() auto-installs this submodule when the language module is already enabled.

How it works at runtime

Override resolution

This module registers a second config.factory.override service on top of the one provided by Domain Config:

  1. domain.config_factory_override (priority -253, from Domain Config) -- loads the domain override from the domain.{domain_id} collection.
  2. domain.language.config_factory_override (priority -252, from this module) -- loads the domain+language override from the domain.{domain_id}.language.{lang_code} collection.

Higher priority numbers run earlier; the language-aware override is applied after the per-domain one, so it sits on top of the cascade:

Base config (default collection)
  ↓ merged with
Domain override (domain.{domain_id} collection)
  ↓ merged with
Domain+language override (domain.{domain_id}.language.{lang_code} collection)
  = Final runtime config

Example with system.site on domain two_example_com, language es:

# Base config (default collection):
system.site:
  name: "My Site"

# Domain override (domain.two_example_com collection):
system.site:
  name: "Two"        # overrides "My Site" → "Two"

# Domain+language override (domain.two_example_com.language.es collection):
system.site:
  name: "Dos"        # overrides "Two" → "Dos"

# Final result at runtime: name = "Dos"

Caching

The language-aware override service provides a cache suffix that combines the current domain ID with the language code, so a config object cached for (two_example_com, es) is not served to (two_example_com, en).

The cache metadata adds languages:language_interface to the existing domain cache context, so rendered output that depends on per-language overrides is properly varied.

Config export and import

Drupal's FileStorage converts dots in collection names to directory separators. On top of the per-domain export structure described in the Domain Config documentation, this module adds a language/{langcode}/ subdirectory under each domain folder:

config/sync/
  system.site.yml                              # Base config
  domain/
    one_example_com/
      system.site.yml                          # Domain override
      language/
        fr/
          system.site.yml                      # Domain+language override
    two_example_com/
      system.site.yml
      language/
        en/
          system.site.yml
        es/
          system.site.yml

Modules can ship default domain+language overrides using the same convention in their config/install/ directory:

mymodule/config/install/
  domain/
    one_example_com/
      language/
        fr/
          system.site.yml
    two_example_com/
      language/
        en/
          system.site.yml

When a new domain entity is created or this module is installed, Drupal's ConfigInstallerInterface::installCollectionDefaultConfig() picks these up into the matching domain.{domain_id}.language.{langcode} collection.

Migration from 2.x to 3.x

In Domain 2.x, language-aware overrides were stored as separate configuration objects in the default collection alongside the per-domain ones, using a naming convention with the language code embedded in the flat name:

domain.config.{domain_id}.{langcode}.{config_name}

For example, a French-specific override of the site name on one_example_com:

domain.config.one_example_com.fr.system.site

Limitation: language-specific overrides had to be handled separately from Drupal's own LanguageConfigOverride system; they did not flow through any standard config-override pipeline.

The 3.x format unifies both kinds of overrides under config collections (see Storage layout above for the per-language collection name). The migration to that format is shipped as domain_config_language_install() -- it runs automatically the first time this module is installed (which on upgrading sites is triggered by domain_config_update_10002()).

The migration is performed by the Drupal\domain_config_language\Service\DomainConfigMigration service and covers both the per-domain overrides (no langcode) and the per-language ones in a single pass:

  1. Scans legacy config objects -- finds all domain.config.{domain_id}.* entries in the default collection.
  2. Parses each legacy name -- extracts the domain ID, optional language code, and config name using the pattern:
    /^domain\.config\.{domain_id}(?:\.([a-z]{2}))?\.([^.]+\.[^.]+)$/
    
  3. Writes to collections -- copies the data into the appropriate domain.{domain_id} (when no langcode) or domain.{domain_id}.language.{lang_code} (when a langcode is captured) collection.
  4. Updates the registry -- if Domain Config UI is installed, updates the overridable_configurations setting in domain_config_ui.settings.
  5. Cleans up -- deletes the legacy domain.config.* objects from the default collection.

The migration is idempotent: on a fresh install with no legacy data it is a no-op. If any domain fails to migrate, exceptions are collected per-domain and reported in the migration result.

Services

Service Class Role
domain.language.config_factory_override DomainLanguageConfigFactoryOverride Domain+language config overrides (priority -252)
domain.language_manager DomainConfigLanguageManager Decorates language_manager to integrate domain language overrides
domain_config_language.config_migration DomainConfigMigration 2.x → 3.x migration service (internal)

Relationship to Domain Config UI

If you also enable Domain Config UI, install Domain Config Language UI so the admin overview page shows per-language overrides and language-aware overrides are cleaned up alongside their base override on delete.