Locale and Translation need a deep refactoring in order to take advantage of #367595: Translatable fields, moreover we need to decide if node translation and field translation should coexist and if not find an acceptable upgrade path for D6 translation sets.

CommentFileSizeAuthor
#66 field_translation-539110-66.patch76.4 KBplach
#61 field_translation-539110-60.patch64.01 KBplach
#61 tf-permissions.png38.44 KBplach
#61 tf-translate-overview.png29.9 KBplach
#61 tf-translate-en-it.png28.75 KBplach
#61 tf-translate-fr-it.png26.07 KBplach
#61 tf-edit-outdate.png38.08 KBplach
#61 tf-view-it-fallback.png39.1 KBplach
#61 tf-view-it-unavailable.png38.37 KBplach
#61 tf-translate-overview-url-alias.png34.21 KBplach
#61 tf-config-article-auth-info.png37.84 KBplach
#61 tf-view-fr-show.png43.92 KBplach
#61 tf-view-fr-replace.png42.61 KBplach
#61 tf-config-article-comment.png38.44 KBplach
#61 tf-view-en.png43.65 KBplach
#61 tf-config-entity-types.png29.87 KBplach
#61 tf-translate-comment.png28.71 KBplach
#53 field_translation-539110-53.patch54.01 KBplach
#57 field_translation-539110-57.patch63.84 KBplach
#59 field_translation-539110-59.patch63.84 KBplach
#45 field_translate_labels-549698-45.patch50.29 KBplach
#44 field_translation-539110-44.patch44.9 KBplach
#43 field_translation-539110-43.patch43.16 KBplach
#42 field_translation-539110-42.patch43.16 KBplach
#40 field_translation-539110-40-entity-add-translation.png37.43 KBplach
#40 field_translation-539110-40-node-add-translation.png28.49 KBplach
#40 field_translation-539110-40-entity-translations.png26.57 KBplach
#40 field_translation-539110-40-node-translations.png45.35 KBplach
#40 field_translation-539110-40-node.png47.73 KBplach
#40 field_translation-539110-40-node-local-tasks.png40.89 KBplach
#40 field_translation-539110-40-node-type.png44.16 KBplach
#24 field_translation-539110-24.patch28.81 KBplach
#19 field_translation-539110-19.patch31.55 KBplach
#19 proof-of-concept-189-all.patch.txt202.91 KBplach
#16 field_translation-539110-15.patch30.73 KBplach
#7 tf-config-fr.png37.87 KBplach
#7 tf-config-languages.png42.91 KBplach
#7 tf-edit-node.png35.39 KBplach
#7 tf-edit-translation.png71.15 KBplach
#7 tf-translations-overview.png42.39 KBplach
#7 tf-view-en-en.png39.59 KBplach
#7 tf-view-it-en.png39.7 KBplach
#7 tf-view-it-fr.png39.45 KBplach
#7 tf-view-local-task.png47.54 KBplach
#3 field_translation-539110-3.patch24 KBZoeN
#2 field_translation-539110-2.patch1.28 KBZoeN
#1 field_translation-539110-1.patch.txt24 KBplach
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

plach’s picture

Status: Active » Needs review
FileSize
24 KB
ZoeN’s picture

Re-rolled patch, so that the testing bot will notice it.

ZoeN’s picture

Hmm, that didn't work. Re-re-rolled.

Status: Needs review » Needs work

The last submitted patch failed testing.

plach’s picture

Status: Needs work » Needs review

The first patch depends on #367595: Translatable fields, it won't pass the tests before that one is committed.
See #367595-183: Translatable fields for a patch including all the needed code.

catch’s picture

subscribing.

plach’s picture

Here are some screenshots of the patch applied along with its dependencies: tf-view-* images show the separation between UI language and content language, tf-config-* show the language configuration, tf-translation-overview, tf-edit-node and tf-edit-translation show how the transaltion workflow is handled.

nedjo’s picture

Excellent work. This patch illustrates the power of field-based translation and the relative ease of implementing an admin UI for the backend infrastructure achieved in #367595: Translatable fields.

While I briefly read over the code, this patch review is based on a demo site and focuses on the translation design and interface.

Rather than imagining a translation UI from scratch, the patch takes the approach of emulating what we already have in the translation module. I think this is the right approach. No doubt through a lengthy process we could evaluate and refine our translation interface, possibly completely redesigning it, but here isn't the place to do so. This patch wisely sticks closely to what we have.

Patch summary

The patch implements two new modules and a new locale include file.

The first module, entity_translation, provides a set of API functions for handling records related to the existence of translations of entities. E.g., when a node is translated, a record is created noting the entity type (node), the language of the translation, etc.

The second module is field_translation, parallel to translation module. Like translation, field_translation provides a tab on the node page alongside "Edit" for managing translations of the node. Here the tab is "Field translation" and, as with translation module, is shown when a node has a designated language. The field translation page for a node shows enabled content languages and links for creating new translations or editing existing ones.

The language of the original node is designated the "source" translation. Clicking on a link provides a form for editing a translation.

Comments, questions, and issues

  1. Relation to translation.module. Implementing this in a new module separates the two models of translation (field-based and node-based). However, the interaction of the two models is something we need to think through. Unless we explicitly prevent it - which is not done in the current patch, and is in any case probably not a good idea - both models may coexist on a given site.

    This coexistence will be potentially confusing both to site admins and to end users. Particularly confusing would be the coexistence of both models for a given content type. Is there any scenario where we would want both models to coexist for the same content type? I.e., a translation set, each member of which can have fields that are translated - back? - into multiple languages? This would be a mess. It seems like we should prevent the coexistence of both models for a given content type.

    How? The cleanest way I can think of is to test the language_content_type setting. If it's TRANSLATION_ENABLED, we don't offer the Field translation tab, leaving translation to translation.module.

  2. What fields to show on field translation edit form? Currently, the field translation edit form shows a mix of translatable and non-translatable fields. I found this confusing, particularly as there is no visual clue as to which fields are translatable. My inclination was to translate all fields on the form, but doing so produced an odd outcome: some changes were made to the original node (e.g., body), others to the translation.

    The simplest and I think most intuitive solution would be to show only translatable fields on the translation form.

  3. Theming. Currently we are in the default theme rather than the admin one when viewing lists of existing translations and when creating/editing a translation. Presumably we want to be in the admin theme.

  4. Meaning of "source". The term "source" is used in two different ways for field translation. First, the language of the node is termed the "source" language. To change this source language, we can edit the node and change its language. (I noticed that the available languages are filtered to ensure that we can't change the node to the language of one of the existing translations. Good call.)

    However, "source" is also used in a second sense, to describe the language that a translation is being created from (e.g., from English to French). This "source" can be changed on the translation overview page. At least, we probably need to use different terms for these two different concepts. But I don't know if I have a good suggestion. Maybe instead of "Source translation language" we could put "Create new translations from:".

  5. Default text when creating a translation. I expected to find the existing source text given when I went to create a new translation (as is the case when creating a translation with translation module), but instead the field is empty. This looks like a bug. In translation module, hook_nodeapi_prepare_translation() is invoked to allow modules to provide such default values, which in some cases will differ from the source language's values. It looks like we need to invoke this hook.

  6. Losing tabs. When creating or editing a translation, we lose our tab (local task) context.

  7. Reusability for non-nodes. It's great to see the abstract entity_translation methods, ready to be applied to other entities. But does it merit a distinct module? Do we have any other pure API modules in core? Looking to field_translation, it's currently limited to nodes, but is that a necessary limit? My initial feeling is, no, field_translation should eventually enable translation of any fieldable entity. And so it seems to me that entity_translation could be rolled into field_translation rather than being a separate module.

  8. Flag translations as outdated. This is a bit complicated. The current patch implements a flag in the node edit form, but not on the translation edit forms. This works if we assume the node language is the original source. But IIRC translation module offers this flag in all languages. That is, when editing the French translation of a node created in English, one can choose to flag existing translations - including the English original - as outdated. Do we need the same here? In practice this would be a switch on the translation form.

    Another minor point is that the vertical tab should show the current status of the field and update when the field is updated. Suggested text: "Don't flag translations as outdated" and "Flag translations as outdated".

Overall, this patch is a great draft in the right direction and shows the strength of the great foundational work in #367595: Translatable fields.

plach’s picture

Status: Needs review » Needs work

Thanks for the great review, nedjo! This patch started as a proof of concept work so I deliberately didn't address some "minor" aspects, if, as it seems, we are on the right way I'll soon have fixed all the open issues you described.

  1. Translation module. I am not sure this is a valid use case but there could be a multlingual site where a piece of content is strictly translated through field-based translation while a looser translation relationship is introduced through node-based translation: e.g. a product node might be straightforwardly translated in french trough fields but also have a french node translation which describes a slightly different model, the equivalent on the french market. As I said before probably this is an edge case, if we don't want to support it I'd go with nedjo's suggestion of using the language_content_type setting: "enabled" would enable field translation while "enabled, with translation" would enable node translation and exclude field translation.
  2. Untranslatable fields. Having untranslatable field values on the field translation form might be useful, to avoid undesired modifications we might display them as readonly.
  3. Theming. Admin theme, sure.
  4. Meaning of "source". As nedjo pointed out this is a little bit tricky: the translation corresponding to the node language is termed "source translation" and it has $source_language == '' the other translations have a non-empty source language. We might label the the original node translation as "original" instead of "source" and keep the current source language definition.
  5. Default text when creating a translation. I omitted this one intentionally to save time, but it should pretty easy to achieve; probably there will be no need to implement hook_nodeapi_prepare_translation, just mess a little bit with field_attach_form.
  6. Losing tabs. To be fixed.
  7. Reusability for non-nodes. Being able to extend this functionality to any fieldable entity would be great, but currently there are some hook implementations which are strictly node-related. Probably we would need to leverage #488542: Tie Field UI pages to any fieldable entity. Suggestions from yched would be welcome here.
  8. Flag translations as outdated. AFAICT the patch behavior strictly emulates the current node translation behavior: you can flag translations a outdated only from the source one. The vertical tab needs to be fixed.
yched’s picture

I'm *out* of this discussion (even though it's great to see it happen) ;-)

Just a quick note: "Having untranslatable field values on the field translation form might be useful, to avoid undesired modifications we might display them as readonly "
Tricky, because readonly means different things for each widget. That means we need to require every widget for every field type to support a readonly state. If we can avoid this, it's better IMO.

yched’s picture

7. Reusability for non-nodes. Being able to extend this functionality to any fieldable entity would be great, but currently there are some hook implementations which are strictly node-related. Probably we would need to leverage #488542: Tie Field UI pages to any fieldable entity. Suggestions from yched would be welcome here.

Hm, a bit lost here :-) Can you summarize the problem ?

plach’s picture

@yched:

#10: sorry, I did not make myself understood, I meant using field_attach_view for untranslatable fields instead of field_attach_form.

#11: nedjo was righlty pointing out that most of the current field-based node translation could be generalized to support any fieldable entity translation, this way we could join the Entity Translation API and the Entity Translation UI into a single module.

yched’s picture

Got that part, but what's the kind of stuff that is specific to nodes and would need to be abstracted out "leveraging #488542: Tie Field UI pages to any fieldable entity" ?

plach’s picture

Well, there is a form alter which is needed to filter out existing translations from the language selector in the node form and to add some info needed in the subsequent hook_node_update implementation. Moreover most of the node api hooks are implemented: hook_node_delete, hook_node_insert, hook_node_load, hook_node_update, but probably these could be replaced by their hook_field_attach_x equivalents.

I am a bit scared by the need of attaching the UI to multiple URLs, this is the part I'd mainly need support on.

plach’s picture

I implemented nedjo's suggestions from #8 except 1 and 7, I also updated the demo site

plach’s picture

the patch

yched’s picture

re #14: yes, hook_field_attach_x() should be fine for hook_node_x() equivalents. We don't have any actual use cases for them currently, so using them would provide a good safe-check ;-)
Also, note that a form_alter() should be able to recognize any form with fields by checking for the presence of $form['#fields'].

If there are other, specific needs, I guess it's possible to enrich the 'translatable' section in hook_fieldable_info().

yched’s picture

Side note: the link in #15 is wrong :-)

plach’s picture

the link in #15 is wrong :-)

Yesterday I was a little bit on hurry, AAMOF there were a couple of small regressions :)
I fixed them in the attached patch (the whole needed code is also provided if you wish to test it on your box), the demo has been updated accordingly.

Here is a small description of the changes in #15/16:

  1. Translation module. I left everything untouched as this is still to be decided.
  2. Untranslatable fields. Now untranslatble values are displayed as in node view instead of being editable.
  3. Theming. Now the "Translate fields" (new name) section uses the new, shiny, admin theme :)
  4. Meaning of "source". I used "original" to name the first translation created, any source translation is named, well, "source" :)
    I reworked the translation overview to display more information about source languages and (hopefully) made some small usability improvement on the source language selector.
  5. Default text when creating a translation. Now the field form elements are populated with the selected source language values.
    One little thing about this: when unistalling the Entity Translation API module the translation table is dropped but the field translations are kept. This way if it gets re-enabled the translations have to be recreated but when creating a new translation the existing field translations are loaded again as suggested values.
  6. Loosing tabs. I introduced a couple of second level tabs.
  7. Reusability for non-nodes. I am still thinking on this one: my main doubts are about the node form alterations (the language selector filtering and the translation vertical tab injection), if we want generalize the entity translation UI this alterations are needed and have to be somehow generalized. My initial thought about this is: if Entity Translation becomes general it gains a lower "dependency level" than Node module so it would become Node's responsibility (or of an additional small Node Translation module) to interface with the entity translation code. The vertical tab might be a simple fieldset which is turned (altered) into a vertical tab by the Node-specific code, for the language selector filtering I am still thinking about a possible solution. I'll make some experiment in the next days.
  8. Flag translations as outdated. Awaiting feedback on this, the vertical tab is fixed.

If we decide not to extend the UI to all the fieldable entities I think there are only of couple of issues left: handling node revisions which I'll do ASAP and taking into account the translation status (published/unpublished) which is waiting for the content language negotiation to be defined. The permission checkings also need a careful review.

While working on this I realized there are some node components which will need special attention to have fully-translatable nodes:

  • Node title. We should really convert also the node title into a (translatable) field, we could leave the node.title column in the node table but this should serve only as a cache for performance purposes (a pseudo-materialized view approach) and obviously would be untranslatable, only the main language would fit in.
  • Comments. I think we could easily alter the current logic to allow the recording of the content language along with the comment data. This way we could introduce an option to display only the comments matching the current content language, if properly implemented this should not introduce performance issues as on unilingual sites we would simply ignore the language column.
  • Taxonomy. This might be tricky: I don't know well how taxonomy has been converted to fields but I am hoping we can exploit translatable fields also for this, this might need some additional code however.
catch’s picture

Node title I can't see being converted to a field this release (although it should be eventually). However when displaying the node title in various places, it ought to be possible to replace it with the contents of a translated field via contrib - so even if we can't deal with that in core, it ought to be possible to fix.

I can see use cases for a language column on comments regardless of field translation - sites are often listing recent comments regardless of node associations, and they could use that to filter on current language. It's also possible we'll eventually make comments into a field (so removing comment->nid and allowing a comment thread to be attached to terms or users) - at which point translatable comment fields looks like a possibility.

Taxonomy - currently we have a 'term reference' field, but haven't converted all the taxonomy_node_* to use fields yet. When that's done, it's likely that similar to node.title, it'd be easier to swap out term->name with the contents of a translated field - since we'd be overriding a formatter instead of hard-coded term->name everywhere.

plach’s picture

I forgot:

  • Authoring information. This could easily be replaced/augmented with translation authoring metadata, we might also have an option for choosing between replacing and augmenting.
  • URL Path settings. We already have paths per language, this should be pretty straightforward to implement.
yched’s picture

Well, more generally, in an approach where translated nodes are not "different" nodes, any nodeapi addition loses translatability.

plach’s picture

@yched: yes, that's true, the problem is what additions can be achieved through field api and what not, that's one reason for which I changed my mind about the old Translation module.

Edit: however each module I have seen making additions to nodes had to face at least the need of synchronizing its values between translations, so this is not a real "shift of responsibility" to use quicksketch's definition.

plach’s picture

In the attached patch I implemented a couple of suggestions I received from Bojhan: mainly moving the source language selector in the translation page. I also started a minimum of work to support #282191-16: TF #1: Allow different interface language for the same path.

nedjo’s picture

@yched, "any nodeapi addition loses translatability"

Yes. I think it's okay though. This is after all "field translation". As with many other features offered by Field API but not by ad-hoc nodeapi additions, the choice for module developers becomes, if you want it translatable via field translation, make it a field.

sinasalek’s picture

subscribe

catch’s picture

Title: Refactor Locale and Translation to exploit Field API » Interface for translatable fields

nodeapi additions not being translatable is a strength of this approach - with module like nodequeue, voting API/fivestar, flag and others, you have to add a whole extra layer of support for 'translation sets' as a distinct unique entity apart from nodes to get them to work on a single piece of content as a single piece of content. This was a major part of the CivicActions/Sony internationalisation project, and dealing with tnids was the most difficult part of that project, since it requires every single nodeapi module to refactor itself if site builders are going to have any kind of option. With translatable fields, assuming more and more modules move towards field API for attaching themselves to things (quite possible to do even if you want to maintain your own specialized storage), we'd need only a conversion to a field, not a lot of special-cased code for translation sets.

For fields themselves, the i18nsync module exists in contrib, for the sole purpose of copying field values between translation sets for when they need to be duplicated, this patch also partially grew out of #132075: Attach fields to translation sets. which tried to solve the same issue with Field API in core (although a bit differently).

Since we don't remove tnid handling (not yet anyway), this gives site builders some options on how to do things for nodes, not to mention the benefits for translation of non-nodes, when we can finally stop abusing the {locale} table with all the user interface and performance implications that has.

sun’s picture

Title: Interface for translatable fields » Refactor Locale and Translation to exploit Field API
sun’s picture

Title: Refactor Locale and Translation to exploit Field API » Interface for translatable fields

.

nedjo’s picture

One detail I'm concerned about is that users setting up their site will be lost trying to choose between "Translation" module and "Field translation". We can't offer any help before the modules are enabled, so we've got only a short module description to try to convey the difference. I don't think we have any other similar case in core where we have alternate versions of comparable functionality that users can choose between.

I wonder if we could/should roll this into translation module rather than introducing a new module. It would be a bit more complicated. I guess we'd need a way to enable multi-node translation, field translation, or both. At least we could offer some reasonable help to guide admins in making this choice.

webchick’s picture

Status: Needs work » Needs review

#367595: Translatable fields was committed, so setting this to needs review so test bot can have a crack at it.

sun’s picture

Issue tags: +i18n sprint

Tagging.

sun’s picture

Title: Interface for translatable fields » TF #3: Interface for translatable fields
moshe weitzman’s picture

sun’s picture

Title: TF #3: Interface for translatable fields » TF #3: Translatable fields UI
plach’s picture

Title: TF #3: Translatable fields UI » TF #4: Translatable fields UI

The issue has been split in two: #565480: TF #2: Multilingual field handling is the part of this patch absolutely needed to complete multilingual field handling in core. Here I'll post the UI patch ASAP.

int’s picture

Issue tags: +Exception code freeze

add tag

plach’s picture

@nedjo:

I don't think we have any other similar case in core where we have alternate versions of comparable functionality that users can choose between.
I wonder if we could/should roll this into translation module rather than introducing a new module. It would be a bit more complicated. I guess we'd need a way to enable multi-node translation, field translation, or both. At least we could offer some reasonable help to guide admins in making this choice.

If we succeed in making the field translation UI available for eny entity, people will have to choose between enabling a functionality which enables translations for any field and the plain old node translation.

IMO these are not mutually exclusive. I keep having in mind a couple of examples:

  • In the case of a wikipedia article we can have a version in another language which is completely unrelated to the first one, they may treat the same argument in a slightly different way or even in a completely different way. They only share a common starting point and a marco-topic. I think this is a good use case for the node translation module which actually offers a loose relationship between different entities.
  • In the case of product page belonging to a e-shop catalog, we might have that the same product needs to be described in more than one language. The description might have different fields and each field should be translated or at least - if not available - presented in the original language. This the classic use case for translatable fields: the same entity (node) has multiple versions in different languages. The current API/UI enforce a strict relationship between entity translations.
  • We might have the same e-shop which needs to present different products for different countries/markets. It might present an audio player's description in french (with an english translation) for the Candian market; it might also have a slightly different version (shipping a different AC adapter) for the european market available in english (with french, german, spanish and italian translations). In this case field translations and node translations might live together.

We might describe the use cases above in the help. I think we could turn Translation into a simple stub module which just augments the multilingual options for the node types with the following values:

  • multilingual with node translation
  • multilingual with field translation
  • multilingual with node and field translation

On submit Translation might install/enable one or both of the following modules: Node Translation (translation_node, the old Translation module) and Field Translation (translation_field, the current field translation API/UI).
My main doubt is about the local task titles: perhaps we might have a first local task named Translate and a couple of second level local tasks labeled Field and Node.

Surely we need Bojhan and Yoroy's feedback on this.

nedjo’s picture

I think we could turn Translation into a simple stub module

I was thinking something similar, except that, instead of further modules, the two implementations would be in include files that are loaded as needed. But, on second thought, that could quickly get messy, e.g., when each needs separate hook implementations. We might be best keeping the current patch structure, introducing a new module, and just focus on making the module descriptions and help as clear as we can.

plach’s picture

Bojhan asked some screenshots to better understand the problem discussed in #30, #38 and #39.

My point is: field-based entity translation should become the right way to translate content so I'd prefer to use the "Translate" label for it, IMO it's more correct. How do we call the old node translation? Do we mention the translation set in any way? IMO we should make clear that with node translation we actually create new nodes. This should be reflected also by the module name, possibly, and by the module description.

catch suggested the possibility of moving the old node translation in contrib: I'd like this solution, but it won't solve the problem above.

alexanderpas’s picture

how about "page translation" (node based) and "content translation" (field based) or something similar.

plach’s picture

Here is my first attempt to make this work with any fieldable entity. Needs lots of polishing but works :)

@alexanderpas: I'm really no UX expert, we have seen that in TF1 ;)

plach’s picture

the right one

plach’s picture

This one should be ready on the coding side. Needs some documentation. I'll provide it ASAP.

plach’s picture

OK, now we should be ready to go!

Crell’s picture

EntityTranslationHandler should be named EntityTranslationHandlerInterface. All interfaces should have an Interface suffix.

You should also not force-include files that contain only classes. Classes and interfaces will be autoloaded by PHP automatically.

No time right now to review other stuff, but catch pointed me here and that's what jumped out at me.

catch’s picture

OK I read through this. Can't really comment on OOP conventions but this looks somewhat similar to how entity loading does it, which got in already.

I have two main concerns here from just reading through:

1. The patch relies on etid (an internal ID for an entity type) - this is a concept currently only known to field_sql_storage.module. If we can think of this use-case for using it, it's likely contrib modules will too, so this requires possibly factoring the etid code from field_sql_storage.module upwards into field.module somewhere so that other modules can access it without making assumptions. This would be neither a schema nor API change (possibly an API additional though). Although frankly the only time I've seen etid used, was when a join on that table caused temp tables and filesorts in field_attach_query(), so I'm not at all convinced there's a benefit to having it at all.

2. The patch defines a new hook, hook_translation_info() - which merges it's results via hook_entity_info_alter(). Part of the plan for hook_entity_info() was to have a single routing hook for all info about an entity. I'm not sure at the moment whether declaring separate hooks and merging the data is a great and amazing way to avoid 2000 line hook implementations and keep things readable, or if it's unnecessarily adding hooks when technically one is enough. So just bringing it up as a point to consider.

I found a few code style/docs issues. This isn't exhaustive since I was a bit distracted by irc while looking through.

+++ modules/entity_translation/entity_translation.handler.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,427 @@
+   * Fix the language of the original field values.
+   *
+   * Ensure that the original translation language matches the language assigned 
+   * to the original field values.

I'd rather this was "resolve" than fix - it's post-hoc cleanup rather than actually fixing a bug isn't it?

+++ modules/entity_translation/entity_translation.handler.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,427 @@
+  /**
+   * Return the entity type identifier.
+   */
+  protected function getEtid() {
+    // @todo: Consider avoiding the use of a field SQL storage function to 
+    // identify the entity: we might have different storage engines.
+    return _field_sql_storage_etid($this->entityType);
+  }

Why do we need the etid here? As far as I know that should be completely internal to field_sql_storage.module

+++ modules/entity_translation/entity_translation.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,360 @@
+/* Helper functions */

No need for these.

+++ modules/entity_translation/entity_translation.node.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,181 @@
+ * Implement hook_form_alter().

hook_form_FORM_ID_alter()

+++ modules/entity_translation/entity_translation.node.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,181 @@
+/**
+ * Implement hook_node_load().
+ */
+function entity_translation_node_load($nodes) {
+  // @todo: Multiple load.
+  foreach ($nodes as $node) {
+    entity_translation_get_handler('node', $node)->load();
+  }
+}

Why hook_node_load() here, but no other hook_$entity_load() anywhere else? It looks like the supporting code has been added for nodes, but not yet for everything else. In which case that's probably OK since none of that is an API change to tidy up later.

I'm on crack. Are you, too?

webchick’s picture

Summary of an IRC discussion tonight:

- This still needs some work, and is not going to be RTBC by the deadline for API/feature freeze.
- While this could live in contrib now that the remaining TF patches were committed, we've seen with Field API that people don't really test out the features without a UI to bang on.
- It also seems fairly tragic for us to have this much more robust field-level translation system and no way for anyone to actually use it without turning to contrib, particularly since Drupal 6 was "the internationalization release".
- While the issue doesn't reflect it (grrr! :P), Bojhan did give this a UX review earlier on IRC. He pointed out some issues that have since been resolved and said he could live with the rest. (apparently)
- This patch is basically going to render the old one-node-per-language way of doing translations moot, in favour of field-level translation. This basically creates a double-UI which is incredibly confusing. One way suggested to circumvent this was to move the old node-based translation to contrib. IMO though, core should provide an upgrade path from the old way to the new way. This is dependent on CCK having an upgrade path in core as well (which as of now is vaporware)

I think Dries and I need to have a discussion as to the fate of this patch. Whether we extend the exception for it as a "release blocker', whether to include it under the banner of "UI clean up", or whether to hold the line and chuck it to contrib to deal with.

Please continue to work on it in the meantime; it certainly won't hurt anything.

yoroy’s picture

This is one of the UI's I just haven't been able to follow/review. Bojhan has been working with plach on it, but didn't comment here.

Two remarks:

1.FWIW, quickly scanned the screenshots in #40 and they don't seem to scary. Interface text very important here, needs work
2. trategically I might prefer to add a UI for another use case with field-level complexity interactions. Just so that core has this problem to solve, not contrib. If smallcore + ui layer is the way for D8, then translatable fields UI is one for the core ui layer to provide patterns for, not contrib.

As such, for UX future this is worthwhile to try and get committable.

Status: Needs review » Needs work

The last submitted patch failed testing.

catch’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch failed testing.

plach’s picture

The attached patch implements the suggestions from #46-#47. It also adds a configuration page where choose on which fieldable entities enable field translation as it makes translation available for entitities not implementing hook_translation_info().

For example for a comment the translation UI is available at the address:

http://example.org/entity/comment/1/entity-translation

As in the case of comments there might not be a usable UI to attach the translation UI to, we might want to implement an additional page (similar to the string translation page) on which enter the entity type and the entity id (maybe through an autocompletion widget) and access the above URL. We absolutely need a UX expert's feedback about this.

plach’s picture

Status: Needs work » Needs review
Issue tags: +Needs usability review

@catch:

1. The patch relies on etid [...] this requires possibly factoring the etid code from field_sql_storage.module upwards into field.module somewhere so that other modules can access it without making assumptions. This would be neither a schema nor API change (possibly an API additional though).

I totally agree on this.

2. The patch defines a new hook, hook_translation_info() - which merges it's results via hook_entity_info_alter(). Part of the plan for hook_entity_info() was to have a single routing hook for all info about an entity. I'm not sure at the moment whether declaring separate hooks and merging the data is a great and amazing way to avoid 2000 line hook implementations and keep things readable, or if it's unnecessarily adding hooks when technically one is enough. So just bringing it up as a point to consider.

In this case we need to collect all the translation information before returning from hook_entity_info_alter(), as we must provide defaults where there are missing values.

yched’s picture

Many of this is above my head, here are a couple notes from a cursory look at the patch.

+++ modules/entity_translation/entity_translation.handler.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,507 @@
+   * Return TRUE if the entity translation is actually being translated.

"translation being translated" ?

+++ modules/entity_translation/entity_translation.handler.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,507 @@
+   * @param $transation

typo

+++ modules/entity_translation/entity_translation.handler.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,507 @@
+    foreach (field_info_instances($this->entityType, $bundle) as $instance) {
+      $field_name = $instance['field_name'];
+      $field = field_info_field($field_name);
+
+     // do something on $this->entity->{$field_name}
+
+    }

The pattern in field API (more specifically field.attach.inc, which controls field operations) is to use field_invoke() for those "do something on all the fields of an object". Not sure this can easily be applied here, but it's a bit sad to see all those foreach duplicate the work of the field_invoke() iterator.

+++ modules/entity_translation/entity_translation.handler.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,507 @@
+        foreach ($entity->{$field_name} as $lang => $items) {
+          if ($lang != $langcode) {
+            $this->entity->{$field_name}[$lang] = $items;
+          }
+        }

This looks like a no-op to me, but I'm probably missing something.
A comment might be good.

+++ modules/entity_translation/entity_translation.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,422 @@
+            'entity key' => '#node',

Just a note that I think #367006: [meta] Field attach API integration for entity forms is ungrokable makes $form['#{entity_type}] standard for entity forms.

+++ modules/entity_translation/entity_translation.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,422 @@
+      'object keys' => array(
+        'humanReadableId' => 'title',
+      ),

should this be camel cased ? the rest of the translation_info() array uses simple space separated strings as keys.

+++ modules/entity_translation/entity_translation.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,422 @@
+  variable_set('entity_translation_edit_form_info', $edit_form_info);

Why is this part stored separately ?

+++ modules/entity_translation/entity_translation.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,422 @@
+    'entity translation' => array(
+      'title' => t('Translate entities'),

perm machine name seems off - why not just 'translate entities' ?
Side note : do we need separate, per-entity-type permissions ? (as is : common users should be able to translate their own profile info but not actual nodes)

+++ modules/entity_translation/entity_translation.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,422 @@
+/**
+ * Implement hook_field_attach_pre_insert().
+ */
+function entity_translation_field_attach_pre_insert($obj_type, $object) {
...
+function entity_translation_field_attach_pre_update($obj_type, $object) {

hook_field_attach_pre_[insert|update]() : those hooks, although misnamed, were originally targeted at 'advanced' storage tricks (like per-bundle storage). Can this use the regular hook_field_attach_[insert|update]() (non-"pre") hooks instead ?
Edit: er, those hooks are currently missing from field_attach_[insert|update]() - well, it's a perfectly valid use case for adding them, then.

I'm on crack. Are you, too?

Status: Needs review » Needs work

The last submitted patch failed testing.

plach’s picture

This one implements most of yched's suggestions from #55, it also introduces comment filtering and translation authoring info handling (see the next post). Some notes:

Not sure this can easily be applied here, but it's a bit sad to see all those foreach duplicate the work of the field_invoke() iterator.

I thought a bit about this, to re-use _field_invoke() (which should become field_invoke(), then) we would need some refactoring, mainly the ability to specify a callback, possibily through $options['callback']. AAMOF the current behavior is strictly tied to the assumption that the only pieces of code defining callbacks are: 1) the module implementing the field being processed, 2) the field API itself through the field_default_*() callbacks.

This looks like a no-op to me, but I'm probably missing something.

You're quite excused ;) as I used really poor variable names ($entity vs $this->entity). Changed them.

Just a note that I think #367006: [meta] Field attach API integration for entity forms is ungrokable makes $form['#{entity_type}] standard for entity forms.

Just subscribed to the issue, I'll stay tuned :)

Why is this part stored separately ?

Because we need a data structure keyed by form id to avoid iterating through the entity info at each form alter.

Side note : do we need separate, per-entity-type permissions ? (as is : common users should be able to translate their own profile info but not actual nodes)

This one implements per-entity permissions, although I think access control should be mainly focused on entity instances rather than on the entity types.

Can this use the regular hook_field_attach_[insert|update]() (non-"pre") hooks instead ?
Edit: er, those hooks are currently missing from field_attach_[insert|update]() - well, it's a perfectly valid use case for adding them, then.

We need a separate issue here, I suppose.

The module introduced by this patch is probably the first piece of code exploiting the Field API which dose not actually focus on a single entity type. This way it makes evident some needs that might be common for future modules. This is particulary clear in three aspects:

  • Entity info: to be able to attach (and make work) the UI to any entity I had to introduce several additional entity information which an entity has to provide in order to exploit all of the features this patch provides. I tried to provide sensible defaults for this information but the absence of actual values severely limit the UI functionalities. IMO many of these informations should be provided while defining the entity and not by this module, as they might be useful for many other pieces of code, e.g.:
        <?php
          'node' => array(
            'translation' => array(
              'entity_translation' => array(
                'class' => 'EntityTranslationNodeHandler',
                'base path' => 'node/%node',
                'edit path' => 'node/%node/edit',
                'path wildcard' => '%node',
                'alias' => TRUE,
                'access callback' => 'entity_translation_node_tab_access',
                'access arguments' => array(1),
                'edit form' => array(
                  'form id' =>  'node-form',
                  'entity key' => '#node',
                ),
              ),
            ),
            'object keys' => array(
              'human readable id' => 'title',
            ),
          ?>
        

    should become:

        <?php
          'node' => array(
            'translation' => array(
              'entity_translation' => array(
                'class' => 'EntityTranslationNodeHandler',
                'access callback' => 'entity_translation_node_tab_access',
                'access arguments' => array(1),
              ),
            ),
            'object keys' => array(
              'human readable id' => 'title',
              'base path' => 'node/%node',
              'edit path' => 'node/%node/edit',
              'path wildcard' => '%node',
              'alias' => TRUE,
              'edit form' => array(
                'form id' =>  'node-form',
                'entity key' => '#node',
              ),
            ),
          ?>
        

    and all the 'object keys' above should be defined in the very entity definition and not added later as alterations.

  • hook_field_attach_*() implementations: these hooks are currently the only way to write code that works for any entity, with the exception of hook_entity_load(); the problem is sometimes one has to call field_attach_X() in a hook_field_attach_X() implementation and this potentially leads to infinte recursion. The solution here would be having hook_entity_X() but this not possible for D7 so we have to find a best practice to address this situation, which might become fairly common.
  • etid: as catch was pointing out in #47 we might need etids outside the field_sql_storage module.

The last submitted patch failed testing.

plach’s picture

Status: Needs work » Needs review
FileSize
63.84 KB

really weird, rerolled

yched’s picture

plach’s picture

The current patch fixes a couple of bugs and string issues. As it implements all the features I originally planned for entity translation, I am providing here a summary of the current status. If we exclude some possible Field API cleanup and some usability improvement I think this is ready to go (it is still to be decided if in core or in contrib). As webchick was pointing out in #48, if this has to go in core we still have to find a solution for the old node-based translation issue.

The aim of this patch is providing a way to translate any fieldable entity. The UI design has been derived from the node-based translation UI with some adaptation. AAMOF one of the goals of this patch is emulating all the node-based translation (core) behaviors and replacing it, as providing entity translations as facets of the same entity has been deemed more correct than having different entities for each translation. While discussing this solution we found that also the former approach does have some use cases, so our inclination would be to move the current translation module in contrib and replace it with the field-based entity translation.

The patch introduces a new module, Entity Translation, which allows users with the appropriate permissions (tf-permissions.png) to translate the fields of any fieldable entity. It provides the concept of entity translation, which you can think of as a view on the entity's field content in a particular language. While creating a new translation all the new field values are assigned the same language, e.g. if you are entering a french translation all the translatable field values are marked as french. Untranslatable fields are left alone. The field values actually displayed depend on the negotiated content language.

The patch provides a unified UI for translation, but each entity has its own UI to edit the original field values, e.g. node has the usual node edit form, taxonomy has the term editing form and so on. Everytime a new revision is created for the original field values, a corresponding revision is created for each field translation, this way revision_ids are always synchronized between field translations.

The translation UI is attached to each entity's UI as the Field UI already does. It provides two main pages: the translations overview (tf-translate-overview.png), which lists all the available translations, and the translation page (tf-translate-en-it.png), which let the user insert the field translated values. The user can choose from which language translate, if more than one source translation is available (tf-translate-fr-it.png). While editing a new content the user can choose to mark its translations as outdated (tf-edit-outdate.png), from the translation page it is possibile to mark the translations as updated (tf-translate-en-it.png).

If a translation is not available, i.e. it does not exist or the user has not the permission to access it, there are two possible behaviors: if language fallback is enabled (default behavior) language fallback rules will be applied and fields will displayed in an existing language (tf-view-it-fallback.png). If language fallback is disabled (Locale currently provides no UI for this) a system message is displayed warning the user that the requested translation is not available (tf-view-it-unavailable.png); this behavior works also when displaying multiple entities and is provided through a theme function, so it can be overridden.

To fully achieve the emulation of the node-based translation we made different translation appear as different nodes, when possible. With this in mind several functionalities were implemented:

This patch supports any fieldable entity and allows to choose for which types actually enable translation (tf-config-entity-types.png), but different entities may get a different level of support, depending on whether or not they were fully converted to Field API:

  • Nodes are fully supported and, once #571654: Revert node titles as fields gets in, entity translation will allow to fully emulate node-based translation.
  • Taxonomy terms are fieldable but term name and description are not fields so they rely on different translation methods.
  • Users are fieldable and, given that the username does not need to be translated, they should fully support entity translation.
  • Comments are fieldable, but subject and body are not fields so they can't be fully translated; moreover comments' UI is tightly tied to node's one so I could not manage to attach the translation UI to them, although the translation overview/form pages are available (tf-translate-comment.png); I proposed an alternative solution in #53. However the need of actually translating comments should be pretty rare and comments filtered per language should be enough for most multilingual sites.

To have this patch committed to core we need to make an upgrade path available. IMO this upgrade path should be optional: people wishing to keep using the node-based translation should be able to do so by simply installing its contrib version; instead people wishing to convert their sites to entity translation could run the upgrade path and then live happy. For this reason I suggest to implement the upgrade path as an additional entity translation submodule which would also take care of the possible broken internal links left by node translations converted to fields, e.g.:

Before the upgrade:

http://example.org/node/1         [english version]
http://example.org/fr/node/2      [french version]

After the upgrade:

http://example.org/node/1         [english version]
http://example.org/fr/node/1      [french version, a request to /fr/node/2 should be permanently redirected to /fr/node/1]

Usability and design feedbacks are warmly welcome :)

peximo’s picture

I have some questions mainly related to usability issues:

  1. in entity_translation_overview() the original translation has the title as link while the other translations have 'view'. Why this discrepancy? Is it only an implementation issue?
  2. In the translation page we have "translation settings" and "url alias" mixed in the form. Can we put this elements in a vertical tab like in the node form?
  3. If I click the edit tab from a translated version of the node I go to the original node edit form. IMO for a user this behavior can be weird and misleading; normaly the edit in drupal is contextual to the displayed content so I expect to edit the translation and not the original node.
  4. In admin/content we could add the 'translate' operation.
  5. In the translate form the untranslatable fields is showed like in the node view; would not it be better to display this fields as not editable or at least with a description of the field?
        if (!$field['translatable']) {
          $form[$field_name] = array(
            '#markup' => drupal_render($field_view[$field_name]),
            // Place the element where it would appear if displayed.
            '#weight' => $instance['display']['full']['weight'],
          );
        }
    
  6. should not the translation be published by default as it is for a node?
Bojhan’s picture

Issue tags: -Needs usability review

So, I have actually given this a big review a few weeks ago - going trough a lot of interfaces with plach. I think this is a good state to go from, there are some minor text issues - but I don't see much value in working on those here.

@peximo

2. Seems somewhat over-done, to put these in a vertical tab. These are clearly, and will only be two elements - vertical tabs is not supposed to be used for that.

3. Should see a fix.

4. Could bloat the table?

yched’s picture

Issue tags: +Needs usability review

5. In the translate form the untranslatable fields is showed like in the node view; would not it be better to display this fields as not editable or at least with a description of the field?

That would mean we need to require each widget to support a 'disabled' state. A little bit of work, outside the scope of this issue.
I agree, however, that the "test / test" text on translate entity form currently looks confusing. We should probably at least display the field styled with the regular field.tpl.php output and CSS. That would mean moving forward on #612894: field_format() is just a pile of code that doesn't work.

sun’s picture

Category: feature » task
Priority: Normal » Critical

I was unsure whether we want to add this to core. However.

After working on some other patches (f.e. #301902: Allow more users to see the node admin page), I realized that I have absolutely no clue how the field structure of a translatable field looks like and how to code for it when it is populated with translations. Even after being involved in all these TF patches. That's a major problem. And I have no clue what strange/wrong code we will see in contrib, if contrib developers don't have something to play + understand as well.

Hence, my point is that there is no way around committing this. So we need to get the implementation into RTBC state as soon as possible. UX tweaks can follow afterwards.

Honestly, I didn't expect so much OOP in here. I guess it makes sense, and I really like how the module alters itself into the otherwise untouched entity handling. But it also means that we need Crell or some other OOP masters to review this patch. I will try to review and test this patch more in-depth this weekend.

plach’s picture

And here's a (apparently) working upgrade path. It needs feedback on design and implementation (see #61 for details) and more testing but the results are encouraging :)

The rest of the code is identical to the patch in #61.

sun’s picture

Reading #61 once again, I'd say that we want to move Translation module into contrib (as node_translation module), and at the same time, make this patch replace translation.module in core. "Entity translation" does not sound like something I'd want to enable as a regular user. But a "Translation" module that allows me to translate fields everywhere - that, I absolutely want to enable. :)

catch’s picture

Agreed with sun. The core translation module has very few open issues, and hasn't had many over the whole of the D7 release cycle. It shouldn't require a huge maintenance burden during the release cycle, and if Jose didn't mind, we could move it back to the i18n project maybe?

While we could write an upgrade path for body (and title if translatable titles gets in), doing so for CCK fields relies on the overall CCK to Field API upgrade path, with additional tweaks, so there's a node translation -> translatable entity conversion, CCK -> Field API conversion, then CCK/nodetransation -> Field API/Entity translation patht to wrangle. But keeping node translation in contrib means there's an upgrade path regardless.

sun’s picture

yeah, i18n_node sounds even more appropriate. I totally forgot that it originally came out of it.

Created #627734: Take back Translation.module?

@webchick: Would you be ok with this?

yched’s picture

Two things bug me in the patch:

  1. Just viewing the translation_overview page can make some writes to field values ?
    function entity_translation_overview() {
      if ($handler->initOriginalTranslation()) {
        field_attach_update()
      }
    }
    

    I sort of gather this is in order to do some verification / cleanup / preparation, but updating field data on a simple page view sounds scary.

  2. {entity}_save() cascades down to a field_attach_load():
    {entity}_save() - field_attach_update() - hook_field_attach_pre_update() - $handler->updateTranslations() - $handler->prepareRevision() - field_attach_load()
    Load within a save, and that deeply nested, sounds like a recipe for strange effects and hair-pulling debug debug...
plach’s picture

@peximo:

1. in entity_translation_overview() the original translation has the title as link while the other translations have 'view'. Why this discrepancy? Is it only an implementation issue?

Yes, not all entities might have a translatable human-readable identifier (e.g. username). That column is a legacy of the node translation UI, we might want to remove it and add a view link in the Operations column.

3. If I click the edit tab from a translated version of the node I go to the original node edit form. IMO for a user this behavior can be weird and misleading; normaly the edit in drupal is contextual to the displayed content so I expect to edit the translation and not the original node.

You're right but I really can't imagine a fix for this, except perhaps adding a new tab saying something like "Edit translation", but I am afraid it would bloat the tabs area.

4. In admin/content we could add the 'translate' operation.

AFAIK this would introduce a dependency between Node and Entity Translation which I tried to avoid as TF4 might not get into core in D7.

5. In the translate form the untranslatable fields is showed like in the node view; would not it be better to display this fields as not editable or at least with a description of the field?

Currently fields are shown exactly as they appear with the full build-mode, the title field is an exception as currently it is unset from the structured array to be built. This should be solved when #571654: Revert node titles as fields gets in, as a text field would be shown instead of the plain text.
However I agree that having read-only field widgets would be the best solution, if this were still feasible I'd be happy to take this way.

6. should not the translation be published by default as it is for a node?

It's just matter of changing a value. I'd like a usability feedback about this.

plach’s picture

@sun:

"Entity translation" does not sound like something I'd want to enable as a regular user. But a "Translation" module that allows me to translate fields everywhere - that, I absolutely want to enable. :)

I have to admit this was my secret plan too ;)

However "i18n_node" sounds definetly better, however I think the original module implementing node translation was http://drupal.org/project/localizer.

plach’s picture

While we could write an upgrade path for body (and title if translatable titles gets in), doing so for CCK fields relies on the overall CCK to Field API upgrade path, with additional tweaks

I realized I owe people a more detailed description of the upgrade path implementation.
As proposed in #61, to make the upgrade path optional I created an Entity Translation submodule called Entity Translation Upgrade.

This module, after being installed, launches a batch process which actually converts all the existing node translations into field-based translations. The conversion is done through various steps:

  1. Content conversion: for each batch step N node translations are loaded and for every one we iterate on all the attached translatable fields which get converted in field translations of the original node. This way the upgrade path supports the CCK > Field API upgrade but does not depend on it, it already works with body and (hopefully soon) title. A site wishing to perform a full upgrade should first perform the CCK > Field API upgrade and after the Entity Translation one.
  2. Node additions conversion: a hook is invoked letting any module convert (if possible) its node additions. Entity Translation Upgrade implements this hook itself and converts comments.
  3. Node translations history: all the converted node translations get unpublished and for each one a record is created in a history table storing its nid, tnid and language. ETU overrides the node/%node access callback and check if a user has the right to see an unpublished node translation, if not it redirects the user to the corresponding field translation. If the node translation gets deleted and a user tries to view a non-existing node, the history table is checked and, if a record corresponding to the requested nid is found, the user gets redirected to the field translation.

The batch process can be called multiple times, as it works only on node translations not present in the history table. This way if an (evil) translator creates more node translations after ETU has been installed, a site admin just needs to launch the process again.

plach’s picture

@yched:

Just viewing the translation_overview page can make some writes to field values ?

This is a initialization of translations that needs to be done (if necessary) before accessing the entity translation form. This is due to the fact that in a unilingual site we can't assign a language to fields but we can assign a language to nodes (ugly, I know); this way if we enable entity translation after having already created some nodes and assigned a language to them, we could be unable to populate field form elements with the source language values as they might not exist (they would have FIELD_LANGUAGE_NONE language).

Load within a save, and that deeply nested, sounds like a recipe for strange effects and hair-pulling debug debug...

The problem here is that while performing a node_save() we don't get all the field translation values but only the ones being edited ($node->language ones) as field_attach_form() works in single language mode. In this situation the $node object we get is the form values array cast to object. Moreover we don't have access to the $form['#node'] object in hook implementations, and we shouldn't as we only know an object is being saved.

I opened #629252: field_attach_form() should make available all field translations on submit and proposed a solution there.

plach’s picture

webchick suggested in IRC to generalize the comment_filter query tags. I opened #634262: Make entity lists filterable for this.

Edit: I misunderstood her, this is the right issue: #641034: Make comment lists filterable.

sun requested that failed test be re-tested.

sun’s picture

We MUST commit this.

Can we get a new patch that replaces Translation module?

Jose Reyero’s picture

This is cool but not really a replacement for translation module.

This is not really about having a few fields that are tranlatable, but about having a *full site workflow* for multilingual content. Like who creates/can create each translation?, should all translations have the same owner/status/date/comments/taxonomy?, what happens with my node listings?, will all content need to be translated / is meant to be translated? will my translators need to be able to edit the original content / content in any language?...

Really, this patch is *far* from figuring out all that questions and from providing flexible answers for them. While on the other side we have an *already working* translation module

I think for Drupal 7 we should go without this module, multilingual fields are a big enough step forward and this 'Entity translation' could live in contrib until the solution proves to be mature enough to work for a few hundred live multilingual sites.

catch #68> The core translation module has very few open issues, and hasn't had many over the whole of the D7 release cycle

Wouldn't that mean that it actually works for a lot of people and its a good reason to keep it in core? The i18n module has more than 15000 (reported) users (who use translation module too), so at least I can tell the lack of issues is not because no one uses it.

So if you guys want to get this in core, ok, go ahead. But please don't say it is a replacement for translation module because it is far from that yet.

plach’s picture

@Jose Reyero: hi Jose, it's good to see you here :)

I'll provide a complete answer later, but for now I'd need to be sure you read #61, just to know where to start.

We tried to design this to allow users to choose which content translation workflow suits better their needs, as you can read in #73.

I only want to add that we never said this is ready and that covers all the possible uses cases that node translation can face. And we never said that committing this presents no risks, but we have a new and possibily fragile API in core that without this one can't be tested at all.

arhak’s picture

subscribing

mcload’s picture

subscribing

catch’s picture

@Jose "Wouldn't that mean that it actually works for a lot of people and its a good reason to keep it in core? "

It works pretty well by itself, however as soon as you need to integrate translated nodes with other modules, you quickly run into problems:

1. Menu links can only link to one system path path - translated nodes each have their own translation - this requires duplication of menu links or entire menus, when other multilingual paths are handled fine with a single sytem path.

2. Any module acting on hook_nodeapi() needs to also account for translation sets - CCK, nodequeue, voting API, flag - these all need additional code branches or special workarounds on sites running translation module.

#307810: Multilingual support for flagging
#307207: Multilingual voting: option to tally votes by translation set
#251092: Multilingual Support

Those patches are just a handful of examples - the reality is that many modules simply don't work with translation sets as a consistent thing at all.

So the reason D6 translation module has few issues itself, is because it pushes the responsibility for managing data which needs to be shared or tracked across translation sets down to every single module interacting with nodes in contrib. This was the initial impetus behind this patch, and remains the compelling reason to try to make it the primary mechanism for translating nodes (whether that's in core in Drupal 7 or 8 is a different thing of course).

Jose Reyero’s picture

@plach #79,
I only want to add that we never said this is ready and that covers all the possible uses cases that node translation can face. And we never said that committing this presents no risks, but we have a new and possibily fragile API in core that without this one can't be tested at all.

Ok, then we just need a contrib module to test the core API, right?

@catch #82,
So the reason D6 translation module has few issues itself, is because it pushes the responsibility for managing data which needs to be shared or tracked across translation sets...

While if replacing translation by this new one, it won't allow to manage some data that needs to be kept separate for each translation. Then we'd just have the opposite feature requests. "Hey , I want poll votes to be kept separated by language, then be able to display them aggregated". What about content status, author, title, taxonomy????

Really, we've had this dicussion for years about which model is better, you can read this from the beginning, http://groups.drupal.org/i18n

Just to save you the reading, the answer is "no one knows". But this is the fact: there are thousands of multilingual sites running with the current translation module.

So my main issue with all this is: OK, it looks cool, now please: Show me your multilingual site (And until we can see that, please let us have the current module, which at least works... sometimes... for some people)

And please, don't get me wrong. This development here is great. But still we don't have a working site workflow.

For me, this is one clear case where the concept needs to live and grow up in contrib for a while. Because if it gets into core, cool, then you'll have it frozen for one year until the next Drupal version, no improvements allowed.

So IMHO the best thing that could happen to this module is to be moved into contrib. It doesn't really matter that much what happens with the translation module....

plach’s picture

Ok, then we just need a contrib module to test the core API, right?

Well, if we assume that possible bugs in the Field API code will cause no problems to the rest of the core, yes. But we might end finding critical bugs only after the D7 release if this one lives in contrib. Not saying this is an absolute release blocker, but that is surely a worrying scenario.
In any case this is a good reason to try to get this in a RTBC status ASAP, this won't surely hurt.

While if replacing translation by this new one, it won't allow to manage some data that needs to be kept separate for each translation.

This is not completely true: if Poll leveraged the Field API (and I won't be surprised if Advanced Poll did) TF would allow to support both cases: an untranslatable 'votes' field for aggregate votes or a translatable 'votes' field for per-language votes.

What about content status, author, title, taxonomy????

The current Entity translation system allows to store per-translation status and authoring metadata.
Node title is almost certainly going to be translatable.
Taxonomy is now attached to nodes through fields, this allow us to support both main use cases: we can have per-translation taxonomy terms (we could even synchronize them if needed) or per-node (shared) taxonomy terms whose names will be translated at UI level through DDT.

But this is the fact: there are thousands of multilingual sites running with the current translation module.

Let's face the truth: in D6 setting up a truly working multilingual sites (at any level) requires a Drupal expert and, as you know better than any other, absolutely requires i18n. By moving Translation in it we don't change the requirements needed for a D6 > D7 upgrade and all sites developer/builders wishing to keep using node translation will keep doing it with almost no difference in their development workflow.
This scenario would allow a solid and stable module to keep being used but, at the same time, would allow to start a gradual transition to a (potentially) better translation system.

So my main issue with all this is: OK, it looks cool, now please: Show me your multilingual site.

When D7 and i18n for D7 will be released I promise I will :)

For me, this is one clear case where the concept needs to live and grow up in contrib for a while. Because if it gets into core, cool, then you'll have it frozen for one year until the next Drupal version, no improvements allowed. So IMHO the best thing that could happen to this module is to be moved into contrib. It doesn't really matter that much what happens with the translation module....

I almost completely agree with you, but the perspective of having a broken D7 core scares me more than the one of having an incomplete translation system (which might always be enhanced through contrib).

sun’s picture

Yes, as mentioned before - the current state of translatable fields in D7 is mostly unknown, because no one is able to use and test it. No developer is able to see how a $node data structure looks when there are translatable fields in it. And I'm 100% sure that we have a lot of broken code in HEAD currently that was committed, because no one is really aware of how to deal with translatable fields in their patches and tests.

Brainstorming... what about not dropping Translation module (yet) and merging TF UI into Translation module? Would that be doable? Although I don't see any need for the old Translation module functionality with translatable fields being in place, this perhaps would be a compromise we could agree on...

plach’s picture

Brainstorming... what about not dropping Translation module (yet) and merging TF UI into Translation module? Would that be doable?

There is no problem on the functional side: ET and NT are fully compatible on that aspect, the problem is mainy about UX.

See #30-#40 for details.

webchick’s picture

Version: 7.x-dev » 8.x-dev

I was e-mailed and asked to respond to the question about how to handle entity translation in Drupal 7 in relation to this patch, Content Translation, etc. and a path forward for core. Here's a summary:

Pros:

  1. Translation works for any entity, not just nodes.
  2. Supports lots of scenarios that previously needed field synchronization (poll votes, ubercart products, etc.)
  3. Actually puts our translatable fields API into practice, so that we can properly test it (and probably find lots of bugs in the process ;))
  4. Has UI thumbs up from UX team
  5. Upgrade path seems to be working from an API side
  6. Helps move the Drupal community into new paradigm thought rather than legacy thought about translation

Cons:

  1. All of the functionality of Content Translation is not yet replicated. For example, fine grained access control between different entity translations.
  2. Having both Content translation and Entity translation together creates a big UX mess.
  3. No test coverage
  4. Work on this is still in the experimental stage; While Content Translation has several thousand sites to show for it, this new paradigm is untested
  5. Committing it means freezing its development

I've given this a lot of thought, and I hope that comes through (perhaps too much :P) in my response below. I want to emphasize again my profound gratitude and respect for you all and the tremendous work you've done here.

First, some timeline stuff. The original labelling of the Translatable Fields issues (TF #1, #2, #3, and #4) was explicitly laid out in order of "impossible/difficult/easy to achieve from contrib." This was exemplary project planning, and hats off to plach and/or sun, and/or whoever else came up with this great scheme. By feature freeze (10/15), #1, #2, and #3 were thoroughly reviewed, RTBCed, and committed. #4 was the only one that remained. The original patch had one hunk that required a core change but that was committed a couple of weeks ago.

plach, sun and I had a similar discussion to what's being asked here on IRC the day after feature freeze. Basically discussing whether this patch could make it in if it were ready by UI freeze. I had expressed doubts then that the patch would in fact be RTBC before UI freeze, and had further doubts about locking this into core when it was undergoing such rapid development, particularly given that TF API was brand-spankin'-new.

Today we are at 12/6, which is an additional 3 weeks past the original UI freeze date, and 3 months past the original code freeze date. And, unfortunately, this patch is still not RTBC. This most certainly is NOT a negative reflection of the people doing tremendous work here (this issue, in fact, exemplifies how core developement should be done, in my view). But rather it's a reflection of the scope of the paradigm shift we're dealing with in this new module. It's also lacking tests, so we still don't know what bugs lie under the surface, nor how long it will take to clear those up.

(And just to head this off at the pass, before someone inevitably drags Overlay module into this dust-up, remember that that patch had been specced in April, initial prototype posted in July, and has been basically functionally identical since then. It was only under-the-hood tweaks that had been made over the subsequent months, and that module was literally doing nothing but sitting there and getting buggier by it not being hammered on by 200 people.)

I have chosen to bump this issue to 8.x, and attack Entity Translation from contrib, for the following reasons:

  1. Timing. The comments in this thread rightly describe this patch as a fundamental paradigm shift. The time to commit paradigm-shifting patches is early in the release cycle, not when you're past the final marker just before the last critical bug sprint before a release.
  2. Foundation. When Gábor went to tackle the problem of bringing i18n features into core, what he did was carefully analyze and cherry-pick the best architecture, functionality, and features that were available in contrib at the time. Not all of this made it in in time for code freeze, but the parts that did were very solid and contrib modules such as i18n were able to build off if them, and in a much more integrated way.

    I believe you have all done exactly that for Drupal 7's release cycle: put the necessary API bits into core, so that contrib may innovate solutions around it, and during the Drupal 8 release cycle the next maintainer can carefully cherry-pick from the available modules that best utilize the TF functionality, rather than locking people into the first serious stab we took at it.

  3. Lack of strong, compelling pros. While the pros list above has TONS of great stuff on it, there's not one of them that are no longer in the list if we move this module to contrib (with the possible exception of forcing a paradigm shift on people, but see #2).

    I know sun stressed above that this module provides a way to test core's TF APIs, which currently have no means in core of doing so. This is true. But this is also not the only API in core that has no core implementation, and yet we still manage to find bugs in the node access system, private file handling system, XML-RPC handling, and so on. Sorry, but this is not a compelling enough reason to commit, given the other things to consider here.

  4. Retaining momentum. On the other hand, one of the enormous benefits we gain by NOT committing this patch now and locking its behaviour into core for 3+ years is allowing the wonderful work that's gone on in this issue to continue in contrib, over multiple iterations and multiple branches. There's a strong team here that I'm confident can give this paradigm shift the focused attention it deserves if they don't have deadlines hanging over their heads like guillotines all the time.
  5. Risk. There's no test coverage here. There is strong opposition from the maintainer of the i18n project, whose cooperation is needed in order to carry out the plan of pulling the current Content Translation module out of core and into i18n. Yet, as José points out, CT is working well enough for thousands of sites atm, where this new module is totally untested "in the field".

Again, I want to sincerely thank everyone here for their work. Just... I have to put on my release manager hat on this, and this is the only sensible thing to do, from my point of view. I hope you understand.

mcload’s picture

Does it mean that the translatable fields API is available in the core, but its user interface will be in a contributed module?

webchick’s picture

Correct.

plach’s picture

I hope you understand.

Nothing to add, everyone here did what she/he has to do :)

mcload’s picture

As the decision is given, can you put it to a contributed module? I am eager to try it and and start working on a multilingual site using translatable fields :)

sun’s picture

Let's discuss this contrib part first. I'm preparing in the meantime.

plach’s picture

I'll ask a CVS account ASAP. Meanwhile we can discuss the module name: I'd keep Entity Translation.

Jose Reyero’s picture

I think 'recycling' the 'translatable' one would sound good (long names are awful for developing).

Let's save the 'translation' one for when you guys finally kick it out of core :-)

plach’s picture

@sun:

sorry, perhaps I misunderstood you: do you want to discuss the core vs contrib issue?

@Jose Reyero:

Only 'translatable'? I ain't sure it would be self-explicative.

sun’s picture

I considered to use Translatable module for DDT only, but we may re-consider this.

However, I see no problem with overriding core's Translation module from contrib. This would make the upgrade path for existing users easier, decrease confusion, but also make the upgrade path into D8 core easier. I therefore opened #652930: Project ownership of Translation module and also contacted the current project owner.

plach’s picture

In this case we would need some kind of internal prefixing, e.g. translation_menu() would be needed in both modules.

What about

translation_entity_*

or

translation_field_*

?

sun’s picture

Perhaps, that's not evident: sites/all/modules/translation/translation.module completely replaces modules/translation/translation.module, if existent. That's a fairly hidden core feature. ;)

plach’s picture

Ah, now I get what you mean. But wouldn't this prevent people from enabling both modules if they wished to do so?

sun’s picture

That would surely depend on whether we would want to maintain Content translation module's functionality at all or just provide an upgrade path. If you look at the commit history, then it's obvious that this module didn't see any love and no major changes in the past years. Hence, the second option would be to just copy and mix in the old functionality, so we'd have entity (more than nodes?) AND field translation within one module.

So it wouldn't really matter. But, honestly, I think we discussed translatable fields to death and the result was always same: the fragile synchronization approach of Content translation module must die and there is no use-case that we cannot handle with translatable fields. Hence, instead of providing the option for the user at all, we probably want to nuke it.

nedjo’s picture

plach’s picture

+1 for #100, but for the 1.x branch I would simply copy the translation.module: I ain't sure the translation sets approach deserves additional development time.

Jose Reyero’s picture

This is where I start to worry. Why everybody wants to drop the old one before the new one is really working?

There are many use cases 'entity translation' cannot handle yet. What about different taxonomy for each language? What about having independent revisions for each translation? Published / promoted / etc different for each translation? And in general what about not having translated content but different content for each language? And what about benchmarks for entity translation?

So I would allow site builders to decide. If you provide a different (compatible) module maybe we could choose a different approach for each content type. Though I agree only one approach is good for core, there's nothing wrong with having options in contrib.

I would be really happy if we can use fields translation for content like polls or images, really I cannot wait to drop that hacks from i18n. But if you tell me I need to use entity translation too for my forum which is 100% independent for each language, and has different taxonomy too for each language, that's where things start feeling wrong.

So please: first provide a solution, then drop the old one if you don't need it, but not before.

plach’s picture

This is where I start to worry. Why everybody wants to drop the old one before the new one is really working?

I absolutely don't want to drop it until we can actually replace it. AAMOF I asked for a solution enabling people to use both modules at the same time.

OTOH at some point we might have a merged module capable of fully replace the Translation module and the proposed solution would let us to actually replace the core version. Moreover we have some explicit dependency in core between Node and Translation so a replaced version would be able to satisfy them where a parallel version couldn't.
But obviously the decision of eventually drop Node translation will have to be discussed and approved by the entire i18n team.

That's why IMO we should have the 1.x branch containing a simple copy of the core Translation module.

plach’s picture

This is going to take over the current Translation contrib project (probably tomorrow).

If there are concerns still pending, now is the last to chance to speak about them :)

plach’s picture

Status: Needs review » Postponed
Issue tags: -Needs usability review, -Exception code freeze

The 2.x version of the Translation module has just been started: #673300: 2.x initial merge.

YK85’s picture

subscribing - looking forward to testing Translation module in D7

sun’s picture

Status: Postponed » Closed (won't fix)

As we already are at > 100 follow-ups here, and we'll surely improve the TF UI in http://drupal.org/project/translation during the D7 release cycle (making this issue and patch obsolete), I'm going to mark this won't fix. We should re-start a new issue from scratch during D8, when we have a solid + working contrib module.

plach’s picture

catch’s picture

Priority: Critical » Major
Gábor Hojtsy’s picture