diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index d7204a5..b5eb488 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -285,7 +285,7 @@ function content_translation_get_config_key($entity_type, $bundle, $setting) { * @param string $setting * The name of the setting. * - * @returns mixed + * @return mixed * The stored value for the given setting. */ function content_translation_get_config($entity_type, $bundle, $setting) { @@ -319,7 +319,7 @@ function content_translation_set_config($entity_type, $bundle, $setting, $value) * (optional) The bundle of the entity. If no bundle is provided, all the * available bundles are checked. * - * @returns + * @return bool * TRUE if the specified bundle is translatable. If no bundle is provided * returns TRUE if at least one of the entity bundles is translatable. * @@ -885,3 +885,41 @@ function content_translation_save_settings($settings) { entity_info_cache_clear(); \Drupal::service('router.builder')->setRebuildNeeded(); } + +/** + * Returns the localized links for the given path. + * + * @param string $path + * The path for which language switch links should be provided. + * + * @return array + * A renderable array of language switch links. + */ +function _content_translation_get_switch_links($path) { + $links = language_negotiation_get_switch_links(Language::TYPE_CONTENT, $path); + if (empty($links)) { + // If content language is set up to fall back to the interface language, + // then there will be no switch links for Language::TYPE_CONTENT, ergo we + // also need to use interface switch links. + $links = language_negotiation_get_switch_links(Language::TYPE_INTERFACE, $path); + } + return $links; +} + +/** + * Populates target values with the source values. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being translated. + * @param \Drupal\Core\Language\Language $source + * The language to be used as source. + * @param \Drupal\Core\Language\Language $target + * The language to be used as target. + */ +function content_translation_prepare_translation(EntityInterface $entity, Language $source, Language $target) { + if ($entity instanceof ContentEntityInterface) { + $source_translation = $entity->getTranslation($source->id); + $entity->addTranslation($target->id, $source_translation->getPropertyValues()); + } +} + diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc deleted file mode 100644 index 4d2fefc..0000000 --- a/core/modules/content_translation/content_translation.pages.inc +++ /dev/null @@ -1,234 +0,0 @@ -getEntityTypeId()); - $languages = \Drupal::languageManager()->getLanguages(); - $original = $entity->getUntranslated()->language()->id; - $translations = $entity->getTranslationLanguages(); - $administrator = \Drupal::currentUser()->hasPermission('administer languages'); - - $rel = array(); - foreach (array('canonical', 'edit-form', 'drupal:content-translation-overview') as $name) { - $rel[$name] = $entity->getSystemPath($name); - } - - $header = array(t('Language'), t('Translation'), t('Source language'), t('Status'), t('Operations')); - $rows = array(); - - if (\Drupal::languageManager()->isMultilingual()) { - - // Determine whether the current entity is translatable. - $translatable = FALSE; - foreach (\Drupal::entityManager()->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { - if ($field_definition->isTranslatable()) { - $translatable = TRUE; - break; - } - } - - foreach ($languages as $language) { - $language_name = $language->name; - $langcode = $language->id; - - $add_path = $rel['drupal:content-translation-overview'] . '/add/' . $original . '/' . $langcode; - $translate_path = $rel['drupal:content-translation-overview'] . '/edit/' . $langcode; - - $add_links = _content_translation_get_switch_links($add_path); - $edit_links = _content_translation_get_switch_links($rel['edit-form']); - $translate_links = _content_translation_get_switch_links($translate_path); - $delete_links = _content_translation_get_switch_links($rel['drupal:content-translation-overview'] . '/delete/' . $langcode); - - $operations = array( - 'data' => array( - '#type' => 'operations', - '#links' => array(), - ), - ); - $links = &$operations['data']['#links']; - - if (isset($translations[$langcode])) { - // Existing translation in the translation set: display status. - $source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : ''; - $is_original = $langcode == $original; - $label = $entity->getTranslation($langcode)->label(); - $link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array('href' => $rel['canonical'], 'language' => $language); - $row_title = l($label, $link['href'], $link); - - if (empty($link['href'])) { - $row_title = $is_original ? $label : t('n/a'); - } - - // If the user is allowed to edit the entity we point the edit link to - // the entity form, otherwise if we are not dealing with the original - // language we point the link to the translation form. - if ($entity->access('update')) { - $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $rel['edit-form'], 'language' => $language); - } - elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) { - $links['edit'] = isset($translate_links->links[$langcode]['href']) ? $translate_links->links[$langcode] : array('href' => $translate_path, 'language' => $language); - } - - if (isset($links['edit'])) { - $links['edit']['title'] = t('Edit'); - } - - $translation = $entity->translation[$langcode]; - $status = !empty($translation['status']) ? t('Published') : t('Not published'); - // @todo Add a theming function here. - $status = '' . $status . '' . (!empty($translation['outdated']) ? ' ' . t('outdated') . '' : ''); - - if ($is_original) { - $language_name = t('@language_name (Original language)', array('@language_name' => $language_name)); - $source_name = t('n/a'); - } - else { - $source_name = isset($languages[$source]) ? $languages[$source]->name : t('n/a'); - if ($controller->getTranslationAccess($entity, 'delete')) { - $links['delete'] = isset($delete_links->links[$langcode]['href']) ? $delete_links->links[$langcode] : array('href' => $delete_links, 'language' => $language); - $links['delete']['title'] = t('Delete'); - } - } - } - else { - // No such translation in the set yet: help user to create it. - $row_title = $source_name = t('n/a'); - $source = $entity->language()->id; - - if ($source != $langcode && $controller->getTranslationAccess($entity, 'create')) { - if ($translatable) { - $links['add'] = isset($add_links->links[$langcode]['href']) ? $add_links->links[$langcode] : array('href' => $add_path, 'language' => $language); - $links['add']['title'] = t('Add'); - } - elseif ($administrator) { - $links['nofields'] = array('title' => t('No translatable fields'), 'route_name' => 'language.content_settings_page', 'language' => $language); - } - } - - $status = t('Not translated'); - } - - $rows[] = array($language_name, $row_title, $source_name, $status, $operations); - } - } - - $build['#title'] = t('Translations of %label', array('%label' => $entity->label())); - - // Add metadata to the build render array to let other modules know about - // which entity this is. - $build['#entity'] = $entity; - - $build['content_translation_overview'] = array( - '#type' => 'table', - '#header' => $header, - '#rows' => $rows, - ); - - return $build; -} - -/** - * Returns the localized links for the given path. - * - * @param string $path - * The path for which language switch links should be provided. - * - * @returns - * A renderable array of language switch links. - */ -function _content_translation_get_switch_links($path) { - $links = \Drupal::languageManager()->getLanguageSwitchLinks(Language::TYPE_CONTENT, $path); - if (empty($links)) { - // If content language is set up to fall back to the interface language, - // then there will be no switch links for Language::TYPE_CONTENT, ergo we - // also need to use interface switch links. - $links = \Drupal::languageManager()->getLanguageSwitchLinks(Language::TYPE_INTERFACE, $path); - } - return $links; -} - -/** - * Page callback for the translation addition page. - * - * @param EntityInterface $entity - * The entity being translated. - * @param Language $source - * (optional) The language of the values being translated. Defaults to the - * entity language. - * @param Language $target - * (optional) The language of the translated values. Defaults to the current - * content language. - * - * @return array - * A processed form array ready to be rendered. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Use \Drupal\content_translation\Controller\ContentTranslationController::add(). - */ -function content_translation_add_page(EntityInterface $entity, Language $source = NULL, Language $target = NULL) { - $source = !empty($source) ? $source : $entity->language(); - $target = !empty($target) ? $target : \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT); - // @todo Exploit the upcoming hook_entity_prepare() when available. - content_translation_prepare_translation($entity, $source, $target); - $form_state['langcode'] = $target->id; - $form_state['content_translation']['source'] = $source; - $form_state['content_translation']['target'] = $target; - $form_state['content_translation']['translation_form'] = !$entity->access('update'); - return \Drupal::service('entity.form_builder')->getForm($entity, 'default', $form_state); -} - -/** - * Page callback for the translation edit page. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity being translated. - * @param \Drupal\Core\Language\Language $language - * (optional) The language of the translated values. Defaults to the current - * content language. - * - * @return array - * A processed form array ready to be rendered. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Use \Drupal\content_translation\Controller\ContentTranslationController::edit(). - */ -function content_translation_edit_page(EntityInterface $entity, Language $language = NULL) { - $language = !empty($language) ? $language : \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT); - $form_state['langcode'] = $language->id; - $form_state['content_translation']['translation_form'] = TRUE; - return \Drupal::service('entity.form_builder')->getForm($entity, 'default', $form_state); -} - -/** - * Populates target values with the source values. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entitiy being translated. - * @param \Drupal\Core\Language\Language $source - * The language to be used as source. - * @param \Drupal\Core\Language\Language $target - * The language to be used as target. - */ -function content_translation_prepare_translation(EntityInterface $entity, Language $source, Language $target) { - if ($entity instanceof ContentEntityInterface) { - $source_translation = $entity->getTranslation($source->id); - $entity->addTranslation($target->id, $source_translation->toArray()); - } -} diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php index b9b4904..d85c4a3 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php @@ -53,8 +53,8 @@ public function access(Route $route, Request $request, AccountInterface $account switch ($operation) { case 'create': - $source = language_load($request->attributes->get('source')); - $target = language_load($request->attributes->get('target')); + $source = $request->attributes->get('source'); + $target = $request->attributes->get('target'); $source = !empty($source) ? $source : $entity->language(); $target = !empty($target) ? $target : \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT); return ($source->id != $target->id @@ -66,8 +66,8 @@ public function access(Route $route, Request $request, AccountInterface $account case 'update': case 'delete': - $language = language_load($request->attributes->get('language')); - $language = !empty($language) ? $language : \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT); + $language = $request->attributes->get('language'); + $language = !empty($language) ? $language : language(Language::TYPE_CONTENT); return isset($languages[$language->id]) && $language->id != $entity->getUntranslated()->language()->id && isset($translations[$language->id]) diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationHandler.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationHandler.php index 76723c2..087ae13 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationHandler.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationHandler.php @@ -197,8 +197,11 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac // A new translation is not available in the translation metadata, hence // it should count as one more. $published = $new_translation; - foreach ($entity->translation as $translation) { - $published += $translation['status']; + // When creating a brand new translation, $entity->translation is not set. + if (!($new_translation)) { + foreach ($entity->translation as $translation) { + $published += $translation['status']; + } } $enabled = $published > 1; } diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php index 31a33d2..3c7d833 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php @@ -7,41 +7,281 @@ namespace Drupal\content_translation\Controller; +use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Url; +use Drupal\field\FieldInfo; +use Drupal\Core\Language\Language; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; /** * Base class for entity translation controllers. */ -class ContentTranslationController { +class ContentTranslationController extends ControllerBase implements ContainerInjectionInterface { /** - * @todo Remove content_translation_overview(). + * The field info service. + * + * @var \Drupal\field\FieldInfo + */ + protected $fieldInfo; + + /** + * Constructs a ContentTranslationController object. + * + * @param \Drupal\Field\FieldInfo + * The field info service. + */ + public function __construct(FieldInfo $field_info) { + $this->fieldInfo = $field_info; + } + + /** + * {inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('field.info') + ); + } + + /** + * Populates target values with the source values. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entitiy being translated. + * @param \Drupal\Core\Language\Language $source + * The language to be used as source. + * @param \Drupal\Core\Language\Language $target + * The language to be used as target. + */ + function prepareTranslation(EntityInterface $entity, Language $source, Language $target) { + if ($entity instanceof ContentEntityInterface) { + $source_translation = $entity->getTranslation($source->id); + $entity->addTranslation($target->id, $source_translation->toArray()); + } + } + + /** + * Translations overview page builder. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object from which to extract the entity type. + * + * @return array + * Array of page elements to render. */ public function overview(Request $request) { - $entity = $request->attributes->get($request->attributes->get('_entity_type_id')); - module_load_include('pages.inc', 'content_translation'); - return content_translation_overview($entity); + $entity_type = $request->attributes->get('_entity_type_id'); + $entity = $request->attributes->get($entity_type); + $account = $this->currentUser(); + $controller = $this->entityManager()->getController($entity_type, 'translation'); + + $languages = language_list(); + $original = $entity->getUntranslated()->language()->id; + $translations = $entity->getTranslationLanguages(); + $field_ui = $this->moduleHandler()->moduleExists('field_ui') && $account->hasPermission('administer ' . $entity_type . ' fields'); + + $path = $entity->getSystemPath(); + $base_path = $entity->getSystemPath('drupal:content-translation-overview'); + $edit_path = $entity->getSystemPath('edit-form'); + + $header = array( + $this->t('Language'), + $this->t('Translation'), + $this->t('Source language'), + $this->t('Status'), + $this->t('Operations'), + ); + $rows = array(); + + if ($this->languageManager()->isMultilingual()) { + // Determine whether the current entity is translatable. + $translatable = FALSE; + foreach ($this->fieldInfo->getBundleInstances($entity_type, $entity->bundle()) as $instance) { + if ($instance->isTranslatable()) { + $translatable = TRUE; + break; + } + } + + foreach ($languages as $language) { + $language_name = $language->name; + $langcode = $language->id; + $add_path = $base_path . '/add/' . $original . '/' . $langcode; + $translate_path = $base_path . '/edit/' . $langcode; + $delete_path = $base_path . '/delete/' . $langcode; + + $operations = array( + 'data' => array( + '#type' => 'operations', + '#links' => array(), + ), + ); + $links = &$operations['data']['#links']; + + if (array_key_exists($langcode, $translations)) { + // Existing translation in the translation set: display status. + $source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : ''; + $is_original = $langcode == $original; + $label = $entity->getTranslation($langcode)->label(); + $link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array( + 'href' => $path, + ); + $row_title = l($label, $link['href'], $link); + + if (empty($link['href'])) { + $row_title = $is_original ? $label : $this->t('n/a'); + } + + // If the user is allowed to edit the entity we point the edit link to + // the entity form, otherwise if we are not dealing with the original + // language we point the link to the translation form. + if ($edit_path && $entity->access('update')) { + $links['edit'] = array ( + 'href' => $edit_path, + ); + } + elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) { + $links['edit'] = array ( + 'href' => $translate_path, + ); + } + + if (isset($links['edit'])) { + $links['edit']['title'] = $this->t('Edit'); + } + $translation = $entity->translation[$langcode]; + $status = !empty($translation['status']) ? $this->t('Published') : $this->t('Not published'); + // @todo Add a theming function here. + $status = '' . $status . '' . (!empty($translation['outdated']) ? ' ' . $this->t('outdated') . '' : ''); + + if ($is_original) { + $language_name = $this->t('@language_name (Original language)', array('@language_name' => $language_name)); + $source_name = $this->t('n/a'); + } + else { + $source_name = isset($languages[$source]) ? $languages[$source]->name : $this->t('n/a'); + if ($controller->getTranslationAccess($entity, 'delete')) { + $links['delete'] = array ( + 'title' => $this->t('Delete'), + 'href' => $delete_path, + ); + } + } + } + else { + // No such translation in the set yet: help user to create it. + $row_title = $source_name = $this->t('n/a'); + $source = $entity->language()->id; + + if ($source != $langcode && $controller->getTranslationAccess($entity, 'create')) { + if ($translatable) { + $links['add'] = array ( + 'title' => $this->t('Add'), + 'href' => $add_path, + ); + } + elseif ($field_ui) { + $entity_path = $this->entityManager()->getAdminRouteInfo($entity_type, $entity->bundle()); + + // Link directly to the fields tab to make it easier to find the + // setting to enable translation on fields. + $path = $entity_path . '/fields'; + $links['nofields'] = array( + 'title' => $this->t('No translatable fields'), + 'href' => $path, + ); + } + } + + $status = $this->t('Not translated'); + } + + $rows[] = array( + $language_name, + $row_title, + $source_name, + $status, + $operations, + ); + } + } + + $build['#title'] = $this->t('Translations of %label', array('%label' => $entity->label())); + + // Add metadata to the build render array to let other modules know about + // which entity this is. + $build['#entity'] = $entity; + + $build['content_translation_overview'] = array( + '#theme' => 'table', + '#header' => $header, + '#rows' => $rows, + ); + + return $build; } /** - * @todo Remove content_translation_add_page(). + * Translation addition page builder. + * + * @param \Drupal\language\Plugin\Condition\Language $source + * (optional) The language of the values being translated. Defaults to the + * entity language. + * @param \Drupal\language\Plugin\Condition\Language $target + * (optional) The language of the translated values. Defaults to the current + * content language. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object from which to extract the entity type. + * + * @return array + * A processed form array ready to be rendered. */ - public function add(Request $request, $source, $target) { - $entity = $request->attributes->get($request->attributes->get('_entity_type_id')); - module_load_include('pages.inc', 'content_translation'); - $source = language_load($source); - $target = language_load($target); - return content_translation_add_page($entity, $source, $target); + public function add(Language $source, Language $target, Request $request) { + $entity_type_id = $request->attributes->get('_entity_type_id'); + $entity = $request->attributes->get($entity_type_id); + + $source = !empty($source) ? $source : $entity->language(); + $target = !empty($target) ? $target : language(Language::TYPE_CONTENT); + + // @todo Exploit the upcoming hook_entity_prepare() when available. + $this->prepareTranslation($entity, $source, $target); + $info = $entity->getEntityType(); + + // @todo: $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default'; + $operation = 'default'; + $form_state['langcode'] = $target->id; + $form_state['content_translation']['source'] = $source; + $form_state['content_translation']['target'] = $target; + $form_state['content_translation']['translation_form'] = !$entity->access('update'); + + return $this->entityFormBuilder()->getForm($entity, $operation, $form_state); } /** - * @todo Remove content_translation_edit_page(). + * Page callback for the edit translation page. + * + * @param \Drupal\language\Plugin\Condition\Language $language + * (optional) The language of the translated values. Defaults to the current + * content language. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object from which to extract the entity type. + * + * @return array + * A processed form array ready to be rendered. */ - public function edit(Request $request, $language) { - $entity = $request->attributes->get($request->attributes->get('_entity_type_id')); - module_load_include('pages.inc', 'content_translation'); - $language = language_load($language); - return content_translation_edit_page($entity, $language); + public function edit(Language $language, Request $request) { + $entity_type = $request->attributes->get('_entity_type'); + $entity = $request->attributes->get($entity_type); + + $info = $entity->entityInfo(); + $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default'; + $form_state['langcode'] = $language->id; + $form_state['content_translation']['translation_form'] = TRUE; + return $this->entityManager()->getForm($entity, $operation, $form_state); } } diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php index cab9ac4..c971387 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php @@ -88,6 +88,12 @@ protected function alterRoutes(RouteCollection $collection, $provider) { 'entity' => array( 'type' => 'entity:' . $entity_type_id, ), + 'source' => array( + 'type' => 'language', + ), + 'target' => array( + 'type' => 'language', + ), ), ) ); @@ -111,6 +117,9 @@ protected function alterRoutes(RouteCollection $collection, $provider) { 'entity' => array( 'type' => 'entity:' . $entity_type_id, ), + 'language' => array( + 'type' => 'language', + ), ), ) ); @@ -133,6 +142,9 @@ protected function alterRoutes(RouteCollection $collection, $provider) { 'entity' => array( 'type' => 'entity:' . $entity_type_id, ), + 'language' => array( + 'type' => 'language', + ), ), '_access_mode' => 'ANY', ) diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php index 43e5d77..37e2d72 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php @@ -176,7 +176,7 @@ protected function assertWorkflows(UserInterface $user, $expected_status) { else { $this->drupalGet($edit_translation_path, $options); } - $this->assertResponse($expected_status['edit_translation'], format_string('The @user_label has the expected translation creation access.', $args)); + $this->assertResponse($expected_status['edit_translation'], format_string('The @user_label has the expected translation edit access.', $args)); } /** diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml index 7396d57..be977c0 100644 --- a/core/modules/language/language.services.yml +++ b/core/modules/language/language.services.yml @@ -16,3 +16,7 @@ services: arguments: ['@config.storage', '@event_dispatcher', '@config.typed'] tags: - { name: config.factory.override, priority: -254 } + language_converter: + class: Drupal\language\LanguageConverter + tags: + - { name: paramconverter } \ No newline at end of file diff --git a/core/modules/language/lib/Drupal/language/LanguageConverter.php b/core/modules/language/lib/Drupal/language/LanguageConverter.php new file mode 100644 index 0000000..375cfaf --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageConverter.php @@ -0,0 +1,41 @@ +