diff --git a/behat.yml.dist b/behat.yml.dist index 606fb8b..4528195 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -32,6 +32,6 @@ default: region_map: "language switcher": "#block-oe-multilingual-language-switcher" "language dialog": "#block-oe-multilingual-language-switcher" - page header: ".region-content" + "page content": ".region-content" formatters: progress: ~ diff --git a/oe_multilingual.module b/oe_multilingual.module index 08ade6e..0fb7694 100644 --- a/oe_multilingual.module +++ b/oe_multilingual.module @@ -7,7 +7,10 @@ declare(strict_types = 1); +use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Config\FileStorage; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\Url; @@ -81,3 +84,83 @@ function oe_multilingual_configurable_language_presave(EntityInterface $entity) } } } + +/** + * Implements hook_block_view_BASE_BLOCK_ID_alter() for the Page Header block. + */ +function oe_multilingual_block_view_oe_theme_helper_page_header_alter(array &$build, BlockPluginInterface $block) { + $build['#pre_render'][] = 'oe_multilingual_page_header_pre_render'; +} + +/** + * Pre-render callback for the Page Header block alteration. + * + * We use this to add the language switcher + * to the page header if OpenEuropa Theme is being used. + * + * @param array $build + * The built render array of the block. + * + * @see \Drupal\oe_theme_helper\Plugin\Block\PageHeaderBlock + * + * @return array + * The built render array of the block. + */ +function oe_multilingual_page_header_pre_render(array $build): array { + // Get required services. + $multilingual_helper = \Drupal::service('oe_multilingual.helper'); + $content_language_switcher_provider = \Drupal::service('oe_multilingual.content_language_switcher_provider'); + $language_manager = \Drupal::languageManager(); + $cache = new CacheableMetadata(); + $cache->addCacheContexts(['languages:language_content']); + + $entity = $multilingual_helper->getEntityFromCurrentRoute(); + // Bail out if there is no entity or if it's not a content entity. + if (!$entity || !$entity instanceof ContentEntityInterface) { + $cache->applyTo($build); + return $build; + } + + $cache->addCacheableDependency($entity); + $cache->applyTo($build); + + // Render the links only if the current entity translation language is not + // the same as the current site language. + /** @var \Drupal\Core\Entity\EntityInterface $translation */ + $translation = $multilingual_helper->getCurrentLanguageEntityTranslation($entity); + $current_language = $language_manager->getCurrentLanguage(); + if ($translation->language()->getId() === $current_language->getId()) { + return $build; + } + + $content = &$build['content']; + + $content['#language_switcher']['current'] = $translation->language()->getName(); + + /** @var \Drupal\Core\Language\LanguageInterface[] $languages */ + $languages = $language_manager->getNativeLanguages(); + $content['#language_switcher']['unavailable'] = $languages[$current_language->getId()]->getName(); + + // Normalize the links to an array of options suitable for the ECL + // "ecl-lang-select-pages" template. + $content['#language_switcher']['options'] = []; + foreach ($content_language_switcher_provider->getAvailableEntityLanguages($entity) as $language_code => $link) { + /** @var \Drupal\Core\Url $url */ + $url = $link['url']; + $href = $url + ->setOptions(['language' => $link['language']]) + ->setAbsolute(TRUE) + ->toString(); + + $content['#language_switcher']['options'][] = [ + 'href' => $href, + 'hreflang' => $language_code, + 'label' => $link['title'], + 'lang' => $language_code, + ]; + } + + $content['#language_switcher']['is_primary'] = TRUE; + + return $build; +} diff --git a/oe_multilingual.services.yml b/oe_multilingual.services.yml index 0d4bfde..9076722 100644 --- a/oe_multilingual.services.yml +++ b/oe_multilingual.services.yml @@ -5,6 +5,9 @@ services: oe_multilingual.helper: class: Drupal\oe_multilingual\MultilingualHelper arguments: ['@entity.repository', '@current_route_match'] + oe_multilingual.content_language_switcher_provider: + class: Drupal\oe_multilingual\ContentLanguageSwitcherProvider + arguments: ['@language_manager', '@path.matcher', '@oe_multilingual.helper'] oe_multilingual.content_language_settings: class: Drupal\oe_multilingual\MultilingualConfigOverride tags: diff --git a/src/ContentLanguageSwitcherProvider.php b/src/ContentLanguageSwitcherProvider.php new file mode 100644 index 0000000..479004b --- /dev/null +++ b/src/ContentLanguageSwitcherProvider.php @@ -0,0 +1,80 @@ +languageManager = $language_manager; + $this->pathMatcher = $path_matcher; + $this->multilingualHelper = $multilingual_helper; + } + + /** + * Returns a list of available translation links for a given entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The language manager. + * + * @return array + * Array of available translation links. + */ + public function getAvailableEntityLanguages(EntityInterface $entity) { + $route_name = $this->pathMatcher->isFrontPage() ? '' : ''; + $links = $this->languageManager->getLanguageSwitchLinks(LanguageInterface::TYPE_CONTENT, Url::fromRoute($route_name)); + + $available_languages = []; + if (isset($links->links)) { + // Only show links to the available translation languages except the + // current one. + $available_languages = array_intersect_key($links->links, $entity->getTranslationLanguages()); + $translation = $this->multilingualHelper->getCurrentLanguageEntityTranslation($entity); + unset($available_languages[$translation->language()->getId()]); + } + + return $available_languages; + } + +} diff --git a/src/Plugin/Block/ContentLanguageBlock.php b/src/Plugin/Block/ContentLanguageBlock.php index 1732ec6..7c15cbd 100644 --- a/src/Plugin/Block/ContentLanguageBlock.php +++ b/src/Plugin/Block/ContentLanguageBlock.php @@ -6,12 +6,11 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Path\PathMatcherInterface; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\Core\Url; use Drupal\language\Plugin\Block\LanguageBlock; use Drupal\oe_multilingual\MultilingualHelperInterface; +use Drupal\oe_multilingual\ContentLanguageSwitcherProvider; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -32,6 +31,13 @@ class ContentLanguageBlock extends LanguageBlock implements ContainerFactoryPlug */ protected $multilingualHelper; + /** + * The content language switcher provider. + * + * @var \Drupal\oe_multilingual\ContentLanguageSwitcherProvider + */ + protected $contentLanguageSwitcherProvider; + /** * Constructs an ContentLanguageBlock object. * @@ -47,10 +53,13 @@ class ContentLanguageBlock extends LanguageBlock implements ContainerFactoryPlug * The path matcher. * @param \Drupal\oe_multilingual\MultilingualHelperInterface $multilingual_helper * The multilingual helper service. + * @param \Drupal\oe_multilingual\ContentLanguageSwitcherProvider $content_language_switcher_provider + * The content language switcher provider. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, PathMatcherInterface $path_matcher, MultilingualHelperInterface $multilingual_helper) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, PathMatcherInterface $path_matcher, MultilingualHelperInterface $multilingual_helper, ContentLanguageSwitcherProvider $content_language_switcher_provider) { parent::__construct($configuration, $plugin_id, $plugin_definition, $language_manager, $path_matcher); $this->multilingualHelper = $multilingual_helper; + $this->contentLanguageSwitcherProvider = $content_language_switcher_provider; } /** @@ -63,7 +72,8 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('language_manager'), $container->get('path.matcher'), - $container->get('oe_multilingual.helper') + $container->get('oe_multilingual.helper'), + $container->get('oe_multilingual.content_language_switcher_provider') ); } @@ -86,27 +96,20 @@ public function build() { return $build; } - $route_name = $this->pathMatcher->isFrontPage() ? '' : ''; - $links = $this->languageManager->getLanguageSwitchLinks(LanguageInterface::TYPE_CONTENT, Url::fromRoute($route_name)); + $available_languages = $this->contentLanguageSwitcherProvider->getAvailableEntityLanguages($entity); - if (isset($links->links)) { - // Only show links to the available translation languages except the - // current one. - $available_languages = array_intersect_key($links->links, $entity->getTranslationLanguages()); - unset($available_languages[$translation->language()->getId()]); - - $build = [ - '#theme' => 'links__oe_multilingual_content_language_block', - '#links' => $available_languages, - '#attributes' => [ - 'class' => [ - "language-switcher-{$links->method_id}", - ], + // Currently the language switcher block cannot be cached: + // https://www.drupal.org/node/2232375 + $build = [ + '#theme' => 'links__oe_multilingual_content_language_block', + '#links' => $available_languages, + '#attributes' => [ + 'class' => [ + "language-switcher", ], - '#set_active_class' => TRUE, - ]; - } - + ], + '#set_active_class' => TRUE, + ]; return $build; } diff --git a/tests/features/content-language-switcher.feature b/tests/features/content-language-switcher.feature index 049f0b6..118bc7e 100644 --- a/tests/features/content-language-switcher.feature +++ b/tests/features/content-language-switcher.feature @@ -9,27 +9,41 @@ Feature: Content language selector Given the following "Demo translatable page" content item: | Title | Page title | | Body | Page body | - And the following "Italian" translation for the "Demo translatable page" with title "Page title": - | Title | Titolo pagina | - | Body | Testo pagina | And the following "Greek" translation for the "Demo translatable page" with title "Page title": | Title | Τίτλος σελίδας | | Body | Σελίδα κειμένου | And the following "Spanish" translation for the "Demo translatable page" with title "Page title": | Title | Título de página | | Body | Texto de página | + And the following "Italian" translation for the "Demo translatable page" with title "Page title": + | Title | Titolo pagina | + | Body | Testo pagina | - Scenario: Visitor navigating to an available translation shouldn't see the language selector - Given I visit the "Page title" content + Scenario: Visitor navigating to the original content shouldn't see the language selector + When I visit the "Page title" content Then I should see the heading "Page title" And I should see "Page body" - And I should not see the link "Spanish" in the "page header" region + + And I should not see the link "ελληνικά" in the "page content" + And I should not see the link "español" in the "page content" + And I should not see the link "italiano" in the "page content" + + Scenario: Visitor navigating to an available translation shouldn't see the language selector + When I visit the "Page title" content + And I click "italiano" + Then I should see the heading "Titolo pagina" + And I should see "Testo pagina" + + And I should not see the link "ελληνικά" in the "page content" + And I should not see the link "English" in the "page content" + And I should not see the link "español" in the "page content" Scenario: Visitor navigating to an unavailable translation should see the language selector - Given I visit the "Page title" content + When I visit the "Page title" content + And I click "Deutsch" Then I should see the heading "Page title" And I should see "Page body" - When I click "български" - Then I should see the heading "Page title" - And I should see "Page body" - And I should see the link "español" in the "page header" region + + And I should see the link "ελληνικά" in the "page content" + And I should see the link "español" in the "page content" + And I should see the link "italiano" in the "page content" diff --git a/tests/features/translation.feature b/tests/features/translation.feature index 5aad39a..2032aa1 100644 --- a/tests/features/translation.feature +++ b/tests/features/translation.feature @@ -14,8 +14,8 @@ Feature: Translate content And I press "Save" Then I should see the success message "Demo translatable page Test page has been created." - And I should see "Test page" in the "page header" - And I should see "This is a test" in the "page header" + And I should see "Test page" in the "page content" + And I should see "This is a test" in the "page content" # Translate the Translatable page content into Spanish. When I click "Translate" @@ -25,6 +25,6 @@ Feature: Translate content And I press "Save (this translation)" Then I should see the success message "Demo translatable page Página de prueba has been updated." - And I should see "Página de prueba" in the "page header" - And I should see "Esto es una prueba" in the "page header" + And I should see "Página de prueba" in the "page content" + And I should see "Esto es una prueba" in the "page content"