Problem/Motivation
This is the same issue as described in the issue summary of #2267251: Do not rely on "content language" in hook_entity_presave(). Since it's Entity translation that needs to be fixed but the Title module also needs a test, let's leave the project of that issue as is.
To sum up the issue: I have a translatable node type with the title replaced with a field using the Title module.
When translating the node to German while having the interface in English (i.e. using the /en/node/123/edit/de url) the English title translation will be overridden with the German one.
Proposed resolution
Since the Title module has no clue about how other modules influence the "active language" (see title_active_language()), other modules (like Entity Translation) should let Title know about it. This is the case on a translation edit and add page.
| Comment | File | Size | Author |
|---|---|---|---|
| #35 | et-active_language-2849464-35.patch | 19.7 KB | plach |
Comments
Comment #2
czigor commented(The issue is critical because it causes data loss.)
The attached patch changes the active language used by the Title module and makes the new Title test in #2267251-48: Do not rely on "content language" in hook_entity_presave() pass.
Comment #3
polNice
Comment #4
czigor commentedIt's better to use API functions if we can.
Comment #5
stefanos.petrakisThanks for the work on this!
I think the patch does the job; however I also feel that there are other modules besides the Title module that may find this useful.
I took the liberty of rewriting this into a hook that will notify all hook implementations that there is an incoming content form language to take into account.
Following from this hook, the Title module can have a much simpler way to - at least - get over the contradicting URLs problems. The code I am posting got through all the tests of Title, including the latest one posted as a patch here (https://www.drupal.org/node/2267251#comment-11903632).
Comment #6
czigor commentedHi @stefanos.petrakis,
The root of the issue is that Title module can't properly determine the active language, since it has no clue about routes (e.g. add/edit translation urls) coming from other modules. So if we want to solve this with a hook, then it's the Title module that needs one and not Entity translation. The hook should enable other modules to alter the active language. But title_active_language() (being a getter and a setter as well) already makes this possible, so I don't think new hooks are needed at all.
Comment #7
stefanos.petrakisHi @czigor,
I find that the root of the issue is that the language negotiation logic for URL suffixes is buried deep inside Entity translation. I think this kind of negotiation logic should be exposed on a system-wide level.
In the meantime, I came up with an alternative, without needing to have this dependency between Entity translation and the Title module. https://www.drupal.org/node/2267251#comment-11917811
If that alternative solves the problem, we could close this one without any code modifications.
Comment #8
plachThank you all for opening this and getting the ball rolling!
I've been thinking about #2267251: Do not rely on "content language" in hook_entity_presave() for quite some time (intermittently ;) and I agree with @czigor's conclusion that it's Entity Translation's job to define the active language for field content, since it's exposing the actual translation functionality.
My original idea was to introduce a new
EntityTranslationHandlerInterface::getActiveLanguage()method (with a corresponding setter) to replace the existing::getFormLanguage(). This would be the language to be used byentity_translation_language()(the language callback).EntityTranslationHandlerInterface::getFormLanguage()(and the corresponding setter) would be deprecated and kept around for BC-only. They would just be thin wrappers around the new methods.This approach would allow us to explicitly set the active language through a centralized API that can be reliably used when programmatically dealing with field translations. In Title we would agree on a sensible default behavior and modules implementing use cases for which the default does not work could set the active language explicitly.
Thoughts?
Comment #9
czigor commentedIt sounds good to me. I'm not sure if we should really change the existing getFormLanguage() implementations because of BC. Shouldn't we just leave them as they are and replace it with getActiveLanguage() everywhere in entity_translation module? (Although the only other place I found it in use was media_field_widget_form()).
Comment #10
czigor commented'active language' is a title module concept (see
title_active_langauge()) and Title is not an Entity Translation dependency. What wouldgetActiveLanguage()return if there's no Title module installed? Would it fall back to the currentgetFormLanguage()logic (which btw has changed just yesterday)?Comment #11
plach@czigor:
The reason to replace the actual implementation of
::get/setFormLanguage()is that active language is a broader concept that encompasses form language. Basically the entity active language is the form language when dealing with an entity translation form.I'm talking about active language in these terms because it's a concept "backported" from D8. I introduced it in Title way after the form language concept was added to Entity Translation and that's the reason why in D7 it looks like a Title-only concept. However it's a general concept that architecturally probably belongs to core but, since there's no concept of whole entity translation in D7 core (just translatable fields), ET looks like the best candidate to me.
The Entity module also provides a similar concept with the entity metadata wrapper language, and there's probably quite some overlap it would be worth investigate. Unfortunately when ET was first drafted the Entity module only provided CRUD operations and so its field language handling and ET's diverged quite a bit. It would probably would be a sane idea to rebuild ET 2.0 on top of the Entity API module, but I'm not sure that will ever happen :)
I hope this helps to clarify my position.
Comment #12
czigor commented@plach Thanks, this clarifies it.
I hope I can find some time to prepare a patch tomorrow.
Comment #13
czigor commentedComment #14
czigor commentedI have created the patches for Entity translation and Title, all tests are green locally.
However, I don't feel good about the ET patch. It merely deprecates the formLanguage getter and setter and creates getActiveLanguage() and setActiveLanguage(). I think this will cause more confusion than good. I suggest just to add some comments to the getFormLanguage() and setFormLanguage() docs and continue using them in their current form.
Comment #15
czigor commentedComment #16
czigor commented@plach What do you think?
Comment #17
plachI disagree: so far (hopefully)
::set/getFormLanguage()have (or should have) been used almost as an internal API, while::set/getActiveLanguage()is meant to be a public API everyone dealing with Entity Translations in a programmatic context is expected to use. I agree we probably need to be very clear about this distinction in the PHP docs and perhaps immediately deprecating::set/getFormLanguage()is not a good idea, as we may discover use cases where their behavior may diverge, although I cannot come up with any concrete example right now.What if we just add
::set/getActiveLanguage()and change::set/getFormLanguage()to proxy the call to those for now. If things appear to work smoothly we can then deprecate the form language methods later.I will work on this ASAP if we agree on the ay forward.
Comment #18
plachComment #19
czigor commented@plach The only module I know about using
get/setFormLanguage()is Media.Deprecating the getter is straightforward, but the setter has a
$this->notifyChildren(__FUNCTION__, $args);call. I'm not sure how to handle that one. Maybe you have an idea so I provide my patch here.Comment #20
plachThanks, I will start from there later today!
Comment #21
plachAnd here it is.
Comment #22
plachI decided to keep form language accessors deprecated as deciding when using those or the new ones would probably add confusion. Right now the instructions are easy: just use the new ones instead of the old ones.
Comment #23
polI don't know that part yet but I fully agree with the patch.
Comment #25
plachSmall PHP doc adjustment: the patch is ok, as the test failures were already there in HEAD.
Comment #26
plachtypo
More form terminology.
mmh, not thin enough
Comment #27
stefanos.petrakisCode looks good, tests are all green.
It also allows the latest patch from #2267251-56: Do not rely on "content language" in hook_entity_presave() to turn the Title tests green too.
Thanks a lot for this.
Comment #28
plach@czigor:
Do we have your +1? :)
Comment #29
czigor commented@plach My problem was exactly that setFormLanguage() is not 'thin enough'. Making it thin will break BC. We have 3 options:
1. We don't care, it's deprecated (current approach).
2. We don't make it thin.
3. We remove get/setFormLanguage() and thus force modules to update.
If we say explicitly that it's 1., I'm ok with the patch. We just need to be clear about this.
Comment #30
czigor commentedAlso, let's not forget to create an issue for Media in case it's 1.
Comment #31
plach@czigor:
I don't think making it thin will break BC: if you call
::setFormLanguage()it will proxy to::setActiveLanguage(), which in turn will call::setActiveLanguage()on the children, which should be the same of calling::setFormLanguage()on each of them. The point of the new test was exactly to ensure this behavior is preserved. What am I missing?Comment #32
czigor commented@plach For nodes it's ok, but if a custom entity translation handler overrides setFormLanguage() then it will never be called through setActiveLanguage()'s notifyChildren().
That said, I don't know of any entity having a handler that overrides setFormLanguage(). Just nagging. Still fine with going with this patch. But let's not forget to mention this in the release notes.
Comment #33
plachMmh, perhaps you were thinking of modules overriding
::setFormLanguage()? That could indeed introduce some BC problems, what if we reversed the internal implementation and make::get/setActiveLanguage()thin wrappers around::get/setFormLanguage()? We can go back to the implementation provided by #26 when removing the deprecated functions.Comment #34
plachSorry, crosspost. I checked all the modules I know of that provide translation handlers and none overrides
::setFormLanguage(), OTOH that's not nearly enough to claim we're performing a BC change, so I'd be inclined to go with #33. Thoughts?Comment #35
plachTests are still green. I guess it's either #26 or this.
Comment #36
czigor commented#35 would break BC the same way as #26 just not now but when we remove the deprecated functions.
It still seems to me that the cleanest (and easiest) solution is not to introduce get/setActiveLanguage() but restate the purpose of get/setFormLanguage() from being an inner API function to a public one.
But I'm still fine with going with #26/#35 as well :).
Comment #37
plachYep, but at that point people would be expected to have transitioned to
::get/setActiveLanguage(), so that will be an expected (and documented) BC break.Easiest for sure. Cleanest, I don't agree, sorry :)
I would not want (or think it is right) to use an API that mentions form language when there is no form involved. ET is still in beta after all, so I think it's ok to perform this kind of changes.
Ok, so back to RTBC. I will leave this around for a few more hours before committing it, to give people time to chime in.
Comment #38
plachTypos to be fixed on commit :)
Comment #39
plachComment #41
plachCommitted and pushed, thank you all for your help and feedback!
Now it would be good open issues to update the affected modules providing ET integration. I found the following:
It would also be good to add a change record. I will do it tomorrow if none beats me to it.
Comment #42
czigor commentedAwesome, thanks!
Created the issues with patches (see Child issues) and updated #2267251: Do not rely on "content language" in hook_entity_presave() as well. We should release a new beta asap so that modules can upgrade.
Comment #43
plachThanks!
And here is the change record:
https://www.drupal.org/node/2855208
I'd like to wait a couple of days before publishing a new release to make sure this did not introduce some unforeseen issue.
Comment #44
badrange commentedThere is a patch in Inline Entity Form's issue queue that will probably be affected by this commit, I added a comment in https://www.drupal.org/node/1545896#comment-11953198
Comment #45
badrange commentedSame thing with Paragraphs, I see that Pol knows about this issue:
https://www.drupal.org/node/2452675#comment-11953202
Comment #46
badrange commentedI think Entity Reference also has some kind of Entity Translation integration; I'll leave it to you guys to choose whether to create an issue.
Comment #47
czigor commentedModules are affected only if they call either getFormLanguage() or setFormLanguage(). Entity Reference is not affected.
@plach Shouldn't the change record also include getFormLanguage()?
Comment #48
plachUpdated the change record, thanks.
Comment #49
joseph.olstadAny time-line for a tagged release? This new api function makes sense.
Comment #50
czigor commentedI have opened a followup in #2856927: getActiveLanguage() should delegate its logic elsewhere.
Comment #51
plachbeta6 is out:
https://www.drupal.org/project/entity_translation/releases/7.x-1.0-beta6
Comment #53
joseph.olstadThanks @plach and everyone else for this.
I've written a draft hook_requirements for the other modules that wish to go ahead safely with this change.
Comment #54
joseph.olstadThis change caused a regression which is fixed by patch 4 in the following issue:
#2863794: Translated content is lost when cloning
Comment #55
joseph.olstadmedia 7.x-2.0 was released Thursday and it now requires beta6, needs related issue fixed, please action this related issue asap.
Comment #56
stefanos.petrakis@joseph.olstad: Thanks for the last 2 comments, this is not the right issue/commit though. The code cited in #2863794: Translated content is lost when cloning originates from #2438615: New nodes are always created using LANGUAGE_NONE (only changed to the correct language during form submission).