Translate

Monday, May 13, 2013

The Right Way to Localize your Wordpress Plugin with load_plugin_textdomain

There have been many posts in forums and blogs around, asking about the right use of load_plugin_textdomain to localize your WordPress plugin. After some research, it turns out that many people answer these questions with code snippets where the function is called in the constructor, where there is a lack of information about the language file, or that just don’t do what’s asked.
So, I thought I would write a small article/tutorial about localizing your plugin. First of all, I’m assuming that you’ve followed one of the many tutorials available about creating the language file, and that you’ve downloaded Poedit and loaded your translation strings.
First of all, the function load_plugin_textdomain should not be called anywhere else than on the init hook. This, for example, is not the way to use this function:
/*
Plugin Name: Amazing Plugin
Description: No description is needed. This plugin is amazing.
Version: 1.0
Author: The Amazing Plugin Creators
*/

// Localization
load_plugin_textdomain('domain', false, dirname(plugin_basename(__FILE__)));
The load_plugin_textdomain function should be called only in the WordPressinit hook. So, like this:
/**
 * Action: init
 */
function ap_action_init()
{
 // Localization
 load_plugin_textdomain('domain', false, dirname(plugin_basename(__FILE__)));
}

// Add actions
add_action('init', 'ap_action_init');
Now, let's assume you have a .mo file created by Poedit called awesomeplugin-nl_NL.mo (this would be for a Dutch translation), and you have this file located in/<pluginlocation>/languages/. The code above would not work. Why not?
To figure out why that is, let's take a look at the load_plugin_textdomainfunction (function reference).

Usage

load_plugin_textdomain($domain, $abs_rel_path, $plugin_rel_path);

Parameters

$domain
This is the text domain that you are loading. The text domain is the domain of the strings that are being localized. This way, you can store a different translation of the string "Post" (for example) in each domain. I might have two plugins, both of which I want to hold a different translation for this string. By using the second parameter of the localization functions (___e, etc.), I can specify to the function which domain to use.
$abs_rel_bath (optional) (deprecated) (functional until 2.7)
As this parameter (as stated in the Codex) is only functional until WordPress 2.7, we will not be discussing this parameter here. In short, it describes the absolute path to the .mo translation file. Use "false" when in doubt.
$plugin_rel_path (optional)
This is the parameter used for describing the path to the translation file. This should be the relative path to the WordPress plugins directory in wp-content (WP_PLUGIN_DIR). If your plugin language file's absolute path is, for example,/home/user/domains/example.com/public_html/blog/wp-content/plugins/awesomeplugin/awesomeplugin/languages/, you should use awesomeplugin/languages. It doesn't matter whether you use a trailing slash or not, as WordPress trims them in the function anyway.

Back to our example

From now on, I will not post the full code snippet, but just what should be in ourap_action_init function (unless stated otherwise).
Let's get back to our first example. We have our .mo file called awesomeplugin-nl_NL.mo, and this file is located in /<pluginlocation>/languages/. The code we came up with was
load_plugin_textdomain('domain', false, dirname(plugin_basename(__FILE__)));
Let's first take a look at the last parameter, $plugin_rel_path. As you recall, this parameter should describe the path to the language file, which in our case should be<pluginname>/languages.dirname(plugin_basename(__FILE__)) will yield the plugin directory name, but that's not the directory of our .mo file. So, we append '/languages' to it, because that's the directory for our .mo file in this case.
load_plugin_textdomain('domain', false, dirname(plugin_basename(__FILE__)) . '/languages');
Now, this won't work either. Why not? Both the second and the last parameter are right, but the first one isn't. An important note about the first parameter (the$domain paramater), is that the name of the language file should be <domain>-<langcode>.mo. In our case, the domain is "awesomeplugin" and the language code is "nl_NL" (as the filename is awesomeplugin-nl_NL.mo), so the first parameter should be "awesomeplugin". This leaves us with
load_plugin_textdomain('awesomeplugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
Is the function called with the correct parameters now? Yes, it is, which yields this (full snippet):
/**
 * Action: init
 */
function ap_action_init()
{
 // Localization
 load_plugin_textdomain('awesomeplugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
}

// Add actions
add_action('init', 'ap_action_init');
where 'awesomeplugin' is the textdomain.
Recapping on the text domain: the text domain used should be the same in bothload_plugin_textdomain, the translation functions (___e, etc.) and in the .mo file

Code Snippet to find angle between two points

This snippet will be very useful for game developers for finding the angle between two points on a plane. This function has four parameters, the first pair is the x and y values of your first point and the second pair the x and y values of your second point.

function getAngle (x1:Number, y1:Number, x2:Number, y2:Number):Number 
{

var dx:Number = x2 - x1;
var dy:Number = y2 - y1;

return Math.atan2(dy,dx);
}