Problem/Motivation

The field language API has serious DX problems: the main one is that, since in many cases it is used in a supposedly monolingual context, people cannot figure out which language provide for the various $langcode parameters present in most of the Field API function signatures.

Moreover people often need to access the raw field values, which currently is very uncomfortable because one has to explicitly provide a language, whereas usually Field API functions do not require the user to provide any to behave correctly. As a workaround someone is using field_get_items(), which is intended to provide the field raw items in the language they would be currently displayed: this works wonders in a monolingual context but totally sucks in a multilingual one, since it may return the wrong values in the case the business logic required to operate on a language different from the current one, but the code were not aware of the very concept of language.

Since this situation is likely to be producing broken code, we need to provide developers the right tools to handle field language in language-agnostic contexts.

Proposed resolution

Field language was introduced with the idea of supporting any use case needing this concept, AAMOF content translation is only one of the possible use cases, altough by far the most common. However at the moment the only known contrib module exploiting the field language API is the Entity translation module, which implements precisely this use case. ET assumes that every entity has a language: this language is considered the language the original field content is created in. After creating the original content, it allows to create an entity translation in a given language. This simply means that all (translatable) fields get a value for the corresponding language. These values will be showed when the current content language matches theirs. This can be repeated for every installed language, so an entity might end up having for each attached field a different value for each available language.

There are two alternative ways to handle such an entity in a language-agnostic fashion, i.e. without explicitly specifying a language to act on:

  • Pretending that the entity is monolingual, i.e. it holds only values corresponding to the entity language, which is the scenario in which module act when field language is not used. This would leave out any action on the field translations, but this can be an acceptable behavior in many situations, above all the ones in which only a read only access is needed. Think of the unique field module, which checks that a particular value inserted for a certain field is unique among all the submitted content: it is reasonable to assume that if the original values are unique, field translations are too.
  • Acting on all available languages, which is what the Field API usually does and allows every field translation to be processed.

With this in mind the following functions are going to be introduced:

  • field_get_raw_items() and field_set_raw_items() as language-agnostic item accessors defaulting to the entity language as in D8
  • field_get_display_items() behaving as field_get_items() and deprecate the latter
  • field_working_language() to get the language code for the update phase in opposition to field_language(), which returns the language code to be used to get the items to be displayed
  • field_process_items($entity_type, $entity, $field_name, $callback, $options = array()): a function allowing to invoke a callback on every available field translation as _field_invoke() does. At the moment this function didn't prove as much necessary as the above ones, so it has ben skippped.

The issue has been retargeted to D7 as most of the functions above already have an OOP equivalent in D8 or will have it.

Remaining tasks

  • Collect feedback
  • Check that this actually covers most of developers' needs
  • API implementation
  • Code review
  • Test implementation (See http://drupal.org/project/fieldx)
  • Update existing tests to exploit the new API functions (Non-critical follow-up)

API changes

This is a purely additive issue to make more tools available for developers. No API or behavior change.

Parent issue

#1260534: META-META: Make language support awesome in Drupal 8

Comments

plach’s picture

plach’s picture

Issue summary: View changes

Add parent

gábor hojtsy’s picture

gábor hojtsy’s picture

Issue summary: View changes

Removed a wrong subtitle.

plach’s picture

This needs to be backported.

fago’s picture

looks like this would be covered by something like

Entity::get($property_name, $language = NULL)

?
So $language is optional and can have a proper default. Why should we have two different methods/functions for that?

plach’s picture

@fago:

The proposed solution is mainly about D7 DX, for D8 I totally support an OOP-based solution built on the same background.

webchick’s picture

Issue tags: +Needs backport to D7

Fixing tag.

chx’s picture

field_get_raw_items($entity_type, $entity, $field_name, $langcode = NULL): helps but not enough. A good DX would remove $langcode entirely because I still have no idea what $langcode is and what should I provide or any at all -- my best understanding right now is that most of the time I don't. So if we are enhancing the DX why not go the whole nine yards and just remove it? If it's used really rarely the accessing the raw $entity->$field_name is good enough for that use case.

plach’s picture

So if we are enhancing the DX why not go the whole nine yards and just remove it? If it's used really rarely the accessing the raw $entity->$field_name is good enough for that use case.

Ok, this would work for me. One can always call field_get_items() with an explicit language parameter if she needs to. We should make sure the distinction between the two getter function is totally clear.

Since a few authorative names (not as many as I'd liked) have chimed in here and no one seriously argued against the plan, I'm going on and provide a patch as soon as I can.

yched’s picture

Er, I myself don't see the use of field_get_raw_items() if it has no $langcode param.
The goal is : give me the values of this field in this language if it as any (and without any language fallback rules), right ?

Having a getter and setter that acts only on one single language (the $entity language, if I get things right ?), sounds valid, but why forbid access to the other languages ?

plach’s picture

@yched:

Well, originally my idea was to provide something like:

<?php
function field_get_raw_items($entity_type, $entity, $field_name, $langcode = NULL) {
  if (empty($langcode)) {
    $langcode = entity_language($entity_type, $entity);
  }
  return field_get_items($entity_type, $entity, $field_name, $langcode);
}
?>

This would avoid the need to rewrite the check on the field's translatability to determine if we actually need to care for language at all, but it also involve language fallback, which I ain't sure we want in this scenario. So perhaps we should just leave the $langcode parameter and put a huge phpdoc warning people that it is absolutely optional and that it can be safely ignored in most cases. @chx?

Alternatively we could add an argument to field_language() allowing to prevent it from calling hook_field_language().

chx’s picture

The reason we forbid access to other languages because we want a simple DX. It will still be very, very hard to comprehend the field invoke workflow due to the multilingual fields (this is a major gripe of mine) but at least for contrib developers we provide something. For core developers, hopefully documentation will be enhanced.

fago’s picture

Actually, I don't see where the difference between field_get_items() and field_get_raw_items() should be. In order to improve DX we should not introduce yet another function and confuse developers by forcing them to pick one, but do a helper function that fulfills the common needs by default, while ideally it handles specific needs too.

@chx: I don't see how hiding the language from the developer helps to improve DX when it's there. It's there and we can't do away with it now, so let's improve handling it. So an optional langcode parameter makes perfectly sense to me - you don't need to care if you don't want to.

@field_get_raw_items() vs field_get_items():
Why shouldn't field_get_items() default to the entity language too? It seems weird to me to have two very similar functions with different language defaults! DX-- :(
Any reason field_get_items() cannot default to the entity language?

plach’s picture

Why shouldn't field_get_items() default to the entity language too? It seems weird to me to have two very similar functions with different language defaults! DX-- :(
Any reason field_get_items() cannot default to the entity language?

This is issue is primarily about D7 DX. I hate this choice, but what you are proposing is an API change that cannot be backported to D7 as it would break BC. For D8 the proposed solution makes no sense since hopefully we will have getters/setters with proper defaults in place.

chx’s picture

@fago pray tell me what's the use case of supplying a $langcode ? If I'd know that...perhaps , you know, should be in the doxygen :)

fago’s picture

This is issue is primarily about D7 DX. I hate this choice, but what you are proposing is an API change that cannot be backported to D7 as it would break BC.

Indeed.

@fago pray tell me what's the use case of supplying a $langcode ? If I'd know that...perhaps , you know, should be in the doxygen :)

Of course.

So what's the use-case of getting the field value using the entity language? If I don't care about the language it should probably default to the language currently displayed - as field_get_items() does?

I still think having to functions doing the same with the only difference being the default for $langcode is going to confuse people even more. For them, to get the difference between those functions they need to understand the difference between the entity default language and the display language - thus they'd need to understand all the language quirks. :(

plach’s picture

So what's the use-case of getting the field value using the entity language? If I don't care about the language it should probably default to the language currently displayed - as field_get_items() does?

Nope, we need a predictable result: as explained in the OP, defaulting to the current language works only in the build/render phase, when the data on which one is working is about to be displayed; in all the other phases one needs a reliable result. Defaulting to the entity language ensures that at least the original values get a complete workflow, instead relying on the current language has two problems:

  1. if an entity has field translations for every available language, we are risking that a cross-request workflow is spread against different translation values;
  2. if field translations are missing for the current language one has no items to operate on;

I still think having to functions doing the same with the only difference being the default for $langcode is going to confuse people even more.

The directive here would be: always use field_get_raw_items() unless you are going to display the items you are getting. Stop.

fago’s picture

I see and I agree that the default should be reliable and not vary by requests.

Well, to solve this nightmare I'd think field_get_items() and field_set_items() should be what you propose for the field_get_raw_items() + the build/render phase should use field_language() and specifically use the right display language. That way we'd have a single and straight-forward to use function.

I know that would be an API change, but maybe we should consider it? I guess, some display related modules (like Views?) would have to adapt to go with the right default language..

To go without API change in D7 we could leave field_get_items() as is and only introduce entity_language(), which can be used to figure out the right language to use by default. But that puts the burden again on average developers using field-values.. :(

plach’s picture

I don't think an API change is possible at the moment, but we could ask @webchick. Personally the idea of breaking Views (among others) simply terrifies me, although we would have time to schedule a release. But this is a path I'm not really inclined to take: we already had notices appearing in D7.2 and D7.4 and broke the D6 upgrade path in D7.7 due to field language API adjustments. I'd strongly prefer to have a zero-risk solution.

My counter proposal: let's mark field_get_items() as @deprecated and introduce field_view_items() which would behave the same way. This would be consistent with field_view_field() and field_view_value().

Perhaps in this scenario field_get_raw_items() and field_set_items() would be more acceptable, since the only function clashing with them would be deprecated.

webchick’s picture

I'm all about DX improvements, but not at the expense of the 150K+ users of D7 out there in the wild right now. If we set up a pattern of "you download a stable release of Drupal and your site breaks," it won't be long before that number starts declining, rather than climbing. It's also not fair to contributed module developers to have to "chase HEAD" during a stable release. So no, we can't break APIs for this.

fago’s picture

ad #19:
Thanks for your input - makes sense.

My counter proposal: let's mark field_get_items() as @deprecated and introduce field_view_items() which would behave the same way. This would be consistent with field_view_field() and field_view_value().

I like that proposal, as field_get_items() is really misnamed. So that's probably the best we can do.

Not sure whether we should do field_get_raw_items() and field_set_items() or field_get_raw_items() and field_set_raw_items(). Maybe better the last one, as field_set_items() might let developers think there should be field_get_items() too (which is there but not what they expect).

geerlingguy’s picture

Subscribe. I'm still using D6-era getters, because it's simpler to write out $node->field_name[$node->language][0]['value']; than it is to keep having to remind myself how all the entity and field API wrappers work with languages (regardless of how many posts I read on the Planet feed extolling the virtues of the new field API...).

loganfsmyth’s picture

Status: Active » Needs review
StatusFileSize
new5.83 KB

Here is an attempt to get this implemented.

entity_language will attempt to use $entity->language, but also supports a using a callback function in entity_info if $entity->language does not exist.

field_(get|set)_raw_items are implemented without using any fallback and will use the entity language if the field is translatable, or will use LANGUAGE_NONE if not translatable. I have left out the $langcode parameter because, at least with the current implementation's lack of fallback, if you know the langcode you want to use then you can access the entity directly.

field_process_translations is my implementation of the originally proposed 'field_process_items'. I think the original names was somewhat confusing, since at least to me it made it sound like it was iterating over the items, instead of the translations. What do others thing about this? I also pass tons of arguments to that callback, some of which may be unnecessary.

plach’s picture

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

Thanks for working on this!

+++ b/includes/common.inc
@@ -7610,6 +7610,47 @@ function entity_uri($entity_type, $entity) {
+    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+    // A bundle-specific callback takes precedence over the generic one for the
+    // entity type.
+    if (isset($info['bundles'][$bundle]['language callback'])) {
+      $language_callback = $info['bundles'][$bundle]['language callback'];
+    }

I ain't sure we actually need a per-bundle callback. Looks like a pretty edge case that could be handled in the per-entity-type language callback.

+++ b/includes/common.inc
@@ -7610,6 +7610,47 @@ function entity_uri($entity_type, $entity) {
+    if ($language_callback && function_exists($language_callback)) {
+      $entity->language = $language_callback($entity);
+    }
+    else {
+      $entity->language = LANGUAGE_NONE;
+    }

We should not be explicitly assigning an $entity->langauge property, which would be unreliable since in some cases it would be there only after calling entity_language().

As entity_label() does we should give priority to callback and then look for entity-key-provided language key.

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The type of $entity; e.g., 'node' or 'user'

Missing trailing dot.

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The entity containing th data to be returned.

typo

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The field to be retreived.

typo

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The type of $entity; e.g., 'node' or 'user'

Missing trailing dot.

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The field to be retreived.

typo

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The type of $entity; e.g., 'node' or 'user'

Missing trailing dot.

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The entity containing th data to be returned.

typo

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+ *   The field to be retreived.

Missing trailing dot.

+++ b/modules/field/field.module
@@ -971,6 +971,77 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+        $callback($entity_type, $entity, $field, $instance, $langcode, $options, $entity->{$field_name}[$langcode]);

We are missing return values here: they should be merged in an array, as _field_invoke() does, and returned.

13 days to next Drupal core point release.

loganfsmyth’s picture

Status: Needs work » Needs review
StatusFileSize
new7.12 KB

I've reworked entity_language to not have bundle-specific callbacks, and to have a 'language' entity key. I also fixed the typos and added return value aggregation for the field_process_translations.

plach’s picture

#24 looks good to me, thanks!

Actually it's "needs work" as we need to update core tests to use the new functions (see the OP), aw :(
Anyway, I wouldn't go on with this until the new functions are in RTBC status.

The patch looks RTBC to me, altough I'm leaning more towards having optional language parameters to allow for a consistent use of the accessor functions even when language is known. I won't stress on this, however I'd like that @chx and @yched agreed on one or the other camp, if possible :)

plach’s picture

plach’s picture

Another thing: any other feedback on deprecating field_get_items() and introducing field_view_items() instead?

yched’s picture

Not too fond of field_view_items() myself. The current field_view_*() funcs are really related to displaying stuff, and they return render arrays. Getting raw value items is something else.

plach’s picture

@yched:

So the solution here would only be improving the field_get_items() PHP docs, right?

catch’s picture

Subscribing.

plach’s picture

Status: Needs review » Needs work
+++ b/includes/common.inc
@@ -7610,6 +7610,35 @@ function entity_uri($entity_type, $entity) {
+    $langcode = $info['language callback']($entity);

Just spotted this: we need to pass along also the $entity_type here, otherwise it will be impossible to handle entity language in a generic way.

11 days to next Drupal core point release.

loganfsmyth’s picture

Status: Needs work » Needs review
StatusFileSize
new7.13 KB
fago’s picture

Status: Needs review » Needs work
+++ b/includes/common.inc
@@ -7610,6 +7610,35 @@ function entity_uri($entity_type, $entity) {
+  // Invoke the callback to get the language. If there is no callback, try
+  // to get it from a property of the entity, otherwise LANGUAGE_NONE.
+  if (isset($info['language callback']) && function_exists($info['language callback'])) {
+    $langcode = $info['language callback']($entity_type, $entity);
+  }

Do we really need to have the callback functionality or would a language key suffice? Keys are better as they allow to query for a certain language and having entity-language that is not query-able kind of sounds silly to me.

+++ b/modules/field/field.module
@@ -971,6 +971,91 @@ function field_get_items($entity_type, $entity, $field_name, $langcode = NULL) {
+    $field = field_info_field($field_name);
+    $available_languages = field_available_languages($entity_type, $field);

Do we have notion to get all available languages for an entity? Or is that out of scope for this patch?

+++ b/modules/system/system.api.php
@@ -87,6 +87,9 @@ function hook_hook_info_alter(&$hooks) {
+ *   - language callback: (optional) A function taking an entity type and an
+ *     entity as arguments and returning the langcode of the entity. The entity
+ *     language is used as the default language when processing field values.

I think we need a clear definition of what the entity language is, independent of fields. So is it the default language for a given entity? Also see #1277776-19: Add generic field/property getters/setters (with optional language support) for entities.

8 days to next Drupal core point release.

fago’s picture

Do we really need to have the callback functionality or would a language key suffice? Keys are better as they allow to query for a certain language and having entity-language that is not query-able kind of sounds silly to me.

Gabor couldn't think of a use-case for language callbacks, so I'd propose not supporting that.

I think we need a clear definition of what the entity language is, independent of fields. So is it the default language for a given entity? Also see #1277776-19: define language handling for entities.

#1277776: Add generic field/property getters/setters (with optional language support) for entities now has a definition:

* The entity language is basically the *default* entity language, i.e. callers can expect each translatable property to have a value in that language, or there is no property value. Translations to other languages might exist, but need not.

So let's improve the entity_language() docs to clarify that.

Do we have notion to get all available languages for an entity? Or is that out of scope for this patch?

Not sure, whether it's "in scope" for that patch. How does "entity_translation" solve that?

Function naming ideas:
1) entity_languages()
2) entity_translations()
3) entity_available_translations()

I'd prefer 2) over 1) because it clarifies which languages it actually returns.

plach’s picture

Sorry for not following up immediately. In Montreal @webchick and I agreed that this patch could be split in two parts: one introducing the entity_language() function and the rest in a following patch since it seems feedbacks on the Field API side are missing.

The main reason for this was that in Montreal, after talking with @Gabor and @webchick, I started working on #1282018: Improve UX of language-aware entity forms. Basically rewriting almost all the Entity translation UI. Since the new UI will heavily rely on entity_language() plus some minor core tweaks (basically replacing $node->language occurrences with entity_language() calls), I hoped to be able to have the patch ready in Montreal. I wanted to have that patch ready before commenting here, but I'm still working on it :(

Gabor couldn't think of a use-case for language callbacks, so I'd propose not supporting that.

I'm sorry, but the one above is a big use case: the language-aware entity form relies on the language callback to switch the form language from the entity language to the current translation language. It is not meant to change the notion of the entity language as defined above, but it allows to make entity form widgets using the entity language to support multiple languages without having to write extra code.

This may sound hackish, but at least for D7, it's the cleanest solution I could think of. We might want to revisit it in the D8 issue, but for D7 I definitely think we need the language callback. The actual entity language is always backed by a property so it might make sense to drop it in D8 if we come up with a cleaner solution for the entity form.

Do we have notion to get all available languages for an entity? Or is that out of scope for this patch?

At the moment we don't have such a notion: only fields are natively multilingual and as such might need to be processed in all the available languages, as stated above. The Entity translation module introduces the concept of multiple languages associated to an entity and could back it with an actual implementation, if we decide that having a core function defining it makes sense from an API consumer perspective.

gábor hojtsy’s picture

Yes, overriding the entity language for the purpose of displaying a different language version of it in a form does sound hackish, and I understand it is the best we can do for now in contrib, but we should separate the entity language from the form language in D8.

plach’s picture

michelle’s picture

Looks like the other issue fizzled out a little over 2 months ago with being marked fixed then a little disagreement that didn't result in it being opened again. I'm just an interested observer, not a participant, but thought I'd give this a little bump in case it fell of peoples' radar due to the other one being marked as closed/fixed.

Michelle

gábor hojtsy’s picture

Issue tags: +language-content

Tagging for the content handling leg of D8MI.

fago’s picture

+1 on splitting the entity_language() part out, if we can move on faster this way. entity_language() is as of now required in order to be able to correctly read/write translatable field values at all!

FYI: I've been working on fixing this for the entity-metadata-wrappers of the entity API module: #1376126: Fix language handling for translatable fields

gábor hojtsy’s picture

@fago: would that temporarily break the translatable fields API or you mean we should get in entity_language() first?

sun’s picture

A possibly iterative approach attacking field languages first sounds perfectly fine to me. Especially since the entity language part seems to require a lot more architectural re-thinking at this point. So I'd say; go ahead and keep on rocking :)

fago’s picture

Sry for being imprecise, I meant going with entity_language() first as suggested by plach in #35. We need that helper function for fixing field-language as the entity-language is the default-language for translatable fields.

plach’s picture

Since we are not moving at all with field_* functions I split out the entity language part, for which we have consensus, into its own issue: #1495648: Introduce entity language support. This will need a reroll.

plach’s picture

Issue summary: View changes

Updated remaining tasks

tim.plunkett’s picture

gábor hojtsy’s picture

Status: Needs work » Closed (duplicate)

Sounds like a duplicate to me?

plach’s picture

Status: Closed (duplicate) » Needs work

How is this a duplicate? None of the problems cited in the OP has been solved yet, at least for D7: entity_language() is just a prerequisite. I'm actively collecting use cases of API usage in a document of mine and so far I think that introdcuing 2/3 new API functions as the one cited in the OP might be really useful to improve the Field API DX and make supporting field language correctly a bit saner process.

It seems the discussion blocked on the bikeshed about function names, moreover Angie seemed to want more discussion about the actual functions. Personally I'd be ready to propose a new patch, relying on the new entity_language() function where appropriate. If we speed up the discussion hopefully this can be closed pretty quickly.

gábor hojtsy’s picture

Ok then how does this map with the magic getter/setter rework of field property access (as explained in http://groups.drupal.org/node/237443 for example)?

plach’s picture

That one can covers a part of the functions cited in the OP, but only for D8, namely what I called field_get_raw_items() and field_set_raw_items(). We need at very least those in D7 to call this fixed, however there is another case that might be useful to cover. Sometimes you need just the language code to be used in a save context:

<?php
/**
 * Implementation of hook_node_presave().
 */
function my_module_node_presave($node) {
  $langcode = field_entity_language('node', $node, $field_name);
  if (!empty($node->{$field_name}[$langcode][0]['value'])) {
    $node->{$field_name}[$langcode][0]['value'] = 'manipulated value';
  }
}

/**
 * Returns the language code to be used to update a field items array.
 */
function field_entity_language($entity_type, $entity, $field_name) {
  $field = field_info_field($field_name);
  return field_is_translatable($entity_type, $field) ? entity_language($entity_type, $entity) : LANGUAGE_NONE;
}
?>

At the moment I did not find a great need of the field_process_items() function. We may or may not introduce it now. I don't think that's a critcal part. To sum up this is how I would proceed:

  • Introduce field_get_raw_items() and field_set_raw_items() as language-agnostic item accessors defaulting to the entity language as in D8
  • Introduce field_get_display_items() behaving as field_get_items() and deprecate the latter as per the discussion above
  • Introduce field_entity_language() to get the language code for the update phase in opposition to field_langauge(), which returns the langauge code to be used to view the items to be displayed

This might be probanly retargeted to D7 as most of the functions above won't be needed in D8, perhaps only field_entity_language() (or the OOP equivalent).

Function names are still bikesheddable, obviously :)

gábor hojtsy’s picture

I don't see the bigger picture. Are you saying you target 1 function for D8 inclusion, the rest for D7? Why is this issue marked for D8 then?

plach’s picture

When this issue was opened there was nothing in D8 to address it. Now in D8 it is almost fixed but D7 is still out of luck. This is similar to what happened to entity_language(): actually it never reached D8, but it was "backported" from Entity::language().

That's why I'm saying that we might want to retarget this to D7.

gábor hojtsy’s picture

Version: 8.x-dev » 7.x-dev
Priority: Critical » Major

Then it would be more like a major for D7, right?

plach’s picture

I don't get the reason for demoting the issue: if it's critical stuff, it's more problematic for D7 where people need it now than in D8 where we have time to fix things properly. However I don't want to bikeshed also this aspect. Someone tell me that we have an agreement on the plan outlined in #49, and I'll just go on and provide a patch ASAP.

Edit: possible alternative names: field_items() instead of field_get_display_items() (for consistency with field_language()).

tim.plunkett’s picture

#49 makes a lot of sense to me

plach’s picture

Assigned: Unassigned » plach

@tim.plunkett:

Thanks for trying to keep this alive. I'll provide a patch implementing #49 later today.

plach’s picture

Assigned: plach » Unassigned
Status: Needs work » Needs review
StatusFileSize
new9.54 KB

Here is a patch implementing #49. The only relevant thing is that I used the field_working_language() name instead of field_entity_language() because it seems more self-documenting to me. Moreover the value returned might not always be the entity language. A test is also provided.

chx’s picture

Priority: Major » Critical

OK, let's put this back in the critical queue where this belongs. Note: just because I was alone filing an issue about this, people were unhappy back before D7 was released (https://twitter.com/quicksketch/status/11505377996) and look at this in #21 $node->field_name[$node->language][0]['value']; which I always suspected being wrong and I now suspect even stronger. However, #56 still does not help #7 -- when should I provide $langcode to these new wrappers and what should I provide?

plach’s picture

Yes #21 is wrong: it won't work as soon as you assign a language to nodes.

I have been experimenting #56 for a while and published the result in http://drupal.org/project/fieldx, where I will continue my research: the entity language default works pretty well, and doesn't require you to esplictly specify a language in pratically all the situations where I tested it. However there are some situations where you actually want a language different from the entity one, and in those case the wrapper are still useful because you don't have to manually check field translatability. Otherwise you would need to determine if you need to use LANGUAGE_NONE or the language you wish to.

If it's still unclear that $langcode parameters should never be provided unless you really know what you are doing (same as t()), I think it's a documentation problem.

chx’s picture

Add to "(optional) The language code to be used if the field is translatable. Defaults to the entity language." use what you said "This argument is used very rarely, unless you know what you are doing, it should be left empty."

plach’s picture

Implemented #59.

I'd sugges to include in this patch also a function to retrieve field items from a form state array, which may come very useful in validation and submit handlers where all the wrappers above might not work well.

See http://drupalcode.org/project/fieldx.git/blob/refs/heads/7.x-1.x:/fieldx... for an example of the code I'm talking about.

plach’s picture

StatusFileSize
new10.16 KB

the patch...

chx’s picture

Status: Needs review » Reviewed & tested by the community

That is crystal clear, thanks.

chx’s picture

Issue summary: View changes

Updated issue summary.

plach’s picture

@chx:

While updating the issue summary I noticed that one of the pending tasks is still open: update existing tests to exploit the new API functions. This is one thing you explictly suggested to provide people real-life examples of correct API usages. Should we postpone this to a non-critical follow-up?

chx’s picture

Better to have this in 7.15 than not to. We can give examples in 16.

gábor hojtsy’s picture

Agreed.

gábor hojtsy’s picture

Issue summary: View changes

Updated the summary to reflect the RTBC patch in #60

David_Rothstein’s picture

Version: 7.x-dev » 8.x-dev
Status: Reviewed & tested by the community » Needs review

It sounds to me like most of this is already in D8 (or has equivalent methods in D8), but not all of it... For example, we still have field_get_items() in D8 (and no OOP equivalent of that one yet, that I know of), so why isn't that renamed to field_get_display_items() in D8 also?

In addition, some of the documentation and tests look to me like they should really go into D8 too.

***

I also noticed a few nitpicky things:

  1. If field_get_items() is deprecated, shouldn't the patch change all instances of it in core to field_get_display_items() instead?
  2.  /**
    + * @deprecated
    + * Returns the field items in the language they currently would be displayed.
    

    The @deprecated should probably go later on in the PHPDoc, I think right before the @see line based on other examples.

  3. There are a few instances of this:
    + *   Defaults to the current language. This argument is used very rarely, unless
    + *   you know what you are doing, it should be left empty.
    

    The comma before "unless" should be changed to a semi-colon.

***

Also, some general feedback; as a developer, I think this patch would help a fair amount, but I'm still very confused about the big picture. There's an entire field_language @defgroup at the top of field.multilingual.inc with an overview of the language system; I would find it really helpful if a lot of the practical information about "which function to use when" existed there, and the individual functions' PHPDoc could link to it.

What I think I'm missing overall is that I know functions like _field_invoke_multiple() do a foreach() loop over languages when they set things, so to be safe I've always done something similar in custom code. If the field structure has more than one language why is it safe for field_set_raw_items() to only modify one of them, rather than doing a foreach loop? I'd love to see documentation explaining fundamental things like that.

***

Those last couple of paragraphs are more general feedback that don't necessarily have to be solved in this issue, but I do get the strong impression that we need to get some other things in shape for D8 first, before committing this patch to D7. Sorry.

gábor hojtsy’s picture

Note that our priority is to support the inclusion of the property API from @fago, which changes all this thing up for Drupal 8. So as it stands here, to me this looks like this being moved back to D8 will postpone this until it becomes irrelevant again on Drupal 8 and then we can move back to Drupal 7 with need of a possibly painful reroll. Given we have to focus on other things to solve key problems in Drupal 8 and the DX is taken care of by property API basically.

webchick’s picture

Priority: Critical » Major

I'm moving this down to major. Sorry, chx. We are over thresholds, and my rationale is that it cannot by definition be critical because we shipped D7 despite it, and the D8 situation is already vastly cleaned up.

plach’s picture

Status: Needs review » Postponed

We cannot work on this for D8 until the Entity Field API conversion is done.

plach’s picture

Status: Postponed » Needs work

We are addressing this for D8 in the following issues (see the related change notice):

#1810370: Entity Translation API improvements
#2019055: Switch from field-level language fallback to entity-level language fallback

Hence I'm setting this back to D7. Changing to needs work to address #66.

plach’s picture

Version: 8.x-dev » 8.0-alpha2
plach’s picture

Version: 8.0-alpha2 » 7.x-dev

wtf

pancho’s picture

Status: Needs work » Needs review
Issue tags: -translatable fields, -API addition, -Needs backport to D7, -D8MI, -tfdx, -montreal, -language-content

#61: drupal-tfdx-1260640-61.patch queued for re-testing.

pancho’s picture

Issue summary: View changes

Tests updates will be addressed in a follow-up

chx’s picture

It's been two years since the last, agreed upon patch in this issue. See the avatar to the left for more.

chx’s picture

Issue tags: +sad chx
David_Rothstein’s picture

Status: Needs review » Needs work
Issue tags: -

I just spent part of the last few days in some kind of multilingual Drupal purgatory while working on a particular site :) I remembered this issue and consulted the patch here to get some help, but it actually left me pretty confused...

The big questions I still had were:

  1. The difference between "rendering" and "manipulating" entities (as described in the patch) is not clear to me technically; in some cases it's obvious I'm doing one or the other, but in other cases (loading an entity and checking a particular field value) it seems like neither and I had no idea which function to use. In addition you can have more complicated use cases involving more than one entity; if I'm in the middle of rendering a node and need to make a decision about node rendering based on the value of a field attached to a taxonomy term, which language do I use when getting the field value from the term (the node's display language? the term's display language? the term's working language?). I think we need a more precise technical definition. A concrete example would also be extremely useful here, of a situation in which field_get_raw_items() and field_get_display_items() return different values and why (this could be part of the function docs if the example isn't too convoluted).
  2. When setting field values, I also had trouble (similar to my comment from a couple years ago) figuring out which language or languages to use. If I am saving a field value programmatically (let's say a cron run which loads entities and sets a field and saves them) I may want to save it in a way that guarantees it has that value regardless of which language the entity is being viewed or edited in. The only way I could figure out how to do that was to loop over field_available_languages() and set it for each, which seems to work but results in extra rows in the field database tables after saving (one for each possible language on the site, regardless of whether a translation actually exists for that entity in that language). It doesn't seem to cause any harm but it makes me think I'm doing something wrong. I did not find as many situations where field_set_raw_items() from the patch here seemed useful, since it only sets the field value for one language.

Other smaller issues:

  1. To keep terminology consistent I think we need to deprecate field_language() in this patch and replace it with a new function called field_display_language().
  2. Perhaps field_working_language() should have a static cache, like the other field languages do? (I'm not sure if this is actually important.)
  3. The "display" vs "raw" terminology is confusing (it sounds like it has something to do with sanitizing text). Would something like field_get_items_in_display_language() and field_get_items_in_working_language() be good, or too verbose?
  4. I am not sure "working" is the right terminology either, but it could be (depends on the answers to the bigger questions I asked above).

Hope this is useful feedback and questions - I would really like to get something into Drupal 7 for this issue, to improve the developer experience.

David_Rothstein’s picture

Adding a related issue, which could be used for improving the overall documentation at the top of field.multilingual.inc (with this issue just about adding the new functions and documenting them individually).

plach’s picture

Status: Needs work » Closed (won't fix)

I think there is no chance for this to happen anymore.