API de traducción de Entidades
En Drupal 8, el idioma de los campos ya no se proporciona en la API pública, en su lugar los campos se adjuntan a objetos con soporte de idioma, de los cuales "heredan" su idioma.
Las principales ventajas aquí son:
- No necesitamos preocuparnos por la portabilidad de los campos, ya que esta la gestiona el objeto entidad internamente.
// Determinar de alguna forma el $active_langcode.
$translation = $entity->getTranslation($active_langcode);
$value = $translation->field_foo->value;
- Ya no es necesario pasar el idioma activo, de hecho podemos simplemente pasar el objeto de traducción que implementa EntityInterface y es esencialmente un clon del objeto original, solo con un idioma interno diferente. Esto significa que en muchos casos el código recibido puede no conocer el idioma (por supuesto, a menos que explícitamente trate con el idioma).
// Instanciar el objeto de traducción adecuado solo una vez y pasarlo
// donde sea necesario. Esto es típicamente manejado por subsistemas del core
// y en muchos casos comunes no se requiere recuperar explícitamente
// el objeto de traducción.
$langcode = Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT);
$translation = $entity->getTranslation($langcode);
entity_do_stuff($translation);
function entity_do_stuff(EntityInterface $entity) {
$value = $entity->field_foo->value;
// hacer cosas
}
- Ahora tenemos una API reutilizable para la negociación del idioma de la entidad, que se puede usar para determinar la traducción de la entidad que mejor se adapta a un contexto determinado:
// Código simplificado para generar un array renderizable para una entidad.
function viewEntity(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
// El método EntityManagerInterface::getTranslationFromContext()
// aplicará la lógica de negociación de idioma de la entidad a todo el objeto
// y devolverá el objeto de traducción apropiado para el contexto dado.
// El parámetro $langcode es opcional e indica el idioma del contexto actual.
// Si no se especifica, se usa el idioma de contenido actual,
// que es el comportamiento deseado durante la fase de renderizado.
// Tenga en cuenta que los valores de los campos no se modifican,
// así que los valores vacíos simplemente no se mostrarán.
$langcode = NULL;
$translation = $this->entityManager->getTranslationFromContext($entity, $langcode);
$build = entity_do_stuff($translation, 'full');
return $build;
}
También podemos especificar un parámetro opcional $context, que puede usarse para describir el contexto en el que se usará el objeto de traducción:
// Código simplificado para la generación de reemplazos de tokens.
function node_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
// Si no se especifica idioma para este contexto, simplemente usamos
// el idioma predeterminado de la entidad.
if (!isset($options['langcode'])) {
$langcode = Language::LANGCODE_DEFAULT;
}
// Pasamos un parámetro $context que describe la operación que se realiza.
// La operación predeterminada es 'entity_view'.
$context = array('operation' => 'node_tokens');
$translation = \Drupal::service('entity.repository')->getTranslationFromContext($data['node'], $langcode, $context);
$items = $translation->get('body');
// hacer cosas
return $replacements;
}
La lógica usada para determinar el objeto de traducción devuelto puede ser modificada por módulos. Véase LanguageManager::getFallbackCandidates() para más detalles.
Los datos reales de los campos se distribuyen entre todos los objetos de traducción, y modificar el valor de un campo no traducible lo cambia automáticamente para todos los objetos de traducción.
$entity->langcode->value = 'en';
$translation = $entity->getTranslation('it');
$en_value = $entity->field_foo->value; // $en_value es 'bar'
$it_value = $translation->field_foo->value; // $it_value es 'bella'
$entity->field_untranslatable->value = 'baz';
$translation->field_untranslatable->value = 'zio';
$value = $entity->field_untranslatable->value; // $value es 'zio'
En cualquier momento se puede crear una instancia de un objeto de traducción desde el objeto original o desde otro objeto de traducción mediante el método EntityInterface::getTranslation(). Si se necesita explícitamente el idioma activo, se puede obtener con EntityInterface::language(). La entidad original puede obtenerse con EntityInterface::getUntranslated().
$entity->langcode->value = 'en';
$translation = $entity->getTranslation('it');
$langcode = $translation->language()->id; // $langcode es 'it';
$untranslated_entity = $translation->getUntranslated();
$langcode = $untranslated_entity->language()->id; // $langcode es 'en';
$identical = $entity === $untranslated_entity; // $identical es TRUE
$entity_langcode = $translation->getUntranslated()->language()->id; // $entity_langcode es 'en'
EntityInterface ahora tiene varios métodos que facilitan trabajar con traducciones de entidades. Si un fragmento de código debe actuar sobre cada traducción disponible, puede usar EntityInterface::getTranslationLanguages():
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
entity_do_stuff($translation);
}
También existen formas de agregar una traducción, eliminarla o comprobar si existe:
if (!$entity->hasTranslation('fr')) {
$translation = $entity->addTranslation('fr', array('field_foo' => 'bag'));
}
// Esto es equivalente al siguiente código, aunque si se especifica un código
// de idioma inválido se lanzará una excepción.
$translation = $entity->getTranslation('fr');
$translation->field_foo->value = 'bag';
// Acceder a un campo en un objeto de traducción eliminado provoca una excepción.
$translation = $entity->getTranslation('it');
$entity->removeTranslation('it');
$value = $translation->field_foo->value; // lanza InvalidArgumentException
Cuando se añaden o eliminan traducciones de entidad al almacenamiento, se disparan respectivamente los siguientes hooks:
- hook_entity_translation_insert()
- hook_entity_translation_delete()
El idioma del campo aún puede obtenerse llamando al método correspondiente del propio objeto campo:
$langcode = $translation->field_foo->getLangcode();