Problem/Motivation
We use the locale_deploy module with a custom translation file (translations/custom/custom..po) that is meant to override core/contrib translations. When installing from existing configuration on a site whose default language is not English, these custom overrides are ignored — core's translation wins instead.
Cause: install_import_translations() (core/includes/install.core.inc) calls LocaleFetch::buildUpdateBatch() with no project list and no options. This imports every project, and PoDatabaseWriter runs with empty overwrite_options (first-wins): the first import of a string cannot be overwritten. Projects are imported in getAll() order (drupal first, locale_deploy.custom last), so core is locked in and the custom override is dropped. The subsequent last-wins pass in install_finish_translations() then finds every file already recorded in {locale_file} and re-imports nothing.
Regression from 11.3.x: there, install_import_translations() imported only core (drupal), deferring contrib/custom to the last-wins pass, so overrides applied correctly. The 11.4 locale batch→services refactor changed it to import all projects up front.
Steps to reproduce
1. Site default language de; locale + locale_deploy enabled; overwrite_not_customized: TRUE.
2. A translatable config label whose source string is translated by both core (drupal.de.po: Editor → Herausgeber) and the custom file (custom.de.po: Editor → Editor).
3. drush site:install --existing-config.
4. Expected: the custom override wins (Editor). Actual: core wins (Herausgeber), producing a spurious drush cex diff.
Proposed resolution
Limit the early import pass to core, restoring 11.3 behavior and letting install_finish_translations() import the rest with its last-wins options:
// install_import_translations()
return \Drupal::service(LocaleFetch::class)->buildUpdateBatch(['drupal']);
Remaining tasks
- Add a functional install test: install-from-config with a non-English default language where a contrib/custom project overrides a core string; assert the override wins.
- Confirm no regression for multilingual installs importing several projects.
User interface changes
None.
Introduced terminology
None.
API changes
None.
Data model changes
None.
Release notes snippet
Installing from existing configuration again lets contrib and custom interface translations override core translations. In 11.4.0 the installer imported all projects' translations in a single "first-wins" pass, causing core translations to take precedence over site overrides for non-English default languages.
Disclaimer: Issue was written with the help of claude.
Issue fork drupal-3609363
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:
- 3609363-regression-installimporttranslations-no
changes, plain diff MR !16260
Comments
Comment #2
nicxvan commentedCan you try the latest changes in locale_deploy?
We also have #3609087: \Drupal\locale\LocaleProjectRepository::buildProjects() does not respect weight in 11.4.x
Comment #3
chr.fritschI already use the latest version of locale_deploy and also tried the weight fix you mentioned. For installing from configuration the problem still exists.
Comment #5
alexpottCompare 11.4.x
to 11.3.x
The refactor expanded the scope fo what the method does...
return \Drupal::service(LocaleFetch::class)->buildUpdateBatch();should bereturn \Drupal::service(LocaleFetch::class)->buildUpdateBatch(['drupal']);Comment #7
berdirMakes sense.
Comment #8
alexpottI've managed to write a test for this. It took a while and even throwing some AI time at it did not help. Which goes to show that this code is convoluted and full of side effects.
Comment #9
alexpottThe test-only change is failing as expected... https://git.drupalcode.org/project/drupal/-/jobs/10825284
Comment #10
nicxvan commentedTest looks good too!
Thanks!
Comment #12
catchThis looks good. Painful test to write for a one-line fix...
Committed/pushed to main, 11.x and 11.4.x, thanks!