A demo on how to internationalize, localize (and test) your WordPress code.
I started my career with Symantec in Dublin, working on the localisation of Norton products for European languages. I learned about the coding changes required to allow a product be translated. The changes are similar when enabling WordPress code for translation. In summary, the strings must be marked as available for translation. Tools can extract these strings and then they can be translated.
Internationalize your code
Let us start with a small bit of code that has not yet been internationalized. It simply adds a few comment lines to the page header area.
I included a call to get_locale() to help with testing. In my test site it returned the en_US (English – United States), the default locale.
This code produces comments like:
Changes for internationalization (i18n)
WordPress has a number of functions to help you internationalize your code. I will be using __() which returns a translated string, _e() which echos a translated string and _x() which returns a translated string but allows the developer include a note to help the translator.
For the simple comment I moved the string (‘This is a simple comment.‘) into a PHP block and used _e() to echo it. The ‘i18n-demo‘ is the slug of the plugin and so is the “domain” for translations.
<!-- This is a simple comment. -->
<!-- <?php _e( 'This is a simple comment.', 'i18n-demo' ); ?> -->
I intentionally did not include the comment markup in case the translator is not familiar with HTML comments. My excluding them the comment will not be accidentally broken.
For the printf() call I use __() as I want the translated string returned for use in the printf() call.
printf( "<!-- This site is running WordPress version %s -->\n", get_bloginfo( 'version' ), "\n" );
is changed to:
printf( "<!-- " . __( 'This site is running WordPress version %s', 'i18n-demo' ) . "-->\n", get_bloginfo( 'version' ), "\n" );
Again I exclude the comment markup. I could have moved them outside the printf() call. Both options are okay.
I decided to exclude the %s in the last example but I included a note for the translator.
// Print the day of the week. printf( "<!-- Today is %s -->\n", date( 'l' ) );
is changed to:
printf( "<!-- " . _x( 'Today is', 'The day of the week will be appended.', 'i18n-demo' ) . " %s -->\n", date( 'l' ) );
I have added a note for the translator (“The day of the week will be appended.“). This will only be seen when the translator is translating the text.
The advantage of excluding %s is that there is no chance that the translator will forget to include it in the translated version.
A possible disadvantage is that there may be languages where the day is written first, something like ‘Monday is today‘
Another possible issue is the translation of the day name. This will be coming from PHP so the PHP locale may determine what language it returns and this may not match the WordPress locale. It might be better to create an array of day names, expose them for translation and use one of those strings.
See, there’s a lot of things to consider with internationalization and localization!
I also had to add a call to load_plugin_textdomain() to load the appropriate translations, if present. I chose to put these in a ‘languages‘ subdirectory of the plugin folder.
Full internationalized code
Extracting strings for translation
Next up is the extract the strings into a file that can be given to a translator. For this I used Poedit, so named because we will be editing PO files.
You need a POT (PO file Template) to start with. I downloaded a blank WordPress POT file and edited the date and the names/emails in it. I put it in the ‘languages‘ subdirectory and gave it the name of the domain (i18n-demo.pot) Then I used this to generate a PO file.
In the video I chose to create a English (United States) “translation” because that is the locale that my test site is running. This created a en_US.po file, a text file with the strings extracted from the PHP file and it compiled those strings into a en_US.mo file. I had to rename these to i18n-demo-en_US.po and i18n-demo-en_US.mo to be found by load_plugin_textdomain().
Poedit did not add the extracted strings to the POT file. I don’t know why. I simply copied them from i18n-demo-en_US.po.
I didn’t want to change the site locale so I “pseudo translated” the text in the i18n-demo-en_US.po file. By “pseudo translation” I mean that I changed some of the vowels to versions with an accent. I changed ‘e‘ to ‘é‘ and ‘o‘ to ‘ó‘. This allows me to confirm that the ‘translated’ version of the string is being used without changing the string to a language that I don’t read. We used this method in Symantec, to confirm that all front end string were available for translation and to check whether some translatable strings shouldn’t be translated.
In the screenshot you can see the notes for the translator and my pseudo translations.
When I load the page again it now produces the following comments: