3

I have been trying to add hreflang tags to a multi-language project following the answers to this question. In my setup there is one website, one store and there are three store views (English, German and Dutch).

The code that I'm currently using in

[my Magento install dir]/app/design/frontend/MYPACKAGE/MYTHEME/template/page/html/head.phtml

is the following

<?php foreach (Mage::app()->getWebsites() as $website) {
    foreach ($website->getGroups() as $group) {
        $stores = $group->getStores();
        foreach ($stores as $store) {
            $storeCode = $store->getCode();
            echo '<link rel="alternate" hreflang="' . $storeCode . '" href="' . $store->getCurrentUrl(false) . '"/>' . "\n";
        }
    }
}
?>

This outputs the hreflang tags like this

<link rel="alternate" hreflang="en" href="domain.com/en/bags.html">
<link rel="alternate" hreflang="de" href="domain.com/de/bags.html">
<link rel="alternate" hreflang="nl" href="domain.com/nl/bags.html">

At first glance this looks perfect, however there is this one problem. The store is using different URL Keys per language (store view). To improve SEO, the URL Keys are translated to match the language of the store view. For store views other than the one that is selected, the current hreflang tags point to URLs that do not exist (resulting in 404 Page not Found errors).

So the hreflang tags that I am looking for should look like this

<link rel="alternate" hreflang="en" href="domain.com/en/bags.html">
<link rel="alternate" hreflang="de" href="domain.com/de/taschen.html">
<link rel="alternate" hreflang="nl" href="domain.com/nl/tassen.html">

How can I get the hreflang tags to match the proper URL Key for each store view?

UPDATE July 16, 2015

Even though I have not accepted Marius his answer (because I did not manage to get his second solution to work for me) I used his first approach and created 301 redirects for all pages in different languages.

This seemed to work out just fine but last week Google started sending out error messages to users for incorrect implementation on Hreflang through Google Search Console.

In the Search Console it says that Google has found URLs for my 'en' site and alternate URLs in 'nl' that do not have return tags. A screen shot of the console is below.

enter image description here

The Alternate 'en' URLs that Google is listing do appear in the <head> section of my site, but I have set 301 redirects to the 'nl' URLs in the Magento Admin Panel.

For some reason Google handles this as incorrect implementation of hreflang tags.

2
  • Did you take a look at app/design/frontend/base/default/template/page/switch/languages.phtml Maybe it can help you out
    – Jeroen
    Feb 26, 2015 at 11:37
  • Yes, I checked this. But if I'm not mistaken then the language selector uses URL rewrites to get to the proper URL after the store is selected. I need my hreflang tags to display URL Keys of store views that are not currently selected.
    – MatthijsIJ
    Feb 26, 2015 at 14:35

5 Answers 5

3

I think the simplest solution here will be to create 301 redirects for your pages in different languages in the url rewrites management admin section.
Something like this:

For DE store view redirect from bags.html to taschen.html.
For EN store view redirect from taschen.html to bags.html.

Do the same for all the other combinations.
this way, when you switch from domain.com/en/bags.html to DE you it will points to a page that does not exist domain.com/de/bags.html but that will do a 301 redirect to domain.com/de/taschen.html.
I know that there are a lot of redirects to create, but I can recommend you this extension. All you need to do is to provide the url keys for each store view and it will create automatically the redirects.

But if you don't like this approach, you may need to rewrite the Mage_Core_Model_Store::getCurrentUrl method to look in the url_rewrites table and get the url for each language. But this may cause performance issues and it is not a bullet proof solution, because it will not work for cms pages or other custom page.

I don't have a full working solution for this, but I can give you some pointers.
Look in the core_url_rewrites table for a target path matching the $requestString var in the method mentioned above for the store that should generate the url and append to the target path value the query string.

2

I've managed to get this working for product pages and category pages (a little hacky!), but for some reason the url for the cms pages does not seem to work ok, but maybe it helps someone else to get further ..

<?php 
/**
 *  TODO: fix cms page URLs
 **/

foreach (Mage::app()->getWebsites() as $website) {

    foreach ($website->getGroups() as $group) {

        $stores = $group->getStores();

        foreach ($stores as $store) {

            // store id
            $storeId = $store->getId();

            // get the store code
            $storeCode = substr(Mage::getStoreConfig('general/locale/code', $storeId),0,2);


            // for product pages
            if (Mage::registry('product')) {

                // get the product id
                $productId  = Mage::registry('product')->getId();

                // get the urls
                $url = Mage::getModel('catalog/product')->setStoreId($storeId)->load($productId)->getProductUrl();

                // output the avialable hreflangs
                echo '<link rel="alternate" hreflang="' . $storeCode . '" href="' . $url . '"/>' . "\n";

            }

            // for category pages
            elseif(Mage::registry('current_category')) { // if we are in a cat

                // get current catId
                $categoryId = Mage::registry('current_category')->getId();

                /* is there a better way to get the full url without having to create a composite? */
                // base url
                $base_url = Mage::app()->getStore($storeId)->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK);

                // get the url          
                $url = Mage::getModel('catalog/category')->setStoreId($storeId)->load($categoryId)->getUrlPath();

                // output the avialable hreflangs
                echo '<link rel="alternate" hreflang="' . $storeCode . '" href="' . $base_url . $url . '"/>' . "\n";

            // for cms pages
            } elseif(Mage::getSingleton('cms/page')) {


                // get the page id
                $page_id = Mage::getBlockSingleton('cms/page')->setStoreId($storeId)->getPage()->getId();


                $url = Mage::getBlockSingleton('cms/page')->setStoreId($storeId)->getPageUrl($page_id);

                // TODO: fix output for cms pages

            }

        }
    }
}
?>
1

I had this issue a few days ago too. The way i solved it, was to just place the hrefs in translate-tags, and "translate" them inline, on every store-view.

<a href="/<?php echo $this->__('faq'); ?>/"><?php echo $this->__('FAQ'); ?></a>

Not the cleanest way, but hey, it works pretty well.

1

Have you tried

$store->getCurrentUrl(true)

instead of

$store->getCurrentUrl(false)

?

The argument for Mage_Core_Model_Store::getCurrentUrl is called $fromStore, so it would seem appropriate to use it here.

NB: also, it seems you've set up your store codes to match the language part of you locale codes, so instead of $storeCode = substr(Mage::getStoreConfig('general/locale/code', $store->getId()),0,2); you should be able to use $storeCode = $store->getCode();

So the resulting code would look like this:

<?php foreach (Mage::app()->getWebsites() as $website) {
    foreach ($website->getGroups() as $group) {
        $stores = $group->getStores();
        foreach ($stores as $store) {
            $storeCode = $store->getCode();
            echo '<link rel="alternate" hreflang="' . $storeCode . '" href="' . $store->getCurrentUrl(true) . '"/>' . "\n";
        }
    }
} ?>
1
  • Hi Doug, I have tried $store->getCurrentUrl(true) but this results in ?___from_store=nl being added to the link, which isn't very elegant and also not necessary. The second part of your answer was useful and reduces the amount of code that is needed. The less code, the better so I will update my question and implement this part. But unfortunately your answer does not provide a full solution to my question.
    – MatthijsIJ
    Apr 11, 2015 at 11:54
0

Your URLs come from many different sources including the url-key in products and the url-key in the category pages. To get the URL in another store view is not so simple and there are database calls required. The method you use simply bolts the store code url to the path, there is no magic to it where it somehow works out what the URL should be in another store view.

I decided to not dive down into the rabbit-hole of Magento complexity. Here is what I did and the compromises as a result:

To keep things simple I changed the URLs of the store to be without the .html extensions.

I set the url-key to be a global attribute, with no language variations.

I changed the way the url-key for products was to be generated, this now has the model name and the colour. Fortunately the model name is international and the foreign sounding colour name doesn't detract.

As for the category URLs, how valuable are they for SEO? I had wanted language specific versions of the category names but I don't think that category pages do well in search compared to products.

By compromising on the languages in the URL keys I can have the following block of code in the header to generate hreflang tags, whatever the page:

public function getHrefLangTags()
{
    $hreflangs ='';
    foreach (Mage::app()->getWebsites() as $website) {
        foreach ($website->getGroups() as $group) {
            $stores = $group->getStores();
            foreach ($stores as $store) {
                $code = explode('_', $store->getCode());
                $hreflang = $code[0];
                if(isset($code[1])) $hreflang .= '-' . strtolower($code[1]);
                $hreflangs .= '<link rel="alternate" href="' . $store->getCurrentUrl(false) . '" hreflang="' . $hreflang . '"/>' . "\n";
            }
        }
    }

    return $hreflangs;
}

There is nothing to learn there, however, to achieve what you really want, you will need to load your category (or product), set the store view, load the url-key and do it half a dozen times per page load. With the above things can be kept simple.

3
  • Thanks for your input. I do indeed need those URL Keys for hreflang tags, but your answer only works if URL Keys are the same for all store views. I have different URL Keys per store view and need each hreflang tag to display the correct URL, otherwise they result in 404 Page not Found errors...
    – MatthijsIJ
    Feb 26, 2015 at 13:34
  • I have just rephrased my question to clearify what I am trying to achieve
    – MatthijsIJ
    Feb 26, 2015 at 14:23
  • I think I understood you the first time. Problem is that the page has a lot to do for creating those hreflang tags - realistically it has to iterate over every store view, loading the category (or product) and then building a list of hreflang tags. I think that the solution you are after is possible if there is a quick way. For instance, in the head block you could determine if the page is a product page or a category page and then do things with the url table based on the product id or the category id. All considered though, I went for the easy option. Feb 26, 2015 at 15:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.