The purpose of this article is to provide a quick and easy to follow step-by-step guide to extracting text strings from a WordPress plugin for translation from the command line. Although this is documented in the codex: I18N for WordPress Developers and Translating WordPress, those articles are somewhat lengthy and the first time I was asked to internationalize a plugin it took me some time to find the actual steps to generate my POT file. The codex article might be better if you’re translating a plugin that you have listed on wordpress.org, but since the majority of my plugins are either custom for a client or myself, or listed in the WooCommerce shop, I have to do my translation on the command line.
Extracting Strings for Translation
Although I won’t delve deeply into all aspects of WordPress internationalization (i18n), it is important to note that it’s built on the excellent GNU gettext localization framework. The basic process for translating a plugin or theme is as follows:
- Code the plugin/theme using the translation functions
__('message')
,_e('message')
, etc. Include a call toload_plugin_textdomain()
as I describe in my article Writing a Plugin That Can Be Localized by WPML. - Extract all translatable strings into a POT file
- Provide the POT file to a translator who returns one or more PO files with the text translated
- Convert the PO file into an MO file
- Place the MO file into the directory within your plugin that you specify in your call to
load_plugin_textdomain()
, ie “languages”
Step 1 – Coding
While coding a plugin or theme it’s always good practice to use the special (and odd looking) localization functions whenever displaying text, with the two most common being: __('message')
and _e('message')
. Some additional useful functions to know include _x('message', 'context')
which allows you to provide some context information for translators to provide them with additional comments needed to distinguish between the same word used in different contexts, for instance. Another one is _n('message', 'plural', $number)
which is used to retrieve the singular or plural form of a string, based on the amount $number
. There are a number of variations of these functions, as well as ones specialized for use with HTML attribute values for instance.
You should also use an appropriate text domain string based on your plugin name. If your plugin is called My-Plugin, your text domain might be ‘my-plugin’ for instance.
Step 2 – Extraction
If you have not already done so, start by exporting the WordPress translation tools:
[bash]$ svn export http://i18n.svn.wordpress.org/tools/trunk/[/bash]
Update 2013.03.01: It looks like the current head (revision 21405) of the WordPress translation tools has a broken makepot.php
. When I tried to use it today the POT file it generated only contained strings from the main plugin file and none from the other plugin source files. The previous revision does not have this bug, so until the issue is fixed I’d recommend exporting the tools like so:
[bash]$ svn export -r 20807 http://i18n.svn.wordpress.org/tools/trunk/[/bash]
One of those scripts is makepot.php
which is used like the following:
[bash]$ php makepot.php wp-plugin /path/to/my-plugin[/bash]
Where /path/to/my/plugin/
is the path to the plugin you wish to localize and will generate a POT file named like: my-plugin.pot
. Rename it to match your text domain, and add the language suffix you want (or let your translator do this): ie my-plugin-de_DE.pot
.
Step 3 – Translate POT to PO
Send your POT file to the translator, or translate it yourself if you have the knowledge. If you need to edit the PO file you’ll probably want to do so with a tool with UTF-8 support, depending on your target language. I had good luck with Poedit for this.
Step 4 – Convert PO to MO
Convert your PO file to the MO required by gettext by using the msgfmt
utility:
[bash]$ msgfmt -o my-plugin-de_DE.mo my-plugin-de_DE.po[/bash]
Step 5 – Using the MO File
Finally, drop your my-plugin-de_DE.mo
into the directory within your plugin specified by your call to load_plugin_textdomain()
. So for instance, if you use the following:
load_plugin_textdomain( 'my-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
Then your .mo
file would go in my-plugin/languages/
Testing
Once you put your mo
translation file into the plugin directory that you supplied in your call to load_plugin_textdomain()
(as described in Step 5 above), you can test that your language file is being picked up by editing your sites wp-config.php
. There you should find a line like:
define('WPLANG', '');
Just change this to the language abbreviation, for instance:
define('WPLANG', 'de_DE');
And visit your site to verify the translation is being picked up.
One possible stumbling point for developers: your translation files may not be picked up if you symlink your plugin into your webroot as I do.
Working with WPML, so that the site language isn’t hardcoded in your wp-config.php
file and can for instance be selected by your site visitors, is a simple matter of making the load_plugin_textdomain()
call in the correct place. See my related article Writing a Plugin That Can Be Localized by WPML for details.
Updating Your Translation
If you change text in your plugin and want to update your translation without re-translating everything, Poedit makes this a snap. Simply:
- Follow the steps above to generate a new POT file with the current strings
- Open your out of date PO file with Poedit
- Go to Catalog > Update from POT file… and choose your new POT file
- Poedit will show you the obsolete and new strings. You can choose to delete the obsolete strings, or leave them (they’ll be commented-out in your new PO file) and translate the new strings.
- Save to generate the new PO/MO files
Bonus
For a tip on supporting WPML see my article on Writing a Plugin That Can Be Localized by WPML.
If you create new database tables for your plugin, ensure that the default charset is UTF8, otherwise accented and other special characters that get inserted into them will not render properly:
CREATE TABLE blah ( ... ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Hi Justin, do you have to install the tools folder to any particular location within WordPress? After a lot of advanced googling to even get PHP to run from the Windows command prompt, I get a lot of errors about files not being found.
Hey Kathy, well I know that I always first cd to the directory containing the
makepot.php
script and execute from there. But, trying to get it working on Windows, and not only Windows, but their sorry excuse for a command prompt? Oh man, my first recommendation would be to get a better dev machine (I love my macbook pro), if that’s not an option I’d suggest installing a linux-like command prompt for Windows. I used to use Cygwin back in the day; not sure what the one of choice these days is. Good luck!Ahh well. The errors are for files that are in the includes folder…. like po.php so totally weird. The command prompt is pretty crap, but otherwise it is a solid OS. I’ll never get a Mac! 🙂 Would switch to Linux mint but my hardware seems to be poorly supported due to closed source drivers.
ha ha, but all the cool kids use macs! don’t you want to be a cool kid? Seriously though, try installing one of the linux-like command prompts for Windows and I bet that would be a perfect solution for you
Hello,
I followed your step by step way to translate my plugin, but it doesn’t work. The files are generated correctly (I think) but wp doesn’t take them in account. what is the aim of convert the pot file into a po file, aren’t they supposed to be the same ? I don’t understand very well that step.
Thanks for your answer, and have a good day
Tony
Hey Tony, thanks for the question. You’re correct that the POT and PO files are essentially the same; the difference is that the POT file is untranslated, and the PO has been translated into a particular language. I guess it’s done just to keep things organized. Now, as far as your plugin not being translated, I guess the first thing to check is that you’re making a correct call to
load_plugin_textdomain()
and including the MO file in the directory that you use in that function call. I’ve gone and rewritten portions of this article to try and make that more clear, and hopefully that’s the issue you’re having as it’s a simple fixI replaced poedit with poeditor (https://poeditor.com/). I find it better and fresher, it also has a wordpress plugin and there’s no need to download and install the software, it works online.
Thanks for the tip! That looks like a great option
Actually this version of makepot generates text strings differently than wordpress.org plugin admin. Basically standalone makepot does not split long strings, so poedit will have error reading the pot file.