😛 How to make pot with WordPress

In this article I will detail how to use grunt to build translation files to localize a WordPress plugin. We’ll create separate translation files for the front-end and back-end (admin area), potentially allowing a translator to translate only one or the other.

Have you tried POT?

Hopefully you’ve already used the i18n functions in your PHP code. For example, a simple translatable string would look like:

$text = __( 'Text to internationalize', 'language-domain-slug' );

The purpose of this article is not to list all the relevant functions, you can look them up here. These are all wrappers to gettext.

The important thing to note is that you need to run something to generate your Portable Object Template files. These .pot files can then be copied to Portable Object .po files, then translated to Machine Object .mo files. These .mo binaries are finally loaded by the end user’s WordPress installation to provide the translated strings to the plugin, or theme. No surprises up to here, all of this is standard.

Grow your own with Grunt!

What I will present today is an easy way to use grunt to generate the following:

  1. Two .pot files, one with the front-end strings and one with the admin area strings, and
  2. .mo files for any .po files in your project

You will still need to copy the .pot files to .po files manually and provide the translations. Let’s get started:

The first thing to add to our Gruntfile.js is grunt-wp-i18n. This is a grunt plugin that I have found to be a little more flexible than the alternative, grunt-pot. So, as usual, add this grunt plugin to your dev toolbox:

npm install grunt-wp-i18n --save-dev
and then add the following into your Gruntfile, to get the plugin loaded:
grunt.loadNpmTasks( 'grunt-wp-i18n' );
Now we need to configure this build task. We will hide our pot in our usual stash, the /languages directory of our WordPress plugin. I like to keep my source code in version control under /src and copy it over to assemble the build under /build. I keep any .po translations under /src/languages, and auto-generate .pot files into the /build/languages dir. Any .po files are simply copied from the /src to the /build directory. This is a good practice that keeps our pot always fresh!
makepot: {
    front: {
        options: {
            cwd: 'build',
            potFilename: 'myplugin-front.pot',
            include: [ '/path/to/files', '/more/files', '/etc' ],
            domainPath: 'languages/',
            type: 'wp-plugin'
    admin: {
        options: {
            cwd: 'build',
            potFilename: 'myplugin.pot',
            include: [ '.*' ],
            exclude: [ '/path/to/files', '/more/files', '/etc' ],
            domainPath: 'languages/',
            type: 'wp-plugin'
Notice that in this example there are two files: The myplugin-front.pot file pulls strings from a list of files that make up the front-end, while the myplugin.pot pulls strings from every other thing in my plugin, which would be the admin area.
Now a translator is free to only translate the front-end, leaving the back-end, which may contain a lot more strings, to its default (English?) language.
In functions such as __(), the second argument is a language domain. People usually set here the plugin’s slug, but this is not strictly necessary: You can use different domains for e.g. the front-end and the back-end. Here we will use myplugin-front for the front-end and myplugin for the back-end. This has to be done in the PHP code, wherever the i18n functions are used.

Make pot

If all is set up correctly, you can now easily make pot with:
grunt makepot

This will make two files in your project, /build/languages/myplugin.pot and /build/languages/myplugin-frontend.pot. Do not commit these files, we’ll let them auto-generate every time. The /build directory is in my .gitignore list.

Using pot

For your plugin to actually use this, you need to copy the .pot files into .po files. Depending on your language, you will have to use the correct language code in your filename. See the gettext list of language codes or simply the Wikipedia list of ISO 639-1 language codes. For instance, to do a German translation in our example, you would need to copy the following:
cp build/languages/myplugin-front.pot src/languages/myplugin-front-de_DE.po
cp build/languages/myplugin.pot src/languages/myplugin-de_DE.po
or to do a translation to Arabic:
cp build/languages/myplugin-front.pot src/languages/myplugin-front-ar.po
cp build/languages/myplugin.pot src/languages/myplugin-ar.po
You will need to check to make sure whether you need to use a country code along with your language code.
Once you have your .po files you can load them up in poedit and start translating. Once the translations are ready you can commit them to your source tree.

Packaging it all together for shipment

WordPress will not use your .pot or .po files. You only include these in your plugin so as to help future translators. To actually enable translation you need to convert the .po files to .mo files.

We’ll use another grunt plugin, grunt-po2mo. First add it to your project:

npm install grunt-po2mo --save-dev

And add the following into your Gruntfile to load it:

grunt.loadNpmTasks( 'grunt-po2mo' );

This one requires very little configuration. Add the following target into your Gruntfile:

po2mo: {
    plugin: {
        cwd: 'src',
        src: 'languages/*.po',
        dest: 'build',
        expand: true

This config expects that the .po files are in the /src/languages directory where we placed them earlier. It places the .mo files in the /build/languages dir. All you need to do for this is:

grunt po2mo

You will also need to tell your plugin to actually go and use these .mo files. Easy. Just hook to the plugins_loaded action and load the text domains:

function action_plugins_loaded() {
    load_plugin_textdomain( 'myplugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
    load_plugin_textdomain( 'myplugin-front', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
} // end function action_plugins_loaded

add_action( 'plugins_loaded', 'action_plugins_loaded' );

That’s it!

Make sure to include the makepot and po2mo targets in your build task. Finally use grunt-contrib-compress or another plugin to compress your entire build directory to a .zip file that is ready to be installed into WordPress!

Pass it on.


Leave a Reply

Your email address will not be published. Required fields are marked *