Multilingual compatibility is a core requirement for any premium WordPress theme nowadays. There are many plugins that offer multilingual tools, but arguably the most popular are WPML and Polylang. Let’s look into them!
Meet the Players
WPML is a premium-only plugin that makes it easy to build and run multilingual sites. It’s powerful enough for corporate sites, yet
simple enough for blogs, and it is very popular among WordPress users. Prices start at $29 rising to $159 at the time of writing. WPML offers so-called “GoGlobal”
programs for theme authors to make their items WPML ready. It’s easy to argue that WPML is market leader in the multilingual plugin sphere.
“WPML’s Go-Global helps turn WordPress themes into fully multilingual-ready. When we’re done, your clients can use the theme on multilingual sites, easily and smoothly.” – WPML
Despite its full list of features WPML is not for
everyone, partly due to the lack of a free version. The Polylang plugin, which uses the freemium model, fills that gap. It offers both a free version and premium version. With 400k+
active installs Polylang is the second most popular multilingual plugin.
Coming up
In this tutorial I will show how you can make your WordPress theme
multilingual-ready. Happily, this is a single process, but will allow your theme to fully support both plugins.
WPML is the older of the two, first seeing the light of day in 2007. Polylang, which has also been in development for a long time, purposefully supports most of the WPML API to avoid theme and plugin authors having to do twice the work. This fact means that making a theme WPML-ready in means that, on the whole, you’ll be making your theme
Polylang-ready too.
Translatable Strings
In order for a theme or plugin to be translatable, any hardcoded strings must be wrapped in one of WordPress’ translation functions. For example, instead of Similar products:
you might use <?php _e(‘Similar products:’, ‘textdomain’) ?>
. There are several of these functions available, each behaving slightly differently. We’ve covered them in Tuts+ tutorials in the past–feel free to take a look at this thorough guide if you need to get up to speed:
In this tutorial, however, we’ll be focusing on the dynamic aspects of your theme or plugin, which WPML and Polylang will need to identify as requiring translation.
Configuration
Firstly ensure that your theme has
no issues with Envato Theme Check plugin, and your theme has only one text-domain.
Next create an
XML file in the core folder of the theme and name it wpml-config.xml. Here, we
will guide WPML through all of our custom fields, custom post types, taxonomies,
options, shortcodes, and instruct WPML on how to deal with all their data.
Note: the
config files for themes also work for plugins. So if you’re working on a plugin and all your custom fields,
options, etc. are plugin-based you can add the config file to the plugin core
folder instead.
Configuring Custom Fields
In the wpml-config.xml file create opening and closing tags <wpml-config></wpml-config>
. All of
the data will be placed inside these tags. We’ll start with custom fields. Create
a tag <custom-fields>
. Inside that tag list all of
your custom fields like this:
<custom-fields> <custom-field action="action-name">custom-field-name</custom-field> ... </custom-fields>
You’ll notice the action="action-name"
present in our custom field. There are four types of actions
that tell WPML how to deal with our fields:
-
translate
: allows your user to translate the value of
the custom field. These fields are displayed on the Translation Editor screen
and can be sent to any of the professional translation services on offer. -
copy
: this action copies the custom field value of the default language to the
secondary languages. This means that updating the custom field value of the
default language will always be copied to the secondary language. The custom
fields set to copy do
not show on the Translation Editor screen. -
copy-once
: The
value of the custom field is copied to the secondary language in the initial
translation process. The custom fields that use the copy-once action will not
appear on the Translation Editor screen. However, the user can change the
custom field value of the secondary language to be different from the default
language using the post editing screen. It is preferable to set the custom
fields that hold settings like background color, font color, font size, and
others, to copy-once. This allows the user to have different settings for
translated content than the ones set for the posts and pages in the default
language. For example, the user wants to set the background color of an element to be yellow in the default language and blue in the secondary
language. Please note that editing a field set to copy-once will not mark the
field as needs update. This is because the field will not be copied to an
existing translation, it is only copied when the translation is created. -
ignore
: this
action eliminates the custom field from being copied to the secondary language.
Useful for hidden custom fields or custom fields with non-user data (for
developer use only).
Both WPML and Polyland plugins are compatible with Advanced Custom Fields (ACF) and CMB2 (Custom Metaboxes and Fields 2).
Configuring Custom Post Types
Once you’ve finished the custom fields, let’s move on to custom post
types. Create a tag <custom-types>
and list all
your custom post types with the structure:
<custom-types> <custom-type translate="1 or 0">custom-post-type-name</custom-type> ... </custom-types>
Note: 1
means translate, 0
means ignore.
You can also add a display-as-translated
attribute to the tag
to show the post types in the default language if no translation exists, like
this:
<custom-type translate="1" display-as-translated="1">custom-post-type-name</custom-type>
Configuring Taxonomies
Let’s move on to taxonomies. In the same fashion as before, create a tag <taxonomies>
and list all the custom taxonomies with the structure:
<taxonomies> <taxonomy translate="1 or 0">taxonomy-name</taxonomy> ... </taxonomies>
Again you can add the display-as-translated
attribute to the tag to show
the taxonomies in the default language if no translation exists.
Configuring Options
Next let’s look at the options (wp_options). Create a tag <admin-texts>
and list all of your custom options that need to be translated. Here you
can translate both single options and serialized arrays of options.
<admin-texts> <key name="my_plugins_options"> <key name="option_name_1" /> <key name="option_name_2" /> <key name="options_group_1"> <key name="sub_option_name_11" /> <key name="sub_option_name_12" /> </key> <key name="options_group_2"> <key name="sub_option_name_21" /> <key name="sub_option_name_22" /> </key> <key name="simple_string_option"/> </admin-texts>
Gutenberg
Since the release of WordPress 5.0 you should also include config details for Gutenberg custom
blocks that need translation. To do this create a tag <gutenberg-blocks>
and list all the relevant custom blocks with the structure:
<gutenberg-blocks> <gutenberg-block type="core/block-type" translate="1"> <xpath>//tag/tag</xpath> <xpath>//tag/tag/@attribute</xpath> </gutenberg-block> ... </gutenberg-blocks>
Wait–what’s XPath?! XPath is used to
locate the text that need to be translated. First
you will need to define the Gutenberg block type (you can find all block types here). For example, image the block type
is image, the markup for which looks like:
<!--wp:image {"id":3} --> <figure class="wp-block-image"> <img src="http://webdesign.tutsplus.com/rainbow.png" alt="this is a rainbow" class="wp-image-3" /> <figcaption>Rainbow Image</figcaption> </figure> <!-- /wp:image -->
We will need
to translate the figcaption and the img tag alt text. So our block will be like
this, with the XPath tags effectively locating the items for translation:
<gutenberg-block type="core/image" translate="1"> <xpath>//figure/figcaption</xpath> <xpath>//figure/img/@alt</xpath> </gutenberg-block>
Shortcodes
Next up: shortcodes! Create a tag <shortcodes>
and list
all your shortcodes that need translation like this:
<shortcodes> <shortcode> <tag>shortcode_name</tag> <attributes> <attribute>attribute-name</attribute> <attribute>attribute-name</attribute> ... </attributes> </shortcode> ... </shortcodes>
Shortcodes can have plenty of attributes, so make sure you list all the attributes that need to be translated.
Language Switcher Configuration
Our config
file is almost ready! One final thing to consider though: WPML also offers customization for the built-in language
switcher, something required by the GoGlobal program, so we will need to add styles that match our theme as well as possible. This is done within tags like this:
<language-switcher-settings> </language-switcher-settings>
There are lots of settings you can add between these tags, so I won’t go through them all. You can find them in the WPML docs.
In practice,
creating your own custom language switcher might be the wisest option, giving you way more control over the styling and output. We’ll cover that in a second.
Styling the Default Language Switcher
As we’ve just discussed, WPML comes with a language switcher. It can used in multiple
locations; as part of a menu, in widget areas, the footer, and so on. It can be in the form of a dropdown or list of items, with flags or just text.
If you plan to make your theme WPML ready you should test each version of the default language switcher in all locations. And if you struggle to make the changes you need, WPML provides some help: fixing styling issues for language switchers.
Polylang comes with a predefined language switcher that does not
have user-based configurations and has two possible locations: widget areas and in a menu. Again, to ensure your theme properly supports Polylang you should include styles for the default
language switcher.
Custom Language Switcher
In practice, the default language switchers from both WPML and
Polylang have limitations in terms of design. What I personally prefer is using a custom language switcher which works both as a shortcode, a widget, and has one universal set of styles for both WPML and Polylang.
You can download the switcher from Github.
It creates a shortcode that can be turned into a widget with the do_shortcode()
function. Include the custom language switcher
in your theme linked plugin (shortcodes, widgets are plugin territory).
Here’s a quick run down of what the code is doing:
- First we will check if the WPML is active with the
class_exists()
function, and if so we will use the WPML core function to get all of the
languages in use icl_get_languages(). - Next, if WPML isn’t active, we check the Polylang plugin by using
function_exists()
.
If Polylang is active, we get all of the languages being used with the function pll_the_languages(). - And if neither of the two plugins are active, we alert the user.
- Lastly, if you also need the widget version, you can create a custom widget with
do_shortcode('[custom_language_switcher
The same applies if you want to add the language switcher to your core theme files.
extra_class=""]');
Conclusion
Having finished adding everything you need to the wpml-config.xml file and styled the default language switchers, your theme is ready to be submitted to the GoGlobal
program.
When you submit your theme, WPML will create a demo site, for testing purposes, which you then need to add content to. Make sure at this point
your theme has a demo, so you can import data from there to save you doing the work twice. You will
also get free access to a developer account where you can download the latest WPML
plugin for testing.
Polylang automatically takes the wpml-config.xml configurations, so you don’t need to add more details to cover the two.
And as we discussed, whilst not obligatory, using a custom language switcher will give you more control over the structure and
class naming.
I hope you enjoyed this tutorial and that it helps you develop all your WordPress themes to be multilingual moving forward!