It seems that the current implementation of the alternate hreflang links for the entities is not yet optimal in regard of the following:

Replace html_head_link by html_head

#2873648: With many languages, content_translation_page_attachments adds too many alternate links to the response headers crashing varnish (503)

Was there a reason to set it in html_head_link? Anyway, with this problem or not, it's recommended to use only 1 method for the alternate hreflang (among the 3 methods: meta html, http header, sitemap xml ; see the global SEO recommendations) and html_head_link generate meta html + http header.
I guess it should be set in html_head by default (the most safe we will said) and if someone needs these links to be in the http headers for some specific technical/SEO reason, he can always duplicate/move the existing generated links via some alter, knowing his own environment configuration. At least, "immediately", doing that for the alternate hreflang links in our context (the last comment let suppose that the problem is larger (?)).

Include the hreflang in the key

#2945033: HtmlHeadLink processing does not allow for duplicated alternate hreflang links

Knowing this issue and the previous one, it it move in html_head only, the key should be something like:

languages:rel:hreflang:href

Wrong hreflang values

#2926013: Alternative language link tag being set for content with language "not specificed"
#2811533: wrong hreflang-values for non-translated pages in multi-language environment

The "not applicable" and "not specified" code languages are not valid for the alternate hreflang links context.
If the hreflang is equal to "zxx" (LANGCODE_NOT_APPLICABLE) or "und" (LANGCODE_NOT_SPECIFIED), it should be replace by "x-default" (LANGCODE_DEFAULT) or the alternate link should not be generated.

Hreflangs links when using specific node as a front page

#2796399: Problematic hreflang links when using entity as front page.

No direct issue for the moment, but should be considered

  • 403/404

Global solution

#2303525: Provide link tags to alternate languages (hreflang) in HTML head (last comments)
#2521782: HTML head has alternate hreflang links to unpublished translations

For these issues and for what the hreflang module trying to do in his own.
And following this issue that i've just created recently #2994575: Url access / PathValidator not language/options aware

I guess that what is in content_translation_page_attachments currently should be moved in the language module and manage directly by the path (via some PathValidator), not the route object (view, entity, webform or whatever...), having to duplicate the code logical in multiple modules/places to generate these links (to avoid all the potential problems indicated above and below). Doing like should permit to go through all the access/security layers of what is behind a path and automatically managed any new layer of access added by any kind of modules (if done the "official" way we agree).

On a different note, as indicated in #2994575: Url access / PathValidator not language/options aware. Going that way, it could help to some replacement of the current getLanguageSwitchLinks, or just an addition, to improve the current 'frontend' language switcher? For something more aware-access, relevant for the "80% scenario case" (?).

Example

Below just to give some idea for all the points above, a raw and not really tested php:

function language_get_translations_for_route() {

  // 404/404 case
  if(\Drupal::requestStack()->getCurrentRequest()->attributes->get('exception')) {
    return [];
  }

  // Locked languages case (zxx, und, ...)
  foreach(\Drupal::routeMatch()->getParameters() as $p) {
    if(is_object($p) && method_exists($p, 'language') && $p->language()->isLocked()) {
      return [];
    }
  }

  $paths = [];

  if (\Drupal::languageManager()->isMultilingual()) {

    $languages = \Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE);

    foreach ($languages as $lang) {

      $url = Url::fromRoute('<current>', [], ['language' => $lang]);

      //if ($url) {

        $path =[
          'language' => $lang,
          'access' => FALSE,
        ];

        // Permission/access-layers case
        if (\Drupal::pathValidator()->getUrlIfValid($url->toString())) {
          $path['access'] = TRUE;
        }

        // Frontpage case (managed because of the "canonical" situation (see related issues))
        if (\Drupal::service('path.matcher')->matchPath('/' . $url->getInternalPath(),'<front>')) {
          $url = Url::fromRoute('<front>', [], ['language' => $lang]);
        }

        $path['url'] = $url->toString();
        $path['url_absolute'] = $url->setAbsolute()->toString();

        $paths[] = $path;
      //}
    }
  }

  return $paths;
}

function language_page_attachments(&$page) {

  foreach(language_get_translations_for_route() as $translated_path) {

    if ($translated_path['access']) {

      $hreflang = $translated_path['language']->getId();

      // Html_head case
      $page['#attached']['html_head'][] = [
        [
          '#tag' => 'link',
          '#attributes' => [
            'rel' => 'alternate',
            'hreflang' => $hreflang,
            'href' => $translated_path['url_absolute'],
          ],
        ],
        // Key case
        'language:alternate:' . $hreflang . ':' . $translated_path['url_absolute'],
      ];

    }

  }
}

Comments

raphaeltbm created an issue. See original summary.

raphaeltbm’s picture

Issue summary: View changes
andypost’s picture

Issue tags: +language-base
mfb’s picture

Alternate hreflang links in HTTP headers are mainly useful for non-HTML content, such as PDFs, images etc. (However, there may not be many Drupal-based sites that have actually set this up :)

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

markdatter’s picture

I disagree with the broad assessment made here:

Wrong hreflang values
#2926013: Alternative language link tag being set for content with language "not specificed"
#2811533: wrong hreflang-values for non-translated pages in multi-language environment

The "not applicable" and "not specified" code languages are not valid for the alternate hreflang links context.
If the hreflang is equal to "zxx" (LANGCODE_NOT_APPLICABLE) or "und" (LANGCODE_NOT_SPECIFIED), it should be replace by "x-default" (LANGCODE_DEFAULT) or the alternate link should not be generated.

I think there is a legitimate use case in which any node with language "Not specified" should have a full set of hreflang tags created.

I described this in detail in the Simple XML Sitemap issue queue, since I'm hoping to have this functionality added there, but it seems relevant here for anyone that uses hreflang tags in the page HTML:

https://www.drupal.org/project/simple_sitemap/issues/3081649

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

krzysztof domański’s picture

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.