Problem/Motivation
Do the steps manually, not with drush.
- Install the standard profile
- Install the config_translation module (admin/modules)
- Add a language (any one will do) (admin/config/regional/language)
- Add a site slogan (admin/config/system/site-information)
- Translation that site slogan into the language you just added (admin/config/system/site-information/translate)
- visit home page /langcode (for example /es) to see the translated slogan
- Install the book module (admin/modules)
- You translation will be deleted
- visit home page /langcode (for example /es) to see if the slogan is translated or not
This is critical because it is data destructive.
Proposed resolution
LocaleConfigManager::updateConfigTranslations() keeps config overrides and Locale translation in sync. Locale can not maintain translations for config where the default value is an empty string when the config key is translatable. LocaleConfigManager::updateConfigTranslations() should not overwrite customised translations for such config keys.
The patch adds extensive testing to ensure that Locale and config overrides are keep in sync as expected.
Remaining tasks
Write testFind a fix- Commit
User interface changes
None
API changes
TBA
Data model changes
TBA
| Comment | File | Size | Author |
|---|---|---|---|
| #41 | 2580575-23.patch | 14.96 KB | yesct |
| #31 | 2580575-23-tests-only.patch | 10.69 KB | catch |
| #23 | 2580575-23.patch | 14.96 KB | alexpott |
| #23 | 21-23-interdiff.txt | 1.12 KB | alexpott |
| #21 | 2580575-21.patch | 15.02 KB | alexpott |
Comments
Comment #2
alexpottHere is a test only patch to show what I'm on about.
Comment #3
alexpottI think this is the correct fix - need a D8MI expert to confirm.
Comment #5
wim leersComment #6
gábor hojtsyThe return value is an array or a TranslatableMarkup, we should not return NULL. It does not matter if an element was not transltabale or was but was empty for this processing AFAIS. We should just return the empty array as we do anyway at the end.
AFAIS we should just expand the !empty($definition['translatable']) with the two conditions you wanted to add for value. (Or if that sounds like a performance concern since we'd get the data for things that are not translatable, do it first thing after !empty($definition['translatable']).
Comment #7
alexpott@Gábor Hojtsy I don't think it is a performance concern. And yes it makes more sense to move it there.
Comment #8
gábor hojtsyThat looks like the correct fix, thanks! The key not only need to exist it also needs to be a string :)
Comment #9
wim leersWoohoo!
Nit: one extraneous
\n. Can be fixed on commit.Committer: Please also credit Gábor Hojtsy — he and Alex Pott were instrumental in tracking this down.
Comment #10
xjmReviewing this.
Comment #13
gábor hojtsyDrupal\editor\Tests\EditorSecurityTest seems to be a total random fail on testbot. It passed on CI.
Comment #14
xjmDocumenting some IRC discussion of this issue:
Comment #15
alexpottSo i think we have a problem with mixing things that locale can translate and things that it can't. If system.site also contained a string that was translatable by Locale then I think we'd still have a problem because...
in the code above
$processedwould not be empty and then$this->saveTranslationOverride($name, $langcode, $processed);would come along and overwrite all the data.However I think this is a bug in HEAD.
Comment #16
xjmPutting this one back down pending brain reboot.
Comment #17
alexpottFixing the bug outlined in #15. It is part of this issue since mixing translatable strings which have defaults and which don't will result in those that don't have defaults being removed.
Comment #18
gábor hojtsyI had the fear that this would keep translations that are not in the active data anymore. Looks like we filter this at 2 places. In processTranslatableData(), we only take the source strings from install storage where the keys are still in active config. So $processed would only contain keys that are in active. That is not true for $override->get() necessarily. So this may end up with random keys in the merged array and possibly fatals when code attempts to use this as config. We need to filter it down to keys present in the active config.
Looking at $this->filterOverride() though, we pass over the translatable source data and in your patch here, you make the keys not appear in this array if they were empty or null, so the filter would again filter these out (not just the ones which were actually not in active config, but also the ones that were empty).
Looking at that, I think there is another issue. So $processed only contains elements now that are (a) have the key still in active config (b) translatable (c) not empty. If $processed is entirely empty then there were no such keys. Then we do $data = $this->filterOverride($override->get(), $translatable); so we in fact potentially keep all the keys that were in the default config even if they are not in active config anymore.
So its not just your patched code that has this problem but also the existing code a few lines down.
We were about to revisit this code in #2428045: Comparing active storage to install storage is problematic, install storage may change anytime but unfortunately I did not get around to working on that given all the higher priorities :/
Comment #19
alexpottSo here is a fixed for #18. Working on a test.
Comment #20
gábor hojtsyLooks much better, thanks a lot. Just a minor nit:
elseif
Comment #21
alexpottAdded tests and further refactored code in LocaleManager after conversations with @Gábor Hojtsy in IRC. The aim of the refactor is to make it easier to understand what is happening here.
Comment #22
gábor hojtsyThe tests do test the expected behavior. I also reviewed the new patch all at once and it looks good too (not just the interdiff). I only found some incorrect code docs:
Comment incorrect.
Comment #23
alexpottFixing nits.
Comment #24
gábor hojtsyLooks great, pending testbot feedback :)
Comment #25
gábor hojtsyNote that it is not very apparent on this issue, but @alexpott and I carefully deliberated the legitimacy of each condition and action in this code block and that resulted in the two additional tests for cases not covered before.
Comment #26
alexpottComment #27
alexpottComment #28
catchJust for paranoia's sake I sent off tests for postgresql and sqlite - our locale tests have had amusing bugs with those databases before although this is new test coverage so should not run into the same issue.
Comment #29
yesct commenteddoing manual testing now.
Comment #30
yesct commentedcould not reproduce on head. added more detail to steps in the summary.
(I also did a drush cr after installing book and reloading the /es home page and still seeing the translated slogan, but didn't list that in the steps, as I wasn't sure what the intended behavior was, and if we wanted that to be necessary to see the problem)
I think we should have a tests only patch to see how it fails.
Comment #31
catchHacked the fixes out of the patch.
@alexpott pointed out there is one in #2 but there's more tests now.
Comment #32
alexpottComment #33
alexpottHere are my steps (note I have render cache disabled).
Comment #34
alexpottWith #31 I'm expecting 3 test assertion fails...
These are the assertions that will fail.
testLocelaRemovalAndConfigOverrideDelete()was added to ensure that the fix did not introduce new fails inLocaleConfigManager- basically the problem @Gábor Hojtsy was outlining in #18Comment #35
yesct commentedwith super clean (render caching on), doing everything manually (no drush at all), then it reproduces, and no need to clear the cache after enabling book to see the problem. updated steps in the summary.
and the same steps with the patch fix the problem.
btw, alexpott thought maybe drush module install and locale_modules_installed() might be broken
[edit: https://github.com/drush-ops/drush/issues/1670 ]
Comment #38
effulgentsia commentedThe tests-only patch failing is expected, so back to "needs review".
Comment #39
wim leersThis only went to NR because catch wanted to see it pass on SQLite and Postgres too, and then Alex uploaded a test-only patch which of course failed, which is why it was NW.
On top of that, we have manual testing in #35.
Therefore, back to RTBC per #24.
Comment #40
effulgentsia commentedI did not manually test, but just looking at the code changes in the patch, it all makes sense to me. So RTBC+1 if folks here think it's sufficiently tested.
Minor questions:
Should we also add the "Locale" qualifier to a few other code comments, such as in updateConfigTranslations() where there's a comment that says: "If there is nothing translatable in this configuration or not supported, skip it."?
Why can Locale not translate this?
Comment #41
yesct commentedputting the same passing patch up, so that testbot rerunning while at rtbc doesnt mark it needs work (cause of the very nice tests only patch)
Comment #42
gábor hojtsy@effulgentsia: "Locale can not translate" means there that locale does not have a translation for it, so it will not be able to translate it, not that it could not translate it if there was a translation.
Comment #43
catchCommitted/pushed to 8.0.x, thanks!
Comment #45
gábor hojtsyYay, superb, thanks! Especially for the amazing work to @alexpott.
Comment #46
effulgentsia commentedThis fix was great. I also ran through the same steps as in the issue summary, but for a nested config element in a config entity: specifically, the "Caption" element of the style settings of a table View (in my case, I tried it with the /admin/content View). And that had the same problem prior to this commit and fixed by it. In the process though, I discovered #2581399: views.style.table schema has incorrect label for 'description'.