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:
- Two
.pot
files, one with the front-end strings and one with the admin area strings, and .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
grunt;
/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'
}
}
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.
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
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
.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
cp build/languages/myplugin-front.pot src/languages/myplugin-front-ar.po cp build/languages/myplugin.pot src/languages/myplugin-ar.po
.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
.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;
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.