Problem/Motivation

In #2303525: Provide link tags to alternate languages (hreflang) in HTML head, when viewing a content entity, we added hreflang alternate links to the HTML head for each translation of that entity. However, alternate links will appear for unpublished translations as well. This behavior hurts SEO, as web crawlers will be made aware of links that cannot actually be viewed.

Proposed resolution

Do not include unpublished translations when providing rel="alternate" hreflang links in the HTML head.

Remaining tasks

  1. Re-roll onto 8.7.x HEAD
  2. Fix deprecated use of format_string()
  3. Re-roll onto 8.8.x HEAD
  4. Re-roll onto 8.9.x
  5. Fix deprecated use of EntityInterface::urlInfo() in NodeTranslationUITest
  6. Review and feedback
  7. RTBC and feedback
  8. Commit

User interface changes

None.

API changes

None.

CommentFileSizeAuthor
#74 2521782-74-hreflang-links-to-unpublished-translations.patch5.67 KBmparker17
#73 interdiff.txt2.08 KBmparker17
#73 2521782-73-hreflang-links-to-unpublished-translations.patch5.93 KBmparker17
#72 2521782-72-hreflang-links-to-unpublished-translations.patch5.64 KBmparker17
#69 2521782-69.patch5.26 KBmaebug
#67 2521782-67.patch5.25 KBjkuma
#62 2521782-62.patch5.25 KBMerryHamster
#59 2521782-59.patch5.25 KBMerryHamster
#57 drupal-alternate_hreflang_unpublished_with_test-2521782-57.patch5.25 KBsavkaviktor16@gmail.com
#56 drupal-alternate_hreflang_unpublished_with_test-2521782-56.patch5.21 KBcaspervoogt
#54 interdiff-2521782-49-54.txt673 bytesyogeshmpawar
#54 drupal-alternate_hreflang_unpublished_with_test-2521782-54.patch5.22 KByogeshmpawar
#49 drupal-alternate_hreflang_unpublished_with_test-2521782-49.patch5.23 KBNikolay Borisov
#45 drupal-head_alternate_unpublished-2521782-45.patch1.85 KBckaotik
#21 interdiff-2521782-19-21.txt926 bytespaulmckibben
#21 2521782--hreflang-unpublished-21.patch4.65 KBpaulmckibben
#19 interdiff-2521782-15-19.txt1.49 KBpaulmckibben
#19 2521782--hreflang-unpublished-19.patch4.73 KBpaulmckibben
#15 2521782--hreflang-unpublished-15.patch4.73 KBpaulmckibben
#13 2521782--hreflang-unpublished-13.patch4.26 KBpaulmckibben
#10 2521782--hreflang-unpublished-10.patch4.32 KBpaulmckibben
#1 2521782-hreflang-unpublished.patch4.25 KBpaulmckibben
#6 interdiff.txt7.35 KBswentel
#6 2521782-hreflang-unpublished-6.patch10.54 KBswentel
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

paulmckibben’s picture

Issue summary: View changes
Status: Active » Needs review
FileSize
4.25 KB

I've attached a patch that includes a fix and an updated test. I am unable to run the test locally due to mysql wait time issues that I can't seem to work around.

Status: Needs review » Needs work

The last submitted patch, 1: 2521782-hreflang-unpublished.patch, failed testing.

matsbla’s picture

I tested patch in #1 and looks like it fix the problem

Berdir’s picture

+++ b/core/modules/content_translation/content_translation.module
@@ -554,6 +554,13 @@ function content_translation_page_attachments(&$page) {
+        // Skip any unpublished node translation.
+        if ($entity instanceof \Drupal\node\Entity\Node) {
+          $translation = $entity->getTranslation($language->getId());
+          if (!$translation->isPublished()) {
+            continue;
+          }
+        }

Instead of a hardcoded check for nodes, why not check for access for each translation? That should result in the same and work for everything.

paulmckibben’s picture

@berdir, thanks for the suggestion. I tried the following, but the unpublished translation link still appears. If I enter the URL of the unpublished translation as an anonymous user, I properly get access denied, so I'm not sure what I've done wrong. Here's the code:

        // Skip any translation that cannot be viewed.
        if ($entity instanceof \Drupal\node\Entity\Node) {
          $translation = $entity->getTranslation($language->getId());
          if (!$translation->access('view')) {
            continue;
          }
        }

Any advice?

swentel’s picture

Status: Needs work » Needs review
FileSize
10.54 KB
7.35 KB

Hmm yeah, calling $translation->access('view') will return true if the original entity has access, because in Node.php prepareLangcode() is looking for the current language.

Attached patch fixes that, but not sure if this is the right approach as it's technically an API change.

swentel’s picture

Issue tags: +DUGBE0609

Note, that the isPublished() check might be a go to fix this patch, but I think there's a still a bug with calling $translation->access('view')

The last submitted patch, 6: 2521782-hreflang-unpublished-6.patch, failed testing.

Anonymous’s picture

Status: Needs review » Needs work
paulmckibben’s picture

Status: Needs work » Needs review
FileSize
4.32 KB

Since the comments indicate the original approach/patch is "a go," I've rerolled my original patch against beta16.

Status: Needs review » Needs work

The last submitted patch, 10: 2521782--hreflang-unpublished-10.patch, failed testing.

The last submitted patch, 10: 2521782--hreflang-unpublished-10.patch, failed testing.

paulmckibben’s picture

Status: Needs work » Needs review
FileSize
4.26 KB

Last patch was created in the wrong repository (had a "docroot" directory) and would not apply. Rerolling the patch in a brand new clone of the core repo. Let's try this again!

Status: Needs review » Needs work

The last submitted patch, 13: 2521782--hreflang-unpublished-13.patch, failed testing.

paulmckibben’s picture

Status: Needs work » Needs review
FileSize
4.73 KB

The test code had a bug that caused the test to fail. Fixed the test code. New patch attached.

plach’s picture

Component: language system » content_translation.module
Status: Needs review » Needs work

Thanks for providing a patch, but in the current form it is dealing only with nodes, but it should support any content entity type instead. For instance:

+++ b/core/modules/content_translation/content_translation.module
@@ -562,6 +562,13 @@ function content_translation_page_attachments(&$page) {
+        // Skip any unpublished node translation.
+        if ($entity instanceof \Drupal\node\Entity\Node) {
+          $translation = $entity->getTranslation($language->getId());
+          if (!$translation->isPublished()) {
+            continue;
+          }
+        }

We should check $entity->access() here.

plach’s picture

  1. +++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
    @@ -371,26 +381,38 @@ protected function doTestTranslations($path, array $values) {
    +   * Tests that the path for the given node provides the correct alternate hreflang links.
    

    The first PHP doc line should not exceed 80 chars.

  2. +++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
    @@ -371,26 +381,38 @@ protected function doTestTranslations($path, array $values) {
    +   * @throws \Drupal\Core\Entity\EntityMalformedException
    

    Missing blank line separator before this and the description of when the exception is thrown.

paulmckibben’s picture

Hi @plach, thanks for your review and feedback. Regarding #16, I tried the $entity->access() approach earlier and ran into trouble. Copying my comment #5 above:

I tried the following, but the unpublished translation link still appears. If I enter the URL of the unpublished translation as an anonymous user, I properly get access denied, so I'm not sure what I've done wrong. Here's the code:

        // Skip any translation that cannot be viewed.
        if ($entity instanceof \Drupal\node\Entity\Node) {
          $translation = $entity->getTranslation($language->getId());
          if (!$translation->access('view')) {
            continue;
          }
        }

Any advice?

paulmckibben’s picture

Status: Needs work » Needs review
FileSize
4.73 KB
1.49 KB

Well, I tried the entity->access() approach against Drupal 8.0.3, and this time it worked! Whatever happened #5 seems to have changed now.
I've addressed all comments. Please review. Thank you!

swentel’s picture

Status: Needs review » Needs work
+++ b/core/modules/content_translation/content_translation.module
@@ -566,6 +566,13 @@ function content_translation_page_attachments(&$page) {
+        if ($entity instanceof \Drupal\node\Entity\Node) {

This check should go away, after that it's good to go.

paulmckibben’s picture

Status: Needs work » Needs review
FileSize
4.65 KB
926 bytes

Oops, missed that Node check... thanks, @swentel!
Updated patch and interdiff attached.

swentel’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me now. My patches are completely unrelated, so RTBC

  • catch committed 5a25b3f on 8.1.x
    Issue #2521782 by paulmckibben, swentel: HTML head has alternate...

  • catch committed d48a154 on 8.0.x
    Issue #2521782 by paulmckibben, swentel: HTML head has alternate...
catch’s picture

Status: Reviewed & tested by the community » Fixed
+++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
@@ -372,26 +382,39 @@ protected function doTestTranslations($path, array $values) {
+            $this->assert(isset($links[0]), format_string('The %langcode node translation has the correct alternate hreflang link for %alternate_langcode: %link.', array('%langcode' => $langcode, '%alternate_langcode' => $alternate_langcode, '%link' => $url->toString())));

This is deprecated, but I see it's just been copied from the existing test, so committing as is to 8.1.x and 8.0.x, thanks!

Wim Leers’s picture

Status: Fixed » Active
Issue tags: +D8 cacheability
+++ b/core/modules/content_translation/content_translation.module
@@ -566,6 +566,11 @@ function content_translation_page_attachments(&$page) {
+        // Skip any translation that cannot be viewed.
+        $translation = $entity->getTranslation($language->getId());
+        if (!$translation->access('view')) {
+          continue;
+        }

This should set the access result's cacheability metadata on $page.

The reason tests pass is probably that by default access is granted based on permissions, and user.permissions is a required render cache context.

  • catch committed fda31a6 on 8.1.x
    Revert "Issue #2521782 by paulmckibben, swentel: HTML head has alternate...

  • catch committed 5903ad6 on 8.0.x
    Revert "Issue #2521782 by paulmckibben, swentel: HTML head has alternate...
catch’s picture

Status: Active » Needs work

Good point. Rolled back for now.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

  • catch committed 5a25b3f on 8.3.x
    Issue #2521782 by paulmckibben, swentel: HTML head has alternate...
  • catch committed fda31a6 on 8.3.x
    Revert "Issue #2521782 by paulmckibben, swentel: HTML head has alternate...

  • catch committed 5a25b3f on 8.3.x
    Issue #2521782 by paulmckibben, swentel: HTML head has alternate...
  • catch committed fda31a6 on 8.3.x
    Revert "Issue #2521782 by paulmckibben, swentel: HTML head has alternate...

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

caspervoogt’s picture

I see the status is 'needs work' but reading through the comments it isn't clear to me what exactly remains to be done.. what's the status / what needs to be done to get this committed?

screon’s picture

Same question as above. I see the fix has been committed to 8.3.x but still needs work for 8.2.x?

cilefen’s picture

It is comment #26. And it was reverted in 8.3.x and 8.2.x.

caspervoogt’s picture

Yup, the path from #26 was committed and then rolled back - that's why I was asking what actually remains to be done, since the status is "Needs work".

From #26: "The reason tests pass is probably that by default access is granted based on permissions, and user.permissions is a required render cache context."
I'm not really following this and would need to dive into the code to catch up.. am hoping some of the earlier contributors and provide some explanation of status.

cilefen’s picture

#26 is the reason it was rolled back.

caspervoogt’s picture

@cilefen I get that... but what is lacking here is a summary of what specific action items still have to be done in order to get this patched on the 8.2 branch. I don't know what those steps would be.

cilefen’s picture

Is the question about what steps are required to accomplish the review items in #26?

caspervoogt’s picture

Yes. It says;
'This should set the access result's cacheability metadata on $page.'

The reason tests pass is probably that by default access is granted based on permissions, and user.permissions is a required render cache context.'

I can't tell whether this means that content_translation.module is OK, or whether there's a bug in the tests, or both. Sounds like the tests pass but shouldn't, i.e. the module code is not in fact setting cacheability, even though tests say that it is. Would be great if someone could confirm and explain in a bit of detail.

  • catch committed 5a25b3f on 8.4.x
    Issue #2521782 by paulmckibben, swentel: HTML head has alternate...
  • catch committed fda31a6 on 8.4.x
    Revert "Issue #2521782 by paulmckibben, swentel: HTML head has alternate...

  • catch committed 5a25b3f on 8.4.x
    Issue #2521782 by paulmckibben, swentel: HTML head has alternate...
  • catch committed fda31a6 on 8.4.x
    Revert "Issue #2521782 by paulmckibben, swentel: HTML head has alternate...

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

ckaotik’s picture

Assigned: paulmckibben » Unassigned
Status: Needs work » Needs review
FileSize
1.85 KB

Considering there hasn't been any change in the past 5 months, I've removed the assignment to @paulmckibben.

I assume @wim-leers meant that we should add the cache metadata to the $page render array. I've attached a patch that should add that caching information. Whether the tests need to be updated or not is beyond me, though.

Wim Leers’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

#45: great! Thanks for getting this going again :) The patch looks good! But it'll need to bring back the test coverage changes that #21 was adding.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Nikolay Borisov’s picture

I merged the simple test from #21 with the last patch from #45 and tested the Simple Test locally. Prepared it in its own patch.

Nikolay Borisov’s picture

Status: Needs work » Needs review
Lan Anh’s picture

His solution, worked well ! Well done!

borisson_’s picture

Status: Needs review » Needs work

I was going to rtbc this, but I noticed some whitespace that should be removed. Sorry for the nitpick.

+++ b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
@@ -374,24 +374,35 @@ protected function doTestTranslations($path, array $values) {
+   */ ¶

Whitespace should be removed here.

yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
yogeshmpawar’s picture

Assigned: yogeshmpawar » Unassigned
Status: Needs work » Needs review
FileSize
5.22 KB
673 bytes

Changes done as per comment #52 & also added an interdiff.

borisson_’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Patch doesn't apply anymore and introduces some cs errors. ([] should be used instead of array()).

caspervoogt’s picture

I just tested drupal-alternate_hreflang_unpublished_with_test-2521782-54.patch on it applied cleanly for me on dev and 8.5.0. Tested and working on 8.5.0 for me but I have not tested it on dev specifically. I've re-rolled it with array() converted to [].

savkaviktor16@gmail.com’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
5.25 KB

Re-rolled

MerryHamster’s picture

MerryHamster’s picture

Here is an only reroll to 8.6.x

MerryHamster’s picture

Status: Needs work » Needs review

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

MerryHamster’s picture

raphaeltbm’s picture

I guess that some stuff from #2994800: Improve the alternate hreflang implementation shoud be considered.

Greg Sims’s picture

I performed the following from module_name_preprocess_html():

var_dump($variables['page']['content']['system_main']['#attached']['html_head_link']);

I found the rel="canonical", rel="shortlink" and rel="revision" tags. I was not able to find the rel="alternate" hreflang= even though the resulting page contains three such tags -- one for en, es and zh-hans.

Should hreflang be part of the $variables array as described above? Is there another way to access the hreflang tags from module_name_preprocess_html()? I would like to be able to unset() an hreflang that points to an unpublished translation.

Greg Sims’s picture

It appears that updating the language code in $variables has no impact on the creation of the associated hreflang tag.

I found the hreflang tags here:

var_dump($variables['page']['#attached']['html_head_link']);

I changed the language code for Chinese to zh according to the standard:

  [2]=>
  array(2) {
    [0]=>
    array(3) {
      ["rel"]=>
      string(9) "alternate"
      ["hreflang"]=>
      string(2) "zh"
      ["href"]=>
      string(53) "https://www.raystedman.org/zh/new-testament/ephesians"
    }
    [1]=>
    bool(true)
  }

This resulted in the following hreflang tag on this page:

<link rel="alternate" hreflang="zh-hans" href="https://stg.raystedman.org/zh/new-testament/ephesians" />

Please notice that the hreflang value does not conform to the value in $variables.

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.

jkuma’s picture

The patch on #62 fails on latest version of 8.7.x

Here is a re-roll for 8.7.x

Status: Needs review » Needs work

The last submitted patch, 67: 2521782-67.patch, failed testing. View results

maebug’s picture

The patch in #67 was not applying correctly for me on the latest version of 8.7.x, so I re-reolled it

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.

mparker17’s picture

Assigned: Unassigned » mparker17
Issue summary: View changes

Looks like the test failed on 8.8.x because the call to \Drupal\Core\Entity\EntityInterface::urlInfo() in NodeTranslationUITest became deprecated in 8.8.x

diff --git a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
index 3348ad9d90..d870cfe39c 100644
--- a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
+++ b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
@@ -378,24 +378,35 @@ protected function doTestTranslations($path, array $values) {
   /**
    * Tests that the given path provides the correct alternate hreflang links.
    *
-   * @param \Drupal\Core\Url $url
-   *   The path to be tested.
+   * @param \Drupal\node\Entity\Node $node
+   *   The node to be tested.
    */
-  protected function doTestAlternateHreflangLinks(Url $url) {
+  protected function doTestAlternateHreflangLinks(Node $node) {
+    $url = $node->urlInfo();

... I'll look into fixing this.

mparker17’s picture

Looks like the patch needs to be re-rolled as well; and the re-roll onto 8.8.x and 8.9.x is complex because some other functions (e.g.: format_string()) have been deprecated and replaced, leading to merge conflicts.

This means I'm going to have to re-roll this in stages.

Attached is a re-roll of the patch from #69 onto the HEAD of 8.7.x at time-of-writing, commit 3fe90fe545. I haven't made any changes yet, so no interdiff is necessary.

I fully expect this to fail to patch on 8.8.x and 8.9.x so I'm going to leave the issue as "Needs work" for Testbot's sake.

mparker17’s picture

Here's a patch where I've replaced calls to format_string() with calls to new FormattableMarkup() as per https://www.drupal.org/node/2302363

mparker17’s picture

I was hoping the change in #73 would allow git to rebase the patch onto 8.9.x automagically, because the removed lines would be the same as the lines in the patch; but git still presented me with a merge conflict in NodeTranslationUITest.php. Although, to resolve the merge conflict, I could simply delete the old code - I didn't need to make any changes to the code in the patch.

So here's the patch from #73 rebased onto 8.9.x - since there were no changes, there's no interdiff.

Still "needs work" until I can fix the deprecated use of EntityInterface::urlInfo()

mparker17’s picture

Assigned: mparker17 » Unassigned
Issue summary: View changes
Status: Needs work » Needs review

Oops: I did change urlInfo() to toUrl() in that patch; likely while I was trying to figure out why Git didn't want to rebase the patch without a merge conflict.

That was the only change; although generating an interdiff is not possible since it would include all the changes from 8.7.x to 8.9.x as well.

So I'm setting this to "needs review" so Testbot can try it out.

Sorry for the noise and confusion.

mparker17’s picture

Yay; tests are green again!

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.

weseze’s picture

Using this in production and it works great.

mparker17’s picture

@weseze, if you've tested it (i.e.: in production), and reviewed the patch (i.e.: by reading through it for problems), may I trouble you to change the status to "Reviewed & tested by the community"? (I cannot do that myself because I wrote the patch) Thank you!

weseze’s picture

Status: Needs review » Reviewed & tested by the community
badrange’s picture

If this gets merged on July 2 it will be merged on the fifth year anniversary of the patch!

  • catch committed db46062 on 9.1.x
    Issue #2521782 by paulmckibben, mparker17, MerryHamster, swentel,...

  • catch committed 06b36bd on 8.9.x
    Issue #2521782 by paulmckibben, mparker17, MerryHamster, swentel,...

  • catch committed cc85b48 on 9.0.x
    Issue #2521782 by paulmckibben, mparker17, MerryHamster, swentel,...
catch’s picture

Version: 9.1.x-dev » 8.9.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.1.x and cherry-picked back to 8.9.x, four years after the original fix was reverted but not quite in time to hit the five year birthday.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

FiNeX’s picture

Hi, what if only one language is available? It looks that currently there is always at least one hreflang tag printed. Is this ok?