• Home
  • Technical Guide to HREFLANG Tags for Shopify

    Expanding into multiple international territories using Shopify+ (plus)? In this post, I share various implementation methods to deploy hreflang tags (alternate URL) across page template types and the more granular Shopify id's types. Depending on the size of how many URLs you need to map, the methods shown below could save the expense of subscribing to pay-monthly International SEO Shopify Apps.


    Contents​

    1. Crafting Shopify hreflang alternate URLs
    2. Adding hreflang tags into the Shopify theme
    3. Deploy hreflang tag by template type
    4. Deploy hreflang tag by page.id and other .id types
    5. Wrap up

    Before proceeding, this tutorial assumes that you have prior experience and understand the concepts of a hreflang tag for International SEO targetting?

    A basic shopify hreflang tag (Alternate URL) looks similar to this:

    Code:
    <link rel=”alternate” hreflang=”en” href=”{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}” />


    A regular hreflang tag (Alternate URL) looks similar to this:

    Code:
    <link rel=”alternate” hreflang=”en” href=”http://www.mydomain.com” />


    As you can see from the example above, the major difference between the Shopify hreflang tag and a regular hreflang is the populated href=" " attribute of the tag. Using Shopify's own object handles logic, you can craft the hreflang tag to output the canonical version of the URL. This is useful for saving time.

    There are two object handles (aka handles) that we will be using for this portion of the tutorial.

    They are:
    1, canonical_url
    2, replace: shop.domain

    To learn more about the Shopify object handles and tags, visit the Shopify cheat sheet ( https://www.shopify.co.uk/partners/shopify-cheat-sheet ).

    Using the two handles strung together to output the URL that is required to populate href=" " attribute within hreflang tag.

    Example:


    Code:
     canonical_url | replace: shop.domain


    Lastly, we are going to add the domain name. This is a small manual action required by you. For this action, I shall be using a generic root domain ( you will need to replace www.domain.com with your domain name).

    Example:

    Code:
    href=”{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}”


    The finished version should look like this:

    Code:
    <link rel=”alternate” hreflang=”en” href=”{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}” />


    Now that we have a single fully populated hreflang tag ready for deployment, it is time to move on with creating the international counterparts (alternate URLs). Nearly of the brands with an international presence that uses Shopify+ have opted to use the popular default model of using sub-domains.

    I believe this to be the out-of-the-box approach for Shopify. You can use ccTLD's too I believe, but for the tutorial, I will be sticking with the popular default sub-domain approach. To create counterpart international territory hreflang tags, duplicate the one we built earlier in this guide.

    You will need to update the following 2 portions:
    1, hreflang=" " (with the country-code attribute)
    2, www.domain.com (we are going to replace the www. with the sub-domain name)

    Rinse and repeat this process until you have reached full coverage of the international territories that the business operates in. That means the countries that you have built or building Shopify shops for.

    In this tutorial, I have opted to demonstrate hreflang tags for territories:

    1. Root domain (www.)
    2. United States (usa.)
    3. United Kingdom (uk.)
    4. Germany (de.)
    5. France (fr.)
    6. Finland (fi.)

    Code:
         <link rel="alternate" hreflang="en" href="{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}" />
        <link rel="alternate" hreflang="en-us" href="{{ canonical_url | replace: shop.domain, ‘usa.mydomain.com’ }}" />
       <link rel="alternate" hreflang="en-gb" href="{{ canonical_url | replace: shop.domain, ‘uk.mydomain.com’ }}" />
        <link rel="alternate" hreflang="de-de" href="{{ canonical_url | replace: shop.domain, ‘de.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fr-fr" href="{{ canonical_url | replace: shop.domain, ‘fr.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fi-fi" href="{{ canonical_url | replace: shop.domain, ‘fi.mydomain.com’ }}" />

    When you are duplicating the tags, ensure sure that you are updating the locale to match the territories that you will be targeting.



    Implement the HREFLANG Tags into theme.liquid File​


    Once you have built your hreflang tags it is time to implement the tag into the theme for each international Shopify+ store.

    Log in to the Shopify administration panel. Head to Themes > Edit Code >


    shopify-hreflang.JPG



    Locate the theme.liquid file and paste the hreflang into the head section. Save the file and visit (view source) the website’s homepage, collections and products to double-check the hreflang tags are showing as intended. Rinse and repeat this method for each of the international storefronts until you have reached full coverage.

    Copy/paste the hreflang tag directly into the theme.liquid file assumes that all URL paths match across all of the international territories. That's very unlikely to happen with multilingual websites and different product offerings between territories. Landing pages and blog articles are very likely to differ. Blog articles written in different languages will undoubtedly have different URIs.


    Tutorial updated August 2021​


    That is where the second (updated August 2021) segment of this tutorial will be very valuable to brands facing that very problem. It was not an easy problem to solve initially for myself, but I got there in the end and sharing my learnings from that.



    Deploy HREFLANG by Template File ID​


    Shopify allows greater control of content and hreflang implementation, this is done by using object handle conditionals. Meaning you can trigger the hreflang tag to display on URLs based on the page templates. This is especially useful for triggering hreflang tags on URLs where they are only required, reducing code clutter.



    Trigger HREFLANG on Page Template​


    Code:
           {% if template == 'page' %}
       <link rel="alternate" hreflang="en" href="{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}" />
        <link rel="alternate" hreflang="en-us" href="{{ canonical_url | replace: shop.domain, ‘usa.mydomain.com’ }}" />
       <link rel="alternate" hreflang="en-gb" href="{{ canonical_url | replace: shop.domain, ‘uk.mydomain.com’ }}" />
        <link rel="alternate" hreflang="de-de" href="{{ canonical_url | replace: shop.domain, ‘de.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fr-fr" href="{{ canonical_url | replace: shop.domain, ‘fr.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fi-fi" href="{{ canonical_url | replace: shop.domain, ‘fi.mydomain.com’ }}" />
    {% endif %}


    Trigger HREFLANG on Article Template​


    Code:
      {% if template == 'article' %}
       <link rel="alternate" hreflang="en" href="{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}" />
        <link rel="alternate" hreflang="en-us" href="{{ canonical_url | replace: shop.domain, ‘usa.mydomain.com’ }}" />
       <link rel="alternate" hreflang="en-gb" href="{{ canonical_url | replace: shop.domain, ‘uk.mydomain.com’ }}" />
        <link rel="alternate" hreflang="de-de" href="{{ canonical_url | replace: shop.domain, ‘de.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fr-fr" href="{{ canonical_url | replace: shop.domain, ‘fr.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fi-fi" href="{{ canonical_url | replace: shop.domain, ‘fi.mydomain.com’ }}" />
    {% endif %}



    Trigger HREFLANG on Collection Template​


    Code:
     {% if template == 'collection' %}
        <link rel="alternate" hreflang="en" href="{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}" />
        <link rel="alternate" hreflang="en-us" href="{{ canonical_url | replace: shop.domain, ‘usa.mydomain.com’ }}" />
       <link rel="alternate" hreflang="en-gb" href="{{ canonical_url | replace: shop.domain, ‘uk.mydomain.com’ }}" />
        <link rel="alternate" hreflang="de-de" href="{{ canonical_url | replace: shop.domain, ‘de.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fr-fr" href="{{ canonical_url | replace: shop.domain, ‘fr.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fi-fi" href="{{ canonical_url | replace: shop.domain, ‘fi.mydomain.com’ }}" />
    {% endif %}


    Trigger HREFLANG on Product Template​


    Code:
       {% if template == 'product' %}
        <link rel="alternate" hreflang="en" href="{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}" />
        <link rel="alternate" hreflang="en-us" href="{{ canonical_url | replace: shop.domain, ‘usa.mydomain.com’ }}" />
       <link rel="alternate" hreflang="en-gb" href="{{ canonical_url | replace: shop.domain, ‘uk.mydomain.com’ }}" />
        <link rel="alternate" hreflang="de-de" href="{{ canonical_url | replace: shop.domain, ‘de.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fr-fr" href="{{ canonical_url | replace: shop.domain, ‘fr.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fi-fi" href="{{ canonical_url | replace: shop.domain, ‘fi.mydomain.com’ }}" />
    {% endif %}



    Trigger HREFLANG on Homepage Template​


    Code:
     {% if template == 'index' %}
        <link rel="alternate" hreflang="en" href="{{ canonical_url | replace: shop.domain, ‘www.mydomain.com’ }}" />
        <link rel="alternate" hreflang="en-us" href="{{ canonical_url | replace: shop.domain, ‘usa.mydomain.com’ }}" />
       <link rel="alternate" hreflang="en-gb" href="{{ canonical_url | replace: shop.domain, ‘uk.mydomain.com’ }}" />
        <link rel="alternate" hreflang="de-de" href="{{ canonical_url | replace: shop.domain, ‘de.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fr-fr" href="{{ canonical_url | replace: shop.domain, ‘fr.mydomain.com’ }}" />
        <link rel="alternate" hreflang="fi-fi" href="{{ canonical_url | replace: shop.domain, ‘fi.mydomain.com’ }}" />
    {% endif %}


    Trigger HREFLANG on Custom Templates​


    If you have created a bunch of custom template types, use the template name within the object handle wrapper. Examples below:

    Code:
      {% if template == 'page.custom-page-template' %}
    {% if template == 'article.custom-article-template' %}
    {% if template == 'collection.custom-collection-template' %}
    {% if template == 'product.custom-product-template' %}


    How to deal with the 404 response caused by the non-matching URIs​


    The above-shown method is for creating a like-for-like URL that points to the same relative URL path on the other international stores. If the URL is not present on the other storefronts Googlebot (and your go-to web spidering tool) will be met with a 404 response. The same goes for multilingual international storefronts with URL paths in their native languages.

    To combat that issue additional work is required. You will need to retrieve a list of the accompanying article id's, page id's, product id's and collection id's. To get started on this process, read this guide I put together on scraping the IDs


    Link to guide​

    - tip open it in a new tab




    Shopify - Get Pages, Articles, Products & Collections by ID | Sitebee


    If you are a Shopify web store owner, developer, Technical SEO or marketer. There will be times when you may need to retrieve a list of product ID's, Article ID's and Page ID's. Oddly enough Shopify only allows you this data via an API. Not everyone is familar with the Shopify API. In this post...

    sitebee.co.uk
    sitebee.co.uk




    Matching HREFLANG Tags for non-matching URLs​


    Here is the solution for matching up the hreflang alternate URLs that do not have matching (like-for-like) URLs. Recently I discovered that you can use conditional statements for displaying content at a unique page level by using the page.id, article.id, collection.id and product.id.


    To deploy HREFLANG on Blog Article URLs​


    Using the Shopify liquid if conditional statement code. Insert the numerical article.id into the conditional tag wrapper (see code example below, ensure that you update the article.id to match your own article.id). Then insert the matched-up article hreflang tag within the conditional tag wrapper. The conditional tag is to be placed within the HEAD section of the theme.liquid file.

    The example below should work perfectly if you have matched the article.id correctly.

    Code:
     {% if article.id == 354029576 %}
       <link rel="alternate" hreflang="en" href="https://www.mydomain.com/blogs/this-is-a-blog-article" />
        <link rel="alternate" hreflang="en-us" href="https://usa.mydomain.com/blogs/this-is-a-blog-article" />
       <link rel="alternate" hreflang="en-gb" href="https://uk.mydomain.com/blogs/this-is-a-blog-article" />
        <link rel="alternate" hreflang="de-de" href="https://de.mydomain.com/blogs/das-ist-ein-blogartikel" />
        <link rel="alternate" hreflang="fr-fr" href="https://fr.mydomain.com/blogs/ceci-est-un-article-de-blog" />
        <link rel="alternate" hreflang="fi-fi" href="https://fi.mydomain.com/blogs/tämä-on-blogiartikkeli" />
    {% endif %}



    HREFLANG Object Handles​


    To build dozens or hundreds of hreflang tag liquid object handles. The liquid.code primary files will quickly become bloated. For this, I recommend creating liquid snippet files to house all the hreflang URLs within the object handle wrappers.

    Something like the below snippet:


    Code:
      {% render 'hreflangdata' %}


    Render the snippet file by placing it within HTML head section of the main theme.liquid file. I would recommend using this approach to keep your main theme.liquid file free of clutter.



    Deploy HREFLANG on Page.id URLs​


    The methodology for deploying hreflang tags based on the page.id is the same as the article.id. The real difference is changing the article.id to page.id in the liquid conditional wrapper tag. The code snippet below is how you should construct the liquid object handle tag.

    Code:
      {% if page.id == 3640245761 %}
       <link rel="alternate" hreflang="en" href="https://www.mydomain.com/pages/this-is-a-page" />
        <link rel="alternate" hreflang="en-us" href="https://usa.mydomain.com/pages/this-is-a-page" />
       <link rel="alternate" hreflang="en-gb" href="https://uk.mydomain.com/pages/this-is-a-page" />
        <link rel="alternate" hreflang="de-de" href="https://de.mydomain.com/pages/das-ist-ein-seite" />
        <link rel="alternate" hreflang="fr-fr" href="https://fr.mydomain.com/pages/cest-une-page" />
        <link rel="alternate" hreflang="fi-fi" href="https://fi.mydomain.com/pages/tämä-on-sivu" />
    {% endif %}


    Deploy HREFLANG on Product.id URLs​


    Deploy hreflang tags based on the product.id, the code snippet below is how you should construct the liquid object handle tag.

    Code:
        {% if product.id == 362021234 %}
       <link rel="alternate" hreflang="en" href="https://www.mydomain.com/products/this-is-a-product" />
        <link rel="alternate" hreflang="en-us" href="https://usa.mydomain.com/products/this-is-a-product" />
       <link rel="alternate" hreflang="en-gb" href="https://uk.mydomain.com/products/this-is-a-product" />
        <link rel="alternate" hreflang="de-de" href="https://de.mydomain.com/products/das-ist-ein-produkt" />
        <link rel="alternate" hreflang="fr-fr" href="https://fr.mydomain.com/products/ceci-est-un-produit" />
        <link rel="alternate" hreflang="fi-fi" href="https://fi.mydomain.com/products/tämä-on-tuote" />
    {% endif %}




    Deploy HREFLANG on Collection.id URLs​


    Deploy hreflang tags based on the collection.id, the code snippet below is how you should construct the liquid object handle tag.

    Code:
           {% if collection.id == 367055238 %}
        <link rel="alternate" hreflang="en" href="https://www.mydomain.com/collections/this-is-a-collection" />
        <link rel="alternate" hreflang="en-us" href="https://usa.mydomain.com/collections/this-is-a-collection" />
       <link rel="alternate" hreflang="en-gb" href="https://uk.mydomain.com/collections/this-is-a-collection" />
        <link rel="alternate" hreflang="de-de" href="https://de.mydomain.com/collections/dies-ist-eine-sammlung" />
        <link rel="alternate" hreflang="fr-fr" href="https://fr.mydomain.com/collections/ceci-est-une-collection" />
        <link rel="alternate" hreflang="fi-fi" href="https://fi.mydomain.com/collections/tämä-on-kokoelma" />
    {% endif %}



    End of Tutorial​


    So there you have it, that is my guide to creating and crafting hreflang tags for growing brands. Hopefully, it helped to answer some of the questions that you have been looking for? Feel free to comment below if you need a helping help or would like to share similar experiences and how you overcame them.

    Lastly, it is probably worth mentioning. If you go granular, down to URL ID level. It might take you some time to map and match up those URLs by ID and territory. I used Google sheets with a few added formulas and the Google translate tool for Google sheets to speed up the process.
     

    New Posts