Case Study: Internationalization for a Media Company
Building a website with multilingual capabilities is not for the faint of heart. But thanks to a combination of Drupal’s stellar internationalization functionality and some custom code problem solving, we’ve set up our client for success and speed as they expand their online international presence.
Media Company with High-Traffic Website
Our long-time client, a prominent American media company, delivers industry-specific content and professional listings on a high-traffic website with over 20 million monthly visitors.
Over the last few years, our client began to broaden their online reach by serving site visitors in several English-speaking countries (United States, Canada, Great Britain, Australia). We’d previously implemented internationalization functionality that directs site visitors in those countries to their country-specific site pages. For example, if a person in the United States visits the
example.com site, based on their apparent location, they’ll be automatically redirected to
example.com/us and served relevant US-specific content.
Add Multilingual Support to a Hybrid Drupal 7/Drupal 8 Deployment
Their latest expansion was into Spanish-speaking markets. We needed to update the site to support not only country-specific content, but also translated Spanish language pages. For example, a user in Spain would need to see Spanish language content, and parts of the page need to display professional listings in the cities specific to the region the site visitor is in.
Drupal 8 has remarkable support for internationalization and language translations right out of the box. It has the tools to vary content by language, and there are contributed modules to vary by country as well. Our challenge was that there weren’t any great solutions which allow you to vary by both of those things at the same time and independently of each other. So we developed a custom solution to do this.
Create a Relationship Between Language and Country Contexts
We enabled the language translation modules built into Drupal 8 (Configuration Translation, Content Translation, Interface Translation and Language), and wrote new plugins for Drupal’s context system that allowed us to use the existing language context functionality, while also supporting a new COUNTRY context. The plugins take both of those variables independently and combine them into a single service that delivers the right content. So we now have the ability to define a prefix that contains both language and country, and a system that properly determines country and language context based on that prefix.
Drupal Core Language Functionality Gets in the Way
This solution caused a few downstream issues. Since the language functionality is baked into Drupal 8 core, we also had to keep an eye out for other Drupal core functionality that only accounted for language, and then tweak it. For example, when creating a URL for a page, Drupal will automatically account for language and add the language prefix, like
example.com/es. The prefix determines which page context to show. However, if we need a prefix like
example.com/es-mx, which contains both a language and a country, we have to configure Drupal’s internal link generation system to look at both contexts, not just language.
We also ran into some “chicken and egg” problems. When we used the entity system in Drupal 8 to help us define the country context, we created a custom content entity called COUNTRY that allows admins to add a new country context, by filling in all the fields and properties that are relevant for the country context directly on that entity. While that worked well, we ran into a problem: Drupal’s core context determination system runs before entities are loaded, but we need to load the entities to determine the context.
Solution? We rewrote the country context determination subsystem to read the country entity data directly, so that it didn’t depend on the entity loading subsystems. That way, we got the information we needed to determine country context without having to load the entities first.
Static Site Generation
This client currently has a hybrid Drupal deployment in production: a Drupal 7 site serving most pages, and a Drupal 8 site functioning as a headless content repository for the remainder. We decided to house the translated content on the Drupal 8 site for several reasons:
- While Drupal 7 has multilingual support, it requires a patchwork of contributed modules, whereas Drupal 8 has made multilingual support as part of its core.
- We continue to incrementally upgrade the main Drupal 7 site to Drupal 8, but we need to serve the translated pages right now.
We didn’t want to expose the Drupal 8 site to the public. Exposing a site to the web, especially a large, very high traffic site, carries unwanted risk. We didn’t want to open up those additional attack vectors, and we didn't want to worry about scaling up the capacity for the Drupal 8 site, as we’d already done that on the Drupal 7 site.
We decided that for this content, it was safer to generate those pages statically, so we used a contributed Drupal module named Tome to do this. To capture all the site paths, Tome first grabs every entity ID, and it gets every route that's available to it. Then it does traditional crawling to capture everything else, generating static HTML pages for it all. (To learn more about Tome, check out this recent Lullabot podcast with the creator of Tome, Sam Mortenson).
Besides solving our second site exposure problem, the static site pages setup has several benefits:
- Speed: The pages aren’t making any calls to a database, so flat HTML pages are extremely fast.
- Reliability: Those HTML pages aren’t relying on Drupal at all, so they can never “go down” unless AWS goes down.
- Security: There’s no database or PHP to hack, nor is there a path from the static page to the original Drupal site that generated it.
Serving Spanish-speaking markets in Mexico, South America and Europe is a big step in our client’s international expansion. Over time we’ll be updating the main site to rely completely on Drupal 8 (and then Drupal 9 shortly thereafter), and it will support additional languages and countries. In the meantime, they’re very happy with our solution.