| Project: | Drupal core |
| Version: | 8.x-dev |
| Component: | translation_entity.module |
| Category: | task |
| Priority: | major |
| Assigned: | Unassigned |
| Status: | closed (fixed) |
| Issue tags: | accessibility, D8MI, feature freeze, language-content, Needs usability review, translatable fields, Usability |
Issue Summary
This is a follow-up in response to #1188388-104: Entity translation UI in core
Problem
The core image field widget allows to enter alt and title along while uploading an image file. In many cases a desirable behavior, when translating content with image fields, is keeping the images and just translate their textual metadata. Currently when creating a translation the original image is preserved, but subsequent changes, such as uploading a new image for a certain language or change the image order in case of a gallery, are not reflected in the translations.
Proposed resolution
Introduce a field column synchronization capability which, once enabled, lets the field define which columns should be "shared" across translations, and which ones are allowed to change per-language. The synchronization takes care of propagating any change in the field items order and in the values themselves to all the available translations. This functionality is provided through a generic API which in core is exploited by the file and image fields.
Remaining tasks
Port the functionality.add steps to testin #68(novice) add screenshot of before the patchin #69(novice) add screenshot of after the patchin #70(novice) review patch. Review Task doc: http://drupal.org/node/1488992done by @fago and @yched(novice) manually test the patch. Manually Test Task doc: http://drupal.org/node/1489010done in #72(novice) Improve patch coding standards and documentation http://drupal.org/node/1487976done in #39decide on what follow-ups to do- final? review to rtbc
User interface changes
A checkbox to enable this functionality is added in the field instance settings. See #110 and #120 (#69 and #70).



API changes
A small Field API addition has been performed to allow declaring which field columns must be synchronized across translations.
Follow-ups
- #1919322: entity_load_unchanged() should be part of the storage controller
- #1920876: Add a tiny bit of state magic in the image.module and hide the Title and Alt groups when the related form items are disabled
- #1920888: Follow-up: Add some JS to make the dependent groups always checked (and readonly) when the master group is checked translatable
- #1909202: Use modals in operations column of language settings config page
- #1909212: warn when sync enabled on field with previous values, they are not sync'd until one is changed
- #1909218: add (all languages) hint to synchronized fields on translation add/edit form
- #1935762: Remove entity_load_unchanged() call from FieldTranslationSynchronizer::synchronizeFields()
Comments
#1
This probably can be fully taken from D7, see http://drupalcode.org/project/entity_translation.git/blob/refs/heads/7.x... - http://drupalcode.org/project/entity_translation.git/blob/refs/heads/7.x...
#2
Yes please! Alt and title are used in (views) slideshows as overlaying text and it makes *a lot* of sense to have these translated in order to display the overlayed text in the proper language. So, providing an easy way to translate these texts is key and should be in core.
Of course there is this case where the images included in the slideshow might already include text. These cases call for a separate image per language, but that is OT here and IMO an edge case that has an easy solution.
#3
This one is tagged with feature freeze but has no patch yet. @plach, did you have something started, or should anyone jump in?
#4
I'm planning to post a patch for this during the upcoming weekend. @bforchhammer will help us with code reviews and coding, if needed.
#5
Here is a first draft. Tests missing.
#6
+++ b/core/modules/translation_entity/translation_entity.module@@ -613,6 +624,139 @@ function translation_entity_form_field_ui_field_edit_form_alter(array &$form, ar
+ // current entity, or we haveno contextual information about the translation
typo
#7
+++ b/core/modules/translation_entity/translation_entity.module@@ -574,12 +574,23 @@ function translation_entity_field_extra_fields() {
+ '#states' => array('visible' => array(':input[name="field[translatable]"]' => array('checked' => TRUE))),
There should also be a validation function in case JavaScript is not available.
+++ b/core/modules/translation_entity/translation_entity.module@@ -613,6 +624,139 @@ function translation_entity_form_field_ui_field_edit_form_alter(array &$form, ar
+/**
+ * Performs field column synchronization.
+ */
+function translation_entity_sync(EntityInterface $entity) {
+++ b/core/modules/translation_entity/translation_entity.module@@ -613,6 +624,139 @@ function translation_entity_form_field_ui_field_edit_form_alter(array &$form, ar
+ // current entity, or we haveno contextual information about the translation
+ // being saved, there is nothing to synchronize.
- add a "then" after the comma?
+++ b/core/modules/translation_entity/translation_entity.module@@ -613,6 +624,139 @@ function translation_entity_form_field_ui_field_edit_form_alter(array &$form, ar
+ $langcode = $original_langcode ?: $source_langcode;
The "?:" looks rather strange ;-)
#8
Performed some code clean-up. Could not test it as head is broken atm.
I suppressed the state on the checbox for now. We can restore it once the migration code has been dumped.
#9
Note: the "Synchronize translations" checkbox would also be good to have in the configuration/operation column on the new settings page that is being added in #1810386: Create workflow to setup multilingual for entity types, bundles and fields.
#10
Yes, once we have the modal :)
#11
Ok, here is test coverage. This should be more or less ready to go, hopefully we will be able to remove those ugly request attributes once we figure out how to deal with entity translations properly in #1810370: Entity Translation API improvements.
#12
Any feedback here?
#13
rerolled
#14
The last submitted patch, et-sync-1807692-13.patch, failed testing.
#15
#13: et-sync-1807692-13.patch queued for re-testing.
#16
Needs tests (or a if tests are in place, a test only patch to show the tests work with the bot).
Needs manual testing.
Needs screenshots.
Updating tags and issue summary.
#17
Updated issue summary.
#18
I don't think this needs an accessibility review or manual testing: it's only exposing a checkbox at UI level. And we have test coverage for the implented functionality.
#19
I updated the issue summary: I don't see a great value in copying/pasting generic portions of handbook pages in every issue summary. I'd say tt's better to keep it slim and focused.
#20
I agree that links to the contributor tasks do not need to go into the remaining tasks of every issue. Issues that have people participating, who are experienced already, know how to make a patch or do a review.
But for issues where we want to make it easy to bring people in, and those people may not know what to do, it will let new people come in. Since this issue seemed to need more feedback (it's about 9 days since #12), it's a way of helping people know what feedback is needed and how to give it.
#21
Sure, but some of the stuff that was in the issue summary previously was not relevant to this issue and IMO could mislead new contributors :)
#22
Yeah, I see (looking at the revisions). :) This is cleaner.
Tests are in the patch. So that is done.
And because this is adding a new functionality, we don't need to provide a tests only patch. Putting test only patches with the fix-and-tests patch in the issue queue for the test bot, is just for when there is a bug the patch is fixing. (right?)
#23
Correct. A failing test-only patch indicates some missing code in core, but new features are expected to be missing :)
#24
Thanks!
Oh, and I *think* that the accessibility tag is just that it is related to accessibility. In this case translating alt and title tags is pretty great for accessibility.
+needs accessibility review
is what is used to get a accessibility review.
I agree we don't need an accessibility review. :)
#25
Ok, cool.
#26
Bringing on D8MI sprint board.
#27
#13: et-sync-1807692-13.patch queued for re-testing.
#28
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -608,6 +608,162 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+function translation_entity_sync(EntityInterface $entity, $sync_langcode, $original_langcode = FALSE) {
Good inline code comments in the function itself, but I'm having a hard time figuring out the big picture of what the function actually does overall. Could we expand on "Performs field column synchronization" in the func's phpdoc, that's a bit cryptic.
Also - I might vey well be wrong, but it seems the behavior will only fire on form submission ? Not on programmatic saves ?
28 days to next Drupal core point release.
#29
I wanted to just jump in here to question why this issue is seeking to synchronize alt & title text in images.
Generally with a screen reader it is best to just stick with the alt text. Screen readers may actually read both the alt text & title to the user and if they are identical it really doesn't help.
As stated here:
http://webaim.org/articles/gonewild/
#30
I think there's a misunderstanding here: this issue is about synchronizing the image accross translations, while leaving the possibility to enter different alt and title per language. They still are totally independent from one another.
#31
Here is a reroll to match the BC decorator stuff and improving PHP docs as per @yched request:
4051f83 Issue #1807692: Fixed BC decorator.7fecbcc Issue #1807692: Improved PHP docs.
I also updated the issue summary.
#32
The BC fix has now its own issue: #1880926: Fatal error when retrieving $entity->original on a BC entity. Go and RTBC it!
@yched:
Any other concern?
#33
This looks great, only remark are 2 small typos
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ * Tests entity field synchrnonization.
typo
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -634,6 +634,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // we language we fall back to the new source value.
typo, 'we'
#34
Thanks, typos fixed.
@yched:
Sorry, I forgot:
Nope, the synchronization happens on
field_attach_presave(). If you are referring to the request attributes stuff, it's a temporary solution. I am planning to find a way to pass along the "active" translation, however for now this works also on programmatic saves if you properly fill-in the request attributes.#35
RTBC anyone?
#36
Rerolled
#37
I'm testing this. but first had to reroll.
git apply --index et-sync-1807692-73.patch
error: patch failed: core/lib/Drupal/Core/Entity/EntityBCDecorator.php:75
error: core/lib/Drupal/Core/Entity/EntityBCDecorator.php: patch does not apply
there were no conflicts.
#38
The reroll looks good, it just removed the hunk that was necessary to make tests pass and that had been moved to #1880926: Fatal error when retrieving $entity->original on a BC entity, which has been committed.
#39
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ * @file
+ * Definition of Drupal\entity\Tests\EntityTranslationSyncTest.
Contains
http://drupal.org/coding-standards/docs#files
\Drupal
http://drupal.org/coding-standards/docs#namespaces
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ /**
+ * Overrides \Drupal\simpletest\WebTestBase::setUp().
+ */
+ function setUp() {
http://drupal.org/node/325974 does need some updating but I think the bit about no php doc on getInfo() and setUp() is right.
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ // Setup languages.
+ $this->langcodes = array('it', 'fr');
+ foreach ($this->langcodes as $langcode) {
+ language_save(new Language(array('langcode' => $langcode)));
+ }
+ array_unshift($this->langcodes, language_default()->langcode);
no problem here. I'm just wondering what the unshift is for.
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ for ($delta = 0; $delta < $this->cardinality - 1; $delta++) {
+ // Simulate a field reordering: items are shifted of one position ahead.
+ // The module ensures we start from the beginning after reaching the
+ // maximum allowed delta.
+ $index = ($delta + 1) % $this->cardinality;
+
+ // Generate the item for the current image file entity and attach it to
+ // the entity.
+ $fid = $this->files[$index]->fid;
+ $item = array(
+ 'fid' => $fid,
+ 'alt' => $this->randomName(),
+ 'title' => $this->randomName(),
+ );
+ $entity->{$this->fieldImageName}[$langcode][$delta] = $item;
no problem here, just a question. it's not reordering the fields in the entity, is it? is this changing the order the translations are done?
It gets the fid from the index (shifted delta) but stores it in the [$langcode][delta]
Or perhaps this is the key to the sync'ing. Without the reordering the syncing would not be needed. And this test is making sure, things are sync'd by the fid?
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ // Perform synchronization: the translation language is used as source,
+ // while the default langauge is used as target.
That sounds strange. The default language was the source language. Source means something different here?
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ // Check that one value has been dropped from the original values.
+ $assert = count($entity->{$this->fieldImageName}[$default_langcode]) == 2;
+ $this->assertTrue($assert, 'One item correctly removed from the synchronized field values.');
was one of the files removed from the original language? ... Or is this checking the translation to see if the original value for the translation that was not done is dropped? (only 2 of the three were translated)
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ // Add back an item for the dropped value and perform synchronization again.
...
+ $values[$langcode][$removed_fid] = array(
+ 'fid' => $removed_fid,
+ 'alt' => $this->randomName(),
+ 'title' => $this->randomName(),
+ );
+ $entity->{$this->fieldImageName}[$langcode] = array_values($values[$langcode]);
+ $entity = $this->saveEntity($entity);
This is just translating the remaining file? (only 2 were translated before)
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncTest.phpundefined@@ -0,0 +1,259 @@
+ foreach ($entity->{$this->fieldImageName}[$default_langcode] as $delta => $item) {
+ // When adding an item its value is copied over all the target languages,
+ // thus in this case the source language needs to be used to check the
+ // values instead of the target one.
the langcode on the third image, that was added in the language (not the default language), has a langcode of the langcode? why didn't the other's that were translated get that langcode overridden too?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * Implements hook_form_FORM_ID_alter().
+ */
+function translation_entity_form_field_ui_field_edit_form_alter(array &$form, array &$form_state, $form_id) {
http://drupal.org/coding-standards/docs#hookimpl
Implements hook_form_FORM_ID_alter() for field_ui_field_edit_form().
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * translations. This functionality is provided by defining a 'translation_sync'
+ * key in the field instance settings, holding an array of column names to be
+ * synchronized. The synchronized column values are shared across translations,
...
+ $columns = isset($field['settings']['translation_sync']) ? $field['settings']['translation_sync'] : array();
does this php check each field setting to see if it should by sync'd? I think I'm confusing that a field can be checked to be sync'd and then there is also a setting to say what part of the field (columns?) gets sync'd. Oh, if it's set, it's set to the columns that should be sync'd! "This functionality is provided by defining a 'translation_sync'
+ * key in the field instance settings, holding an array of column names to be
+ * synchronized."
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+function translation_entity_sync(EntityInterface $entity, $sync_langcode, $original_langcode = FALSE) {
+ $translations = $entity->getTranslationLanguages();
+
+ // If we are creating a new entity, or if we have no translations for the
+ // current entity, or we have no information about the translation being
+ // saved, then there is nothing to synchronize.
+ if (empty($sync_langcode) || $entity->isNew() || (count($translations) < 2 && !$original_langcode)) {
+ return;
+ }
Maybe re-write the comment to match the order in the condition.
$entity->isNew()is clearly if it's a "new entity"."no information about the translation being saved" is that... ?
empty($sync_langcode)and also!$original_langcodewhy is count translations being tested for < 2? I would think that "if we have no translations" would be
count($translations) < 1Oh yeah, it counts the language of the entity too. http://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21TypedData%...
but why the && ?
I guess I would suggest:
$include_default = FALSE;
$translations = $entity->getTranslationLanguages($include_default);
$count_translations = count($translations);
// If we have no information about what to sync to, if we are creating a new
// entity, if we have no translations for the current entity, or we have no
// information about the translation being saved, then there is nothing to
// synchronize.
if (empty($sync_langcode) || $entity->isNew() || ($count_translations) < 1) || ($count_translations >= 1) && !$original_langcode)) {
But how do you know if there is more than one translation, if that is the translation being changed and needs the original_langcode?
Maybe the test for having enough information about the translation needs to come later, comparing something like the next block: the original/unchanged values with the current values?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // If the entity language is being changed there is nothing to synchronize.
Really? seems like there might be.
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // Enable compatibility mode for NG entities.
I admit, I dont know what NG stands for and google was less than helpful. I thought maybe if I searched for EntityNG... http://api.drupal.org/api/drupal/core!lib!Drupal!Core!Entity!EntityNG.php/8
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // If the field is empty there is nothing to synchronize. Synchronization
+ // makes sense only for translatable fields.
+ if (!empty($entity->{$field_name}) && !empty($instance['settings']['translation_sync']) && field_is_translatable($entity_type, $field)) {
comment seemed to imply that empty fields were not translatable... which was just a little odd wording.
I started with
Sync when possible. There is nothing to synchronize when the field is empty, if synchronization is not turned on for the field, or it's not translatable.but that's double negatives. So I suggest:// Sync when the field is not empty, when the synchronization translations// setting is set, and the field is translatable.
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // If a translation is being created, the original values should be used
+ // as the unchanged items. In fact there are no unchanged items to check
+ // against.
+ $langcode = $original_langcode ?: $sync_langcode;
Use the source of the translation as the sync source?
You are checking if a translation is being created by checking if the $original_langcode arg was set? I think that means that the first condition in the function checking if we can even do sync'ing cannot know if we "have enough information about a translation".
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -680,6 +680,169 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // Make sure we can detect any change in the source items.
+ for ($delta = 0; $delta < $total; $delta++) {
What are we looping through here? overall, we are doing each field (that it makes sense to sync). are the items each language's value of the field? or is it taking into account the cardinality of the field, so it's the sync source language field's values?
Sorry, that's as far as I got... I didn't actually test it yet.
There is an interdiff with just the code style changes if that is all that is really needed.
Oh, I think in the tests and the code, maybe with some better variable names, things might be easier to understand. source is tricky as this is kind of dealing with translations, which have sources (or original values) and also with the sync, which has a source. Also, values and items were confusing. What to do if anything probably depends on the target audience of the comments.
#40
The last submitted patch, et-sync-1807692-39.patch, failed testing.
#41
I know the condition is wrong still logically... just want to see.
#42
Found this while trying to manually test:
#1884148: field widget disappears when increase cardinality for image to allow multiple values
Reproduced in clean 8.x install
#43
OK, I understand the goal better now, thanks ! Wow, that's a nasty/complex feature to implement :-/
So, yeah, using drupal_container()->get('request')->attributes to set info and get it back in other parts of the code looks like an unholy hack - globals without globals :-). What are the possible ways of getting rid of this ?
There is some hairy logic in translation_entity_sync() to "recognize" and track items between old an new values and figure out which item got reordered where - the part from "// Make sure we can detect any change in the source items." to the end of the function.
It could really use an "algorithmic overview" comment to explain the logic IMO - even though the "step by step" comments that are present are good, I still have a hard time understanding the overall logic and figuring out if it's valid.
- For example, the case where there are several synced columns sounds like it might be complex ? And that case is not covered by the tests. And ouch, testing that case is not going to be trivial. Maybe splitting that logic to a helper func that works with raw arrays and not $field, $instance and $entities, would help make it more unit testable.
- Also, what if several items have the same value for a 'synced' column (e.g same image) ? how do we tell which is which ? I mean, the image might be the same but the alt text and their translations might differ, isn't that a problem ?
+ // If a synchronized column has changed we need to override the full+ // items array for all languages.
+ elseif ($created) {
Looks like the comment doesn't match the logic : "has changed" vs
if ($created))+ $value = $source_items[$delta][$column];+ $change_map[$column][$value]['new'] = $delta;
Are we 10%% sure that $value is safe to use as an array key ? I don't remember offhand what can and cannot be a valid array key...
Minor : s/The module/The modulo operator/ :)
#44
Good suggestions, I'll try to implement them as soon as I can.
#45
hm, that's a pretty complex feature and problem space. I must say I'm not in favour of the syncing solution, but it seems to be the most feasible one.
Thinking about how it could be done with EntityNG, we could support adding 'translatable' flags on the field value level there while by default they are all translatable, and then implement the synching logic inside of the entity field objects - i.e. if translatable is set and FALSE for a property being changed, do something like
$entity = $this->getRoot();$entity->$field[$delta]->$name = $value;
$field, $delta and $name could be determined via $this->getPropertyPath(). Of course we'd have to apply it to all translation languages.
That way, we'd have full support for 'translatable' in the API while the storage layer would only have to take care of field translatability.
Maybe, it would make sense to do it inside EntityNG?
Update: I'm not sure it really makes sense in EntityNG as the field is not really translated anymore. It's kind of being shared while some values are translated - how would this feature be exposed in the UI?
#46
@fago:
Honestly I thought converting this to NG would be pretty straightforward, once all the entities are converted.
I just spent other 3 hours to improve the documentation and write a brand new unit test. Would you consider letting this feature in as-is and talking about the NG conversion later? This could happen also after feature completion, while I'm pretty sure I won't have time to rewrite the code here from scratch since I have more urgent stuff to work on.
#47
This should address #43. Hopefully this can go in now, otherwise I'm afraid we won't have this functionality in D8 core.
#48
The last submitted patch, et-sync-1807692-47.patch, failed testing.
#49
Tests were passing here because I had ET enabled. The attached patch uses
DrupalUnitTestBaseinstead ofUnitTestBase.#50
Sure, it's not my intent to block this in any way - I was just trying to figure out the best way to implement this quite complex feature.
So am I right, that this can be enabled in addition to the general translation option, i.e. you have the choice to do both?
Then, while thinking more about it, I don't think we should have the sync-ing feature in the storage layer as we need to be able to switch between full translation and sync-ing anyway - so it can't just be magically the translation of the field. So the approach taken in the patch makes totally sense for me :-)
#51
Yes, you can choose to have you fields vary independently by language or have part of their values synchronized across languages. There's a new checkbox in the field settings.
Glad to hear that :)
#52
So, I guess we are just missing @yched's ok here, right?
#53
Any other concern/suggestion?
#54
#49: et-sync-1807692-49.patch queued for re-testing.
#55
Sorry, very little drupal time lately :-/
Will try to make some time for this, but if it's not tomorrow it won't be before another week.
#56
#49: et-sync-1807692-49.patch queued for re-testing.
#57
Rerolled
#58
Sorry, wrong one.
#59
The last submitted patch, et-sync-1807692-59.patch, failed testing.
#60
#58: et-sync-1807692-59.patch queued for re-testing.
#61
#58: et-sync-1807692-59.patch queued for re-testing.
#62
@yched:
Even a "yes" or "no" would be welcome :)
#63
Rerolled after #1807776: Support both simple and editorial workflows for translating entities went in.
#64
The last submitted patch, et-sync-1807692-63.patch, failed testing.
#65
#63: et-sync-1807692-63.patch queued for re-testing.
#66
Sorry for the delay, the last fortnight has been pretty dense in my "non-drupal" mode :-/. Slowly getting back at the core queue.
Minor / nitpicky stuff first :
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,176 @@
+ protected $unchangedFieldValues;
+
+
+ public static $modules = array('language', 'translation_entity');
Double empty line (& after getInfo() too)
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,176 @@
+ $this->cardinality = mt_rand(2, 5);
Can we avoid random values, use, like, 4 instead ?
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,176 @@
+ foreach ($this->langcodes as $langcode) {
+ for ($delta = 0; $delta < $this->cardinality; $delta++) {
+ foreach ($this->columns as $column) {
+ $sync = in_array($column, $this->synchronized) && $langcode != $this->langcodes[0];
+ $value = $sync ? $this->unchangedFieldValues[$this->langcodes[0]][$delta][$column] : $langcode . '-' . $delta . '-' . $column; //$this->randomName();
+ $this->unchangedFieldValues[$langcode][$delta][$column] = $value;
+ }
+ }
+ }
A bit cryptic.
"Set up an intitial set of values in the correct state, i.e. with "synchrnozed" values being equal." ?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -766,6 +766,209 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * The unchanged items ti be used to detect changes.
s/ti/to
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,176 @@
+ $sync_langcode = $this->langcodes[mt_rand(0, count($this->langcodes) - 1)];
Likewise, pick one language instead of random ?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -766,6 +766,209 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+function translation_entity_sync_field(&$field_values, $unchanged_items, $sync_langcode, $translations, $columns) {
Yay @ the new unit testable helper func, & extensive comments. I'd just suggest prefixing the func name with an underscore so that it's not mistaken for an API entry point.
-3 days to next Drupal core point release.
#67
More substantial stuff :
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -766,6 +766,209 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * available languages. For this to properly work the column values must be
+ * integer or strings. The synchronized column values are assumed to be unique
"The column values must be integer or strings" - because they're used as array keys, IIRC ?
What about NULL ?
What about decimal / floats ?
What about LONGTEXT ?
Also, where / how do we document that the 'synchronize' feature only works on integer / text values ?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -766,6 +766,209 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * integer or strings. The synchronized column values are assumed to be unique
+ * among the various items, this is necessary to detect and replay changes in
+ * the order. They are also assumed to change simultanously, so that a change in
"The synchronized column values are assumed to be unique among the various items".
OK, but in lots of actual cases they won't be, what happens then ?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -766,6 +766,209 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * the order. They are also assumed to change simultanously, so that a change in
+ * any column indicates that all of them have changed.
Yeah, so that's the "how does this work when there are several synced columns ?" part.
Not sure what are the practical implications of this though ?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -766,6 +766,209 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ // (...) A changed value will be interpreted as a new
+ // value, in fact it did not exist before.
This means that any change in the value of any of the synced columns is seen as "some delta disappeared, that gets removed in all other languages, and a new one appeared, that gets copied over to all languages" ?
Meaning, if I change the image (value for fid column is changed) but not the alt / title texts, then the alt / title texts in other languages (that were possibly translated) will be overwritten with the ones of the language I made the change in ?
Is that really what we want ?
All in all, major kudos for all the work and dedication that were put in this.
I have to admit I'm still scared at the complexity of the feature, and not fully convinced that a) it can be achieved 100% bullet-proof rather than "best effort", and b) that this "best effort" will be "good enough" rather than "unpredictable in practice".
It's not directly in my queue and I won't be in charge of handling followups or bugs ;-), so I won't be blocking this if everyone wants it, though.
#68
steps to test (a simple test)
#69
screen shots before the patch
A.

B.

#70
with patch
1. create article, with translation enabled, but not sync'd
1a. source with image and alt text

1b. translation (using different image and alt text)

2. sync setting in article manage fields edit image field tab
3. create another new article, with sync setting checked on article image
4. create translation, with sync setting on image
(very similar to what form looks like without the patch) if change the image here, though, it changes also for the source. [edit again: corrected.]
[edit: added the missing files here]
#71
opps. missed some files.
updated previous comment to include them.
#72
manually tested. works!
I have some suggestions.
@plach let me know if you want to do them in follow-up (I can open the issues), here, or you think not really a problem or whatever.
from:
Synchronize translationsCheck this option if you just wish to translate the textual elements of this field. All the other aspects, such as the order of items or non-textual values, will be the same for all languages.
to:
Synchronize translationsSynchronize non-textual values, such as the order of items, across all languages. Textual elements of this field will always be translatable.
Would be too specific.. but more clear to say:
Synchronize translationsSynchronize non-textual values, such as the order of items, image, or file, across all languages. Textual elements, such as alt text, of this field will always be translatable.
(novice tasks done for now, will update issue summary, removing tag)
#73
@yched:
Thanks for the review(s)! I'll try to address them ASAP. A couple of answers meanwhile:
Yes, this is a limitation we need to document (and probably better enforce). Our main use case are fids so this perfectly works for now.
One possible solution is that we dected this situation and we bail out without syncing anything. I'd like to explore the possibility to support multiple values, but I'm not sure it's possible without complicating the algorithm too much.
Well, we could refactor the algorithm to combine the column values so that we have a unique key (a primary key). This would allow us to support any combination of the synced columns, provided that at least one value differs from the others.
Yes, I am afraid we cannot get around this: a change in a synced column cannot be intepreted in any other way but "a new value is here". We might not propagate it to all the translations or leave the unsynced columns empty, but in the case of alt/titles a language fallback looks like the most reasonable behavior.
I agree we probably cannot achieve a 100% bullet-proof behavior. What I think we can get is something that works in the 80% of use cases and does not break things in the remaining 20%. The code here has been almost straightforwardly ported from contrib ET (D7), which has around 6000 installations, and no bug about it has been reported so far. Hence I'm pretty confident we can have something that can actually be useful.
#74
I'm not sure, @plach might be in the middle of making a new patch to address some stuff (or maybe just writing explanation).
This help text change could wait and be a follow-up, but here it is, in case it's helpful.
I looked and didn't see any tests that would need be changed as a result of the title change of the checkbox (guess testbot will tell me for sure).
#75
Here is a new patch, I think it's considerably more solid than the previous one: it should now support multiple elements having the same values for the synchronized columns and the ability to detect changes when just one synced column changes.
This should also take care of #66.
As a bonus all the synchronization logic has been moved to an autoloadable class, to reduce memory footprint. Not sure we need to register it into the DIC: would it make sense for the sync logic to be swappable?
Anyway, the attached interdiff shows only the changes in the sync logic and the related tests. The actual one wouldn't be very useful.
@YesCT:
The attached patch incorporates the proposed textual changes, but it collapses them into the checkbox description: the label felt too long in the original proposal.
About #72:
@1: Yes, this should be solved by making translatability an instance setting.
@2: Also @bforchammer propoed something similar above: I think it would be nice if we could have all these settings in a modal dialog, as originally proposed by Bojhan.
@3: I think a warning message after enabling the setting should be enough. Do you wish to provide a text or a patch?
@4: Looks a bit verbose to me: maybe just "Synchronized"? I'd prefer to address this in a follow-up to be sure to have a UX feedback and not hold this patch on it.
#76
sounds good. I think the @2-4 can be follow-ups for sure... and that will make it easier for people (uh hem, me) to try and address some of them.
#77
follow up
for 2 #1909202: Use modals in operations column of language settings config page
for 3 #1909212: warn when sync enabled on field with previous values, they are not sync'd until one is changed
for 4 #1909218: add (all languages) hint to synchronized fields on translation add/edit form
#78
It would really important to get this RTBC before feature completion.
#79
Here is a reroll.
#80
The last submitted patch, et-sync-1807692-79.patch, failed testing.
#81
#79: et-sync-1807692-79.patch queued for re-testing.
#82
My remarks on the latest code. Mostly nitpicks / code organisation remarks.
+ * must be integer or strings.Nitpick : both singular or both plural ?
core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.php+ // Continuous field values: all of values are equal.
Minor: missing word or extra 'of'.
class FieldTranslationSynchronizer:+ * @param $sync_langcode
+ * The language of the translation whose values should be used as source for
+ * synchronization.
+ * @param $original_langcode
+ * (optional) If a new translation is being created, this should be the
+ * language code of the original values. Defaults to FALSE.
+ */
+ public function synchronizeEntity(EntityInterface $entity, $sync_langcode, $original_langcode = FALSE) {
Missing type hints for the langcode args.
Also, "$original_langcode is either a string or FALSE" looks sloppy, that's precisely why NULL exists.
But more generally, the method is always called with a value for $original_langcode, so why is it optional ?
[Edit : oh ok, FALSE is because the value comes from EntityTranslation::getSourceLangcode(), which currently returns "string or FALSE".
I'd be inclined to change that to NULL upstream, but then I guess that's not for this patch.]
FieldTranslationSynchronizer:synchronizeField()+ $old_delta = -1;
+ $new_delta = -1;
...
+ $created = $created && $old_delta < 0;
+ $removed = $removed && $new_delta < 0;
Looks a bit hackish, relying on the fact that "real" deltas are positive, and then interpret "< 0" for "not found".
Initializing $old_delta & $new_delta to NULL and checking isset($old_delta) / isset($new_delta) should work.
class FieldTranslationSynchronizer:+ protected function itemIdentifier(array $items, $delta, array $columns) {
Could it be named itemHash() instead ?
What this does is compute a hash for (the values of the synced cols of) an item, not an id. What makes the patch complex is precisely that field values don't have ids that we could use to track them, but this does not add ids, just works around the absence thereof :-).
FieldTranslationSynchronizer::itemIdentifier() :+ $values[] = $this->validateValue($items[$delta][$column]);
...
+ return !empty($values) ? implode('.', $values) : FALSE;
Minor : implode($empty_array) == '', so the last line could be just
return implode('.', $values);?+ I'd tend to think validateValue() as a separate method adds an unnecessary mental indirection, could be inlined within itemIdentifier() now.
+ I guess we could use a real hash, like md5(serialize(array_intersect($item, $synced_cols)) and therefore support more than number & strings, but maybe that's not worth the runtime overhead ?
FieldTranslationSynchronizer::validateValue()+ if (!is_numeric($value) && !is_string($value)) {
+ throw new \InvalidArgumentException('A synchronized value can only be a number or a string.');
is_numeric(NULL) == FALSE, is_string(NULL) == FALSE
That's annoying because a numeric db column could have a value of NULL. (for image fields that would mean the field is empty and is not stored, but for multi-column fields that could happen). It seems allowing NULL would be doable ?
implode('.', array(1, NULL, 2)) == '1..2', works.
+ // Set contextual information that can be reused during the storage phase.+ $attributes = drupal_container()->get('request')->attributes;
+ $attributes->set('working_langcode', $form_langcode);
+ $attributes->set('source_langcode', $source_langcode);
This is still an ugly hack :-(. Not sure what are the plans to get rid of that ?
@todo + followup issue ?
FieldTranslationSynchronizer :+ protected function loadUnchanged($entity_type, $id) {
+ $controller = $this->entityManager->getStorageController($entity_type);
+ $controller->resetCache(array($id));
+ $result = $controller->load(array($id));
+ return reset($result);
+ }
Interesting.
In the previous patch, this was translation_entity_sync() function calling entity_load_uncached().
So now that the code moved to a class, we avoid calling functional stuff like entity_load_uncached() within a method and go for the OO equivalent ?
Does that mean that entity_load() too is considered bad practice in OO code, and that any class willing to load an entity needs to get an injected EntityManager and do
$this->entityManager->getStorageController($entity_type)->load(array($id));instead ? Ew :-/Definitely not a debate for this thread, but IMO that would deserve a spin off discussion to establish the practice.
At the very worst, this loadUnchanged() method should be provided by EntityStorageController, not recoded in FieldTranslationSynchronizer and in each other class that might have a use for entity_load_uncached().
For this patch, I'd be inclined to keep FieldTranslationSynchronizer::synchronizeEntity() calling entity_load_uncached() for now...
Regarding the behavior itself :
FieldTranslationSynchronizer::synchronizeField()+ $field_values[$langcode][$delta] = $source_items[$delta];
...
+ $field_values[$langcode][$new_delta] = $item;
As I mentioned earlier, I still don't get why we overwrite the whole item in the target languages, instead of only the values of the synced columns, which would be easily doable ?
The current code does : if I edit the node in german and change an image, the english and french alt & title texts that I previously entered will be overwritten with the german values, and I need to edit the other languages to switch them back.
I would think we want : if there are corresponding values in english and french, only synchronize the columns that are supposed to be synchronized and keep the unsynchronized ones untouched ?
In other words, the patch introduces the notion of "synchronized columns", but those are in fact only triggers for the sync behavior, and all columns are in fact synchronized.
But @plach says the current behavior comes straight from D7 ET contrib, where it received no major complaints, so I'm fine with committing the current and possibly discuss / revisit in a followup.
#83
yched, thanks for the thorough review.
Once we hear back from plach, and follow-ups are opened, then this sound ok to proceed with.
#84
Thanks for the review, I think I agree with almost everything. I'll be a bit busy this weekend. Hope to be able to squeeze some time out and get this done...
#85
This should address more or less everything in #83. Could not launch tests so I hope this is still good.
Some answers:
I originally thought about real hashing too, but I dismissed this solution because of performance concerns. After rethinking about it I went for a hybrid approach: a hash is computed only for non-string and non-int values.
In #1810370: Entity Translation API improvements we agreed to try and introduce an
EntityLanguageDecoratorthat should allow us to remove this hack. Since this would be the only example in core right now, I think it would be fine to fix it there. Added a @todo about this.I think we already implicitly dediced to go this way when we introduced the DIC: no point in using procedural functions which are in the global scope and make everything harder to unit-test. That said nothing would prevent us from somehow improving the entity manager DX. Agreed on having this dicussion elsewhere. Reverted the change for now, since
loadUnchanged()totally belongs to the storage controller.Well, the point is that if you change a synchronized value, supposedly you are invalidating also the other ones: think of a node with an image of an apple (english alt = "An apple", french alt = "Une pomme"), then you replace it with the photo of a kitten (I cannot image why you would do this, but let's suppose for a moment :) and enter an english alt the says "A cute kitten". For me it makes far more sense to have a french translation where you have an untranslated english alt rather than having the photo of kitten with a alt saying "Une pomme" :)
#86
No idea of wtf just happened :(
#87
Yup, great idea !
Although, my bad, md5() is actually forbidden in core now even for non security-related uses, hash('sha256') is the standard. See http://drupal.org/node/845876 and the recent #1884826: Regression - replace md5 in Block module calls with sha2 hashes.
elseif ($old_delta >= 0 && $new_delta >= 0) {Shouldn't that be changed to
isset($old_delta) && isset($new_delta)too ?#88
Indeed, fixed. Thanks!
#89
Thanks !
RTBC if green.
#90
The last submitted patch, et-sync-1807692-88.patch, failed testing.
#91
This should be more like it :)
#92
:-)
#93
OK I don't have time for a full review but I don't see any discussion here about converting these to fields on the file entity instead of serialized data in the field storage. The problem here is really with the image field for me, and introducing this to work around that doesn't feel good.
#94
@catch:
This has a far broader scope than just image fields: it will work with any field having item values needing to be synchronized across entity translations. Image fields are just a prominent example. This API would make sense even if we had no use case in core. Please, reconsider the issue status.
I don't think we can realistically make file entities fieldable in core one week before feature freeze, since this would require a dedicated UI, unless there already has been significant work on this that I totally missed. Which could be very well possible, btw :)
#95
Retitling to be more clear about the goal of this issue.
#96
#97
Tagging for D8MI.
@catch:
This is the only issue I was able to find where the discussion you are missing might have happened: #376287-16: Make files fieldable.
I have no idea how we could get this done in the scope of the multilingual initiative. My approach with this was: if the Media initiative succeeds and we have fieldable files in core, wonderful, otherwise we need an alternative solution. Hence I came up with the current proposal which meanwhile matured in something that can totally live without image fields exploiting it. AAMOF if you review the patch you'll discover that only 2 lines of it, leaving alone a dedicated test, is dealing specifically with images/files.
To sum up I think this is a cool feature to exploit in contrib and leaving out from core will mostly kill it since I probably won't maintain a D8 version of contrib ET just for it, although people upgrading from D7 might expect to find it still there.
#98
Talked to plach about this in irc. I asked for use-case other than image fields (which should really just be a reference). Plach mentioned a map field with a label - pointing out you probably wouldn't want that to be a reference to an entity with a text field.
Moving back to CNR - I still haven't reviewed the code here, only worried about the use cases at this point.
#99
Actually we have at least another use case in core: the link field, which has an optional Title textual column.
#100
Also, the displayed label for a referenced image field might vary by usage? Having it in the file entity would not account for that.
#101
Looks like plenty of other use cases to back this up, back to RTBC then.
#102
The UI elements introduced in this patch don't cut it for me. 'Synchronize translations' is just too cryptic of a setting for the end user. It's misleading actually as its suggests that the strings will be synchronized. In the case of the Image field, as far of the site builder is concerned, it would be better to write something like
[ ] Use different image for all translations. If we do this right, we may be able to get rid of the help text too. Right now, it seems like the current patch exposes implementation details in the UI.#103
@Dries:
I must say I completely agree :)
I spent some time thinking about an alternative when writing the first patches but nothing really user-friendly came to my mind and then forgot about it. Now I spent some more time thinking about this: would something like the screenshot below work better?
#104
I think that's an improvement, but how about this idea?
Instead of making the field type module decide which columns are 'sync' worthy, and implicitly assuming that everything else is "textual", and then needing to add an extra form element to the field settings form, we instead:
- Let field modules specify "column groups". For example, image_field_info() could add the following:
'column_groups' => array(array('title' => 'File', 'columns' => array('fid', 'width', 'height')),
array('title' => 'Alternative text', 'columns' => array('alt')),
array('title' => 'Title', 'columns' => array('title')),
),
- Have translation_entity.module enhance the admin/config/regional/content-language form (where the administrator decides which fields to make translatable), such that when a field that has 'column_groups' is set to translatable, that a subrow appears for each column group, initially starting off checked, but the admin could uncheck as desired. Basically, the same pattern as how a bundle is expanded into its fields, just applied again to a field expanding to its column groups.
@plach: what do you think?
#105
Yeah, this would have been great, but it didn't happen. The challenge with making file entities fieldable is that it's only useful if you define bundles. You don't necessarily want an "alt" field on all files, just on the image bundle. But implementing administrator-customizable bundles that properly relate to mime types turns out to be non trivial. #1260050: Provide administrative UI for adding/removing file types is an issue that's been open over a year in the File entity queue. Looks like it's gonna need more time in contrib, but would be awesome to get into D9.
Also a good point, and there's been discussion in various places (sorry, don't have links handy) about needing a "File usage" entity in addition to file entities to support that cleanly.
That's just background for folks interested in helping out with fieldable files. I also agree with #99 that the Link field provides another use case and several contrib field types do as well.
#106
Had a short call with plach. This looks like a good idea, I agree with Dries that the word sync is misleading. I really like effulgentsia his proposal, I think thats exactly what people will expect!
The ability to toggle translatability on properties, this should come with some sensible defaults and should be displayed on both the i18n setup page as one the field settings of an individual field. Otherwise we might end up to require way too much setup work for fields with properties.
#107
Ok, I will work on this ASAP. I'd just want to point out that the items order when we have a multiple field is not covered by the current proposal. Bojhan suggested to make it explicit by having an "Order" checkbox, but since this would force us to totally revisit the sync algorithm and that it would be more stuff for the user to configure, I'd suggest that that we just make it implicit and sync also the order when there is at least one synchronized column.
#108
That makes complete sense to me. I can't think of a use case where it makes sense to vary item order by language, but require syncing of some part of each item. (says a person who's never operated a multilingual site)
#109
FYI, just to avoid confusion I just figured that would be a way to handle such a use case - if the use case does not exist we should definitely not support it.
@effulgentsia your last remark, is quite funny. I challenge you to just try it sometime :)
#110
Here is a patch implementing #104 + #107.
As suggested by @Bojhan in the content language settings we have a slightly different behavior wrt fields: instead of making all columns translatable, when making a field translatable smart defaults are applied. In the screenshot only the textual columns are made translatable by default (thus triggering column synchronization). When making a field untranslatable column settings are reverted to their defaults. The same settings with their defaults are available on the instance settings page when the field is translatable.
Since the behavior implemented now might not satisfy every possible use case, above all because of #107, I decided to make the field sync logic swappable and registered the implementing class into the DIC.
Moreover while fixing tests I noticed that the image field sync test was not extending the ET base test, hence I updated it accordingly.
The end result is that the interdiff is pretty useless :)
#111
I didn't foresee to work on this so much, I'd really like to see this going in now :)
#112
this looks great! very clear!
#113
postponed #1909218: add (all languages) hint to synchronized fields on translation add/edit form on this. it will needs to be looked at again after this lands.
#114
#115
in general this code is very well documented, and is even beautiful in some places.
some of the following are nits, and I'm happy to do them now, or after commit. documenting them here so I dont forget them.
I dont think any of these are blocking.
I did not (yet) test it manually. When I do, I'll check the hidden labels on the fields, but the code looked to add the necessary accessibility things. (or maybe someone will beat me to it)
-------------
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.phpundefined@@ -0,0 +1,193 @@
+ // By picking the maximum size between updated and unchanged items, we make
+ // sure to process also removed items.
+ $total = max(array(count($source_items), count($unchanged_items)));
...
+ for ($delta = 0; $delta < $total; $delta++) {
+ foreach (array('old' => $unchanged_items, 'new' => $source_items) as $key => $items) {
+ if ($item_id = $this->itemHash($items, $delta, $columns)) {
+ $change_map[$item_id][$key][] = $delta;
should this be max, or source + unchanged?
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.phpundefined@@ -0,0 +1,193 @@
+ // Reset field values so that no spurious one is stored. Source values must
+ // be preserved in any case.
+ $field_values = array($sync_langcode => $source_items);
spurious? as in bogus?
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.phpundefined@@ -0,0 +1,193 @@
+ // By inspecting the map we built before we can tell whether a value
+ // has been created or removed. A changed value will be interpreted as
+ // a new value, in fact it did not exist before.
+ $created = TRUE;
+ $removed = TRUE;
+ $old_delta = NULL;
+ $new_delta = NULL;
+
+ if ($item_id = $this->itemHash($source_items, $delta, $columns)) {
+ if (!empty($change_map[$item_id]['old'])) {
+ $old_delta = array_shift($change_map[$item_id]['old']);
+ }
+ if (!empty($change_map[$item_id]['new'])) {
+ $new_delta = array_shift($change_map[$item_id]['new']);
+ }
+ $created = $created && !isset($old_delta);
+ $removed = $removed && !isset($new_delta);
...
+ // Otherwise the current item might have been reordered.
+ elseif (isset($old_delta) && isset($new_delta)) {
why && with created or removed, they are hard coded to be TRUE.
should there be an else in here? can something be both created (not old) and removed (not new)?
Oh... it can be neither...
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.phpundefined@@ -0,0 +1,193 @@
+ * @param integer $delta
+ * The delta identifying the item to be processed.
...
+ protected function itemHash(array $items, $delta, array $columns) {
http://drupal.org/node/1354#types says to use "int"
add int to function declaration for $delta.
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncImageTest.phpundefined@@ -0,0 +1,229 @@
+ function setUp() {
setUp should be public
http://drupal.org/node/325974
(in the past this was not generally done though)
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncImageTest.phpundefined@@ -0,0 +1,229 @@
+ // Simulate a field reordering: items are shifted of one position ahead.
shifted one position ahead (remove of).
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,262 @@
+ protected function setUp() {
http://drupal.org/node/325974 says setUp should be public.
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,262 @@
+ // Add a new item to the source items and check that its added to all the
+ // translations.
...
+ // Remove an item from the source items and check that its removed from all
+ // the translations.
its -> it's (or: it is)
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.phpundefined@@ -0,0 +1,262 @@
+ /**
+ * Tests that items holding the same values are correctly synchronized.
+ */
+ public function testMultipleSyncedValues() {
what does this test, that the other tests do not?
+++ b/core/modules/translation_entity/translation_entity.admin.incundefined
@@ -6,11 +6,44 @@
+ * (proxied) Implements hook_form_FORM_ID_alter().
@@ -57,6 +94,112 @@ function _translation_entity_form_language_content_settings_form_alter(array &$f
+ * (proxied) Implements hook_preprocess_HOOK();
what does proxied indicate?
+++ b/core/modules/translation_entity/translation_entity.moduleundefined
@@ -802,7 +802,7 @@ function translation_entity_field_extra_fields() {
- * Implements hook_form_FORM_ID_alter().
+ * Implements hook_form_FORM_ID_alter() for field_ui_field_settings_form().
*/
function translation_entity_form_field_ui_field_settings_form_alter(array &$form, array &$form_state, $form_id) {
@@ -837,6 +837,41 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * Implements hook_form_FORM_ID_alter().
+ */
+function translation_entity_form_field_ui_field_edit_form_alter(array &$form, array &$form_state, $form_id) {
add a similar for ... field_ui_field_edit_form ?
#116
I haven't reviewed the code in-depth, or manually tested the syncing functionality, but from the little I've seen, I think this is really good. I hope @YesCT or someone else more familiar with this issue than I am can RTBC it in the next 24 hours or so. Otherwise, I'll try to detail review it then.
#117
Great review, thanks!
Max is enough, since we just need to ensure we reach the end of both source and unchanged items arrays. Anyway this is not needed when populating the change map, so moved it down and cleaned-up the related code.
Yep :)
There was a lot of cruft here left out from previous iterations. Cleaned-up.
Nope, type-hinting works only with classes.
It tests the sync behavior when multiple items have the same values to ensure they are retained and moved around if needed.
That it is the actual hook implementation. The proxy one is in the .module file. This helps reducing the memory footprint.
That one is not touched by this patch, so it would be out of scope.
#118
Opened #1919322: entity_load_unchanged() should be part of the storage controller to address #82-#85.
#119
wow! quick turn around. glad some of that was helpful. I'm going to do manual testing now.
@plach
I thought that doc block was changed lines in the patch...
+++ b/core/modules/translation_entity/translation_entity.moduleundefined@@ -837,6 +837,41 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
+ * Implements hook_form_FORM_ID_alter().
+ */
+function translation_entity_form_field_ui_field_edit_form_alter(array &$form, array &$form_state, $form_id) {
+ if ($form['#field']['translatable']) {
+ module_load_include('inc', 'translation_entity', 'translation_entity.admin');
+ $element = translation_entity_field_sync_widget($form['#field'], $form['#instance']);
+ if ($element) {
+ $form['instance']['settings']['translation_sync'] = $element;
+ }
+ }
#120
1. labels make sense, match the checkbox id, and have invisible text to help identify it's content to compensate for the equivalent visual indent.
2. unchecking all parts of image, on save the image is unchecked too. that makes sense.
3. I was confused because even though I tried unchecking and check translatable for title on image, it did not show in edit or add translation form. Then I remembered I might have to configure image itself to make title field show. yep.
suggestion, i) expect people will figure this out. ii) dont show fields are translatable if they are not enabled.
is this image specific? if so, go with i)
4. things make sense when file, alt, title are all translatable.
they make sense when file is not but alt and title are translatable.
stuff gets weird when the file is translatable but the alt and title are not. In practice this does not make sense anyway. During testing, the result is that changing an image means deleting it, and the alt and title are deleted when that happens also, so their values are not sync'd. since that behavior is image specific. it's a works as designed or wont fix too. since the code is generic, I think its behavior is ok.
that's as far as I got with manual testing and it works. rtbc from me.
#121
Yes, this is image-specific: we could have a novice follow-up to add a tiny bit of state magic in the image.module and hide the Title and Alt groups when the related form items are disabled.
Yep, this is why I originally went for a less flexible approach: less stuff to figure out for the user. Also here we might have a follow-up and add some JS to make the dependent groups always checked (and readonly) when the master group is checked. E.g. when I check "File", "Alt" and "Title" are automatically checked and cannot be unchecked.
#122
I agree those follow ups make sense. I can help open follow up issues.
Should we open follow ups for some of the @todos in the code?
#123
Yep, probably. Already created #1919322: entity_load_unchanged() should be part of the storage controller for:
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.php@@ -0,0 +1,191 @@
+ // @todo Use the entity storage controller directly to avoid accessing the
#124
opened
as mentioned in #121
I'll add to issue summary
#125
The old issue summary linked to #89/90 and I was very scared because that UI needed a ton of work. But very stoked to see the screenshots at #110! That's exactly what I expected.
I did not test this patch myself, but it's clear that YesCT did a bang-up job of this already, and both she and yched have given it a thorough code review. I also think that being able to translate fields, but not being able to translate attributes of fields, is a WTF so am going to re-classify this as a major task.
Committed and pushed to 8.x. Thanks!
#126
Wonderful, thanks!
#127
Removing from sprint, thanks everyone for the help!
#128
Automatically closed -- issue fixed for 2 weeks with no activity.
#129
@yched:
Opened #1935762: Remove entity_load_unchanged() call from FieldTranslationSynchronizer::synchronizeFields() so we can dicuss the DX implications of injecting the entity manager.