Exemplo n.º 1
0
 /**
  * {@inheritdoc}
  */
 protected function alterRoutes(RouteCollection $collection)
 {
     $mappers = $this->mapperManager->getMappers($collection);
     foreach ($mappers as $mapper) {
         $collection->add($mapper->getOverviewRouteName(), $mapper->getOverviewRoute());
         $collection->add($mapper->getAddRouteName(), $mapper->getAddRoute());
         $collection->add($mapper->getEditRouteName(), $mapper->getEditRoute());
         $collection->add($mapper->getDeleteRouteName(), $mapper->getDeleteRoute());
     }
 }
 /**
  * Checks access to the overview based on permissions and translatability.
  *
  * @param \Symfony\Component\Routing\Route $route
  *   The route to check against.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The currently logged in account.
  *
  * @return string
  *   A \Drupal\Core\Access\AccessInterface constant value.
  */
 public function access(Route $route, Request $request, AccountInterface $account)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
     $mapper->populateFromRequest($request);
     $this->sourceLanguage = $mapper->getLanguageWithFallback();
     // Allow access to the translation overview if the proper permission is
     // granted, the configuration has translatable pieces, and the source
     // language is not locked.
     $access = $account->hasPermission('translate configuration') && $mapper->hasSchema() && $mapper->hasTranslatable() && !$this->sourceLanguage->locked;
     return $access ? static::ALLOW : static::DENY;
 }
 /**
  * Checks access to the overview based on permissions and translatability.
  *
  * @param \Symfony\Component\Routing\Route $route
  *   The route to check against.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The currently logged in account.
  *
  * @return \Drupal\Core\Access\AccessResultInterface
  *   The access result.
  */
 public function access(Route $route, AccountInterface $account)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
     $this->sourceLanguage = $this->languageManager->getLanguage($mapper->getLangcode());
     // Allow access to the translation overview if the proper permission is
     // granted, the configuration has translatable pieces, and the source
     // language is not locked if it is present.
     $source_language_access = is_null($this->sourceLanguage) || !$this->sourceLanguage->isLocked();
     $access = $account->hasPermission('translate configuration') && $mapper->hasSchema() && $mapper->hasTranslatable() && $source_language_access;
     return AccessResult::allowedIf($access)->cachePerRole();
 }
Exemplo n.º 4
0
 /**
  * Language translations overview page for a configuration name.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   Page request object.
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match.
  * @param string $plugin_id
  *   The plugin ID of the mapper.
  *
  * @return array
  *   Page render array.
  */
 public function itemPage(Request $request, RouteMatchInterface $route_match, $plugin_id)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $page = array();
     $page['#title'] = $this->t('Translations for %label', array('%label' => $mapper->getTitle()));
     $languages = $this->languageManager->getLanguages();
     if (count($languages) == 1) {
         drupal_set_message($this->t('In order to translate configuration, the website must have at least two <a href="@url">languages</a>.', array('@url' => $this->url('entity.configurable_language.collection'))), 'warning');
     }
     $original_langcode = $mapper->getLangcode();
     if (!isset($languages[$original_langcode])) {
         // If the language is not configured on the site, create a dummy language
         // object for this listing only to ensure the user gets useful info.
         $language_name = $this->languageManager->getLanguageName($original_langcode);
         $languages[$original_langcode] = new Language(array('id' => $original_langcode, 'name' => $language_name));
     }
     // We create a fake request object to pass into
     // ConfigMapperInterface::populateFromRequest() for the different languages.
     // Creating a separate request for each language and route is neither easily
     // possible nor performant.
     $fake_request = $request->duplicate();
     $page['languages'] = array('#type' => 'table', '#header' => array($this->t('Language'), $this->t('Operations')));
     foreach ($languages as $language) {
         $langcode = $language->getId();
         // This is needed because
         // ConfigMapperInterface::getAddRouteParameters(), for example,
         // needs to return the correct language code for each table row.
         $fake_request->attributes->set('langcode', $langcode);
         $mapper->populateFromRequest($fake_request);
         // Prepare the language name and the operations depending on whether this
         // is the original language or not.
         if ($langcode == $original_langcode) {
             $language_name = '<strong>' . $this->t('@language (original)', array('@language' => $language->getName())) . '</strong>';
             // Check access for the path/route for editing, so we can decide to
             // include a link to edit or not.
             $edit_access = $this->accessManager->checkNamedRoute($mapper->getBaseRouteName(), $route_match->getRawParameters()->all(), $this->account);
             // Build list of operations.
             $operations = array();
             if ($edit_access) {
                 $operations['edit'] = array('title' => $this->t('Edit'), 'url' => Url::fromRoute($mapper->getBaseRouteName(), $mapper->getBaseRouteParameters(), ['query' => ['destination' => $mapper->getOverviewPath()]]));
             }
         } else {
             $language_name = $language->getName();
             $operations = array();
             // If no translation exists for this language, link to add one.
             if (!$mapper->hasTranslation($language)) {
                 $operations['add'] = array('title' => $this->t('Add'), 'url' => Url::fromRoute($mapper->getAddRouteName(), $mapper->getAddRouteParameters()));
             } else {
                 // Otherwise, link to edit the existing translation.
                 $operations['edit'] = array('title' => $this->t('Edit'), 'url' => Url::fromRoute($mapper->getEditRouteName(), $mapper->getEditRouteParameters()));
                 $operations['delete'] = array('title' => $this->t('Delete'), 'url' => Url::fromRoute($mapper->getDeleteRouteName(), $mapper->getDeleteRouteParameters()));
             }
         }
         $page['languages'][$langcode]['language'] = array('#markup' => $language_name);
         $page['languages'][$langcode]['operations'] = array('#type' => 'operations', '#links' => $operations);
     }
     return $page;
 }
 /**
  * Provides the listing page for any entity type.
  *
  * @param string $mapper_id
  *   The name of the mapper.
  *
  * @return array
  *   A render array as expected by drupal_render().
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Throws an exception if a mapper plugin could not be instantiated from the
  *   mapper definition in the constructor.
  */
 public function listing($mapper_id)
 {
     $mapper_definition = $this->mapperManager->getDefinition($mapper_id);
     $mapper = $this->mapperManager->createInstance($mapper_id, $mapper_definition);
     if (!$mapper) {
         throw new NotFoundHttpException();
     }
     $entity_type = $mapper->getType();
     // If the mapper, for example the mapper for fields, has a custom list
     // controller defined, use it. Other mappers, for examples the ones for
     // node_type and block, fallback to the generic configuration translation
     // list controller.
     $build = $this->entityManager()->getHandler($entity_type, 'config_translation_list')->setMapperDefinition($mapper_definition)->render();
     $build['#title'] = $mapper->getTypeLabel();
     return $build;
 }
 /**
  * {@inheritdoc}
  */
 public function getDerivativeDefinitions($base_plugin_definition)
 {
     $mappers = $this->mapperManager->getMappers();
     foreach ($mappers as $plugin_id => $mapper) {
         /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
         $route_name = $mapper->getOverviewRouteName();
         $base_route = $mapper->getBaseRouteName();
         if (!empty($base_route)) {
             $this->derivatives[$route_name] = $base_plugin_definition;
             $this->derivatives[$route_name]['config_translation_plugin_id'] = $plugin_id;
             $this->derivatives[$route_name]['class'] = '\\Drupal\\config_translation\\Plugin\\Menu\\LocalTask\\ConfigTranslationLocalTask';
             $this->derivatives[$route_name]['route_name'] = $route_name;
             $this->derivatives[$route_name]['base_route'] = $base_route;
         }
     }
     return parent::getDerivativeDefinitions($base_plugin_definition);
 }
Exemplo n.º 7
0
 /**
  * {@inheritdoc}
  */
 public function getType(JobItemInterface $job_item)
 {
     if ($job_item->getItemType() == static::SIMPLE_CONFIG) {
         $definition = $this->configMapperManager->getDefinition($job_item->getItemId());
     } else {
         $definition = $this->configMapperManager->getDefinition($job_item->getItemType());
     }
     return $definition['title'];
 }
Exemplo n.º 8
0
 /**
  * {@inheritdoc}
  */
 public function hasTranslatable()
 {
     foreach ($this->getConfigNames() as $name) {
         if ($this->configMapperManager->hasTranslatable($name)) {
             return TRUE;
         }
     }
     return FALSE;
 }
 /**
  * {@inheritdoc}
  */
 public function getDerivativeDefinitions($base_plugin_definition)
 {
     // Create contextual links for all mappers.
     $mappers = $this->mapperManager->getMappers();
     foreach ($mappers as $plugin_id => $mapper) {
         // @todo Contextual groups do not map to entity types in a predictable
         //   way. See https://drupal.org/node/2134841 to make them predictable.
         $group_name = $mapper->getContextualLinkGroup();
         if (empty($group_name)) {
             continue;
         }
         /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
         $route_name = $mapper->getOverviewRouteName();
         $this->derivatives[$route_name] = $base_plugin_definition;
         $this->derivatives[$route_name]['config_translation_plugin_id'] = $plugin_id;
         $this->derivatives[$route_name]['class'] = '\\Drupal\\config_translation\\Plugin\\Menu\\ContextualLink\\ConfigTranslationContextualLink';
         $this->derivatives[$route_name]['route_name'] = $route_name;
         $this->derivatives[$route_name]['group'] = $group_name;
     }
     return parent::getDerivativeDefinitions($base_plugin_definition);
 }
 /**
  * {@inheritdoc}
  */
 public function getTranslateRoute()
 {
     // @todo There should be some way for Config Translation module to alter
     //   this information in on its own.
     if ($this->mapperManager) {
         $entity_type = 'menu_link_config';
         /** @var \Drupal\menu_link_config\MenuLinkConfigMapper $mapper */
         $mapper = $this->mapperManager->createInstance($entity_type);
         $mapper->setEntity($this->getEntity());
         return array('route_name' => $mapper->getOverviewRouteName(), 'route_parameters' => $mapper->getOverviewRouteParameters());
     }
 }
Exemplo n.º 11
0
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, array &$form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $language = language_load($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     return parent::buildForm($form, $form_state);
 }
Exemplo n.º 12
0
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, RouteMatchInterface $route_match = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRouteMatch($route_match);
     $language = $this->languageManager->getLanguage($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     return parent::buildForm($form, $form_state);
 }
Exemplo n.º 13
0
 /**
  * Tests ConfigNamesMapper::hasTranslatable().
  *
  * @param array $mock_return_values
  *   An array of values that the mocked configuration mapper manager should
  *   return for hasTranslatable().
  * @param bool $expected
  *   The expected return value of ConfigNamesMapper::hasTranslatable().
  *
  * @dataProvider providerTestHasTranslatable
  */
 public function testHasTranslatable(array $mock_return_values, $expected)
 {
     // As the configuration names are arbitrary, simply use integers.
     $config_names = range(1, count($mock_return_values));
     $this->configNamesMapper->setConfigNames($config_names);
     $map = array();
     foreach ($config_names as $i => $config_name) {
         $map[] = isset($mock_return_values[$i]) ? array($config_name, $mock_return_values[$i]) : array();
     }
     $this->configMapperManager->expects($this->any())->method('hasTranslatable')->will($this->returnValueMap($map));
     $result = $this->configNamesMapper->hasTranslatable();
     $this->assertSame($expected, $result);
 }
 /**
  * Implements \Drupal\Core\Form\FormInterface::buildForm().
  *
  * Builds configuration form with metadata and values from the source
  * language.
  *
  * @param array $form
  *   An associative array containing the structure of the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   (optional) The route match.
  * @param string $plugin_id
  *   (optional) The plugin ID of the mapper.
  * @param string $langcode
  *   (optional) The language code of the language the form is adding or
  *   editing.
  *
  * @return array
  *   The form structure.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Throws an exception if the language code provided as a query parameter in
  *   the request does not match an active language.
  */
 public function buildForm(array $form, FormStateInterface $form_state, RouteMatchInterface $route_match = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRouteMatch($route_match);
     $language = $this->languageManager->getLanguage($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     // ConfigTranslationFormAccess will not grant access if this raises an
     // exception, so we can call this without a try-catch block here.
     $langcode = $this->mapper->getLangcode();
     $this->sourceLanguage = $this->languageManager->getLanguage($langcode);
     // Get base language configuration to display in the form before setting the
     // language to use for the form. This avoids repetitively settings and
     // resetting the language to get original values later.
     $this->baseConfigData = $this->mapper->getConfigData();
     // Set the translation target language on the configuration factory.
     $original_language = $this->languageManager->getConfigOverrideLanguage();
     $this->languageManager->setConfigOverrideLanguage($this->language);
     // Add some information to the form state for easier form altering.
     $form_state->set('config_translation_mapper', $this->mapper);
     $form_state->set('config_translation_language', $this->language);
     $form_state->set('config_translation_source_language', $this->sourceLanguage);
     $form['#attached']['library'][] = 'config_translation/drupal.config_translation.admin';
     // Even though this is a nested form, we do not set #tree to TRUE because
     // the form value structure is generated by using #parents for each element.
     // @see \Drupal\config_translation\FormElement\FormElementBase::getElements()
     $form['config_names'] = array('#type' => 'container');
     foreach ($this->mapper->getConfigNames() as $name) {
         $form['config_names'][$name] = array('#type' => 'container');
         $schema = $this->typedConfigManager->get($name);
         $source_config = $this->baseConfigData[$name];
         $translation_config = $this->configFactory()->get($name)->get();
         if ($form_element = $this->createFormElement($schema)) {
             $parents = array('config_names', $name);
             $form['config_names'][$name] += $form_element->getTranslationBuild($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents);
         }
     }
     $form['actions']['#type'] = 'actions';
     $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save translation'), '#button_type' => 'primary');
     // Set the configuration language back.
     $this->languageManager->setConfigOverrideLanguage($original_language);
     return $form;
 }
Exemplo n.º 15
0
 /**
  * Implements \Drupal\Core\Form\FormInterface::buildForm().
  *
  * Builds configuration form with metadata and values from the source
  * language.
  *
  * @param array $form
  *   An associative array containing the structure of the form.
  * @param array $form_state
  *   An associative array containing the current state of the form.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   (optional) Page request object.
  * @param string $plugin_id
  *   (optional) The plugin ID of the mapper.
  * @param string $langcode
  *   (optional) The language code of the language the form is adding or
  *   editing.
  *
  * @return array
  *   The form structure.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Throws an exception if the language code provided as a query parameter in
  *   the request does not match an active language.
  */
 public function buildForm(array $form, array &$form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $language = language_load($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     $this->sourceLanguage = $this->mapper->getLanguageWithFallback();
     // Get base language configuration to display in the form before setting the
     // language to use for the form. This avoids repetitively settings and
     // resetting the language to get original values later.
     $config_factory = $this->configFactory();
     $old_state = $config_factory->getOverrideState();
     $config_factory->setOverrideState(FALSE);
     $this->baseConfigData = $this->mapper->getConfigData();
     $config_factory->setOverrideState($old_state);
     // Set the translation target language on the configuration factory.
     $original_language = $this->languageManager->getConfigOverrideLanguage();
     $this->languageManager->setConfigOverrideLanguage($this->language);
     // Add some information to the form state for easier form altering.
     $form_state['config_translation_mapper'] = $this->mapper;
     $form_state['config_translation_language'] = $this->language;
     $form_state['config_translation_source_language'] = $this->sourceLanguage;
     $form['#attached']['library'][] = 'config_translation/drupal.config_translation.admin';
     $form['config_names'] = array('#type' => 'container', '#tree' => TRUE);
     foreach ($this->mapper->getConfigNames() as $name) {
         $form['config_names'][$name] = array('#type' => 'container');
         $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name), $config_factory->get($name)->get(), $this->baseConfigData[$name]);
     }
     $form['actions']['#type'] = 'actions';
     $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save translation'), '#button_type' => 'primary');
     // Set the configuration language back.
     $this->languageManager->setConfigOverrideLanguage($original_language);
     return $form;
 }
Exemplo n.º 16
0
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, array $build = NULL, $plugin_id = NULL)
 {
     // Store the entity in the form state so we can easily create the job in the
     // submit handler.
     $mapper_definition = \Drupal::service('plugin.manager.config_translation.mapper')->getDefinition($plugin_id);
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRouteMatch($this->routeMatch);
     $form_state->set('mapper', $mapper);
     if (!isset($mapper_definition['entity_type'])) {
         $form_state->set('item_type', ConfigSource::SIMPLE_CONFIG);
         $form_state->set('item_id', $mapper_definition['id']);
     } else {
         $id = $mapper->getConfigNames()[0];
         $form_state->set('id', $id);
         $form_state->set('item_type', $plugin_id);
         $form_state->set('item_id', $id);
     }
     $form['#title'] = $this->t('Translations of @title', array('@title' => $mapper->getTitle()));
     $overview = $build['languages'];
     $form['top_actions'] = array('#type' => 'details', '#title' => t('Operations'), '#open' => TRUE, '#attributes' => array('class' => array('tmgmt-source-operations-wrapper')));
     $form['top_actions']['request'] = array('#type' => 'submit', '#button_type' => 'primary', '#value' => $this->t('Request translation'), '#submit' => array('::submitForm'));
     tmgmt_add_cart_form($form['top_actions'], $form_state, 'config', $form_state->get('item_type'), $form_state->get('item_id'));
     // Inject our additional column into the header.
     array_splice($overview['#header'], -1, 0, array(t('Pending Translations')));
     // Make this a tableselect form.
     $form['languages'] = array('#type' => 'tableselect', '#header' => $overview['#header'], '#options' => array());
     $languages = \Drupal::languageManager()->getLanguages();
     // Check if there is a job / job item that references this translation.
     $items = tmgmt_job_item_load_latest('config', $form_state->get('item_type'), $form_state->get('item_id'), $mapper->getLangcode());
     foreach ($languages as $langcode => $language) {
         if ($langcode == LanguageInterface::LANGCODE_DEFAULT) {
             // Never show language neutral on the overview.
             continue;
         }
         // Since the keys are numeric and in the same order we can shift one element
         // after the other from the original non-form rows.
         $option = $overview[$langcode];
         if ($langcode == $mapper->getLangcode()) {
             $additional = array('data' => array('#markup' => '<strong>' . t('Source') . '</strong>'));
             // This is the source object so we disable the checkbox for this row.
             $form['languages'][$langcode] = array('#type' => 'checkbox', '#disabled' => TRUE);
         } elseif (isset($items[$langcode])) {
             $item = $items[$langcode];
             $states = JobItem::getStates();
             $additional = \Drupal::l($states[$item->getState()], $item->urlInfo()->setOption('query', array('destination' => Url::fromRoute('<current>')->getInternalPath())));
             // Disable the checkbox for this row since there is already a translation
             // in progress that has not yet been finished. This way we make sure that
             // we don't stack multiple active translations for the same item on top
             // of each other.
             $form['languages'][$langcode] = array('#type' => 'checkbox', '#disabled' => TRUE);
         } else {
             // There is no translation job / job item for this target language.
             $additional = t('None');
         }
         // Inject the additional column into the array.
         // The generated form structure has changed, support both an additional
         // 'data' key (that is not supported by tableselect) and the old version
         // without.
         if (isset($option['data'])) {
             array_splice($option['data'], -1, 0, array($additional));
             // Append the current option array to the form.
             $form['languages']['#options'][$langcode] = $option['data'];
         } else {
             array_splice($option, -1, 0, array($additional));
             // Append the current option array to the form.
             $form['languages']['#options'][$langcode] = array(drupal_render($option['language']), $additional, drupal_render($option['operations']));
         }
     }
     return $form;
 }
 /**
  * Gets a configuration mapper using a route match.
  *
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match to populate the mapper with.
  *
  * @return \Drupal\config_translation\ConfigMapperInterface
  *   The configuration mapper.
  */
 protected function getMapperFromRouteMatch(RouteMatchInterface $route_match)
 {
     $mapper = $this->configMapperManager->createInstance($route_match->getRouteObject()->getDefault('plugin_id'));
     $mapper->populateFromRouteMatch($route_match);
     return $mapper;
 }
 /**
  * Language translations overview page for a configuration name.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   Page request object.
  * @param string $plugin_id
  *   The plugin ID of the mapper.
  *
  * @return array
  *   Page render array.
  */
 public function itemPage(Request $request, $plugin_id)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $page = array();
     $page['#title'] = $this->t('Translations for %label', array('%label' => $mapper->getTitle()));
     // It is possible the original language this configuration was saved with is
     // not on the system. For example, the configuration shipped in English but
     // the site has no English configured. Represent the original language in
     // the table even if it is not currently configured.
     $languages = $this->languageManager->getLanguages();
     $original_langcode = $mapper->getLangcode();
     if (!isset($languages[$original_langcode])) {
         $language_name = $this->languageManager->getLanguageName($original_langcode);
         if ($original_langcode == 'en') {
             $language_name = $this->t('Built-in English');
         }
         // Create a dummy language object for this listing only.
         $languages[$original_langcode] = new Language(array('id' => $original_langcode, 'name' => $language_name));
     }
     // We create a fake request object to pass into
     // ConfigMapperInterface::populateFromRequest() for the different languages.
     // Creating a separate request for each language and route is neither easily
     // possible nor performant.
     $fake_request = $request->duplicate();
     $page['languages'] = array('#type' => 'table', '#header' => array($this->t('Language'), $this->t('Operations')));
     foreach ($languages as $language) {
         $langcode = $language->id;
         // This is needed because
         // ConfigMapperInterface::getAddRouteParameters(), for example,
         // needs to return the correct language code for each table row.
         $fake_request->attributes->set('langcode', $langcode);
         $mapper->populateFromRequest($fake_request);
         // Prepare the language name and the operations depending on whether this
         // is the original language or not.
         if ($langcode == $original_langcode) {
             $language_name = '<strong>' . $this->t('@language (original)', array('@language' => $language->name)) . '</strong>';
             // Check access for the path/route for editing, so we can decide to
             // include a link to edit or not.
             $route_request = $this->getRequestForPath($request, $mapper->getBasePath());
             $edit_access = FALSE;
             if (!empty($route_request)) {
                 $route_name = $route_request->attributes->get(RouteObjectInterface::ROUTE_NAME);
                 // Note that the parameters don't really matter here since we're
                 // passing in the request which already has the upcast attributes.
                 $parameters = array();
                 $edit_access = $this->accessManager->checkNamedRoute($route_name, $parameters, $this->account, $route_request);
             }
             // Build list of operations.
             $operations = array();
             if ($edit_access) {
                 $operations['edit'] = array('title' => $this->t('Edit'), 'route_name' => $mapper->getBaseRouteName(), 'route_parameters' => $mapper->getBaseRouteParameters(), 'query' => array('destination' => $mapper->getOverviewPath()));
             }
         } else {
             $language_name = $language->name;
             $operations = array();
             // If no translation exists for this language, link to add one.
             if (!$mapper->hasTranslation($language)) {
                 $operations['add'] = array('title' => $this->t('Add'), 'route_name' => $mapper->getAddRouteName(), 'route_parameters' => $mapper->getAddRouteParameters());
             } else {
                 // Otherwise, link to edit the existing translation.
                 $operations['edit'] = array('title' => $this->t('Edit'), 'route_name' => $mapper->getEditRouteName(), 'route_parameters' => $mapper->getEditRouteParameters());
                 $operations['delete'] = array('title' => $this->t('Delete'), 'route_name' => $mapper->getDeleteRouteName(), 'route_parameters' => $mapper->getDeleteRouteParameters());
             }
         }
         $page['languages'][$langcode]['language'] = array('#markup' => $language_name);
         $page['languages'][$langcode]['operations'] = array('#type' => 'operations', '#links' => $operations);
     }
     return $page;
 }