Problem/Motivation
In
#2212069: Non-English Drupal sites get default configuration in English, edited in English, originals not actually used if translated โ
, the function locale_system_set_config_langcodes()
was introduced in locale.module. This function is called whenever a module (hook_modules_installed()) or theme (hook_themes_installed()) is installed, and it forces the language of configuration to be set to the site default language.
My case is that we have a multi-site (shared codebase, separate databases). Some of these sites have English as default language, many of them have some other language as the default. English language is still enabled on all sites. Currently, each site has its full configuration exported to /config/<site name>
. As the sites mostly consist of the same set of features, a lot of the configs are just duplicates.
Our plan is to start combining all of the common config using the config_split module, in order to reduce duplicate config files. Due to this, we want to keep the configuration language as English, since otherwise it would become really hard to start combining those.
We currently have the configuration language as English for all configs, but whenever a module gets installed, the behaviour of locale module causes the configuration language to change, which we would not want.
Another factor is that when we are developing the sites, for example creating new fields and such, we want to do the development in English as we don't necessarily speak all of the languages that the multi-site uses. Example: If I were to create a new field to example.no, I want to enter the field label in English and not in Norwegian. The same config would then be used also in example.se and example.fi, and the field label would be translated on each site via the Translate Interface tool.
Proposed resolution
I came up with three different approaches:
- Defining our own implementations of hook_modules/themes_installed(), make sure that they are called after locale modules hooks, and then call our own helper function, which reverses the logic of what
locale_system_set_config_langcodes()
does
- Defining our own implementations of hook_modules/themes_installed(), make them be run instead of locale modules hooks, and make them be identical with locale modules hooks with the difference that they don't call
locale_system_set_config_langcodes()
- Patching the locale module and remove the body of the
locale_system_set_config_langcodes()
function
-
Approach 1:
use Drupal\locale\Locale;
/**
* @file
* My module.
*
* We have multiple different sites and most of them have a non-English default
* language. Currently the sites have their own configs exported to their own
* directories, but we aim towards combining all of the common config using
* config_split. Therefore we want to keep all of the configs in English.
*
* The locale module implements hook_modules_installed() and
* hook_themes_installed(). Both implementations call
* locale_system_set_config_langcodes(), which will force the langcode of all
* config files to be set to the site's default language.
*
* In this module we implement the same hooks and then call our own function,
* which basically reverses what locale module does.
*
* We also make sure our hooks are implemented after local module's hooks.
*/
/**
* Implements hook_module_implements_alter().
*/
function mymodule_module_implements_alter(&$implementations, $hook) {
if ($hook === 'modules_installed' || $hook === 'themes_installed') {
// Move this module's hook's to be run immediately after the locale
// module's hooks, since our code fixes configuration langcodes issues that
// locale messes up.
// The code below removes 'mymodule' from which ever position it
// currently is in, finds the position of 'locale', and inserts
// 'mymodule' immediately after it.
$group = $implementations['mymodule'];
unset($implementations['mymodule']);
$local_position = array_search('locale', array_keys($implementations)) + 1;
// Slice 1 contains all elements from the start to 'locale'.
$slice1 = array_slice($implementations, 0, $local_position);
// Slice 2 contains all elements that are after 'locale'.
$slice2 = array_slice($implementations, $local_position);
// Insert 'mymodule' so it's directly after 'locale'.
$slice1['mymodule'] = $group;
// Merge slices back together.
$implementations = array_merge($slice1, $slice2);
}
}
/**
* Implements hook_modules_installed().
*/
function mymodule_modules_installed($modules) {
mymodule_set_config_langcodes();
}
/**
* Implements hook_themes_installed().
*/
function mymodule_themes_installed($theme_list) {
mymodule_set_config_langcodes();
}
/**
* Updates configuration langcodes to English.
*
* Basically this reverts what locale_system_set_config_langcodes() does, since
* we want config langcodes to be in English.
*/
function mymodule_set_config_langcodes() {
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
if ($default_langcode !== 'en') {
$names = Locale::config()->getComponentNames();
foreach ($names as $name) {
$config = \Drupal::configFactory()->reset($name)->getEditable($name);
if (!$config->isNew()) {
$langcode = $config->get('langcode');
if ($langcode === $default_langcode) {
$config->set('langcode', 'en')->save();
}
}
}
}
}
Approach 2:
use Drupal\locale\Locale;
/**
* @file
* My module.
*
* We have multiple different sites and most of them have a non-English default
* language. Currently the sites have their own configs exported to their own
* directories, but we aim towards combining all of the common config using
* config_split. Therefore we want to keep all of the configs in English.
*
* The locale module implements hook_modules_installed() and
* hook_themes_installed(). Both implementations call
* locale_system_set_config_langcodes(), which will force the langcode of all
* config files to be set to the site's default language.
*
* In this module we implement the same hooks and use them INSTEAD of locale's
* hooks. They are identical with the original hooks but with the callback to
* locale_system_set_config_langcodes() removed.
*
* We use hook_module_implements_alter() to remove the original hooks.
*/
/**
* Implements hook_module_implements_alter().
*/
function mymodule_config_module_implements_alter(&$implementations, $hook) {
if ($hook === 'modules_installed' || $hook === 'themes_installed') {
unset($implementations['locale']);
}
}
/**
* Implements hook_modules_installed().
*
* @see locale_modules_installed()
*/
function mymodule_config_modules_installed($modules) {
$components['module'] = $modules;
locale_system_update($components);
}
/**
* Implements hook_themes_installed().
*
* @see locale_themes_installed()
*/
function mymodule_config_themes_installed($themes) {
$components['theme'] = $themes;
locale_system_update($components);
}
Approach 3:
See attached patch and apply it using composer.
This approach allows us to notice if locale.module is changed by a core update, since the patch would probably not apply anymore.
I can only assume that others have also faced similar problems as what I've described here. Even though I understand that this is the intended behaviour of Drupal core and of the locale module, it just doesn't fit our needs at the moment, which is why I posted some different approaches here for those who are interested.