diff --git a/src/Context/ContextDefinition.php b/src/Context/ContextDefinition.php
index 7950b8c6..6b0249f9 100644
--- a/src/Context/ContextDefinition.php
+++ b/src/Context/ContextDefinition.php
@@ -118,6 +118,13 @@ class ContextDefinition extends ContextDefinitionCore implements ContextDefiniti
    */
   protected $assignmentRestriction = NULL;
 
+  /**
+   * An array of data type => widget id pairs.
+   *
+   * @var string[]
+   */
+  protected $widgetList;
+
   /**
    * {@inheritdoc}
    */
@@ -206,4 +213,80 @@ class ContextDefinition extends ContextDefinitionCore implements ContextDefiniti
     return $this;
   }
 
+  /**
+   * An array of standard data types and their corresponding widget id.
+   *
+   * This covers all the data types provided by Core and Typed Data API
+   * Enhancements, apart from 'binary' and 'map'.
+   *
+   * @return string[]
+   *   An array of data type => widget id.
+   */
+  protected static function getStandardWidgetList() {
+    return [
+      // Single line text input.
+      'any' => 'text_input',
+      'boolean' => 'text_input',
+      'email' => 'text_input',
+      'entity' => 'text_input',
+      'entity_reference' => 'text_input',
+      'float' => 'text_input',
+      'integer' => 'text_input',
+      'language' => 'text_input',
+      'language_reference' => 'text_input',
+      'string' => 'text_input',
+      'uri' => 'text_input',
+
+      // Multi-line text area.
+      'list' => 'textarea',
+      'text' => 'textarea',
+
+      // Date.
+      'datetime_iso8601' => 'datetime',
+      'timestamp' => 'datetime',
+
+      // Date range.
+      'duration_iso8601' => 'datetime_range',
+      'timespan' => 'datetime_range',
+
+      // Selection list.
+      'none-yet' => 'select',
+    ];
+  }
+
+  /**
+   * Derive a widget id from a datatype.
+   *
+   * The widget id is used to generate a form element provided by Typed Data.
+   * Modules can implement hook_typed_data_widgetlist_alter() to declare which
+   * widget to use for their custom data types.
+   *
+   * @todo this function should be moved into the typed_data module. But whilst
+   * still developing the integration of widgets in Rules it can stay here.
+   * Inject $this->moduleHandler instead of calling \Drupal::moduleHandler() ?
+   *
+   * @param string $dataType
+   *   The datatype to check.
+   *
+   * @return string
+   *   The id of the widget to use when building the input form.
+   */
+  public function getWidgetId($dataType) {
+    if (!isset($this->widgetList)) {
+      $this->widgetList = static::getStandardWidgetList();
+      // Allow other modules to add to the datatype -> widget id list by
+      // invoking all hook_typed_data_widgetlist_alter() implementations.
+      \Drupal::moduleHandler()->alter('typed_data_widgetlist', $this->widgetList);
+    }
+    // Return the widget id for this data type. If none, default to 'broken' id.
+    return $this->widgetList[$dataType] ?? self::BROKEN_WIDGET_ID;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWidgetSettings() {
+    return [];
+  }
+
 }
diff --git a/src/Context/ContextDefinitionInterface.php b/src/Context/ContextDefinitionInterface.php
index db91a049..2bccc9f9 100644
--- a/src/Context/ContextDefinitionInterface.php
+++ b/src/Context/ContextDefinitionInterface.php
@@ -20,6 +20,20 @@ interface ContextDefinitionInterface extends ContextDefinitionInterfaceCore {
   const ASSIGNMENT_RESTRICTION_INPUT = 'input';
   const ASSIGNMENT_RESTRICTION_SELECTOR = 'selector';
 
+  /**
+   * Constant for the undefined/broken widget id.
+   *
+   * @see ::getWidgetId()
+   */
+  const BROKEN_WIDGET_ID = 'broken';
+
+  /**
+   * Constant for "do not use a widget".
+   *
+   * @see ::getWidgetId()
+   */
+  const DONT_USE_WIDGET = 'do_not_use_a_widget';
+
   /**
    * Determines if the context value is allowed to be NULL.
    *
@@ -70,4 +84,24 @@ interface ContextDefinitionInterface extends ContextDefinitionInterfaceCore {
    */
   public function toArray();
 
+  /**
+   * Gets the widget id for a data type.
+   *
+   * @param string $dataType
+   *   The data type of the field.
+   *
+   * @return string
+   *   A string with the widget id. Will return SELF::BROKEN_WIDGET_ID if the
+   *   data type needed by the context is not supported by any widget.
+   */
+  public function getWidgetId($dataType);
+
+  /**
+   * Gets default configuration of the widget.
+   *
+   * @return array
+   *   An associative array with the default configuration.
+   */
+  public function getWidgetSettings();
+
 }
diff --git a/src/Context/ContextHandlerIntegrityTrait.php b/src/Context/ContextHandlerIntegrityTrait.php
index 31b4c231..f4650846 100644
--- a/src/Context/ContextHandlerIntegrityTrait.php
+++ b/src/Context/ContextHandlerIntegrityTrait.php
@@ -129,26 +129,32 @@ trait ContextHandlerIntegrityTrait {
    *   The list of violations where new ones will be added.
    */
   protected function checkDataTypeCompatible(CoreContextDefinitionInterface $context_definition, DataDefinitionInterface $provided, $context_name, IntegrityViolationList $violation_list) {
-    // Compare data types. For now, fail if they are not equal.
-    // @todo Add support for matching based upon type-inheritance.
-    $target_type = $context_definition->getDataDefinition()->getDataType();
+    $expected_type = $context_definition->getDataDefinition()->getDataType();
+    $provided_type = $provided->getDataType();
+    if ($expected_type === $provided_type) {
+      return;
+    }
 
-    // Special case any and entity target types for now.
-    if ($target_type == 'any' || ($target_type == 'entity' && strpos($provided->getDataType(), 'entity:') !== FALSE)) {
+    // Make a special case for 'any' and 'entity' expected types for now.
+    if ($expected_type === 'any' || ($expected_type === 'entity' && strpos($provided_type, 'entity:') !== FALSE)) {
       return;
     }
-    if ($target_type != $provided->getDataType()) {
-      $expected_type_problem = $context_definition->getDataDefinition()->getDataType();
-      $violation = new IntegrityViolation();
-      $violation->setMessage($this->t('Expected a @expected_type data type for context %context_name but got a @provided_type data type instead.', [
-        '@expected_type' => $expected_type_problem,
-        '%context_name' => $context_definition->getLabel(),
-        '@provided_type' => $provided->getDataType(),
-      ]));
-      $violation->setContextName($context_name);
-      $violation->setUuid($this->getUuid());
-      $violation_list->add($violation);
+
+    $provided_class = $provided->getClass();
+    $expected_class = $context_definition->getDataDefinition()->getClass();
+    if (is_subclass_of($expected_class, $provided_class)) {
+      return;
     }
+
+    $violation = new IntegrityViolation();
+    $violation->setMessage($this->t('Expected a %allowed_type data type for context %context_name but got a %provided_type data type instead.', [
+      '%allowed_type' => $expected_type,
+      '%context_name' => $context_definition->getLabel(),
+      '%provided_type' => $provided->getDataType(),
+    ]));
+    $violation->setContextName($context_name);
+    $violation->setUuid($this->getUuid());
+    $violation_list->add($violation);
   }
 
 }
diff --git a/src/Context/ContextHandlerTrait.php b/src/Context/ContextHandlerTrait.php
index 37ccb908..a8b2591e 100644
--- a/src/Context/ContextHandlerTrait.php
+++ b/src/Context/ContextHandlerTrait.php
@@ -267,10 +267,12 @@ trait ContextHandlerTrait {
 
     // Reverse the mapping and apply the changes.
     foreach ($changed_definitions as $context_name => $definition) {
-      $selector = $this->configuration['context_mapping'][$context_name];
-      // @todo Deal with selectors matching not a context name.
-      if (strpos($selector, '.') === FALSE) {
-        $metadata_state->setDataDefinition($selector, $definition);
+      if (isset($this->configuration['context_mapping'])) {
+        $selector = $this->configuration['context_mapping'][$context_name];
+        // @todo Deal with selectors matching not a context name.
+        if (strpos($selector, '.') === FALSE) {
+          $metadata_state->setDataDefinition($selector, $definition);
+        }
       }
     }
   }
diff --git a/src/Context/EntityContextDefinition.php b/src/Context/EntityContextDefinition.php
index c9f41bc6..7e7b135a 100644
--- a/src/Context/EntityContextDefinition.php
+++ b/src/Context/EntityContextDefinition.php
@@ -127,4 +127,22 @@ class EntityContextDefinition extends EntityContextDefinitionCore implements Con
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getWidgetId($dataType) {
+    // @todo This is a temporary work-round. This function will be moving to the
+    // Typed Data module. See src/context/ContextDefinition.
+    return (new ContextDefinition())->getWidgetId($dataType);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWidgetSettings() {
+    // @todo This is a temporary work-round. This function will be moving to the
+    // Typed Data module. See src/context/ContextDefinition.
+    return [];
+  }
+
 }
diff --git a/src/Context/Form/ContextFormTrait.php b/src/Context/Form/ContextFormTrait.php
index 2b24f439..ba12cead 100644
--- a/src/Context/Form/ContextFormTrait.php
+++ b/src/Context/Form/ContextFormTrait.php
@@ -6,25 +6,20 @@ use Drupal\Core\Form\FormStateInterface;
 use Drupal\rules\Context\ContextConfig;
 use Drupal\rules\Context\ContextDefinitionInterface;
 use Drupal\rules\Context\DataProcessorManagerTrait;
+use Drupal\typed_data\Form\SubformState;
+use Drupal\typed_data\Widget\FormWidgetManagerTrait;
 
 /**
  * Provides form logic for handling contexts when configuring an expression.
  */
 trait ContextFormTrait {
   use DataProcessorManagerTrait;
+  use FormWidgetManagerTrait;
 
   /**
    * Provides the form part for a context parameter.
    */
   public function buildContextForm(array $form, FormStateInterface $form_state, $context_name, ContextDefinitionInterface $context_definition, array $configuration) {
-    $form['context_definitions'][$context_name] = [
-      '#type' => 'fieldset',
-      '#title' => $context_definition->getLabel(),
-    ];
-    $form['context_definitions'][$context_name]['description'] = [
-      '#markup' => $context_definition->getDescription(),
-    ];
-
     // If the form has been submitted already take the mode from the submitted
     // value, otherwise check for restriction setting, then check existing
     // configuration, and if that does not exist default to the "input" mode.
@@ -44,6 +39,9 @@ trait ContextFormTrait {
 
     $title = $mode == ContextDefinitionInterface::ASSIGNMENT_RESTRICTION_SELECTOR ? $this->t('Data selector') : $this->t('Value');
 
+    // The default value will be an array if the widget has two or more data
+    // input items, for example datespan, or the Rules context definition has
+    // 'multiple = TRUE'. Otherwise it will be a scalar value.
     if (isset($configuration['context_values'][$context_name])) {
       $default_value = $configuration['context_values'][$context_name];
     }
@@ -53,17 +51,111 @@ trait ContextFormTrait {
     else {
       $default_value = $context_definition->getDefaultValue();
     }
-    $form['context_definitions'][$context_name]['setting'] = [
-      '#type' => 'textfield',
-      '#title' => $title,
-      '#required' => $context_definition->isRequired(),
-      '#default_value' => $default_value,
-    ];
 
-    $element = &$form['context_definitions'][$context_name]['setting'];
+    // Temporary fix: Cast default value to an array if context definition has
+    // 'multiple = TRUE' so that the action can be edited without a fatal error.
+    // @todo Remove when integrity check prevents the wrong type being saved.
+    // @see https://www.drupal.org/project/rules/issues/2723259
+    if ($context_definition->isMultiple() && is_scalar($default_value)) {
+      $default_value = [$default_value];
+    }
+
+    // Derive the widget id using the context definition data type. Take only
+    // the first word before : so that entity:user gives entity.
+    $dataType = explode(':', $context_definition->getDataType())[0];
+    if (in_array($dataType, ['language', 'language_reference'])) {
+      // Language input in Rules forms are better without using a form widget.
+      $widget_id = ContextDefinitionInterface::DONT_USE_WIDGET;
+    }
+    else {
+      $widget_id = $context_definition->getWidgetId($dataType);
+    }
+
+    if ($widget_id === ContextDefinitionInterface::BROKEN_WIDGET_ID) {
+      // The datatype is unknown and/or the typed-data widget has not been coded
+      // yet, so use the 'broken' widget which by design has no input field.
+      \Drupal::messenger()->addError($this->t('No form widget is defined for %label (context name %context_name, data type %dataType). Modules can implement hook_typed_data_widgetlist_alter() to declare which form widget to use. See getWidgetId() and getStandardWidgetList().', [
+        '%context_name' => $context_name,
+        '%dataType' => $dataType,
+        '%label' => is_object($context_definition->getLabel()) ? $context_definition->getLabel()->getUntranslatedString() : '* no label *',
+      ]));
+    }
+
+    if ($widget_id == ContextDefinitionInterface::DONT_USE_WIDGET) {
+      $input_name = 'value';
+      $form['context_definitions'][$context_name] = [
+        '#type' => 'fieldset',
+        '#title' => $context_definition->getLabel(),
+        '#widget_id' => $widget_id,
+      ];
+
+      $form['context_definitions'][$context_name][$input_name] = [
+        '#type' => 'textfield',
+        '#title' => $title,
+        '#description' => $context_definition->getDescription(),
+        '#required' => $context_definition->isRequired(),
+        '#default_value' => $default_value,
+      ];
+    }
+    else {
+      // Create the widget using the widget_id.
+      $widget = $this->getFormWidgetManager()->createInstance($widget_id);
+
+      $dataManager = $context_definition->getTypedDataManager();
+      $dataDefinition = $context_definition->getDataDefinition();
+      $typed_data = $dataManager->create($dataDefinition);
+
+      // Set default before building form, so that widget->form() can use it.
+      $typed_data->setValue($default_value);
+
+      // Create the widget sub-form.
+      $sub_form = [];
+      $sub_form_state = SubformState::createForSubform($sub_form, $form, $form_state);
+      $widget_form = $widget->form($typed_data, $sub_form_state);
+
+      // Add the widget form into the full $form and save the widget_id.
+      $form['context_definitions'][$context_name] = $widget_form + [
+        '#widget_id' => $widget_id,
+      ];
+      // If mode is input (not data-selector) then add a widget class.
+      if ($mode == ContextDefinitionInterface::ASSIGNMENT_RESTRICTION_INPUT) {
+        $form['context_definitions'][$context_name]['#attributes']['class'][] = 'widget-' . str_replace('_', '-', $widget_id);
+      }
+
+      // Get the names of the data item input fields in the widget. Mostly there
+      // is only one input field and often it will be called 'value', but some
+      // datatypes, for example timespan, define two inputs. Remove all array
+      // keys that start with # and this will leave just the input field names.
+      // @todo Could this information be provided more easily, say by calling
+      // $widget->getInputNames() ?
+      $widget_input_names = array_keys($widget_form);
+      $widget_input_names = array_filter($widget_input_names, function ($k) {
+        return substr($k, 0, 1) !== '#';
+      });
+
+      if (isset($widget_form['#theme_wrappers']) && $widget_form['#theme_wrappers'][0] == 'fieldset') {
+        // @todo Is checking #theme_wrappers = fieldset the best way to do it?
+        // Should we count the number of input items? What if the widget had two
+        // or more inputs but was badly formed with no fieldset theme_wrapper?
+        $input_name = end($widget_input_names);
+      }
+      else {
+        // The widget form is simple, so we need to add more of the form here.
+        $form['context_definitions'][$context_name] += [
+          '#type' => 'fieldset',
+          '#title' => $context_definition->getLabel(),
+        ];
+        $input_name = reset($widget_input_names);
+        $form['context_definitions'][$context_name][$input_name]['#title'] = $title;
+      }
+    }
+
+    // Extract the (last) element we have just added.
+    $element = &$form['context_definitions'][$context_name][$input_name];
 
     if ($mode == ContextDefinitionInterface::ASSIGNMENT_RESTRICTION_SELECTOR) {
-      $element['#description'] = $this->t("The data selector helps you drill down into the available data. <em>To make entity fields appear in the data selector, you may have to use the condition 'Entity is of bundle'.</em> More useful tips about data selection are available in <a href=':url'>the online documentation</a>.", [
+      $element['#type'] = 'textfield';
+      $element['#description'] .= ' ' . $this->t("The data selector helps you drill down into the available data. <em>To make entity fields appear in the data selector, you may have to use the condition 'Entity is of bundle'.</em> More useful tips about data selection are available in <a href=':url'>the online documentation</a>.", [
         ':url' => 'https://www.drupal.org/node/1300042',
       ]);
 
@@ -74,18 +166,18 @@ trait ContextFormTrait {
     }
     elseif ($context_definition->isMultiple()) {
       $element['#type'] = 'textarea';
-      // @todo Get a description for possible values that can be filled in.
-      $element['#description'] = $this->t('Enter one value per line for this multi-valued context.');
+      $element['#description'] .= ' ' . $this->t('Enter one value per line for this multi-valued context.');
 
-      // Glue the list of values together as one item per line in the text area.
+      // Convert the array of values into a single string split by newlines, to
+      // show each item on a separate line in the text area.
       if (is_array($default_value)) {
         $element['#default_value'] = implode("\n", $default_value);
       }
     }
 
-    // If the context is not restricted to one mode or the other then provide a
-    // button to switch between the two modes.
-    if (empty($context_definition->getAssignmentRestriction())) {
+    // If the context is not restricted to one mode or the other, and the widget
+    // is ok (not broken) then provide a button to switch between the two modes.
+    if (empty($context_definition->getAssignmentRestriction()) && $widget_id != ContextDefinitionInterface::BROKEN_WIDGET_ID) {
       $value = $mode == ContextDefinitionInterface::ASSIGNMENT_RESTRICTION_SELECTOR ? $this->t('Switch to the direct input mode') : $this->t('Switch to data selection');
       $form['context_definitions'][$context_name]['switch_button'] = [
         '#type' => 'submit',
@@ -132,6 +224,8 @@ trait ContextFormTrait {
   /**
    * Creates a context config object from the submitted form values.
    *
+   * @param array $form
+   *   The complete form array.
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The form state containing the submitted values.
    * @param \Drupal\Core\Plugin\Context\ContextDefinitionInterface[] $context_definitions
@@ -140,27 +234,58 @@ trait ContextFormTrait {
    * @return \Drupal\rules\Context\ContextConfig
    *   The context config object populated with context mappings/values.
    */
-  protected function getContextConfigFromFormValues(FormStateInterface $form_state, array $context_definitions) {
+  protected function getContextConfigFromFormValues(array $form, FormStateInterface $form_state, array $context_definitions) {
     $context_config = ContextConfig::create();
     if ($form_state->hasValue('context_definitions')) {
       foreach ($form_state->getValue('context_definitions') as $context_name => $value) {
+        $context_definition = $context_definitions[$context_name];
+
+        $widget_id = $form['context_definitions'][$context_name]['#widget_id'];
+        if ($context_definition->isMultiple() || $widget_id == ContextDefinitionInterface::DONT_USE_WIDGET) {
+          // If the element does not use a Typed Data widget then get the input
+          // directly from $value. We also use this workaround when isMultiple()
+          // is set, because Rules uses 'multiple = TRUE' combined with a
+          // textarea to allow multiple entry items. However, they have to be
+          // in an array for the TypedData setValue to work without throwing
+          // InvalidArgumentException: Cannot set a list with a non-array value.
+          // @todo This could be changed when issue 2847804 is complete.
+          // @see https://www.drupal.org/project/typed_data/issues/2847804
+          // Remove the 'switch_button' then get the first value, whatever it is
+          // called. Use ?: to convert an empty string to NULL, this is
+          // necessary if the context is given by a data-selector.
+          unset($value['switch_button']);
+          $input = reset($value) ?: NULL;
+        }
+        else {
+          // Create an instance of the widget that was used in this context so
+          // that we can use extractFormValues() to get the entered data.
+          $widget = $this->getFormWidgetManager()->createInstance($widget_id);
+          $data = $context_definition->getTypedDataManager()
+            ->create($context_definition->getDataDefinition());
+          $subform_state = SubformState::createWithParents(['context_definitions', $context_name], $form, $form_state);
+          $widget->extractFormValues($data, $subform_state);
+          $input = $data->getValue();
+        }
+
+        // If the context value has been supplied by a data-selector then store
+        // the mapped value.
         if ($form_state->get("context_$context_name") == ContextDefinitionInterface::ASSIGNMENT_RESTRICTION_SELECTOR) {
-          $context_config->map($context_name, $value['setting']);
+          $context_config->map($context_name, $input);
         }
         else {
-          // Each line of the textarea is one value for 'multiple' contexts.
-          if ($context_definitions[$context_name]->isMultiple()) {
+          // Not data-selector, so it must be input.
+          if ($context_definition->isMultiple()) {
             // Textareas should always have \r\n line breaks, but for more
             // robust parsing we should also accommodate just \n or just \r.
-            //
             // Additionally, we want to remove leading and trailing whitespace
             // from each line, and discard any empty lines.
-            $values = preg_split('/\s*\R\s*/', $value['setting'], 0, PREG_SPLIT_NO_EMPTY);
+            $values = preg_split('/\s*\R\s*/', $input, 0, PREG_SPLIT_NO_EMPTY);
             $context_config->setValue($context_name, $values);
           }
           else {
-            $context_config->setValue($context_name, $value['setting']);
+            $context_config->setValue($context_name, $input);
           }
+
           // For now, always add in the token context processor if it's present.
           // @todo Improve this in https://www.drupal.org/node/2804035.
           if ($this->getDataProcessorManager()->getDefinition('rules_tokens')) {
@@ -174,7 +299,7 @@ trait ContextFormTrait {
   }
 
   /**
-   * Submit callback: switch a context to data selecor or direct input mode.
+   * Submit callback: switch a context to data selector or direct input mode.
    */
   public static function switchContextMode(array &$form, FormStateInterface $form_state) {
     $element_name = $form_state->getTriggeringElement()['#name'];
diff --git a/src/Form/Expression/ActionForm.php b/src/Form/Expression/ActionForm.php
index 93b50e24..f80cda91 100644
--- a/src/Form/Expression/ActionForm.php
+++ b/src/Form/Expression/ActionForm.php
@@ -158,7 +158,7 @@ class ActionForm implements ExpressionFormInterface {
     }
 
     $action_definition = $this->actionManager->getDefinition($action_id);
-    $context_config = $this->getContextConfigFromFormValues($form_state, $action_definition['context_definitions']);
+    $context_config = $this->getContextConfigFromFormValues($form, $form_state, $action_definition['context_definitions']);
 
     // Rename provided variables, if any.
     if ($provided_variables = $form_state->getValue('provides')) {
diff --git a/src/Form/Expression/ConditionForm.php b/src/Form/Expression/ConditionForm.php
index 18108ff2..54b4d01c 100644
--- a/src/Form/Expression/ConditionForm.php
+++ b/src/Form/Expression/ConditionForm.php
@@ -181,7 +181,7 @@ class ConditionForm implements ExpressionFormInterface {
     }
 
     $condition_definition = $this->conditionManager->getDefinition($condition_id);
-    $context_config = $this->getContextConfigFromFormValues($form_state, $condition_definition['context_definitions']);
+    $context_config = $this->getContextConfigFromFormValues($form, $form_state, $condition_definition['context_definitions']);
 
     // Rename provided variables, if any.
     if ($provided_variables = $form_state->getValue('provides')) {
diff --git a/src/Plugin/Condition/PathAliasExists.php b/src/Plugin/Condition/PathAliasExists.php
index 213c6112..3980b3ae 100644
--- a/src/Plugin/Condition/PathAliasExists.php
+++ b/src/Plugin/Condition/PathAliasExists.php
@@ -23,7 +23,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       label = @Translation("Path alias"),
  *       description = @Translation("Specify the path alias to check for. For example, '/about' for an about page.")
  *     ),
- *     "language" = @ContextDefinition("language",
+ *     "language" = @ContextDefinition("language_reference",
  *       label = @Translation("Language"),
  *       description = @Translation("If specified, the language for which the URL alias applies."),
  *       options_provider = "\Drupal\rules\TypedData\Options\LanguageOptions",
diff --git a/src/Plugin/Condition/PathHasAlias.php b/src/Plugin/Condition/PathHasAlias.php
index 3ca95b0c..0b084cb6 100644
--- a/src/Plugin/Condition/PathHasAlias.php
+++ b/src/Plugin/Condition/PathHasAlias.php
@@ -23,7 +23,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       label = @Translation("Path"),
  *       description = @Translation("Specifies the existing path you wish to check. For example, '/node/28' or '/forum/1'.")
  *     ),
- *     "language" = @ContextDefinition("language",
+ *     "language" = @ContextDefinition("language_reference",
  *       label = @Translation("Language"),
  *       description = @Translation("If specified, the language for which the URL alias applies."),
  *       options_provider = "\Drupal\rules\TypedData\Options\LanguageOptions",
diff --git a/src/Plugin/RulesAction/PathAliasCreate.php b/src/Plugin/RulesAction/PathAliasCreate.php
index 8af883c3..32f0c3f4 100644
--- a/src/Plugin/RulesAction/PathAliasCreate.php
+++ b/src/Plugin/RulesAction/PathAliasCreate.php
@@ -27,7 +27,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       label = @Translation("Path alias"),
  *       description = @Translation("Specify an alternative path by which this data can be accessed. For example, '/about' for an about page. Use an absolute path and do not add a trailing slash.")
  *     ),
- *     "language" = @ContextDefinition("language",
+ *     "language" = @ContextDefinition("language_reference",
  *       label = @Translation("Language"),
  *       description = @Translation("If specified, the language for which the path alias applies."),
  *       options_provider = "\Drupal\rules\TypedData\Options\LanguageOptions",
diff --git a/src/Plugin/RulesAction/SystemEmailToUsersOfRole.php b/src/Plugin/RulesAction/SystemEmailToUsersOfRole.php
index 2c92f5d7..7d483623 100644
--- a/src/Plugin/RulesAction/SystemEmailToUsersOfRole.php
+++ b/src/Plugin/RulesAction/SystemEmailToUsersOfRole.php
@@ -30,7 +30,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       label = @Translation("Subject"),
  *       description = @Translation("The email's subject.")
  *     ),
- *     "message" = @ContextDefinition("string",
+ *     "message" = @ContextDefinition("text",
  *       label = @Translation("Message"),
  *       description = @Translation("The email's message body. Drupal will by default remove all HTML tags. If you want to use HTML you must override this behavior by installing a contributed module such as Mime Mail.")
  *     ),
@@ -40,7 +40,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       default_value = NULL,
  *       required = FALSE
  *     ),
- *     "language" = @ContextDefinition("language",
+ *     "language" = @ContextDefinition("language_reference",
  *       label = @Translation("Language"),
  *       description = @Translation("If specified, the language object (not language code) used for getting the email message and subject."),
  *       options_provider = "\Drupal\rules\TypedData\Options\LanguageOptions",
diff --git a/src/Plugin/RulesAction/SystemSendEmail.php b/src/Plugin/RulesAction/SystemSendEmail.php
index 677f36bc..f2f2cad9 100644
--- a/src/Plugin/RulesAction/SystemSendEmail.php
+++ b/src/Plugin/RulesAction/SystemSendEmail.php
@@ -12,7 +12,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 /**
  * Provides "Send email" rules action.
  *
- * @todo Define that message Context should be textarea comparing with textfield Subject
  * @todo Add access callback information from Drupal 7.
  *
  * @RulesAction(
@@ -29,7 +28,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       label = @Translation("Subject"),
  *       description = @Translation("The email's subject.")
  *     ),
- *     "message" = @ContextDefinition("string",
+ *     "message" = @ContextDefinition("text",
  *       label = @Translation("Message"),
  *       description = @Translation("The email's message body. Drupal will by default remove all HTML tags. If you want to use HTML you must override this behavior by installing a contributed module such as Mime Mail.")
  *     ),
@@ -39,7 +38,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *       default_value = NULL,
  *       required = FALSE
  *     ),
- *     "language" = @ContextDefinition("language",
+ *     "language" = @ContextDefinition("language_reference",
  *       label = @Translation("Language"),
  *       description = @Translation("If specified, the language used for getting the email message and subject."),
  *       options_provider = "\Drupal\rules\TypedData\Options\LanguageOptions",
