Last week saw the relaunch of silverstripe.com, the once beleaguered face of our commercial services. To the fanfare and appreciation of our colleagues, customers, and community members, the aptly nicknamed “dot com” now has a more streamlined information architecture and a look-and-feel that is congruous to the much lauded silverstripe.org site. Excitement and enthusiasm are pumping through the company corridors like cheap booze at a casino, and we’re primed to take things further.
As you may know, SilverStripe Ltd is in the midst of a major transformation, with its sights set on global expansion. If that all sounds a bit imperialistic, I can assure you, we’re still very much in a nascent stage. The opening of our U.K. office represented the first milestone of this long-term goal. As we turn our focus more to appealing to progressively diverse audience, it behooves us to develop and/or integrate a means of offering content specific to users all over the globe.
These needs are of course, not unique, and our approach to solving this problem has therefore been threaded with open source ideology and shareable code. In this post, I’ll discuss some of the problems we worked through, challenges we’re still working on, and what we’ve accomplished thus far in our effort to bring targeted content to the SilverStripe websites.
A Dual-Phase Approach
There are actually two flavours of what we have identified as “regional” content:
- Content that is specific to a region that has been activated through the URL, e.g. /en_UK/blog.
- Content that is geolocated and sensitive to the user’s position on the globe, e.g. “partners near me.”
A more practical distinction between these two types of regional content is that the former is CMS-driven content, created by content authors, and the latter is content that is either passively channeled into the site (like meetups) or created by end users (like Developer Network listings). This conceptual boundary may not hold up as the site evolves, but for now, it’s a useful heuristic.
In this post, we’ll only be discussing our approach to regional content. We still have yet to tackle geolocalised content, and it remains an open discussion.
The minimum list of requirements we came up with for regional content was as follows:
- A developer should be able to add region awareness to any given DataObject in the codebase, including code contained in modules
- A developer should be able to define a list of regions in a configuration file, along with their associated domain and/or URL segment
- When creating or editing a content type that is region-aware, a content editor can choose to target that content against any defined region, or choose “global,” which allows the content to be seen across all regions. “Global” should be the default setting.
- An end user should be able to browse an amalgam of both global content, as well as content tagged with his or her specific region.
- An end user should be able to opt out of the region filter through a URL change, but not necessarily anything offered by the UI.
Seeing what's out there
As said before, we went into this effort fully aware that we were not taking on anything groundbreaking or unique. We knew that with just the right elixir of thirdparty tools, we could stand on the shoulders of some pretty big giants, and feel tall enough for the global stage.
One of the first questions we had to answer was “Why not Subsites?” In fact, there was very little good reason for us to implement the Subsites module in service of these requirements. The biggest shortcoming of Subsites is that it offers divergent bodies of content per namespace. In other words, the subdomain uk.silverstripe.org could (and perhaps should) have a completely different site tree than nz.silverstripe.org. What we were after was parallel content that we could serve with uniform taxonomy.
This type of thinking led us off the path of segmented content and down the well-traveled road of translatable content. We found that Damian Mooyman’s Fluent module gave us about 90% of what we needed. We felt that most of what it didn’t do that we needed it to (or did do that we didn’t want it to do) could be mitigated using extensions. So we rolled it out, and invested our first substantial block of development time into working around its shortcomings and over-offerings.
What we loved about Fluent was:
- Our desired routing pattern was supported out of the box (/en_UK/blog)
- Locales could be aliased (/uk/blog)
- We were afforded a high level of control over how much “magic” was injected by the module. The module offers a few extensions that you can opt into if you like. The configuration layer is generous, as well.
Conversely, Fluent left us wanting a bit more.
- The module is very language/locale focused (as it should be, as an unapologetic translation module). We would need to put a coat of paint over it that would express locales more like our “regions.”
- There was no concept of a “global” region. This was critical to our requirements. /uk/blog had to work as well as just /blog/.
- By repurposing the locales as regions, we were setting ourselves up for a scalability problem. We might map en_AU to Australia, but there was no business rule prohibiting us from opening a region specific to, say, Sydney, in which case, we wouldn’t have a very semantically useful locale to which we could map that region. Our concept of “regions” was very much detached from geopolitical boundaries. Sydney, U.K., and North America could all be “regions” in our lexicon.
Once we had settled on a supporting thirdparty module, it was time to roll up our sleeves and put some additions on the proverbial house. First on our list was putting a layer over Fluent that obfuscated its penchant for translation and made it masquerade as regional content provider. The result was fluent-regional, an open source contribution that is now publicly available on Github.
The features offered by this module are largely correctional shims, including:
- Allowing a definition of “regions” in the configuration, each mapped to a locale
- Updating the CMS UI to show region names instead of languages
- Suppressing redirection to the the URL of the default locale, i.e. allowing /blog/ to be accessed as well as /uk/blog and /au/blog.
- Adding a “regionless” route that would trigger that default locale. Achieving this proved quite difficult, and required a painful hack.
Not My Default
One issue remained, however. Although we had a concept of a “global” locale in the CMS (which came to be known as “Rest of the World”), we still had to pin it to an actual locale. We looked into using the esoteric “ZZ” locale, but this is not well supported, especially in SilverStripe’s date-processing powerhouse, Zend Framework. Inevitably, we had to choose a real-world locale that would represent the default, or “rest of the world” locale.
The obvious, instinctive suggestion we received for this problem was to use en_US. The United States locale certainly has a reputation for serving as the default state of many websites. We saw two problems with this, however:
- It is likely that we’ll want to serve a USA region in the near future, which would then be in direct conflict with the default locale.
- The USA does not use the localisation standards we wanted to adopt for our “rest of the world,” especially around date formatting.
Appropriately, the best option was therefore to go with USA’s closest cousin -- a cousin that is part of the Commonwealth and shares much of the same localisation standards used by New Zealand.
The solution was simple -- it was elegant. It was -- clear.
That’s right. When you’re browsing the default region on our website, you’re in Canada. You’re welcome.
As developers, we take a lot of pride in our craft, and we crave a sense of ownership. The more experience I get with big projects, the more I realise that good developers are not made by the things that they create so much as the projects they complete. It’s often so tempting to spurn thirdparty solutions just because they don’t do 100% of what you want, exactly the way you were imagining it. By making a few concessions, scaling back the ego’s impulse to be the creator of all good things, and embracing the offerings of an open source ecosystem, you can often achieve a “1 + 1 = 3” outcome, in which your improvements on someone else’s work can be enjoyed by peer developers in the future.
Plus, you get to put people in Canada. And who doesn’t love Canada? Really.
Did you get our reference to Clearly Canadian? If so, leave a comment below with your fondest memories of this potable folk hero. Oh, and, by the way, it’s coming back.
You’re welcome, again.