logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动
30/09/2025, by Ivan

在 Drupal 8 中,字段语言不再通过公共 API提供,而是附加到支持语言的实体对象上,并从这些对象中“继承”其语言。

这里的主要优点是:

  • 我们不再需要担心字段的可移植性,因为实体对象会在内部处理。
      // 以某种方式确定 $active_langcode。
      $translation = $entity->getTranslation($active_langcode);
      $value = $translation->field_foo->value;
  • 我们不再需要传递活动语言,实际上我们可以直接传递翻译对象,该对象实现了 EntityInterface,实际上是原始对象的克隆,只是具有不同的内部语言。这意味着在很多情况下,生成的代码可能并不知道语言(当然,除非它显式与语言相关)。
      // 只实例化一次合适的翻译对象,并在需要的地方传递。
      // 通常由核心子系统负责,在很多常见情况下不需要显式检索翻译对象。
      $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;
        // 处理逻辑
      }
  • 现在我们有了可重用的实体语言协商 API,可以用来确定最适合某一特定上下文的实体翻译:
      // 生成实体可渲染数组的简化代码。
      function viewEntity(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
        // EntityManagerInterface::getTranslationFromContext() 方法将
        // 对整个实体对象应用实体语言协商逻辑,并返回适用于给定上下文的正确翻译对象。
        // $langcode 参数是可选的,用于指示当前上下文的语言。
        // 如果未指定,则使用当前内容语言,这正是渲染阶段所需的行为。
        // 注意:字段值不会在此过程中更改,因此空值不会显示。
        $langcode = NULL;
        $translation = $this->entityManager->getTranslationFromContext($entity, $langcode);
        $build = entity_do_stuff($translation, 'full');
        return $build;
      }

我们还可以指定可选的 $context 参数,用于描述翻译对象将使用的上下文:

      // 简化的 token 替换生成代码。
      function node_tokens($type, $tokens, array $data = array(), array $options = array()) {
        $replacements = array();

        // 如果此上下文没有指定语言,则默认使用实体的默认语言。
        if (!isset($options['langcode'])) {
          $langcode = Language::LANGCODE_DEFAULT;
        }

        // 我们传递一个 $context 参数,描述正在执行的操作。
        // 默认操作是 'entity_view'。
        $context = array('operation' => 'node_tokens');
        $translation = \Drupal::service('entity.repository')->getTranslationFromContext($data['node'], $langcode, $context);
        $items = $translation->get('body');

        // 执行逻辑

        return $replacements;
      }

用于确定返回的翻译对象的逻辑可以被模块修改。更多详细信息请参阅 LanguageManager::getFallbackCandidates()

字段的实际数据在所有翻译对象之间共享,更改不可翻译字段的值会自动更改所有翻译对象。

  $entity->langcode->value = 'en';
  $translation = $entity->getTranslation('it');
  
  $en_value = $entity->field_foo->value; // $en_value 是 'bar'
  $it_value = $translation->field_foo->value; // $it_value 是 'bella'

  $entity->field_untranslatable->value = 'baz';
  $translation->field_untranslatable->value = 'zio';
  $value = $entity->field_untranslatable->value; // $value 是 'zio'

任何时候都可以通过 EntityInterface::getTranslation() 方法从原始对象或其他翻译对象创建翻译对象的实例。如果显式需要活动语言,可以通过 EntityInterface::language() 获取。原始实体可以通过 EntityInterface::getUntranslated() 获取。

  $entity->langcode->value = 'en';

  $translation = $entity->getTranslation('it');
  $langcode = $translation->language()->id; // $langcode 是 'it';

  $untranslated_entity = $translation->getUntranslated();
  $langcode = $untranslated_entity->language()->id; // $langcode 是 'en';

  $identical = $entity === $untranslated_entity; // $identical 是 TRUE

  $entity_langcode = $translation->getUntranslated()->language()->id; // $entity_langcode 是 'en'

EntityInterface 现在有多个方法简化了实体翻译的使用。如果代码片段需要对每个可用翻译执行操作,可以使用 EntityInterface::getTranslationLanguages():

  foreach ($entity->getTranslationLanguages() as $langcode => $language) {
    $translation = $entity->getTranslation($langcode);
    entity_do_stuff($translation);
  }

还有方法可以添加翻译、删除翻译或检查翻译是否存在:

  if (!$entity->hasTranslation('fr')) {
    $translation = $entity->addTranslation('fr', array('field_foo' => 'bag'));
  }

  // 等价于以下代码,不过如果指定了无效的语言代码会抛出异常。
  $translation = $entity->getTranslation('fr');
  $translation->field_foo->value = 'bag';

  // 访问已删除翻译对象的字段会抛出异常。
  $translation = $entity->getTranslation('it');
  $entity->removeTranslation('it');
  $value = $translation->field_foo->value; // 抛出 InvalidArgumentException

当实体翻译被添加到存储或从存储中移除时,会分别触发以下钩子:

  • hook_entity_translation_insert()
  • hook_entity_translation_delete()

字段的语言仍然可以通过调用字段对象本身的方法来获取:

  $langcode = $translation->field_foo->getLangcode();