In a previous post, I covered the fundamentals of WordPress internationalization (abbreviated as i18n); how to install a localized version of WordPress and how an existing WordPress site can be easily converted to a localized version.
In this article, I’m going to walk you through the process of Internationalizing WordPress plugins. The process isn’t difficult and once the knowledge is acquired, you can have your WordPress plugins easily translated to other languages.
Key Takeaways
- Internationalization (i18n) is the process of developing a plugin to be easily translated into other languages, while localization (l10n) is the subsequent process of translating the internationalized plugin to a new language.
- Making a WordPress plugin translation-ready is crucial to reach a global audience, enhance user experience, and increase the user base by making it accessible to users who speak different languages.
- The process of making a plugin ready for translation involves setting translation headers (Text Domain and Domain Path), loading Text Domain, and internationalizing various elements like strings, placeholders, HTML, plurals, context, and escaping translation strings.
- WordPress i18n uses the GNU gettext localization framework, which provides tools for translating the text. This involves wrapping all text strings in the __() or _e() function and creating a .pot (Portable Object Template) file which contains all the translatable strings from the plugin.
Difference Between Internationalization and Localization
Over the years, developers tend to misconstrue the meaning of these terms – Internationalization and Localization.
- Internationalization is the process of developing your plugin so it can easily be translated into other languages.
- Localization describes the subsequent process of translating an internationalized plugin to a new language.
It is worthy of note that Internationalization is often abbreviated as i18n (because there are 18 letters between the ‘i’ and the ‘n’) and Localization is abbreviated as l10n (because there are 10 letters between the ‘l’ and the ‘n’.)
Why Internationalize?
The answer is simple; WordPress is used all over the world in many different languages. When plugins are internationalized, they attract a larger audience from other parts of the world who would obviously benefit by using the plugin in their own language.
As a developer, you may not have time to provide localized versions of your plugin because you do not speak other languages. However, when you internationalize your plugin, you leave the door open for others to create localization without necessarily modifying the source code.
Plugin Internationalization
Now that we are familiar with the concept of plugin internationalization and localization, let’s dive into the process of making plugins ready for translation.
Set Translation Headers
The first step to take in making a plugin translatable is to include the translation headers among the plugin headers.
The translation headers are the Text Domain and the Domain Path.
The Text Domain is used to denote all text belonging to a plugin. It is a unique identifier which ensures WordPress can distinguish between all loaded translations. This increases portability and plays better with already existing WordPress tools.
The text domain must match the slug of the plugin. For example, if your plugin is a single file called sample-plugin.php or it is contained in a folder called sample-plugin the text domain should be sample-plugin.
A Note on Text Domain
The text domain name must use dashes and not underscores.
Remember I said the text domain must match with the plugin slug? That may not be true after all. I carried out a quick experiment with one of my plugins, instead of the plugin slug, I used a unique text and it worked without any problems.
Moral: ensure the Text Domain is unique so it doesn’t clash with that of other plugins.
The Domain Path is the folder WordPress will search for the .mo translation files.
By default, WordPress searches the plugin directory for the translation files to use. Having the translation file at the root folder of your plugin could disorganize your plugin structure.
If you wish to keep the translation files in a folder, for example; /languages, you need to inform WordPress about it using the Domain Path header.
Below is a typical header of a WordPress plugin including that of the translation.
<span><span><?php </span></span><span><span>/* </span></span><span><span> Plugin Name: Enable Shortcode and PHP in Text widget </span></span><span><span> Plugin URI: http://w3guy.com/shortcode-php-support-wordpress-text-widget/ </span></span><span><span> Description: Enable shortcode support and execute PHP in WordPress's Text Widget </span></span><span><span> Author: Agbonghama Collins </span></span><span><span> Version: 1.2 </span></span><span><span> Author URI: http://w3guy.com </span></span><span><span> Text Domain: espw-plugin </span></span><span><span> Domain Path: /languages/ </span></span><span><span> */</span></span>
Load Text Domain
Now, we would use the load_plugin_textdomain() function to tell WordPress to load a translation file if it exists for the user’s language.
Below is the function synopsis.
<span><span><?php load_plugin_textdomain( $domain, $abs_rel_path, $plugin_rel_path ) ?></span></span>
The first parameter $domain should be the text domain; the $abs_rel_path has been deprecated and should be set to false; finally, $plugin_rel_path is the relative path to translation files.
If translation MO files is in the plugin’s own directory, use as follows:
<span>load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) );</span>
If translation MO files is in the plugin’s languages subdirectory. Use as follows:
<span>load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );</span>
You don’t just call the load_plugin_textdomain function, it should be called in your plugin as early as the plugins_loaded action like this:
<span>function load_plugin_textdomain() { </span> <span>load_plugin_textdomain( 'espw-plugin', FALSE, basename( dirname( __FILE__ ) ) . '/languages/' ); </span><span>} </span> <span>add_action( 'plugins_loaded', 'load_plugin_textdomain' );</span>
Deep Dive into Plugin I18n
Now that the Text Domain and the Domain Path header is set, it’s time to learn how to internationalize a plugin.
This segment of the tutorial will be divided into the following:
- Strings translation
- Using Placeholders
- HTML translation
- Dealing with Plurals
- Disambiguation by context
- Escaping translation strings
Please note: The string espw-plugin will be use as text domain in this tutorial.
1. Strings Translation
To make a string translatable in your plugin, wrap the original string in a __() function call as follows:
<span><span><?php </span></span><span><span>/* </span></span><span><span> Plugin Name: Enable Shortcode and PHP in Text widget </span></span><span><span> Plugin URI: http://w3guy.com/shortcode-php-support-wordpress-text-widget/ </span></span><span><span> Description: Enable shortcode support and execute PHP in WordPress's Text Widget </span></span><span><span> Author: Agbonghama Collins </span></span><span><span> Version: 1.2 </span></span><span><span> Author URI: http://w3guy.com </span></span><span><span> Text Domain: espw-plugin </span></span><span><span> Domain Path: /languages/ </span></span><span><span> */</span></span>
If you want to echo the string to the browser, instead of the echo language construct, use the _e function:
<span><span><?php load_plugin_textdomain( $domain, $abs_rel_path, $plugin_rel_path ) ?></span></span>
2. Using Placeholders
As PHP and WordPress developers, I assume you know what placeholders are. You can quickly skim through the sprintf and printf() PHP documentation for further information.
If you are using variables in strings like the example below, you should use placeholders.
<span>load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) );</span>
The right way is to use the printf()function as follows:
<span>load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );</span>
Going through the code of some plugins hosted in the WordPress plugin repository, I do see things like this:
<span>function load_plugin_textdomain() { </span> <span>load_plugin_textdomain( 'espw-plugin', FALSE, basename( dirname( __FILE__ ) ) . '/languages/' ); </span><span>} </span> <span>add_action( 'plugins_loaded', 'load_plugin_textdomain' );</span>
<span>$text = __( 'Hello, SitePoint Readers!', 'espw-plugin' );</span>
Although the strings are now translatable, the PHP variable $city also becomes translatable.
This is a bad practice because a translator could mistakenly alter the variable or inject malicious code into the plugin code base, and this will ultimately make the plugin malfunction.
The sprintf function is similar to the printf in that they format string using placeholders while printf outputs a formatted string, the sprintf return the string.
Example: the following code assign a formatted string to the variable $text.
<span>_e( 'Hello, SitePoint Readers!', 'espw-plugin' );</span>
3. HTML Translation
Including HTML in translatable strings depends on the context.
An example is a link (separated from text surrounding it):
<span>echo 'Your city is $city.'</span>Another example is a link in a paragraph (not separated from text surrounding it):
<span>printf( </span> <span>__( 'Your city is %s.', 'espw-plugin' ), </span> <span>$city </span><span>);</span>
4. Dealing with Plurals
A string that changes when the number of items changes can be internationalized using the _n() function.
This function accepts 4 arguments, namely:
- Singular – the singular form of the string
- Plural – the plural form of the string
- Count – the number of objects, which will determine whether the singular or the plural form should be returned
- Text Domain – the plugins text domain
Let’s see some examples to comprehend how the _n() function works.
In English you have "One comment" and "Two comments". In other languages you can have multiple plural forms.
The code below demonstrates how to handle such scenario using the _n() function.
<span>echo __('Your city is $city', 'espw-plugin');</span>
Code Explanation the code above consist of these three functions – printf, _n and number_format_i18n.
For easy assimilation, the function code will be dissected with each function component explained.
<span><span><?php </span></span><span><span>/* </span></span><span><span> Plugin Name: Enable Shortcode and PHP in Text widget </span></span><span><span> Plugin URI: http://w3guy.com/shortcode-php-support-wordpress-text-widget/ </span></span><span><span> Description: Enable shortcode support and execute PHP in WordPress's Text Widget </span></span><span><span> Author: Agbonghama Collins </span></span><span><span> Version: 1.2 </span></span><span><span> Author URI: http://w3guy.com </span></span><span><span> Text Domain: espw-plugin </span></span><span><span> Domain Path: /languages/ </span></span><span><span> */</span></span>
The first argument passed to the _n function is the text to be displayed when the number of comments is singular.
The second is the text displayed when the number of comments is greater than one.
The placeholder %s will contain the value of number_format_i18n( get_comments_number() ) which will be explained shortly.
The third argument get_comments_number() is assumed to be a function that returns the comment count.
If it returns 1, the first argument One comment get outputted by printf otherwise the second argument %s comments is returned if it is greater than 1.
Please note: The placeholder %s gets replaced by the integer returned by number_format_i18n( get_comments_number() ) which is the second argument passed to the printf function.
Finally, the fourth argument is the translation text domain.
The function number_format_i18n() converts the comment count to format based on the locale. See the documentation for more information.
Similar to the number_format_i18n() is the date_i18n that retrieves the date in localized format, based on timestamp.
Still on the _n() function, below is another demonstration of how the function works.
<span><span><?php load_plugin_textdomain( $domain, $abs_rel_path, $plugin_rel_path ) ?></span></span>
If the variable $count returns 1, the text We deleted one spam message will be displayed; but if it is greater than 1, We deleted %d spam messages will be displayed with the placeholder %d replaced by the integer value of $count.
5. Disambiguation by Context
Sometimes one term is used in several contexts, although it is one and the same word in English it has to be translated differently in other languages.
For example the word Post can be used both as a verb as in "Click here to post your comment" and as a noun "Edit this post".
In such cases the _x or _ex function should be used.
It is similar to __() and _e(), but it has an extra argument — $context.
<span>load_plugin_textdomain( 'espw-plugin', false, dirname( plugin_basename( __FILE__ ) ) );</span>
Using this method in both cases we will get the string Comment for the original version, but the translators will see two Comment strings for translation, each in the different context.
When the strings made translatable by the function _x() get parsed by a translation tool such as Poedit, the context argument provides a hint to the translator on the context the string/text was used.
In German, the Post as a noun is Beitrag while as a verb is verbuchen.
Below is a screenshot of Poedit translating the string Post to German with the context squared-bracketed.

While _x() retrieve translated string, _ex() displays it.
6. Escaping Translation Strings
WordPress has a number of functions for validating and sanitizing data.
Among the list are functions for escaping translation texts – esc_html(), esc_html_e(), esc_html_x(), esc_attr(), esc_attr_e() and esc_attr_x(). You can get more information on each of these functions at WordPress Codex.
I don’t need to explain every one of these, but what they do is basically escape translatable texts.
Wrap Up
One of the goals of WordPress is to make it easy for users across the world to publish content. As a plugin developer, you can help to further ease the publishing process for users when you internationalize your plugins.
The first part of this tutorial was essentially about everything you need to know about plugin i18n.
The concluding part will be a walk-through on how to make a plugin translation ready as well as learning how to localize a plugin to a new language.
I do hope you have learned something new from this tutorial.
Happy coding!
Frequently Asked Questions (FAQs) about WordPress i18n and Plugin Translation
What is the importance of making a WordPress plugin translation-ready?
Making a WordPress plugin translation-ready is crucial for reaching a global audience. Not all WordPress users are English speakers. By internationalizing your plugin, you make it accessible to users who speak different languages, thereby increasing your user base. It also enhances user experience as users can interact with your plugin in their native language, making it more user-friendly.
How does WordPress i18n work?
WordPress i18n, or internationalization, works by making your plugin’s text strings translatable into other languages. This is achieved by wrapping these text strings in a special function that allows them to be translated. WordPress uses the GNU gettext localization framework for this purpose, which provides a set of tools for translating the text.
What are the steps to make a WordPress plugin translation-ready?
Making a WordPress plugin translation-ready involves several steps. First, you need to internationalize your plugin by wrapping all text strings in the __() or _e() function. Next, you need to create a .pot (Portable Object Template) file which contains all the translatable strings from your plugin. This file is used as a template for creating .po (Portable Object) and .mo (Machine Object) files, which contain the translated strings.
What is the difference between __() and _e() functions in WordPress i18n?
The __() and _e() functions are both used in WordPress i18n to make text strings translatable. The main difference between them is that __() returns the translated string, while _e() echoes or outputs the translated string directly. Therefore, you would use __() when you want to store the translated string in a variable, and _e() when you want to display the translated string to the user.
How can I translate my WordPress plugin into different languages?
To translate your WordPress plugin into different languages, you need to create .po and .mo files for each language. These files contain the translated strings and are named according to the ISO-639 language code (e.g., en_US for English, fr_FR for French). You can use a tool like Poedit or Loco Translate to create and manage these translation files.
What tools can I use for WordPress i18n?
There are several tools available for WordPress i18n. Poedit and Loco Translate are popular tools for creating and managing translation files. GlotPress, a web-based translation tool, is also used by the WordPress community to translate WordPress core, plugins, and themes.
How can I test my WordPress plugin for translation readiness?
You can test your WordPress plugin for translation readiness by changing the language in your WordPress settings and checking if the plugin’s text strings are correctly translated. You can also use a tool like the WordPress i18n checker plugin, which checks your plugin for common i18n errors.
How can I contribute to WordPress plugin translations?
You can contribute to WordPress plugin translations through the WordPress Polyglots team. They are responsible for localizing WordPress into different languages. You can join the team and start translating plugins and themes into your native language.
What is the role of .pot, .po, and .mo files in WordPress i18n?
.pot, .po, and .mo files play a crucial role in WordPress i18n. The .pot file is a template that contains all the translatable strings from your plugin. The .po file is a human-readable file that contains the translated strings, and the .mo file is a machine-readable file that is used by WordPress to display the translated strings.
How can I make my WordPress theme translation-ready?
Making a WordPress theme translation-ready involves a similar process to plugins. You need to internationalize your theme by wrapping all text strings in the __() or _e() function. Then, you need to create a .pot file and use it as a template for creating .po and .mo files for each language.
The above is the detailed content of WordPress i18n: Make Your Plugin Translation Ready. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

When managing WordPress projects with Git, you should only include themes, custom plugins, and configuration files in version control; set up .gitignore files to ignore upload directories, caches, and sensitive configurations; use webhooks or CI tools to achieve automatic deployment and pay attention to database processing; use two-branch policies (main/develop) for collaborative development. Doing so can avoid conflicts, ensure security, and improve collaboration and deployment efficiency.

The key to creating a Gutenberg block is to understand its basic structure and correctly connect front and back end resources. 1. Prepare the development environment: install local WordPress, Node.js and @wordpress/scripts; 2. Use PHP to register blocks and define the editing and display logic of blocks with JavaScript; 3. Build JS files through npm to make changes take effect; 4. Check whether the path and icons are correct when encountering problems or use real-time listening to build to avoid repeated manual compilation. Following these steps, a simple Gutenberg block can be implemented step by step.

Use WordPress testing environments to ensure the security and compatibility of new features, plug-ins or themes before they are officially launched, and avoid affecting real websites. The steps to build a test environment include: downloading and installing local server software (such as LocalWP, XAMPP), creating a site, setting up a database and administrator account, installing themes and plug-ins for testing; the method of copying a formal website to a test environment is to export the site through the plug-in, import the test environment and replace the domain name; when using it, you should pay attention to not using real user data, regularly cleaning useless data, backing up the test status, resetting the environment in time, and unifying the team configuration to reduce differences.

In WordPress, when adding a custom article type or modifying the fixed link structure, you need to manually refresh the rewrite rules. At this time, you can call the flush_rewrite_rules() function through the code to implement it. 1. This function can be added to the theme or plug-in activation hook to automatically refresh; 2. Execute only once when necessary, such as adding CPT, taxonomy or modifying the link structure; 3. Avoid frequent calls to avoid affecting performance; 4. In a multi-site environment, refresh each site separately as appropriate; 5. Some hosting environments may restrict the storage of rules. In addition, clicking Save to access the "Settings>Pinned Links" page can also trigger refresh, suitable for non-automated scenarios.

TosetupredirectsinWordPressusingthe.htaccessfile,locatethefileinyoursite’srootdirectoryandaddredirectrulesabovethe#BEGINWordPresssection.Forbasic301redirects,usetheformatRedirect301/old-pagehttps://example.com/new-page.Forpattern-basedredirects,enabl

UsingSMTPforWordPressemailsimprovesdeliverabilityandreliabilitycomparedtothedefaultPHPmail()function.1.SMTPauthenticateswithyouremailserver,reducingspamplacement.2.SomehostsdisablePHPmail(),makingSMTPnecessary.3.SetupiseasywithpluginslikeWPMailSMTPby

To implement responsive WordPress theme design, first, use HTML5 and mobile-first Meta tags, add viewport settings in header.php to ensure that the mobile terminal is displayed correctly, and organize the layout with HTML5 structure tags; second, use CSS media query to achieve style adaptation under different screen widths, write styles according to the mobile-first principle, and commonly used breakpoints include 480px, 768px and 1024px; third, elastically process pictures and layouts, set max-width:100% for the picture and use Flexbox or Grid layout instead of fixed width; finally, fully test through browser developer tools and real devices, optimize loading performance, and ensure response

Tointegratethird-partyAPIsintoWordPress,followthesesteps:1.SelectasuitableAPIandobtaincredentialslikeAPIkeysorOAuthtokensbyregisteringandkeepingthemsecure.2.Choosebetweenpluginsforsimplicityorcustomcodeusingfunctionslikewp_remote_get()forflexibility.
