Translating WordPress Plugins & Themes

Step 1: Producing a POT file

The first step involved in translating a plugin or theme is to produce a POT file, which is a list of all localizable text. Sometimes this file will be provided by the theme or plugin author, but if not it can be automatically generated using gettext or poEdit.

Before you begin you will need access to the theme or plugin you want to translate. You may find it easier to have WordPress installed on your own machine, but it is not essential. You should download and unzip the theme or plugin from the author’s site and store it in a known location:

Unzipping on Windows

poEdit

From the File menu select New Catalog. A window will appear asking for catalog settings. Fill this out as follows, changing the details as appropriate to your chosen theme or plugin and target language:

Catalog Project Info

The character set (charset) encoding tells poEdit how characters in your file are stored. Character sets contain special characters that some languages need (such as letters with accents – é). Where possible you should use UTF-8 as your character set as this is the default encoding used by WordPress and supports almost every character you could need. If you do require another encoding then make sure this is clearly marked in the final output file (more details about this later).

The plural forms is a complicated setting and requires a section of its own. For the moment you can leave this empty and if you discover at a later date that you do require support for plurals then you can change the catalog options and update the value.

When you’ve entered the catalog settings you should select the Paths tab and enter the full path to the location of your theme or plugin. Specify it both in the base and as an additional path:

Configure Poedit Paths

Finally select the keywords tab and enter the following keywords to tell poEdit what is used to mark localizable text:

Configure Poedit Keywords

Note that the interface is very ‘fiddly’, and you need to select an empty row and then click the small square box to edit text (as highlighted). The list of keywords matches the functions provided by WordPress and, in the case of ngettext, configures poEdit for correct pluralization.

Finally you should press OK whereupon you will be prompted to save the catalog file. Note that poEdit will save the file as a .PO, not a .POT. The file extension has no bearing at this point, so just chose a location and name for the file (remembering the naming conventions).

When you save your catalog file, poEdit will automatically scan through the theme or plugin and generate a list of all localizable text:

Poedit Scan

Press OK to accept this list and accept any messages that appear. Note that if the plugin or theme is updated you can use poEdit to refresh this list and it will tell you what text has changed, and what text has been deleted. This is a very useful feature to stop you having to re-translate text.

That’s it! You should now have a .POT file stored wherever you told poEdit to save the catalog. This file will contain a list of all localizable text, and poEdit will display this in its main window ready for you to move on to step 2.

GNU gettext

Using gettext to produce a POT file is similar to poEdit but without all the niceties of a graphical interface. From a terminal, change to the theme directory and enter the following commands:

find . -iname "*.php" >files.tmp

xgettext --language=PHP --indent --keyword=__ --keyword=_e \
--keyword=__ngettext:1,2 -s -n --from-code=UTF8 -f files.tmp

rm files.tmp

Note that the second line is displayed across two lines, but should be executed on one. An output POT file called messages.po will be produced:

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE’S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: PACKAGE VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2007-08-10 11:07+0800n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>n"
"Language-Team: LANGUAGE <LL@li.org>n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=CHARSETn"
"Content-Transfer-Encoding: 8bitn"

#: view/admin/redirections.php:2
#, php-format
msgid "%s Redirections"
msgstr ""

The top of this file contains ‘header’ information describing the localization. You should update this section to reflect your target language, pluralization (see next section), and yourself.

Plural Forms

A plural form is a rule that determines how a language pluralizes a particular word. For example, in English if you wish to display how many messages a user has you would have the following forms:

You have 1 message
You have 2 messages

However, it cannot be assumed that to pluralize the word ‘message’ you simply add an ‘s’, and a lot of languages either require no pluralization, or have other rules. To work around this problem the GNU gettext framework allows the programmer to leave the pluralization up to the translator by marking such words using the __ngettext function. How they do this is not important here (and is discussed in Preparing A Theme Or Plugin for Localization), but if plural forms do exist in the plugin or theme you are translating then you need to tell poEdit or gettext about the rules of your target language.

GNU gettext makes use of a special plural command syntax to describe pluralization rules. Exact details of this syntax is beyond the scope of this document and can be found on the GNU gettext documentation site. However, to aid translators a list of the most popular rules is given below (as described on the GNU site):

Language Plural form
Hungarian, Japanese, Korean, Turkish nplurals=1; plural=0;
Chinese nplurals=2; plural=1;
Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish, Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto nplurals=2; plural=n != 1;
French, Brazilian Portuguese nplurals=2; plural=n>1;
Latvian nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;
Gaelic nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;
Lithuanian nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;
Croatian, Czech, Russian, Ukranian nplurals=3; plural=n%100/10==1 ? 2 : n%10==1 ? 0 : (n+9)%10>3 ? 2 : 1;
Slovak nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;
Polish nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
Slovenian nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;

A full list can be found here: Plural forms

poEdit

The plural forms value is set from the catalog settings page:

Catalog Project Info

GNU gettext

Put the plural form value in the header section of the PO file:

"Plural-Forms: nplurals=2; plural=n != 1;n"

61 comments

  1. WordPress汉化手册…

    WordPress在国内的发展正在走向正轨,这是每一个热爱WP的人都乐意看到的。随着WP的普及,越来越多的人们开始将WP作为架构Blog的程序,用户的增多,加之用户知识层次的良莠不齐,或者是个…..

  2. i used your redirect plugin, to translate it to Arabic, Saudi Arabia, utf-8, i added the path of the extracted plugin, i put the keyword(–,-e,-ngettext:1,2), then i pressed ok, it did some scan put nothing appeared in the list & no .pot file was created. thanks for your time in advance.

  3. Very helpful article. I have a question about the catalog path settings. The poEdit help states that the path information is relative to the location of the .po file. This is a little different than what you’ve said here. If I follow the help and the .po file is in c:/thisplace/lang then the path for the source files are relative. (This is what the help file states). In practice, however, this doesn’t seem to work. Setting additional paths to “../src” with a basepath of c:/thisplace.lang” yields ‘no source files found in ../src”. Setting a complete path to a plugin directory under say under “c:/mypath/wp-contents/plugins/myplugin” will allow it to locate source files if the additional path “src” is added. The ‘#:’ statement contains “src/somefile.php:” – this seems ‘non-portable’ to me somehow. The ‘src’ file is relative to the base path but not relative to the location of the .po file – as a result translations don’t appear to work. I say ‘non-portable’ because not everyone is going to have the same path as the developer. Most .pot files I’ve looked at have a base path of ‘.’ – this makes more sense to me because the basepath is the ‘current directory’.

    I’m not understanding this and the help file isn’t much help. Maybe you could shed some light on this.

    – thanks –

  4. Hi…its very ice post!
    In way translating my WordPress, I have big problem. PLS help me.
    I translating English vertion to Uighur language version(Arabic). Uighur later writing from right side to left( likes Arabic). How can I make it from left to right side?

  5. Hello,

    I have translated the theme I’m using for my site into two languages; Danish and Arabic.
    My Danish site shows the translation, but the Arabic one doesn’t, it uses the Danish translation.

    I can’t see what the problem is.. Any idea?

  6. No, I mean to configure WP to use a localization – putting the files in the directory is not enough, you need to tell it to use the files too

  7. Hello again,
    I added the following code to wp-config.php:
    define (‘WPLANG’, ‘ar_BH’);

    That means I have these to lines now:
    define (‘WPLANG’, ‘da_DK’);
    define (‘WPLANG’, ‘ar_BH’);

    But nothing happened..

  8. Great post, I have been using Global translator plugin for WP as I did not think that I was up to writing something of my own, but it has led to being banned from Google translation I suppose due to not having the /en /fr /de etc folders nofollowed or blocked in my robots.txt file. I am now left with a whole bunch of links in google in different languages that now cannot be reached. Since our blog does not change that frequently other than the items in our nextgen gallery I will definately explore the options you have outlined above. Thanks very much for these options.

  9. Great article! It looks to take a bit of time to setup WordPress for other languages but when I have the time I am definitely going to move forward using this article.

    Thanks for the research,
    Richard

  10. And it would be very very nice to have a tag for themes on http://wordpress.org/extend/themes/ which just says “localizable” or “i18n”! Otherwise you always have first to download a nice looking theme to find out if it is using the __ or _e functions so that you could easily tranlate it into german or turkish for a customer. If not you only have a nice looking theme and nothing else. (Maybe english theme editors are too lazy to care for l10n or i18n…)

  11. Great Tutorial! Unfortunately you didn’t mention anything about using: load_theme_textdomain() for including the theme xx_XX.mo file. Took me some time, a break and then another 5 minutes 😉 Thanks anyway for this introduction to gettext.

  12. Great post and very useful but I have a problem and it’s killing me , I have my plugin in this path
    C:/AppServ/www/wordpress/wp-content/plugins/mine
    I wrote in the Base path :
    C:/AppServ/www/wordpress/wp-content/plugins/
    and in the paths
    mine/
    well it give me an error that there is no files in this path , my question is : what kind of files does it searches for ??
    because I only have mine.php inside it .
    Could you please help ?

    Regards,
    Feras Allaou

    1. Feras, instead of full Windows paths, use relative paths in Poedit, defined via “.” (the dot character) in your Catalog Settings. I’ve found the following path settings work for me, when translating John’s plugins on Windows.

      – Base path: .
      – Paths: ..

      As far as I can tell, single dot means “this folder” and 2 dots mean “one level up”. Since John has the language files always under the sub folder ‘locale’, Poedit needs instructions to look one level up in the folder structure for source code files, thus the “..” path.

      I suggest you use Poedit’s Catalogs Manager for all your translation projects. There, when staring a new project, you get to choose the project’s folder via convenient ‘Browse’ button. Poedit scans this folder for language files and presents you with a list. Open the desired language file (catalog), go to ‘Catalog’ > ‘Settings’ and define the paths with dots. As a final thing, make sure that on the ‘Catalog’ > ‘Settings’ ‘Keywords’ tab, the PHP gettext language variables “_e” and “__” are defined.

      If you get the ‘Paths’ and ‘Keywords’ right you can do a catalog update every time the plugin source code has been updated to let Poedit find the changed strings for you. It’s very, very convenient.

      Hope this helped.

  13. I have generatet two .po files from Icanlocalize.
    One for wp-admin, which is allready localized. And one for wp-content.

    My question is, how can I integrate my second .po file from wp-content, in the first one?
    And afterwards convert this to a new .mo file.

    Or is this the right way to do this?

  14. Finally found an in-depth explanation for localization. Thanks for the detailed how to on name conventions.

  15. there is something missing here. where to put this pluginname-fr_FR.po file? in the directory of plugin or a sub-directory called langues or smilar?

  16. Hi, I am trying to translate wordpress network site into icelandic and translations are not showing. Does anyone have a clue if there is some solution to translation of wordpress network sites?

Comments are closed.