May 5, 2021 4:33:00 PM
In this tech log, I'll be looking at how I built a custom language/region switcher in HubSpot's CMS Hub.
HubSpot offers multi-language capabilities and limited ways of working with multiple regions, but our client needed a site where they could freely choose which regions were used.
Here's a sneak peek of the solution:
You can also visit the full live example site to trial the solution:
The solution isn't perfect. In the end, it is a hack around HubSpot limitations. But it is flexible, and can be reused for any "region" type, not just for countries. Maybe you want to have different pages depending on the different markets your business has, for example, in cities or provinces?
This blog is a technical guide to help you implement this solution. It assumes prior developer knowledge of CMS Hub. Are you interested in this solution, but don't have the skills to make it work? Let us know if you need help via the form at the bottom of the page! HubSpot is one of the platforms we use to help our clients reach their goals.
CMS Hub already supports multiple languages by default, and in a way, regions too. However, these regions only reflect the official languages of a country. For example: in Finland, where both Finnish and Swedish are official languages, you can only select these two languages when making a new website page:
Moreover, HubSpot's default Language Switcher module is bare-bones, only displaying a plain list of languages. CSS hacks are possible, but its final source code is not accessible.
The screenshot is from here.
The approach in this article is to make a fully custom language switcher, with different pages per language/region pair. This approach makes sense when you need your website pages to vary depending on the region and language of the visitor. For example, you may intend to have a different layout and different modules installed for an English-speaking visitor in Sweden, compared to an English-speaking visitor in the UK.
If you need only small variations on a single page this solution might not be the best fit. It's probably better to look into IP-country based smart content, or a custom module that accesses the visitor's IP-country and achieves a similar effect.
The goal is to build a custom language and region switcher where:
The solution technically comprises of a few parts:
In the next two sections, we'll show how to implement these parts.
Before starting this hefty implementation endeavour though, you should be aware that there are some SEO limitations to the solution that might affect the discoverability of the site's multi-region variations in the search engines.
/<language_code>/<country_code>URL scheme. This is not the same as the conventional
hreflangtag for alternate languages, but it will not take into account our custom-built alternate regions (countries). Unfortunately, these includes can't be edited. It's also not possible to use
hreflangHTTP headers (since we can't change the HubSpot backend), and HubSpot's
sitemap.xmlcustomization is just not flexible enough.
So, is the solution worthless? Not at all! Utilising this solution in practice has shown that Google is smart enough to detect other signs that the site is multi-region, by looking at the country name in the path and the language content of pages. But, the performance is not guaranteed in the same way when using more explicit signals. If you're very strict on SEO and need a multi-region solution, HubSpot might not be your best choice right now!
This section guides you through the parts of the solution that don't require code. You don't have to implement these first, but reading about them first should help to clarify the idea behind the solution in more depth.
The basis of our website will remain its HubSpot Website Pages. With this multi-region/multi-language solution, you typically will need to create one page per targeted language in a region (a language-region pair).
This guide (and the sample site!) does not cover what you should put inside the page, it just ensures that you have different pages per region and per language, and that you can switch between them.
We will need to define which regions and languages we want to add to the solution, and what slugs they will have. The slugs in this case will be based on language and region codes:
In the sample, I used Sweden and Belgium with these languages:
Next, we create the pages. We'll create one page variation per language-region pair.
For the sample, our website has 2 pages:
So for the sample site, we create (2 pages) * (2 + 3 language-region variations) pairs = 12 individual pages.
English should be the same in Sweden and Belgium and you don't want to create a duplicate, the best workaround for this is to just create one page in one region, and create a Custom Redirect from the other region's slug to the existing one)
Let's look at how to set up each individual page.
As an example, here is the English-language page for the 'Sweden' region.
When we create our pages, we will leverage the built-in CMS Hub multi-language features. Note the green markings in the page settings that reflect these settings.
However, assigning a custom region to a page is not supported by HubSpot (that's the problem we're tackling!), so we apply a custom workaround with **a naming convention:
website.com/<language_code>/<country_code>/<page_slug>, for example
website.com/en/se/min-sida. This is not ideal, but better than nothing.
[SE]to the Internal page name for clarity.
After having created all pages, we get a result like this:
Note that for each of the two pages (Home and About us), we now have two "region groups" with each their own language variations.
Now that the pages are created, it's time to create their accompanying menus.
Each region-language pair has a separately configurable menu (in the sample, we will have 2 + 3 = 5!). They will be managed/linked to in the language switcher module (see the next section about configuring the header module).
Here are the menus from the sample website:
All that is needed here is to make sure that the menus link to page links for the same region and language. If your page names are all in English, you should check the URLs that pop up to make sure you're linking the right versions.
Next, we build the HubDB for equivalent pages. The use case for this is the following: imagine you need to find a company's contact details, and you got a link from your international colleague that sends you to
company.com/en/se/about-us. However, you're in Belgium, and you need to see the Belgian office's contact details. On the Sweden-English site, you go to the language switcher, and click Belgium → Dutch. Now you expect it to arrive on the About Us page there as well:
But HubSpot templates (that will render the language/region switcher module) have no clue that
/nl/be/over-ons are related to each other! The template can't know that they are both "About Us" pages. So, we need to tell our module explicitly which pages are conceptually related. We do this with a HubDB.
This is somewhat optional: you don't actually have to use this database or fill it, but you should create it (even if you leave it empty) because for now, the sample code expects it to exist (if you're feeling brave, feel free to refactor the code and make it optional!).
If you don't fill this database, clicking Belgium → Dutch from
/en/se/about-us will lead you to the
/nl/be regional homepage, which might be just fine for your needs.
Here is an example database that has the needed entries to link the about-us pages in the sample site to each other:
Steps to create the database
key, label: you choose (here it is "Concept") This is meant to group together pages with the same concept, but in different regions/languages
key, they will be considered conceptually 'equivalent' (in this example: the "About Us" page across regions and languages).
Finally, here are some last things to configure before going to the code part:
Here you'll have to get your code hands dirty. But don't worry, most of it can be copy-pasted from the sample.
@hubspot/sessiontheme. You can adapt these steps to your own theme if needed.
If you're using the Session base theme, go ahead and grab the readymade module from here (just make sure you replace the HubDB ID, see below in step II.2).
Otherwise, instructions to replicate it in another theme.
hs create moduleto create a new module. It should be a global module usable in pages, blog posts and blog listings.
Open the module in the Design Manager. From there, you can edit its global contents.
Here is where we can use the language/region data model defined above!
See this example configuration for the Swedish-Sweden region-language pair. Make sure to hook up the right menu for the region-language pair here. You can also choose which localized labels the language switcher should have ('Select Country' and 'Select Label').
I won't cover this in the same detail as the header module above, since the edits are similar to the above ones in the header module.
Refer to the code changes needed for the Session theme in this commit.
In a nutshell, here is what is needed to happen:
And that was it! 😅 Not a simple workaround, but thanks to the built-in multi-language features, we didn't have to completely reinvent the wheel. If you're curious whether this solution would fit your business case, feel free to get in touch with us below.
I'll end with some extension possibilities of the solution:
/frpage) by providing translations of the global module. This will give some overhead.
Enjoyed the blog? Maybe you would be interested in working at Columbia Road! We're constantly looking for nice and skilful people to work with – and to have some good banter with! If you are interested in reading more about the work we do at Columbia Road, have a look at our client cases.
May 3, 2021
Managing Consultant, Technology
Stay up-to-date what’s going on in digital commerce and Columbia Road. We’ll email you once a month with news, interesting articles and studies from the industry and the crème de la crème of our blog.