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
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
Comment #2
raphaeltbm commentedComment #3
andypostComment #4
mfbAlternate 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 :)
Comment #6
markdatter commentedI disagree with the broad assessment made here:
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
Comment #8
krzysztof domańskiI added #2796399: Problematic hreflang links when using entity as front page. to the issue summary.