Problem/Motivation

TokenTokenInfoHooks::tokenInfoAlter() automatically registers a language chained token ([entity_type:language:*]) for every content entity token type that doesn't already define its own.

This registration does not check whether the entity type actually has a langcode field. When the token is resolved, TokenTokensHooks::tokens() reads the field directly instead of using the entity's \Drupal\Core\Entity\EntityInterface::language() method.

Not every content entity is required to declare a langcode entity key / base field - it's entirely valid for a simple, non-translatable content entity to omit it. For such entities this throws:

php.ERROR: InvalidArgumentException: Field langcode is unknown. in Drupal\Core\Entity\ContentEntityBase->getTranslatedField() (line 630 of /var/www/html/web/core/lib/Drupal/Core/Entity/ContentEntityBase.php).

Note that for the field tokens it is already using this method for getting a proper language instead of direct access: TokenTokensHooks::fieldTokens()

Steps to reproduce

  1. Define a content entity type that does not declare a langcode entity key
  2. Save an instance of this entity.
  3. Resolve any token from its token tree that touches the auto-registered language sub-tree, e.g:
    <?php
    
    use Drupal\module\Entity\MyEntity;
    use Drupal\Core\Render\BubbleableMetadata;
    
    $entity = MyEntity::load(1);
    $replacements = \Drupal::token()->generate(
      'my_entity',
      ['language:langcode' => '[my_entity:language:langcode]'],
      ['my_entity' => $entity],
      [],
      new BubbleableMetadata(),
    );
    

    This also happens implicitly when eagerly resolving a full token tree with
    'values' => TRUE via TreeBuilder::buildTree() (e.g. any token browser/preview UI built on top of Token module).

  4. Field langcode is unknown. is thrown.

Proposed resolution

Replace the direct field access with the entity's own language method:

$language_options = array_merge($options, [
  'langcode' => $entity->language()->getId(),
]);

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork token-3611063

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

niklan created an issue. See original summary.

niklan’s picture

Status: Active » Needs review
csakiistvan’s picture

Assigned: Unassigned » csakiistvan
csakiistvan’s picture

Assigned: csakiistvan » Unassigned
Status: Needs review » Reviewed & tested by the community

Environment

  • Drupal: 11.4.4
  • PHP: 8.5.5
  • Database: MariaDB 10.11.16
  • DDEV: v1.25.2
  • Token: 8.x-1.17

Prerequisites

  • Enable the module's token_module_test testing submodule, which provides a content entity type (token_test_no_langcode) that does not declare a langcode entity key:
    ddev drush en token_module_test -y
  • Install the new entity type's schema and save an instance of it.

Steps

  1. Apply the fix from MR !134: use the entity's language() method instead of reading the langcode field directly when resolving [entity:language:*] chained tokens.
  2. Rebuild caches: ddev drush cr
  3. Resolve a [token_test_no_langcode:language:langcode] token against a saved instance of the entity (e.g. via \Drupal::token()->generate()).

Expected results

  • The token resolves without throwing an exception, returning the entity's language code (e.g. und).

Actual results

Before the fix, resolving the [token_test_no_langcode:language:langcode] token threw InvalidArgumentException: Field langcode is unknown., since the code read the langcode field directly on an entity type that doesn't declare one. After applying the fix, the token resolved correctly to und with no exception.


Testing produced with the assistance of an LLM.