The Best Global Email Preference Center We’ve Ever Built

Feast your eyes on this global email preference center we built in Marketo for Xerox.   Looks harmless, right?

Except it translates into 23 (!) languages using a single form and a single landing page.  Here’s the full story.

Shelby:  In our team meetings, this Xerox preference center comes up again and again. What was this project and why was it so strategic for Xerox?

Jessica:  Xerox has hundreds of campaigns that go out every quarter.  Every email footer linked to a preference center built in a separate tool, and it didn’t integrate with Marketo or Salesforce!  The team had to manually manage changes and unsubscribe requests.  Plus the actual unsubscribe form was behind a login screen, which wasn’t GDPR-compliant.

Xerox wanted to rebuild the preference center in Marketo so they could reclaim all the time wasted on manually managing contact info changes, preference-driven campaign lists, and unsubscribes.

>> Related: How to Build a Marketo Preference Center <<

Shelby:  What did you tackle first?

Jessica:  The segments and methods of communications Xerox wanted to incorporate into the form. Specifically:

  • what are the different types of communications they were sending out?
  • what were the big buckets people might want to subscribe and/or unsubscribe from?
  • and what were the communication channels (like email, direct mail, SMS) people might want to subscribe and/or unsubscribe from?

In the end, Xerox uniquely wanted to accommodate 23 languages and allow folks to control their preferences across email, SMS, and direct mail.

Shelby:  How long did this project take?

Jessica:  The actual build-out for the preference center only took a couple of weeks.

Shelby:  That’s awesome. And Todd, as the architect, that’s when you came in.

Todd: Yeah. I build a lot of Marketo preference centers.  They’re not usually this interesting.  The standard approach to a preference center is either:

  • You deploy the form in one language.  Too bad if that isn’t the one your contacts actually want to use. ¯\_(ツ)_/¯
  • Or, you have a bunch of different pages with different forms, each one in a different language.  Which of course creates a whole lot of overhead for marketing ops. Reporting on these is a nightmare, and it introduces a lot more opportunity for admin error.

This project is super exciting because we’re supporting so many languages.  I found this blog post by Sanford Whiteman where he unpacks the standard way of embedding Marketo forms and splits it into different parts. This allows you to do some really cool stuff on the front end and translate the form dynamically. So, instead of having different forms and different pages for each language, it’s one page with one form that dynamically loads in the user’s preference. It starts with a default based on a token value. But since part of this project is declaring your preferences, we wanted to have it reload in real time if users change their preference on the form.

Behind the scenes, it’s pretty cool how this works. The bulk of the work is driven by a dictionary based on a JavaScript object of all the different languages and the fields and values used in each language. On the Marketo back end, it’s just one form and one landing page. When you open up the landing page, it’s a standard-looking preference center, but the header text is dynamic content based on the token value and the segmentation, and the form is translated dynamically based on the dictionary.

Here’s a sample demo from Sanford of how the translation works – including for picklist fields.

The use case identified on Sanford’s blog post talks about the real-time translation, but we extended this a little bit to do dynamic reloading, as well.

When the user selects a new language, we append a URL parameter to override the dynamic content and reload the page.  It also translates all of the stuff within the form into the new language too.  There’s a little bit of JavaScript here to set the runtime language.  It’s based on the query string parameter of Language Preference – this is the segmentation that drives the dynamic content. We’re using a JavaScript library called URI.js to handle the parsing of the page URL to determine if there is a runtime language to be used and to build the URL to redirect the user to if they change their language preference.

An additional layer that’s kind of cooll: imagine a user starts filling this out, but decides they want to change their language again. We actually store what they filled out already. So even though the page reloads, we don’t lose what they’ve already filled out.  For this we’re using the browser’s sessionStorage to save and restore (and then delete from sessionStorage after restoration) completed form values on a page reload.

  
  	var supportedLangs = ["US English","British English","Bulgarian","German","Spanish","French","French Canadian","Italian","Dutch","Czech","Danish","Greek","Spanish (Latin)","Finnish","Hebrew","Hungarian","Norwegian","Polish","Portuguese (Brazil)","Portuguese","Romanian","Swedish","Turkish"];
    var pageURI = new URI();
	var langParam = "Language Preference";
	var runtimeLang = pageURI.search(true)[langParam];
    var langpref;
    if(supportedLangs.indexOf(runtimeLang) != -1){
      langpref = runtimeLang;
    }
    else{
      langpref = "US English";
    }    
    var session = {lang: langpref};
    //console.log(session.lang);


<!--put this after loading the Marketo Forms 2.0 library-->

MktoForms2.whenReady(function(form) {
    var formEl = form.getFormElem()[0],
        langField = "preferenceCenterLanguagePreference",
        langEl = formEl.querySelector("[name='" + langField + "']");
    langEl.addEventListener("change", function(e) {
        if (form.getValues()[langField] && form.getValues()[langField] != session.lang) {
            var redirectURI = new URI();
            redirectURI.setSearch(langParam,form.getValues()[langField]);
            window.sessionStorage.setItem('formData', JSON.stringify(form.getValues()));
            document.location = redirectURI.toString();
        }
    });
	form.onSuccess(function(vals,tyURL){
		if(vals[langField]){
			var followUpURL = tyURL+"&Language Preference="+vals[langField];
			document.location.href = followUpURL;
			return false;
		}
	});
});

I especially love this when we load Hebrew, and the text updates right-to-left.

And similarly, if somebody does “unsubscribe from all”, there’s a little script to remove all their other preferences so it’s very clear to the user.

<script>
// <![CDATA[
MktoForms2.whenReady(function(form) {
    //get form fields
    var formEl = form.getFormElem()[0],
        unsubscribeField = 'Unsubscribed',
        unsubscribeEl = formEl.querySelector('#' + unsubscribeField),
        checkboxEls = formEl.querySelectorAll('INPUT[type="checkbox"]');
    Array.prototype.forEach.call(checkboxEls, function(itm) {
        //if unsubscribed is true, uncheck and disable all other checkbox fields
        if (itm !== unsubscribeEl && unsubscribeEl.checked) {
            itm.checked = false;
            itm.disabled = true;
        } else {
            itm.disabled = false;
        }
    });
    //add event listener to unsubscribed field
    unsubscribeEl.onclick = function(e) {
        //not very DRY, but purposefully re-querying checkbox fields in case some items have been hidden
        //or made visible due to Marketo form visibility rules
        var formEl = form.getFormElem()[0],
            unsubscribeField = 'Unsubscribed',
            unsubscribeEl = formEl.querySelector('#' + unsubscribeField),
            checkboxEls = formEl.querySelectorAll('INPUT[type="checkbox"]');
        Array.prototype.forEach.call(checkboxEls, function(itm) {
            if (itm !== unsubscribeEl && unsubscribeEl.checked) {
                //if unsubscribed is true uncheck and disable all other checkboxes
                itm.checked = false;
                itm.disabled = true;
            } else {
                //if unsubscribed is false, re-enable other checkboxes
                itm.disabled = false;
            }
        });
    }
});
// ]]>
</script>

The checkboxes script was inspired by this post on Marketo Nation (again from Sanford 😍) with a little extra in it to also disable the other checkboxes and account for any checkbox fields that may have been hidden or revealed due to Marketo visibility rules.

There are few extra custom behaviors around conditionally requiring certain fields depending on the User’s selections.

What’s super cool?  You could extend this across your entire website and translate any form on the fly in whatever user language you want. You have one script with your language definitions, and presumably your form fields aren’t changing that often.

Jessica: This was a really exciting project.  At first Xerox wasn’t convinced that Marketo had the firepower to directly replicate their preference center.  But they were paying $60k/year for the 3rd-party tool, plus all the [wo]man hours wasted keeping Salesforce up to date.  Not to mention having the unsubscribe form behind a login screen is technically not GDPR-compliant.  Getting everything built in Marketo allowed them to sunset the other tool and recoup time and money for other things.  They’ve saved so much time on maintenance, reduced user errors, and cleaned up their Marketo instance.  It’s just elegant.

Shelby:  That’s so great.  Who would have guessed a preference center would be this cool.

Todd:  It’s quite possibly the coolest thing I’ve ever built.

Read the rest of the preference center series

 

Sponge.io | Marketing and Revenue Ops

Get a System Audit

Whether you inherited a new instance or just want a second opinion, we'll dive in and benchmark your tech stack.

  • Hidden
  • Hidden
  • Hidden
  • Hidden
  • Hidden

Sponge.io | Marketing and Revenue Ops

Download Resource

Use this form to recieve your free resource in your inbox today!