Automating i18n in WordPress themes

i18n-wordpress-automationAccording to the Polygots Make blog, WordPress is used all over the world and in many different languages. To put that in perspective, more than a third of existing WordPress installations are non-English and in his keynote at WordCamp Seattle, core developer Andrew Nacin mentioned that only 5-10% of the world speaks English.

At AudioTheme, we've been selling themes for a little more than a year and have already had sales in at least 45 countries, with only around 52% of total sales coming from customers in the United States.

It doesn't take a math degree to know there's a huge opportunity for growth.

WordPress itself has rich language capabilities and the core developers are continuously improving the experience for non-English users. With a major push in 4.0, and continuing through the end of the year, it'll become easier than ever to use WordPress in other languages.

As a developer, it only makes sense to prepare your themes and plugins for use in other languages — a process called internationalization (i18n). In his primer on i18n and localization (l10n), Brian Krogsgard concluded by saying:

It’s time for us to stop ignoring other languages. Internationalization isn’t a feature. If you don’t have properly [internationalized] plugins and themes, you have bugs in your project. That’s all there is to it.

Needless to say, it can be a frustrating experience when WordPress plugins and themes are difficult to work with in other languages because they're not properly internationalized, but too often, i18n really is an afterthought. Part of that comes down to confusion about what's involved and it's partly due to the tedium of setting up and maintaining the tools and environment.

Fortunately, it's fairly easy to implement i18n and there are tools that can help automate some of the mundane aspects.

i18n Basics

For the most part, preparing a theme consists of a few basic steps:

There is a plethora of resources about getting your projects up to snuff, but i18n is also an ongoing process. Those last two items, in particular, may require continued maintenance.

I'm a big fan of automating workflows to save time; reduce the tedium of repeatable tasks; maintain consistency between team members, operating systems, and tools; and improve quality. At AudioTheme, we've worked hard to refine our processes, build tools, and make our products accessible to international audiences, so I'd like to introduce a couple of the tools and methods we've built and use to create POT files, generate RTL style sheets and enqueue them.

Generating POT Files

WordPress has a fairly robust suite of PHP tools for handling all sorts of internationalization tasks, namely generating POT files, but they were somewhat hidden before being incorporated into the core development branch in 3.6. Even so, they're still awkward to use with plugins and themes and require external dependencies like gettext, which can be hard to install on some systems. Getting everyone on a team properly set up and using them consistently can be quite the hassle.

That's where grunt-wp-i18n helps out. It's essentially a Grunt plugin wrapper for the PHP-based tools, which is important because they handle some WordPress-specific things like package headers, translator comments and page template names.

We've been able to remove many of the dependencies required by the PHP tools, so if you're already using Grunt, you're all set — not even WordPress is needed. The Grunt plugin also provides much more control over the data that ends up in the POT file, allowing you to make it even easier for users to localize their themes using tools like Poedit.

Once a project is configured, the POT file can be regenerated every time a new version is released.

It's already been adopted by plugins like Jetpack, bbPress, BuddyPress and WordPress SEO by Yoast, amongst others. Here's a Gist demonstrating how to incorporate it into your project.

Wait, that's it? Yep, the Grunt plugin automatically reads the text domain and other settings from the theme's headers if they exist, otherwise your set up might require a few more settings. Run grunt makepot and you'll have a new POT file.

RTL Style Sheets

Right-to-left (RTL) language support is largely neglected in most themes. Underscores — a fantastic starter theme — advocates creating an rtl.css style sheet with RTL-specific rules to override default left-to-right rules. While that's better than nothing, the downsides are that it's a manual process prone to errors and requires RTL sites to load two style sheets.

After adopting Grunt, WordPress core implemented a task to automate the generation of RTL style sheets using CSSJanus and they haven't committed any RTL-related CSS patches since last year.

Why not take advantage of the same tools?

CSSJanus converts LTR properties and values in a style sheet to RTL. The project page says it may not always be enough, but it's a start. We can use the same grunt-cssjanus plugin created by Yoav Farhi that's used in core.

As I mentioned, rtl.css is loaded in addition to the main style sheet, so automatically generating it with CSSJanus would mean we're duplicating every rule in style.css and overriding them in rtl.css. Needless to say, that's even worse for performance.

Instead, we can make the CSSJanus Grunt task save the file as style-rtl.css, which has the added benefit of grouping the files by name and further clarifying what the new file does.

There Can Be Only One

If you're not familiar with how rtl.css is enqueued, WordPress automatically loads various style sheets if they exist (rtl.css, ltr.css, {locale}.css, etc.), but style-rtl.css isn't one of those. So how do we enqueue it instead of the default style sheet without resorting to complicated hacks?

Again, we can turn to core for the solution: Use WP_Dependency::add_data() by way of the wp_style_add_data() function added in 3.6. It has a few use cases, but in our situation, we want to specify an RTL style sheet that should replace the default style sheet. After enqueueing the main style sheet as usual, make a call to it like this:

That lets WordPress know we want to replace style.css with style-rtl.css for RTL languages.

Underscores' RTL style sheet contains a default CSS rule for setting the text direction on the body element, so we'll want to add that to style.css like this:

The @noflip comment tells CSSJanus not to convert that rule and the .rtl class ensures it's only applied when an RTL language is loaded.

That's really all there is to it. We now have:

  • An automatically generated RTL style sheet
  • That's grouped with style.css for better organization and improved clarity
  • Only one style sheet being enqueued for RTL support

Wrapping Up

Compared to the time it takes to develop a theme, i18n is quick and easy, so don't limit the theme's potential to English speakers and don't let Brian call your code buggy.

I focused on themes in this article, but a similar setup is possible for plugins and even the editor style sheet in themes. It just requires a little more configuration in the Grunt tasks.

There are plenty of other possibilities for automating tasks with Grunt, from injecting text domains during the build process to managing translations with a service like Transifex — let us know what cool things you're doing with i18n and l10n in your WordPress projects.