0

I'm working on a contrib module port to D8 and can't seem to find appropriate Entity API documentation to figure out how to get a rendered field in a specific language. I have a controller that needs to generate some custom XML based on field data, and I would like to be able to use core's language detection to get an appropriately translated field that can be used to generate this XML.

(As an aside -- I realize that core services could be leveraged to get field data in a RESTful way, but the XML schema needed here is quite custom and warrents its own REST endpoint and controller.)

Consider a route for a REST endpoint, that simply accepts the entity and field specifics needed, via the path like:

mymodule.xml_field:
  path: '/mymodule/xml/field/{entityType}/{entityId}/{fieldName}/{displayName}'
  defaults:
    _controller: '\Drupal\mymodule\Controller\MymoduleXmlControllerField::xmlController'
  requirements:
    _access: 'TRUE'

And a matching controller method like:

class MymoduleXmlControllerField implements ContainerInjectionInterface {

  // ... Entity manager and other services are injected ...

  public function xmlController($entityType, $entityId, $fieldName, $displayName) {
    $entity = $this->entityManager->getStorage($entityType)->load($entityId);
    $field = $entity->{$fieldName}->view($displayName);

    // ... Extract needed data from $field, generate XML and return it in new Response() object ...
  }
}

This works great until I try to leverage content translations. If I use default URL language detection and then request the XML via a language specific path (with a prefix, like: /es/mymodule/xml/field/...) the default lanaguage is always still used in the $field output instead of the spanish (es) translation (which I've confirmed is available on the entity).

My initial expectation was that the entity load call, or the field view call, would internally trigger language detection (as in D7), but I see that it's now up to my controller to manually specify and set a langcode somehow as the loaded entity object seems to be language neutral. I'm just wondering how I do this given that the high-level entity/field interfaces I'm using don't take a langcode paramater, and (at the time of writing) the entity API docs don't seem to address this.

I'm assuming that I need to use a setter method somewhere on the entity or fieldlist object to declare a langcode before calling the view method. It looks like I can get the detected language easily via an injected language manager:

$langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();

But I'm not sure where to go from there.

2 Answers 2

1

You need get Translation of entity. function entity load just load default language, not load current language. Try this:

public function xmlController($entityType, $entityId, $fieldName, $displayName) {
    $entity = $this->entityManager->getStorage($entityType)->load($entityId);

    $entity_es = $entity->getTranslation('es');

    $field = $entity->{$fieldName}->view($displayName);
    new Response() object ...
  }

and you need detect language to auto. Code above just example.

3
  • Right, I see. That's fairly simple as getTranslation() just seems to be setting the appropriate langcode internally on a cloned copy of the object. I was so fixated on where this property is "set" (request, entity, field or something else) that I overlooked that method entirely. Now that I see that the variation is entity-specific I think this whole concept should be pretty transferable to other stuff I'm working on.
    – rjacobs
    Commented Jan 28, 2016 at 4:16
  • 1
    I also found this info fairly useful: drupal.org/node/2040721
    – rjacobs
    Commented Jan 28, 2016 at 4:16
  • I did upvote this as it provided the right high-level context. However more research revealed that the language detection and translation can likely be handled in parallel using the Entity Repository service, which may be slightly more targeted to the example case from the question. I've outlined those details in a separate answer.
    – rjacobs
    Commented Feb 1, 2016 at 23:23
0

The answer from MrD very effectively outlines where the "language awareness" happens (on the entity) but it does not reveal how to cleanly leverage core's language detection for the example case.

Calling ContentEntityBase::getTranslation() will produce a translated clone of the base entity but it does not verify if the translation is available and provide any fallback version if not. As a result one could manually detect the language (with LanguageManagerInterface::getCurrentLanguage()) and then manually check if a translations for it exists, etc., before calling getTranslation(). However it looks like there is a single method that does all this in one call:

EntityRepository::getTranslationFromContext()

So the solution for the XML example generation case could likely be handled with one additional line:

class MymoduleXmlControllerField implements ContainerInjectionInterface {

  // ... Entity Type Manager and Entity Repository services are injected ...

  public function xmlController($entityType, $entityId, $fieldName, $displayName) {
    $entity_base = $this->entityTypeManager->getStorage($entityType)->load($entityId);
    // Get proper translation, with appropriate fallback, via core auto-detection.
    $entity_trans = $this->entityRepository->getTranslationFromContext($entity_base);
    $field = $entity_trans->{$fieldName}->view($displayName);

    // ... Extract needed data from $field, generate XML and return it in new Response() object ...
  }
}

As far as I know this would be a fairly universal programmatic recipe that's compatible with multilingual and non-multilingual setups.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.