Very recently I spotted a conversation somewhere online asking about page translations (unfortunately, I don’t even remember on which platform/community did this happen. Anyway, if I recall correctly, the request was to track the original title of the page in GA (rather than the translated one) and that way keep the data purity at a higher level. In opposite, others were defending that this is very valuable information that can signal the need for a localized website’s version (read: no need for purification).
And this conversation planted a seed in my head. Wouldn’t it be cool to track page translations with Google Tag Manager? Every time someone translates a page, we could track it as an event and later check the aggregated information. One thing led to another and here I am writing my take on this topic.
The context
When it comes to translating a page, there are two mainstream ways of doing that (as far as I know):
- By using the Translate to… feature in Google Chrome
- By using one of the translation websites (this blog post’s solution supports Google Translate website and Bing Translator).
There are probably more solutions but I found these two the most often used (sorry, no particular data, just a gut feeling). If you notice that something big is missing here, post a comment! I’ll be more than happy to update the solution.
When a visitor uses one of the aforementioned translation solutions, the language of the entire content of the website (text) is automatically changed to another language.
In this blog post, we’ll track exact moments when the translation has been activated and will send such data as events to Google Analytics (via GTM). Additionally, we’ll track the translation language and what kind of translation solution was used (Chrome’s built-in translation feature, Google Translate website or Bing Translator website).
I also tried to translate a website with Firefox and it looks like the Google Translate plugin just redirects a visitor to the Google Translate Website.
But before we dive into the actual solution on how to track page translations with Google Tag Manager, first let’s take a look at how the actual translations are working.
Translation Method #1: “Translate to…” feature in Google Chrome
If you’re on Chrome, do the right-click anywhere on a page and choose Translate to [some language].
Boom, the entire page’s text content is now translated to your chosen language.
If you want to change the translation options, click the Google Translate icon in the website’s address bar (right corner).
This translation is possible because the browser manipulated the website’s Document Object Model (DOM) (a quick introduction to the DOM is available here). Several modifications include:
- translated-rtl or translated-ltr class is added to the <html> node of the website
- lang attribute’s value in that very same <html> node is changed to the translation language of visitor’s choice
Luckily with JavaScript, this DOM manipulation can be tracked. Therefore, we can use it as a triggering condition to capture the exact moment when a visitor translates the page. More on that – a bit later.
Once you reload the page, the translation will be gone (unless, maybe, there are some settings that always for the translation. Not sure about that).
Translation Method #2: Google Translate or Bing Translator websites
The second translation method that I stumbled upon is by simply using the web interface of the popular translation services. You just have to visit their websites, paste the URL you wish to translate and then the translator will generate a link to the translated page.
Once you click the URL, the translated page will be opened in the iFrame. If (at the same time) you open the GA Real-time reports, you’ll notice that the hostname of the page contains a totally different hostname (if you have implemented the Show Full URL filter):
- Google’s translated page’s URL will belong to translate.googleusercontent.com
- Bing’s translated page’s URL will belong to www.translatoruser-int.com
Also, the URLs of the translated page contain various query parameters. One of them indicates the translation language. We’ll use that in our tracking too.
To sum up, every time a page is loaded and the hostname belongs to Bing’s or Google’s translation services, we’ll track that as a page translation (+ read the value of the query parameter that contains the translation language).
Solution: Track Page Translations with Google Tag Manager
GTM Recipe
For your convenience, I have also prepared a GTM recipe that will automatically create all the items described below. Download the Page Translation Tracking Recipe here
Curious how this Page Translations tracking with Google Tag Manager is working in detail? Continue reading.
Page Translation Listener
And here is the listener written by yours truly. Compatibility: works on all modern browsers (IE10 and lower won’t be able to track the first translation method). However, since translation-related DOM mutation is possible only in Chrome (as far as I know), Internet Explorer will never be an issue.
<script> (function() { // Observe DOM mutations whether the <html> node was changed by Google Translate if (window.MutationObserver) { var mutationObserver = new MutationObserver(function(mutations) { mutations.forEach(function (mutation) { var oldElementClass = mutation.oldValue; var currentElementClass = mutation.target.className; if (oldElementClass.indexOf('translated-') === -1 && currentElementClass.indexOf('translated-') > -1) { window.dataLayer.push({ 'event' : 'pageTranslated', 'translationLanguage' : mutation.target.lang, 'translationService' : 'google translate in chrome' }); } }) }) var htmlNode = document.querySelector('html'); mutationObserver.observe(htmlNode, { attributes: true, attributeOldValue: true, attributeFilter: ['class'] }) } // Let's also track pageviews when the page is translated directly from translate.google.com or bing.com/translator // A function that can return individual query parameter (borrowed from https://davidwalsh.name/query-string-javascript) function getUrlParameter(name) { name = name.replace(/[[]/, '[').replace(/[]]/, ']'); var regex = new RegExp('[?&]' + name + '=([^&#]*)'); var results = regex.exec(location.search); return results === null ? '' : decodeURIComponent(results[1].replace(/+/g, ' ')); }; // Check if the page is being translated directly from translate.google.com (viewed within the iframe) if (window.location.href.indexOf('translate.googleusercontent.com') > -1 ) { window.dataLayer.push({ 'event' : 'pageTranslated', 'translationLanguage' : getUrlParameter('tl'), 'translationService' : 'google translate website' }); } // Check if the page is being translated directly from bing.com/translator (viewed within the iframe) if (window.location.href.indexOf('translatoruser-int.com') > -1 ) { window.dataLayer.push({ 'event' : 'pageTranslated', 'translationLanguage' : getUrlParameter('to'), 'translationService' : 'bing translator website' }); } })(); </script>
If you don’t trust my JS code’s quality, you’re not alone. I don’t trust my coding skills either. One of my goals in 2019 is to learn at least solid essentials of JavaScript, therefore, hopefully, that will improve. Luckily, tasks/problems (like the one explained in this blog post) are a great way of learning anything and feeling the joy of discovery.
Back to the quality of the listener. Since I also do not trust myself with JS (at least for now), I reached out to his excellence Simo Ahava to do a quick code review. Simo was kind to suggest some improvements and give some tips.
So don’t be afraid and feel free to use the code above for your GTM tracking needs. If you’re still not convinced, here is Simo’s Seal of Approval (I just totally made that up).
P.S. I think I need to seriously rethink my personal/work priorities because the design of this seal required way too much time (“thanks” to my limited photoshopping skills).
I’m slightly proud of myself that I was able to pull this seal off but at the same time, I’m horrified of how many other useful things I could have done instead.
Anyway, enough of self-loathing for this time. Let’s get back to business. First, we can take a quick look at the listener’s code and I’ll explain what each part does.
<script> (function() { // Observe DOM mutations whether the <html> node was changed by Google Translate if (window.MutationObserver) { var mutationObserver = new MutationObserver(function(mutations) { mutations.forEach(function (mutation) { var oldElementClass = mutation.oldValue; var currentElementClass = mutation.target.className; if (oldElementClass.indexOf('translated-') === -1 && currentElementClass.indexOf('translated-') > -1) { window.dataLayer.push({ 'event' : 'pageTranslated', 'translationLanguage' : mutation.target.lang, 'translationService' : 'google translate in chrome' }); } }) }) var htmlNode = document.querySelector('html'); mutationObserver.observe(htmlNode, { attributes: true, attributeOldValue: true, attributeFilter: ['class'] }) } </script>
This part of the code is checking the DOM manipulations that are happening to the very top <html> node in the Document Object Model. If the change actually occurs and that change involves adding a class that contains “translated-” string, then an event pageTranslated is pushed to the Data Layer (together with the new value of lang attribute (which basically returns the translation language)).
The 2nd part of the listener (starting from the line 30) is responsible for tracking translations that are happening within the iframe (remember the Translation Method #2 mention in this blog post).
This part is optional and you could actually achieve the same result by using built-in GTM functionality (Pageview trigger and URL variable that reads a query parameter).
But I decided to include URL parsing in the listener in order to keep the number of variables and triggers at a minimum.
First I borrowed a function from David Walsh’s blog post that allows returning a value of a particular query parameter that is currently in the URL.
<script> // Let's also track pageviews when the page is translated directly from translate.google.com or bing.com/translator // A function that can return individual query parameter (borrowed from https://davidwalsh.name/query-string-javascript) function getUrlParameter(name) { name = name.replace(/[[]/, '[').replace(/[]]/, ']'); var regex = new RegExp('[?&]' + name + '=([^&#]*)'); var results = regex.exec(location.search); return results === null ? '' : decodeURIComponent(results[1].replace(/+/g, ' ')); };
Then I created two IF statements that are checking the Page URL. If it contains translate.googleusercontent.com or translatoruser-int.com, it will fire an event pageTranslated to the Data Layer. Thanks to the function mentioned above, it’s easy to return the actual translation language that a visitor/user has chosen (because it is stored in “to” or “tl” query parameters).
<script> // Check if the page is being translated directly from translate.google.com (viewed within the iframe) if (window.location.href.indexOf('translate.googleusercontent.com') > -1 ) { window.dataLayer.push({ 'event' : 'pageTranslated', 'translationLanguage' : getUrlParameter('tl'), 'translationService' : 'google translate website' }); } // Check if the page is being translated directly from bing.com/translator (viewed within the iframe) if (window.location.href.indexOf('translatoruser-int.com') > -1 ) { window.dataLayer.push({ 'event' : 'pageTranslated', 'translationLanguage' : getUrlParameter('to'), 'translationService' : 'bing translator website' }); } })();
Custom HTML tag
Copy the entire code of a listener, create a Custom HTML tag and paste the code there. Set the listener to fire on All Pages.
Trigger
Every time a translation is spotted, the listener will push its information to the Data Layer and the value of an event key will be pageTranslated.
That’s why we need to create a Custom Event trigger with the very same name.
Data Layer Variables
With every dataLayer.push, there will be two additional keys passed to the Data Layer, translationLanguage and translationService. Currently, the translationService will return one of the 3 possible options:
- google translate in chrome (this means Translate to… feature after doing a right-click)
- google translate website (when a visitor lands on a translate.google.com and pastes the URL of the page that he/she wants to translate)
- bing translator website (when a visitor lands on a bing.com/translate and pastes the URL of the page that he/she wants to translate)
In order to create those variables in GTM interface, go to Variables > New > Data Layer Variable and and enter the following information (case-sensitive!).
Now repeat the same actions and enter the information of the translationService variable).
Universal Analytics Event Tag
Now, let’s send the translation information as an event to Google Analytics. In GTM interface, go to Tags > New > Universal Analytics and enter the following settings:
- Track type – Event
- Event Category – page translation
- Event Action – {{dlv – translationLanguage}} (you should enter here the name of your Data Layer Variable)
- Event Label – {{dlv – translationService}} (you should enter here the name of your Data Layer Variable)
- Non-interaction hit – true
- Select the Google Analytics Settings Variable that you’re using in your GTM container
- Choose the trigger – Custom – pageTranslated (this is the trigger you have created in one of the previous chapters of this blog post)
Preview and Debug
Now the time has come to test. Save all the changes, refresh/enable Preview and Debug mode and see whether everything works. Possible testing scenarios:
- Open your website in Chrome. Do the right-click (anywhere on the content), choose Translate to [some language]
and see if your GA Event is visible in the GA Real-time report. Important: GTM preview and debug mode does some crazy stuff when the page is translated, therefore, you should be checking events directly your GA Real-time reports. - Open a JavaScript console and enter the following commands in it:
- google_tag_manager[“YOUR_CONTAINER_ID”].dataLayer.get(‘translationLanguage’)
- google_tag_manager[“YOUR_CONTAINER_ID”].dataLayer.get(‘translationService’)
If they return something similar to the screenshot below (of course, the values might be different) and not undefined, the listener did its job properly.
With these commands you are checking what values do those two keys store in the internal Google Tag Manager Data Model.
- Go to Google Translate, paste the URL of your page and then click the generated URL of a translated page. It will open an iFrame with the translated page. With the help of Preview and Debug mode, check whether the translation events were sent to Google Analytics.
- Repeat the same thing with Bing Translator. Enter the URL, click the generated URL and then in the Preview and Debug mode, check whether the GA Event tag has fired. And, of course, check the Real-time reports in GTM.
- IMPORTANT: Since the GA event is set to be as non-interaction hit: true, you won’t be able to see them in the Active Users tab of your GA Real-time event reports. Switch to Events (last 30 minutes), instead.
Track Page Translations with Google Tag Manager and Google Analytics: Final words
OK, now what? Sit back and be patient. The translation data will start coming into your Google Analytics reports. Thanks to it, you will be able to measure the need for a localized version of your website. If many people are coming to your pages and are translating them, maybe that’s a good sign to invest and make the website more accessible to residents of that particular country.
Also, it would be interesting to compare the conversion rate of those sessions/visitors when page translation is used vs is not used. It might have a huge impact (or it might not at all). So what are you waiting for? Be curious, try this solution and let’s see what comes out of it.
If you know more ways how a visitor/user can translate a page, feel free to contact me. I’m more than open to update the listener (if, of course, that translation method is actually popular).
Did miss something related to page translations and Google Tag Manager? Let me know in the comments below!
Source: analyticsmania