Updated: Comment #166
Suggested commit message
Issue #2019055 by plach, fago, berdir, yched, Gábor Hojtsy, kfritsche, jibran: Switch from field-level language fallback to entity-level language fallback.
Problem/Motivation
Currently language fallback is supported only for fields provided by the Field API. This means that in any context where language fallback is required (the most common use case is entity rendering), Field API fields for which there is no translation available in the active language will be replaced by an existing translation. However this will not happen for any other field attached to the entity being displayed.
Additionally the current code provides two non-cleanly separated tasks: entity language negotiation and field language fallback.
- Entity language negotiation aims to determine which actual entity (translation) language should be used in a particular context, such as entity rendering. Usually a context implies a language, for instance the current page language, but we might not have an entity translation matching that language, hence we may need to apply some fallback logic to determine which actual language we should use.
- Field language fallback aims to provide an alternative field translation value, should a value for the language implied by the current context be missing. Also in this case some fallback logic needs to be applied.
The D7 code was initially wrote to solve a rendering issue: when displaying an entity in a language for which a translation was not available, field values disappeared. This clearly was not desirable. Since in D7 core there is no concept of entity translation (we have only language-aware fields), the only way we found to fix this issue was applying some fallback rules to every field so that existing values would be rendered. The underlying assumption was that all fields would behave the same way. What we didn't take into consideration was that empty fields values are not stored, hence they appear as missing values. The unwanted consequence was we unwillingly introduced a new "feature", that is language fallback at field level. In fact in D7 you can render the french translation of an english node, but if the body translation is empty its original english value will be rendered. This behavior was not intended initially and it does not even make much sense in many scenarios. Instead we should leave translators the ability to enter empty values if they need to, but this is now impossible without disabling field language fallback altogether.
Goal
Providing a reusable/swappable language fallback system that coupled with an entity language negotiation system can improve DX when dealing with multilingual entities.
Proposed resolution
The current solution is based on the Entity Translation API. Basically everything boils down to retrieving the most appropriate entity translation available for a particular context. A $context
array holding arbitrary data useful to describe the context where fallback is being perfomed can be optionally passed. The most common case is the "entity_view" context, but we can have for instance a "views_query" context or a (not present in this issue) "entity_serialize" context:
<?php
$translation = \Drupal::entityManager()->getTranslationFromContext($entity, 'it');
// $translation might or might not be italian depending on its availability
$value = $translation->field_foo->value;
?>
The current language fallback code has been moved to the language manager and is now responsible for building a list of language fallback candidates, possibily based on a given context. The returned values are alterable through dedicated hooks.
EntityManagerInterface::getTranslationFromContext()
performs entity language negotiation by inspecting the language fallback candidates and returning an existing translation object.
This was discussed in Prague. The discussion was attended by: attrib, berdir, das_peter, fago, Gábor Hojtsy, plach, swentel, tstoeckler, webflo, yched.
Remaining tasks
Ensure there is consensus on the current approach or explore new onesComplete test coverageComplete PHP docs and inline commentsFix failing tests- Reviews
(follow-up to be created) Evaluate whether it makes sense to provide a generic language fallback API that could be used for implementing field-level fallbacks in contrib.
(follow-up to be created) Evaluate whether it makes sense to perform entity language negotiation in the route param converters.
User interface changes
None
API changes
- The current language fallback code has been moved to the language manager.
- Field-level language fallback is no longer available.
Original report by fago
Right now we've language fallback implemented for field API fields, however with entity fields being translatable as well, we really to support language fallbacks on entity field level as well. Then, we can probably remove the field API variant in favour of the general one.
I think we should remove any possibility to have run-time alterations language fallbacks that depend on context, but get a general list of language fallback rules (per entity type, per field?), which then could be applied to queries as well — later on (contrib?, d9?).
As this is rather important to have for multi-lingual content, setting to major.
Comment | File | Size | Author |
---|---|---|---|
#193 | et-language_fallback-2019055-193.patch | 100.23 KB | plach |
Comments
Comment #1
fagorelated discussion: #1810370: Entity Translation API improvements
Comment #2
fagoComment #3
effulgentsia CreditAttribution: effulgentsia commentedThe link in #1 points back to this issue. Was it intended to point to a different one?
Comment #4
fagothanks, fixed that. This happens to me too often :/
Comment #5
plachI was going to add language fallback support in the next iteration of #1810370: Entity Translation API improvements but I am fine also with two separate issues. I thought about this for some time and I think we no longer need language fallback with field granularity: in D8 all fields are explictly attached to an entity translation, thus there is no way for a field translation to be missing and another one to be available. We can have an empty field translation, but this should not trigger language fallback IMO.
That said, I think that all we need is falling back to the most appropriate entity translation for a particular context. A very simple implementation of this logic would be:
The example above assumes we already have the #1810370: Entity Translation API improvements.
Comment #6
plachI'll work on this as soon as we have an agreement.
Comment #7
plachHere is a first one: it relies on #1810370: Entity Translation API improvements so I provided also a full diff to send it to the bot.
Comment #9
plachOk, small improvements. Now bot show me the failures!
(The first one is the one to review)
Comment #11
plachGood news, no mopre failures than the parent issue: #1810370-100: Entity Translation API improvements. Working on the following plan:
Comment #12
Dries CreditAttribution: Dries commentedAs this discussed with the D8MI team, this is part of 4 critical issues for D8MI:
Comment #13
plachStill many things to fix but I wanted to see how many fails we have.
Comment #15
plachJust a reroll against head. I expect more or less the same failures. Fixing some missing stuff meanwhile.
Comment #16
plachMissing stuff:
Proper fallback manger injection.
Proper $context initialization in
EntityNG::getCurrentTranslation()
.Test coverage, including for the no fallback manager.
Comments and PHP docs.
Comment #18
plachFixed the first two points. Let's see what the bot thinks about this.
Comment #20
plachAdded some test coverage and fixed some bugs. Bot?
Comment #22
plachThis should be better.
Comment #24
plachHere is my latest work. I expect more failures as properly detecting a NULL value is not exactly easy. I am going to talk to @fago about this tomorrow.
Comment #26
plachThis reverts
EntityNG::__isset()
changes and tries a different approach: syncing entity values each time they are modified, which allows us to use$entity->values
to detect NULL fields.For perfomance reasons values syncing is limited to multilingual entities. I am attaching a patch removing this check to test the new code more thoroughly.
Comment #28
Gábor HojtsyTagging as per explicit approval from @Dries in #2004626-38: Make non-configurable field translation settings available in the content language settings.
Comment #29
plachAdded an issue summary describing the current status.
Comment #30
plachI had an email discussion with @fago about this, posting the relevant excerpts with his permission :)
(me)
(fago)
(me)
(fago)
Comment #31
plach@fago:
Yes, that's the main uses case, but another case where applying fallback rules is needed is for instance in
node_page_title()
. I guess it would be something like the following:VS
It's not, just an internal entity property :)
Most of the fallback code is confined in the language fallback manager service implementations. The changes in EntityNG involve just an additional method and a couple of ifs.
I'm not so sure about this. The equivalent of
hook_field_attach_view()
orhook_field_attach_prepare_view()
might behave more consistently if the data structure they receive matches the one that will be used to display the entity.I didn't see a big value in updating the
$values
array in monolingual contexts but I would not be opposed to it if performance doesn't degrade.Actually this is exactly symmetric to what we do in
EntityNG::getTranslatedField()
: if a value is NULL we instantiate a non-NULL field with a single NULL item. This is why I think this makes sense. I don't think there would be another way to detect a NULL field value from aField
object.There's a big difference between an empty field translation and a NULL (missing) field translation. In D7 an empty field translation does not trigger fallback and I believe this is the correct behavior, since it respects an explicit choice. AAMOF fallback can be "simulated" by translators by just leaving the original values in place, if they need to for any reason, but they have to be allowed to enter empty values without those triggering fallback if they require to do so.
Comment #32
fagoFirst off - thanks for all the great work, I'm looking forward to having that issue solved! :-)
AFAIK field API filters out empty values, i.e. they are not saved. So entering empty values won't save them but lead to nothing be saved, i.e. a NULL value appearing after the next load. Imo, that's fine as semantically it means the same: the field is empty, nothing won't be rendered. Consequently, I think we should trigger fallback behaviour as soon as a field is left empty.
Well, they could still receive the regular entity but go via the decorator to access the field items ($item), which then is rendered. $items still has access to $entity as usual, so that should be fine.
Yep, that sucks - but the API could look as you suggested without the fallback-object being an entity:
So besides complexity what I dislike about the approach of the current patch is that we introduce state in the entity object (yes, once again ;-). Different to the recently introduced active language we do not clone of the object but directly on the object. So this state-change applies to everyone making use of the "shared" object - that's a problem.
So that's the state - the flag which stores whether fallback is enabled. Also, how do I check whether it's enabled or disable it?
Then, I'm not sure about the method name "getCurrentTranslation" as this would suggest me I get a regular entity translation object - but it is not. Imho we need a method that tells me I get something with language fallback enabled back.
So what about piggy-backing upon regular translation objects (= always use their langcode) and optionally apply language fallbacks? So this is how I'd imagine it:
applyLanguageFallback() would return an object using a decorator. Imo it shouldn't be passed around in methods or hooks, if you want to pass your entity on, pass on the entity translation. The code there can itself decide whether language fallback is desired for a certain action or not.
Imho, at the very least we need to clone entity objects before changing their state. Then, the question is whether passing on fallback-enabled objects is something we want to do/encourage - if not, I think a decorator is completely fine and better separates code + state.
Comment #33
plachI had a closer look to the entity/field render-related code. Actually it seems that a decorator might be a good choice, as I wasn't able to identify a recurring pattern for the fallback use-case. Depending on the implemented logic you might want to apply it or not.
The main problem I see with the suggestion above is that there is no way for
to throw an exception, as the
onChange()
method will be called directly on the$entity_translation
object, unless we deep-clone the field objects, which might be very bad for performance.Comment #34
plachOk, here is a new approach based on the discussion with @fago. Still needs work but I want to see how many failures we have.
Comment #35
plachComment #37
plachThis should be mostly ready from the functional POV. Still a couple of details to be fixed as well as test failures and PHP docs.
Comment #39
plachLet's get rid of those notices :)
Comment #41
plachOk, we should be really done with functional stuff now. Missing test fixes + PHP docs.
Comment #42
yched CreditAttribution: yched commentedJust curious: what becomes of field_language_fallback() and the functions in field.multilingual.inc ?
Comment #43
yched CreditAttribution: yched commentedAlso, FYI : opened #2051721: Get rid of field_get_items() - would welcome confirmation of the changes.
Comment #45
plachI am planning to deprecate
field_language_fallback()
andfield_language()
in this patch but not removing them to avoid unecessary API changes. We can get rid of those in a follow-up.In general the functions in field.multilingual.inc will be useless once we have fineshed moving the field.attach.inc code to the entity controllers.
Comment #46
plachAlso, the issue summary needs an update after #32 - #33.
Comment #47
yched CreditAttribution: yched commentedThanks for those answers, @plach.
[edit: removed stupid question, never mind]
Comment #48
plachThis should fix most failures.
Comment #50
plach#48: et-language_fallback-2019055-48.patch queued for re-testing.
Comment #52
plachThis one should be green unless some new test has been added meanwhile. Please note that there still some things to fix before this is ready for review, hope to have them done by tomorrow night.
Comment #53
plachThere are still a couple of things I wish to fix (improve test coverage and some minor clean-up) but now we should be mostly ready for review. Hopefully I didn't break anything :)
Comment #55
plachMerged and (hopefully) fixed failures.
Comment #56
plachThis time for real :)
Comment #58
plachAnother reroll
Comment #60
plachThis should work better.
Comment #62
plachAnother attempt.
Comment #64
plachThis should hopefully be green again.
Comment #65
amateescu CreditAttribution: amateescu commented@yched opened a followup issue for cleaning up what's left in field.multilingual.inc after this patch lands.
Comment #66
plachSome test coverage improvements and a small clean-up. We should be ready for review now.
This is the follow-up @amateescu was referring to: #2067079: Remove the Field Language API.
Comment #66.0
plachAdded issue summary
Comment #67
plachRerolled and updated the issue summary. Reviews welcome :)
Comment #67.0
plachUpdated issue summary
Comment #68
plachComment #68.0
plachUpdated issue summary.
Comment #69
mradcliffeI stumbled upon this issue, so here are a few comments.
Second use of deprecated is misspelled "deprectaed"
This could be re-worded a bit to not use fallback in the definition of fallback manager.
I don't know if I understand it appropriately, but maybe:
"The language fallback manager to be used to determine the current translation and field values when no translation exists."?
Comment #70
plachThanks, merged in the latest changes and addressed #69.
Comment #71
plachRerolled
Comment #72
plachRerolled
Comment #74
plach#72: et-language_fallback-2019055-72.patch queued for re-testing.
Comment #75
andypostThe fallback manager already knows it's language manager so any reason to set both services on entity and storage controller?
Maybe better use language manager from fallback manager?
$this->languageFallbackManager->getLanguageManager()
Comment #76
plachRerolled after #1497374: Switch from Field-based storage to Entity-based storage.
@andypost:
I considered this possibility too. The main reason I choose not to go this way was that from a very theoretical POV a language fallback manager implementation might not need to depend on the language manager, while the entity will definitely need both in any case (to retrieve languages, for instance). Makes sense?
Comment #77
plachComment #80
plach#76: et-language_fallback-2019055-76.patch queued for re-testing.
Comment #82
plachBetter reroll
Comment #83
yched CreditAttribution: yched commentedStarted looking at this - great work, this should let us clean a lot of ugly stuff !
Still wrapping my head around it a bit, and I still need to read the exchanges with @fago higher.
Just a couple non-fundamental remarks for now:
Adding whitespaces
Just wondering: shouldn't this use hasTranslation() ? (proper encapsulation of the logic around $this->translations ?)
Wrong copy / paste.
"fallback manager" looks like a misnomer IMO. A manager manages other stuff. Those are not "managing" anything, they hand out fallback langcodes according to some implementation logic.
Seeing those named managers make me look for the objects they manage. We shouldn't use "manage" as a synonym of "do some stuff" IMO.
Also, the class name should carry the fact that this is about language stuff, without needing to look at the namespace (that's in our code standards)
What those do is map langcodes to langcodes - so maybe LanguageFallbackMapper / LanguageFallbackMapperInterface ?
Method name is confusing, makes you wonder what a Candidate object is. This returns langcodes, so getCandidateLangcodes() ?
(similarly, the $candidate / $candidates used by calling code to manipulate the results - e.g. in EntityNG::getExistingTranslation() - could be renamed $candidate_langcode IMO)
I don't get why this is needed / how this is related ?
This method is only ever called to obtain a langcode (calling code gets the ->id of the result). Keeping an internal copy of language_list() objects just to be able to return a Language looks like overhead for not much ?
#2078625: Field/FieldItem value objects should carry their language went in meanwhile, added Field/FieldItem::getLangcode() rather than getLanguage() for those exact same reasons. Maybe we should move to getPropertyLangcode() here too ?
Grammar ("same type than..." .)
+ double "this"
"apply" sounds like you're transforming the object itself, not that you're returning a new one.
getLanguageFallbackData() would be more accurate for the current architecture of the code ?
Can we split that line in two or three ? ;-)
The comment is misleading - "the previous update" means field_update_8003() :-p
+ if we don't care about migrating 'field_language_fallback' anymore, then it looks like it should be removed from field/config/field.settings.yml ? (and also the old 'field_language_fallback' var should be deleted here ?)
+ trailing whitespace
One separate file for a single value ? There is already language.detection.yml with a single value as well... Should we consolidate things a bit ? (maybe in a followup ?)
(also, needs reroll)
Comment #84
yched CreditAttribution: yched commentedAlso, wondering - I thought I understood (don't remember how/where/who, though) that the plan was to drop field-level language fallback and keep only entity level fallback ?
Meaning, you ask for the entity in a language L1, we determine an existing translation L (= either L1 if it exists or some other L2 if it doesn't), and you get an entity with fields in either L or 'und' but we don't mix and match and fetch fields in some L3 if they don't exist in L ?
The assumption being: there are no partial entities, if a field is empty in a language, then it's just empty and we don't try to find a replacement in another language.
Did I dream that ? Or maybe that was a discussion with someone else ?
Comment #85
plachThanks for the review, I'll address it ASAP!
That was my original approach to this issue, then @fago convinced me we might still need field-level fallback somewhere here.
Comment #86
yched CreditAttribution: yched commentedOK. This does add a huge amount of complexity to the system though - including all the discussions about "should this "fallbacked" set of fields be a decorator / should this act as a real entity / we have issues if people start to write to it" in #30 and below.
And yes, that LanguageFallbackInterface object is specifically the part that puzzles / worries me in the current patch :-/.
For instance, the fact that it's the entity itself in monolingual cases and is a different stub object that's totally not an Entity in multilingual cases, feels dangerous. It widens the runtime difference between mono and multilingual sites. opens the door to "my code / contrib module works because I only tested it on monolingual, but breaks on multilingual sites".
Then, the fact that entity rendering currently doesn't use the LanguageFallbackInterface object directly, but still goes through the "old" field_attach_*() with the "old" field.multlingual.inc functions (that are kept alive by returning logic based on the LanguageFallbackInterface object), makes it difficult to evaluate how the "real" final code would work and whether that LanguageFallbackInterface fits the needs. We can't be sure we nailed right it until we use it for real...
More importantly though, I don't get how field-level fallback would be triggered. An empty value is a totally valid value in a translation, it shouldn't mean "try to find a non-empty value somewhere else". Why shouldn't translations be allowed to leave a field empty ?
Comment #87
plachThis should address most of #83. I intentionally left out all the stuff that has an impact on the current architecture, since I need to mull on #86. To shortly summarize my thoughts, I am a bit torn: I hate the amount of complexity we are adding to support a use case which is frankly marginal, to say it politely, at least in my experience. OTOH in D7 we could not properly separate field language fallback from entity language negotiation, hence we introduced a "feature" which we might need to keep support for.
Comment #89
plachFixed a merge issue
Comment #90
yched CreditAttribution: yched commentedThanks @plach
Still some language / grammar issues :
"...hence any field has always..." is off.
"... hence a translatable field always has the active language" ?
(not that I understand the overall sentence ;-), but... )
still wrong - "of the same type than..."
Comment #91
BerdirPossibly related, might already be (partially) solved by this, but wanted to write the issue down: #2090983: ContentEntityInterface::getTranslation() should throw an exception when an invalid language is specified.
Comment #92
plachJust fixed #90 for now. I am going to provide my updated POV on this tomorrow.
Comment #93
plachFor reals now
Comment #94
plachI spent the last days thinking about this as soon I had a free minute and I concluded that I have no interest in supporting language fallback at field level. Let me explain why.
In this issue we are dealing with the conversion to the D8 API of a D7 subsystem that provided two non-cleanly separated tasks: entity language negotiation and field language fallback.
I initially wrote the D7 code to solve a rendering issue: when displaying an entity in a language for which a translation was not available, field values disappeared. This clearly was not desirable. Since in D7 core there is no concept of entity translation (we have only language-aware fields), the only way I found to fix this issue was applying some fallback rules to every field so that existing values would be rendered. The underlying assumption was that all fields would behave the same way. What I didn't take into consideration was that empty fields values are not stored, hence they appear as missing values. The unwanted consequence was I unwillingly introduced a new "feature", that is language fallback at field level. AAMOF in D7 you can render the french translation of an english node, but if the body translation is empty its original english value will be rendered. This behavior was not intended initially and it does not even make much sense in many scenarios.
In my personal experience I cannot recall a single case where such behavior would be the desired one. Instead I think we should leave translators the ability to enter empty values if they need to, but this is now (both in D8 core and with the current patch) impossible without disabling field language fallback altogether.
The other reason I've started leaning towards this position is that to cleanly implement the approach summarized in the OP, we are adding an insane amount of complexity and code to a system that is already not straightforward. And this is to support a use case that is far from being a common one, at least in my experience. Without that translators needing to show an untranslated value as fallback could simply copy the original values as the translated ones, but this way empty values would stay empty :)
To sum up I would like to propose to go back to my original approach and get rid of the fallback data decorator. That logic can easily be implemented by contrib code relying on the language fallback manager service introduced by this patch, but I'd avoid baking that concept into the core entity system.
Comment #95
yched CreditAttribution: yched commentedThanks @plach. We'll get to discuss this in Prague, but +3000 on #94.
Comment #96
dsnopek+1 on what @plach said in #94 - I've done a number of multi-lingual sites and I'd never want individual fields to fallback.
Comment #97
andypostShould be \Drupal according #2053489: Standardize on \Drupal throughout core
Comment #98
kfritscheWanted to review and had first to reroll.
Had some trouble to reroll this, but hopefully this works now.
I will continue review and fixing this later, on the first glance I already see some missing comments in the modified __constructs.
Also fixed #98.
Comment #100
plachWorking on implementing #94 as per the multilingual fields hard problems discussion conclusions.
Comment #101
plachHere is a patch removing field language fallback. Let's see how many failures we have.
Comment #103
plachFixed/removed field translation tests and fixed a stupid mistake.
Comment #104
BerdirSeems like we should have an interface for this?
Also wondering if we can come up with a better name than SomethingManager. You should hear @msonnabaum's opinion about naming things Managers ;) No idea what we could name it, we could ask him.
We should probably at least wait until we get the config/content separation in, so that we don't need to add those methods here and then remove them again.
I'm also not too convinced about using setter injection for a one-off like this that has to go through the database storage controllers :( Seems like that's something that we'll just have to revert once we have a generic solution to inject dependencies into entity and field classes.
Not sure if someone suggested to do it like this, haven't read the full thread.
Comment #106
plachJust fixing tests for now, reviews above still to be addressed.
Comment #107
jibran{@inheritdoc}
80 char limit.
Maybe we have to add $context details here.
Comment #108
plachTesting to see whether a change is actually necessary.
Comment #109
plachThis should address what was left of #83, #104 and #107. Some answers below:
@yched:
The default fallback rules inspect languages by their weight. The sort function actually does nothing without the ampersand, now we have test coverage for that :)
@berdir:
We already have it: it's
LanguageFallbackMapperInterface
(now).@jibran:
There's a pointer to the language fallback mapper interface, which has full docs about it :)
Comment #109.0
plachUpdated issue summary.
Comment #110
plachThis is yet another switch field/entity issue now, updated issue summary accordingly :)
I think we just need to figure out the actual EntityNG method names (@fago expressed some concerns about those) and then we should be done.
Comment #112
plachCompletely removed service injection from entities for now. We'll have to deal with that in a separate issue.
Comment #113
plachRerolled
Comment #114
yched CreditAttribution: yched commented(partial review, need to run, stopped in the middle :-/)
Seems unneeded ? (or we'd be basically adding this kind of documentation in a lot of other interfaces)
Looks like the "Normally..." part is redundant with the doc for the @return statement, I'd vote for removing it.
Actually - I'd think that at least $langcode (and maybe $operation ?) would make more sense as standalone parameters for the method. Those will always be present (right ?), and it would make the contract clearer : "I want fallback languages for this language (and this operation)". Let's avoid squashing fundamental parameters into a single array param. DX++
"in which" ?
"is being applied" ?
Nitpick: "monolingual environments" / NoLanguageFallback feels weird. Monolingual is "one single language", not "no language" :-)
trailing whitespaces
Comment #115
BerdirHm.
getFallbackTranslation()
getTranslationOrFallback()?
getAvailableTranslation()
askMarkBecauseHeAlwaysSaysNamingThingsIsEasy()?
Hm2.
This seems to be one that you should be using in most cases if you load an entity and want to render/access check it based on the current content language. So we should make it easy to find this one.
"current" seems to be something that's invented here, especially because it doesn't seem to refere to the current *content* language.
Default already has specific meaning, so that's a no-go.
Could we just make the $langcode argument optional and say that's what it defaults to?
=> $entity->getExistingTranslation()? (or whatever it will be named)
Trailing spaces.
Should use \Drupal::languageManager()...
+1000.
Those tests are *so* confusing right now.
Wondering if using fluent interfaces would be easier to read, that's what we did usually before switching to yaml:
<?php
$container->getDefinition()
->setClass()
->addArgument();
manager => mapper (much better, BTW!)
Left-overs.
Comment #116
BerdirOne thing is I think important to consider.
While the API makes a lot of sense, combined with #2072945: Remove the $langcode parameter in EntityAccessControllerInterface::access() and friends, #2073217: Remove the $langcode parameter from the entity view/render system, we do introduce one problem.
We lose the ability to have defaults. If you pass in an entity object into the access or render system, we have absolutely no way of knowing if you did apply some sort of language logic. We don't know if you really want to check/display the entity in that language or the current content language.
So, we need to figure out and document where and by whom the language handling needs to be applied. Because this could result in troubles if people forget to do it and then the wrong language will be displayed while they didn't have to care before.
Comment #117
BerdirCrosspost with myself. Achievement unlocked.
Comment #118
Berdir#2004244: Move entity revision, content translation, validation and field methods to ContentEntityInterface went in, so this will need a re-roll and we can remove the empty implementations in Entity and ViewsUI for the new methods.
Comment #119
plachCool, will work on this tonight.
Comment #120
plachRerolled and addressed #114 and #115.
@yched:
I don't agree: no parameter is required or even implied here, AAMOF we don't even have a sensible default to apply for them. If you look at
LanguageFallbackMapper::getCandidateLangcodes()
, we are explictly taking into consideration the fact that no desired language is specified, which might make sense if one just needs a plain list of fallback candidates.You are not reading it right :)
In a monlingual environment you have just one language so "no language fallback" can be applied. I asked Gabor for suggestions and he was pretty much agreeing with this name, alternative suggestions welcome.
@berdir:
:)
I spent quite some time thinking of a name conveying the proper concept and I think the current version does the job well: my goal was trying to make the method name very easy and straightforward. This method needs to be called to ensure code will work with an existing/valid/available translation object, no other cognitive burden is needed. The fact that we are applying a fallback logic is an implementation detail, a method name should tell what it does not how it does it.
After spending a very long time thinking how the Entity Translation API could look like, I realized that the ideal DX in most scenario would be being able to write language-agnostic code wherever possible. I think that if we get it right, what you are outlining above will be the key factor in the API success. By always moving the reponsibility to pass the proper translation object on the caller, we are ensuring that in most scenarios people will not have to deal with language, unless their business logic requires it obviously. I totally agree that we must define very clearly where people should be authorized to deal with an entity (translation) object as-is, but ideally the rule of thumb should be "always unless told otherwise", although I don't think we will be able to achive that. I think the form controller is a good example: it figures out which translation it needs to work on early and all handlers always get the proper translation object. If we can reach the same level of reliability, at least in the most common contexts (rendering, access-checking, token-processing), we should be able to guarantee that a fair amount of code out there won't need to worry about language at all.
Comment #121
plachComment #122
yched CreditAttribution: yched commentedGoing afk for the next couple weeks, I'll let you drive this home folks :-)
Just crossposting to #2095195: Remove deprecated field_attach_form_*(), that contains an initial version of how the integration of widgets / formatters in forms / entity_views would work after this gets in.
Comment #123
BerdirDocs will need to be updated for the $langcode change.
Comment #124
plachDone
Comment #125
BerdirLooks like this has some left-overs/accidental reverts in Entity.php
Another left-over?
Also not necessary anymore.
Another left-over.
Yeah, so that's the part that I don't fully understand yet. Can we look at a few examples and where exactly the content negotiation for access checks and rendering will happen and who will do it?
1. Viewing node/1 (This is the easy one, to get started! :))
2. A few with rendered entity, e.g. the front page.
3. A view that displays entity fields.
4. Custom code that loads a node and displays it, e.g. in a block.
I have a feeling that we could really use some functional tests, e.g. for the second one. Create a few nodes with translations and then look at the frontpage view with different languages. Because that view right now displays duplicates if you do that :)
Comment #126
plachJust a reroll for now. I am going to address #125 soon.
Comment #127.0
(not verified) CreditAttribution: commentedUpdated issue summary.
Comment #127.1
plachUpdated issue summary.
Comment #128
plachComment #129
plachAddressed the code review in #125. Working on the meaty part now.
Comment #130
Gábor Hojtsy@plach: the issue summary says only reviews are missing. You said you are working on the meaty part (without specifying what it is). It is hard to follow the status of this issue :/
Comment #131
plachI was referring to the last part of #125, but I am a bit swamped these days.
Comment #132
plachHere it is: it turns out that satisfying bullets #125.1, #125.2 and #125.4 is rather easy and just requires to adjust the entity render controller and some minor stuff. We have both unit and functional tests covering these.
Duplicates aren't something we can fix here, AAMOF they are caused by the way Views joins on node tables and retrieves results. We have #2006606: Views rendering ignores entity language, eg: Identical nodes rendered in views when nodes have translations to fix this, where we probably want to introduce a new Views field group (Content translation matching Content revision) exposing the
langcode
anddefault_langcode
columns of the data table. After having those an optional filter, like the one in the node admin view, would do the trick by filtering stuff matching the current language or having no language, just when Content Translation is enabled. Another possibile solution would be refactoring Views to make it just select entity ids and then loading entities, but this sounds way more invasive.Anyway, in this scenario #125.3 would just work, as it already shows translated values (although not filtered by language).
Just a side note: with this one node title translations are displayed correctly in the most common scenarios :)
Comment #134
plach#132: et-language_fallback-2019055-132.patch queued for re-testing.
Comment #136
plachRerolled, test fails still there I guess.
Comment #138
plachLet's try this.
Comment #139
plachCleaning up the stuff below.
Merge issue.
This could use an inline comment.
This is a bit uninformative.
Unneeded line
@berdir:
I'd like to discuss the suppression of
EntityInterface::getCurrentTranslation()
a bit more, as it seems to me that a line like the following would be way more self-documenting than the one above:The
::getExistingTranslation()
method is completely meaningless to my eyes when used without a parameter. I'd definitely like to restore the old approach where the$langcode
parameter is mandatory and reintroduce the::getCurrentTranslation()
method as implemented in #113:I don't understand this argument: "current" refers exactly to the content language of the current context, which is provided by the language manager.
Aside from this I think we are ready to go.
Comment #140
Gábor Hojtsy@Berdir, @fago, @yched: what do you think? This is a blocker to make non-configurable field translations display, so it would be important to get over this soon if we have agreement and the code implements the agreement proper :)
Comment #141
yched CreditAttribution: yched commentedJust went through the first couple lines for now, need to run :-/
Can't this be simplified a bit ? Can there really be cases where getLanguage(CONTENT)->id would be empty ?
(also, the combination of isset / empty checks looks needlessly confusing - could we unify on one or the other ?)
We intend to use the fallback mapper on other stuff than entities ? If not, the 'data' key might be better/less generically named 'entity' ?
"Most fitting" is just "the first that is present" ?
Comment #142
plachWell, it depends on the language manager implementation :)
The fallback mapper has no knowledge of the entity system, so theoretically it could be used with other data than entities. Using an
'entity'
key would make little sense form an architectural POV IMHO.Yes, but only before the alter hook is run. We can change that but it might be inaccurate after the alteration.
@Gabor, @yched, @berdir:
Any comment on #139.5?
Comment #143
yched CreditAttribution: yched commentedWhat I mean is : getLanguage() returns something, and we unconditionally take its ->id, so we expect that "something" to be not NULL in the first place. Can this ->id then be NULL ? (Can a non null $language object have a NULL id ?)
Still need to review the rest of the patch, but I tend to agree with #139.5 - i.e. +1 on getCurrentTranslation() vs getExisistingTranslation(NULL is understood as "the current language")
Comment #144
plach@yched:
Ok, now it makes sense to me, fixed it the way you suggested :)
Also I spoke about #139.5 with @Berdir in IRC and he expressed some concerns about the DX of the current method namings:
In an attempt to reconcile his POV and mine I tried to rename
::getExistingTranslation()
to::getTranslationFromContext()
. Personally I find it works both with the$langcode
argument and without, so I am happy enough with it to provide a reroll. How does it look now?Comment #145
plachWe also had a discussion about #116 (you can find the full log attached).
Berdir's concerns is about figuring out a reliable logic to determine when we should expect a translation object to be passed around and when code should be in charge of instantiating it. He correctly pointed out that in the latest versions of this patch the
EntityRenderController
accepts a$langcode
parameter and determines the translation object to act on based on it. This breaks the rule we previously identified that a piece of code should normally rely on the caller code to get the proper translation object.I realized this too while working on the latest patches and I came to the conclusion that whenever a known language logic is available, it should be applied to determine the proper translation object. If no context is available to reliably determine the correct translation, just act on the entity object as-is. This translates ;) to the following rule of thumb: "If you have no idea of what is the correct translation to act on that probably means you want to act on the entity object you receive as-is". Obviously we cannot entirely avoid applying some form of language logic somewhere in the call stack, we can just avoid the need to repeat it everywhere.
We took in consideration three examples to proof-check this rule. Both form and render entity controllers usually have a well-defined context, from which it's easy to derive the desired language logic: hence both can determine the translation object to act on by themselves. OTOH an entity access controller implies no clear context and as such it should be passed the correct translation object.
Berdir pointed out, and I believe he might be correct, that the proper place to perform entity language negotiation could be the route param converter, after that every piece of code would automatically get the proper translation object. This sounds a bit scary, as it implies going with the current language in lots of places, but it might be definitely worth a try, as it would avoid the need to explictly perform entity language negotiation in most entity routes.
However this would be another reason to find a clean solution for #1810394: Site configuration with domain based language negotiation results in redirecting authenticated users to a different domain when accessing a content entity route for translation language different from the interface language, which at this point would probably need to rely on explicitly switching content language instead of forcing just a form language change.
Comment #146
yched CreditAttribution: yched commentedMulling a bit over #145, meanwhile, end of my review.
Do we still even need that method ? It's only called in init(), and is simple enough that it could be inlined in there ?
I'm aware it's semi-related to "who is responsible for setting the entity to the right language ?", but even in the current situation, the method seems extraneous ?
I'm not sure I understand this. If we use 'entity_view' operation even for forms, what is the point of having an "operation" in the context ? Wouldn't we be better off with just 'type' => 'entity' in the context instead ?
Looks weird ? EntityFormController::submit() already calls updateFormLangcode() and then buildEntity(). So updateFormLangcode() would end up being called twice ?
(also, having buildEntity(), whose advertized job is to return an $entity, perform side effects on $this, is a bit unexpected/confusing)
leftover of "manager"
Nitpick : "matching" is a bit misleading.
"Returns ... candidates for a given context" ?
I still don't get the absence of an explicit $langcode param in getCandidateLangcodes(). We're in the interface for *Fallback*Mappers, right ? I don't understand what "fallback" means if it's not in the context of "I want a specific language, give me a list of fallbacks for that language". I don't see why something called FallbackMappers would be used for a different task ?
- The actual consuming code (other than tests and the BC for language_fallback_get_candidates(), which we could remove IMO ?) always calls that method with an actual langcode.
- It's even more confusing since Entity::getTranslationFromContext() *does* take separate $langcode & $context params, and puts the former in the latter before calling getCandidateLangcodes() :-/. Which then makes it difficult to properly document the $context param in getTranslationFromContext(), since it's slightly different than the one accepted by getCandidateLangcodes().
"manager"
I definitely find it hard/impossible to read NoLanguageFallbackMapper as "no fallback" rather than "no language" :-)
NoFallbackMapper ?
MonolingualFallbackMapper ?
This code is just moved around (and I understand why), but isn't it weird that CommentFormController needs to take care of initializing the langcode of new comment entities ? Should there be a "@todo fix this" on a separate issue ?
Uh ? Is that really related ?
Same, really related ?
field_invoke*() are most probably on their way out after this lands, but could you briefly explain why this change is needed ?
Minor: indent error on the second closing bracket.
There is no isEnabled() method in LanguageFallbackMapperInterface ?
Shouldn't we just remove the function, since it is not used and looks like it cannot work anyway ?
"manager"
Minor, but it would make more sense to move this down to where it makes an actual difference, before the key() call a couple lines below ?
(also, not sure why this ksort is now needed, field_available_language() doesn't seem affected by the patch ?)
var name mismatch between param and sample code ($candidates / $fallback_candidates)
Same in the _OPERATION_ specific hook variant.
Why does it extend NoLanguageFallbackMapper ? Doesn't seem to make much sense in itself, and no code is shared anyway.
(also, NoLanguageFallbackMapper doesn't actually need a $language_manager to do its job, it's only LanguageFallbackMapper that needs it, so it should be in LanguageFallbackMapper's construct rather than NoLanguageFallbackMapper ?)
The method looks a bit overkill, could easily be inlined in getCandidateLangcodes ?
(+ do we really need a separate hook_language_fallback_candidates_OPERATION_alter() hook ?)
Class name looks off in the comment ?
Minor: we usually leave empty implementations on one single line:
function foo() { }
(+ silly question: why does the class implement ServiceProviderInterface if it needs no register() ?)
Comment #147
fagoThat would make sense to me also. Our way to get the current request entity, really should get it in the usual translation. Then, rendering and forms should just default to that. Consquently, it would make sense to me to remove the $langcode paraemter from the render controller.
Yeah, if the caller wants another translation, the caller should pass it. Partly having $langcode parameters instead just seems inconsistent to me. That does not mean, we'd have to convert all code as a part of this issue of course ;-)
ad #145: I'm glad to see more discussion about the method names as you know I've not been happy about the previous ones either.
Shouldn't that be part of the context? Maybe, I'd like to override the language type being used, but passing a langcode seems weird.
I must say I like the context naming more, +1 on that.
However, I'm unsure on having the method + the logic on the entity class. It does not seem to be entity-related logic, nor is it obvious that it is an operation that requires data from the entity. (Although it does, it requires its translation languages). Maybe could we have just a helper method on the entity and have the logic elsewhere, as we do for access, validation, storage, ... ?
Not sure, where it should be though. I cannot really see the need for a separate EntityTranslation controller/handler/service, so it would be best situated at the manager which could have the respective services injected properly?
Maybe 'language_negotiant'?
I'd see it as the job of the fallback-mapper to give me the explicit langcode, given a set of available languages I have and the context and target language type. There might be different fallbacks depending on context so just having one desired langcode is probably not enough?
Comment #148
BerdirRe ServiceProviderInterface, this was necessary until today, see #1988508: Stop pretending we support Symfony bundles when we really just have service providers. There's a base class now that you can extend and only implement the alter hook.
Comment #149
plachThis should address #146-#148.
@yched:
2. Well, we have other contexts where the operation key makes sense, for instance tokens. The op-specific alter for instance allows CT to alter candidates only when it makes sense.
3. Fixed this: we need to update form language before validation handlers are run, otherwise the entity is not built properly for those.
9. That is going to be addressed in #1966436: Default *content* entity languages are not set for entities created with the API.
10. Yes, a wrong permission string is being used: the changes here are probably exposing this bug and tests now fail without this.
11. Same as 10. Moreover you asked to split that in multiple lines somewhere above :)
12. By acting on all available languages, new translation objects were instantiated and then erroneously stored. Again some change here exposed this bug.
14. Yep, that was a leftover of when supported field-level fallback.
16. Probably this is related to the change to
Language::sort()
.19. That made more sense when (in the early patches) we had two methods needing to alter their stuff. If we end-up adding back the value map method we should need it again. It seems totally hamless to me. And yes, as explained in 2. we still need the OPERATION-specific variant.
@fago:
Entirely removing those might be problematic: for instance we may want to specify an explict translation, but we are no longer able to get a default behavior if we trust the entity translation language. In that case we would be forced to perform entity language negotiation outside. Doing that in the route param converter might mitigate this DX problem probably.
1. @yched above is advocating for the opposite, I adopted his suggestion as it makes the most common usages less verbose.
2. Moving fallback stuff on the entity manager looks indeed cleaner, I don't think we need a helper method on the entity object.
3. No strong preference: I will adopt any solution you and @yched will agree on ;)
Comment #151
plachFixed failing tests
Comment #152
plachI'm not clear where we are with this issue: are we happy with the current status? I think exploring the approach where we perform entity language negotiation in route param converters should be a separate issue.
Comment #153
yched CreditAttribution: yched commentedThanks for the update, @plach!
So, re #145: Resolving entity language as part of the routing mechanism - I guess that seems like the most reasonable option. I kind of fear it kicks language resolution to the "black magic / dark land that few braves dare looking into" area (controllers will be the entry point for most people looking at code), but doing it below the controllers seems clunky/confusing too, so...
No strong opinion on whether this should be done in this patch, though. Maybe that's tied to the "remove $langcode param in entity_view()" task? @Berdir, what do you think ?
Not sure I see the win in moving getTranslationFromContext() from the entity to the entity manager - but if @plach and @fago both think it's better, I have no real objection myself.
(side note: the code sample in the OP needs to be updated for the newest shape of the language fallback API)
I don't understand why the latest patch changes EntityViewBuilder CommentEntityViewBuilder to receive $entity_info as an injected param ? Doesn't seem related at all to this patch ?
Comment #154
yched CreditAttribution: yched commentedMore remarks on the interdiff in #149:
Comment is wrong.
Service locator instead of injection ? Why isn't the manager injected in the ContentEntityFormController controller ?
Drawback of moving the getTranslationFromContext() method from the entity to the entity manager : now the manager needs to be injected in more places - would be mitigated if this language resolution is pre-done in the routing layer, but meanwhile we add clunky code and dependencies to a couple classes here :-/
Does this really make sense for non-content entities ? ContentEntityFormController does it in its own override of validate(), so this code is for non-content entities. Why would non-content entities support switching the language ?
Comment #155
yched CreditAttribution: yched commentedI'd like to take a step back on the FalbackMapper classes.
We have a service whose job is to return a list of candidate languages for a requested language and a given context. Two implementations are provided:
- default implementation (NoFallbackMapper) is used on monolingual sites, and unconditionally returns array(LANGCODE_NOT_SPECIFIED)
- when enabled, language.module switches in a different service in the container instead (LanguageFallbackMapper), that unconditionally sets the result to "all enabled languages ordered by weight", and calls an alter hook to let modules modify that. In the end, this alter hook is the only place where there can be logic based on $context.
This is a simple case of "trivial unconditional logic + some alter hook", and the actual extensibility and granularity ("what are we doing fallback for ? entities ? other stuff ?") resides in the hook. Isn't the "service + service override" approach way overkill ? It obfuscates the actual logic IMO - took me that long (155 comments in...) to realize that it was in fact as simple as described above...
Couldn't we:
- get rid of the 'language_fallback_mapper' service altogether
- add a getCandidateLangcodes() method directly to \Drupal\Core\Language\LanguageManager, that does:
?
Comment #156
plach@yched:
Thanks for the new reviews :)
In the latest patches we have a solution that we know will work. While the other alternative might be preferrable I think we need to experiment with it before being sure. I think this issue has been open for way too long time to wait for this experimentation to happen, so I would be really happy if we could move on with this one and try the other approach in a separate issue.
Well, since I had to change the signatures of
EntityViewBuilder
/CommentEntityViewBuilder
anyway to inject the entity manager, I thought it would be ok to inject also the entiy info instead of relying on that stupid procedural helper.Because
ContentEntityFormController
extendsFormBase
which in turn uses the service locator pattern.Well, in the previous patches we simply skipped DI for entities since there is no way to do that properly atm, so the entity manager would actually simplify that a bit. However if this becomes awkward we can always inject the entity manager in the entity objects and restore the helper method on those.
I may want to change the language of a View, for instance, but I am not sure it actually works that way. We should check with Gabor.
Yep, that service made way more sense when we had field-level fallback. Moreover #1862202: Objectify the language system is going to introduced a stripped-down version of the language manger for monolingual sites, hence we will be able to restore the no fallback logic there.
Comment #157
Gábor HojtsyRe: non-content entities, config entities cannot load themselves in different languages, they are not aware of the language support underneath them. Yeah, that is not very consistent with how language works on content entities, but at least for now, config entities don't know if they are loaded in their original form or in a translated form, they are just "dumb" classes getting data from the config system, and the config system knows if there are overrides to add. Once the config entity is loaded, it does not really know if its a translation or not. This may or may not change with #2098119: Replace config context system with baked-in locale support and single-event based overrides but I don't think this patch should deal with that at all.
Comment #159
yched CreditAttribution: yched commentedre: entity-language handling for non-content entity forms: Yes, the patch here just moves some code around. It seems there are simplifications to be made, but it's outside of scope here. I opened #2123867: Simplify/cleanup language handling in EntityFormController.
Will look into #156 more closely tonight - yay for the simplification !
Just: NoFallbackMapper returned LANGCODE_NOT_SPECIFIED, now the new patch returns LANGCODE_DEFAULT ?
Comment #160
plachBecause non-eneglish monolingual sites by default create entities in the default language. Right now both constants have
'und'
as value, so the difference is putrely academic. However with #2076445: Make sure language codes for original field values always match entity language regardless of field translatability we will have an actual difference and I thinkLANGCODE_NOT_SPECIFED
would not always work.Comment #161
plachRerolled
Comment #162
plach@Gabor:
I just posted a patch in #2123867: Simplify/cleanup language handling in EntityFormController, can you check that I am not breaking config entity language assignment please? :)
Comment #162.0
plachUpdated issue summary.
Comment #163
yched CreditAttribution: yched commentedLast nitpicks...
- About "service locator" in ContentEntityFormController
You replied: "ContentEntityFormController extends FormBase which in turn uses the service locator pattern".
It doesn't appear so ? FormBase uses the static create($container) factory pattern, and child classes can (and some do) override it to extract dependencies in there and pass them to the __construct(). Why couldn't we inject the entity manager this way ?
- Yay for getting rid of the "fallback service", and putting the getCandidateLangcodes() method directly on the LanguageManager, but the method name kind of lacks context now - the word "fallback" doesn't appear anywhere. It's also totally disconnected from the name of the alter hook it triggers (hook_language_fallback_candidates_alter()).
--> getLanguageFallbackCandidates() ? getFallbackCandidates() ?
- Sorry, patch doesn't apply anymore ;-)
Other than that, I think this is ready. I'm fine with exploring the "resolve entity language during route param upcasting" in a separate issue, if @Berdir is ok with it too (although not sure exactly when @Berdir goes afk)
Comment #164
plachYou are right, sorry, I completely missed that :(
Comment #165
plachComment #166
plachWhile updating the issue summary I realized the new method was not declared on the entity manager interface.
Comment #167
plachComment #168
yched CreditAttribution: yched commentedYay. As far as I'm concerned, RTBC if green. Thanks!
(I guess test failures will tell us that, but shouldn't we check for classes that subclass ContentEntityFormController and might already implement create() __construct() themselves ?)
Comment #168
yched CreditAttribution: yched commentedYay. As far as I'm concerned, RTBC if green. Thanks!
(I guess test failures will tell us that, but shouldn't we check for classes that subclass ContentEntityFormController and might already implement create() __construct() themselves ?)
Comment #171
YesCT CreditAttribution: YesCT commented#166 failed with 463 fail(s), and 232 exception(s), lots of "simplexml_import_dom(): Invalid Nodetype to import" but others also..
duplicate tests (or testing each hidden) is related to #2126225: PIFT creating duplicate test records, causing PDO exception during pift-cron
Comment #172
plachThere may be a d7do bug around but surely many of those failures are caused by me posting patches at 3AM...
@yched was right once more ;)
Comment #173
plachAnd this is unneeded: to be fixed on the next reroll.
And also this line needs to be fixed.
Comment #175
plachLet's try this one
Comment #177
plachWrong patch, sorry, the interdiff is the same as in #175.
Comment #178
plachComment #179
plachComment #180
plachComment #181
plachComment #182
plachComment #183
yched CreditAttribution: yched commentedMinor: no need to fetch the term storage controller to do that, $status = $term->save() would work equally well ?
Other than that, changes look good. RTBC when green again.
Comment #184
plachYeah, I didn't even look at that code :D
Comment #185
yched CreditAttribution: yched commentedAs a side note, this latest batch of changes since #164 shows that DI is a real problem in terms of API breaks: when a parent class needs a new dependency, all child classes that were overriding create() / __construct() because they had dependencies of their own, suddenly break :-(. Those cases are going to be fun to handle once we release...
Comment #186
plachUpdated suggested commit message.
Comment #187
yched CreditAttribution: yched commentedHem... so I basically tweeted a short version of #185, and it turned into an interesting discussion about DI and base classes, with @msonnabaum & @tim.plunkett advocating that maybe we should move away from constructor injection for classes that are intended to be used as base classes, in favor of service locator-style fetching of dependencies - in other words, exactly what the patch was doing with ContentEntityFormController::entityManager() before I asked for a change in #163 ;-)
(well, with an additional method allowing setter injection for unit testing)
That patch here is green and RTBC now, plus it might be worth having this discussed and agreed upon in a real d.o issue, so I'm leaving this one alone... Worst case, if this gets agreed, the interdiffs in #172 / #175 directly give us the parts that can be reverted later on.
Sorry about that, @plach :-/
Comment #188
Gábor Hojtsy@yched: that sounds like could be done in a followup with more of a recorded discussion :) This issue in itself would enable so much more things, eg. to work on displaying the title of translated nodes, which are currently enterable on forms, stored, etc. but not displayed.
Comment #189
fagoI like the move to the method + alter hook, good improvements!
Here another review:
This should be already handled onChange(), thus does not make sense here?
Minor, but should be public function.
Also, it should not restrict to ContentEntityInterface, should it? I could roll my own entity-class-variant that is translatable, which I would not be able to use then? We have TranslatableInterface so we should check for that, and just type-hint on EntityInterface in that case?
Why is that implemented specifically for comments, isn't that regular content entity form behaviour?
Update: Clarified in #149 9/10.
Not sure how this hunk relates.
Update: Clarified in #149 9/10.
Maybe stuff for another issue, but shouldn't Content translation check for translatbleinterface instead of content entity interface?
That's reasonable behaviour, but not what EntityInterface documents for it. -> Needs update.
Comment #190
fagoComment #191
yched CreditAttribution: yched commented@fago:
3 & 4: See #146, items 9 & 10, answered in #149
Comment #192
fagoAh, thanks - I've updated my comment to show which part of the review are already clarified.
Comment #193
plachThanks for the review!
It does because in the line above we have
$this->get($property_name)->setValue($value, FALSE)
, thusonChange()
is not called.Yes, actually a class implementing
EntityInterface
+TranslatableInterface
should be enough for CT to work. Totally another issue though :)I updated the docs so that they make sense but we should probably get rid of that parameter and just rely on the active language.
Comment #194
fagoTrue, that's a bit weird though and probably stems from an unncessary, previous optimization where onChange() was empty. Howsoever, let's streamline $notify handling of set() methods in a follow-up.
Improvements are good, thanks. Back to RTBC then.
Comment #195
catchNice to see this one green.
Committed/pushed to 8.x, thanks!
Will need a small change notice I think.
Comment #196
catchMissed the suggested commit message (as always), so reverted then re-committed with that. Sorry folks.
Comment #197
plachWho-hoo! Enjoy translated node titles :)
Comment #198
yched CreditAttribution: yched commentedAwesome! @plach++!
Comment #199
plachUpdated the Entity Translation API change notice:
https://drupal.org/node/2040323/revisions/view/2879569/6683781
Also created a new one for the language manager changes:
https://drupal.org/node/2129611
Comment #200
Gábor HojtsyLooks almost good, however the $context and what it may contain is not explained. Also https://drupal.org/node/2129611 needs to link to https://drupal.org/node/2040323 for explanation of that context (where the operation comes from). The operation is not clear from the new change notice. The old one also does not list what those may be (i.e reserved operation names).a
Comment #201
BerdirDoes the updated change notice also be reflected in https://drupal.org/node/2040721, which was created based on the old change notice?
Comment #202
plach@Berdir:
I will update the documentation page as soon as the change notice is ok :)
Comment #203
hass CreditAttribution: hass commentedIs this issue the reason why #2132705: Translation system is completely broken / cannot save translation and maybe #2132707: Fatal error: Call to a member function isFieldTranslatable() on a non-object, too?
Comment #204
Gábor Hojtsy@hass: neither. Eg. the translation save problem was reproduced as early as Prague (if not before).
Comment #205
Gábor HojtsyFor me as soon as #200 is resolved, this is done :) I don't fully understand what context may be myself, otherwise I would do it :/ Sorry.
Comment #206
plachI will take care of that tonight.
Comment #207
plachI updated the new change notice with a more accurate description of the context array. The old one has a link to it.
Comment #208
Wim LeersComment #209
Gábor HojtsyChanges look good to me, thanks!
Comment #210
plachI just updated the documentation page with the latest stuff.
Comment #211
fagoCreated the related #2137309: Typed data does not handle set() and onChange() consistently.
Comment #213
SiliconMind CreditAttribution: SiliconMind commentedNow when this has been resolved, any chance that we could also add language fallback for string translations? #2122175: String translation does not honor language fallback