Translating WordPress Plugins and Themes: Don’t Get Clever

When you use the WordPress translation functions to make your plugin or theme translatable, you pass in a text domain as a second parameter, like so:

<?php _e( 'Some Text String', 'my-plugin-name' ); ?>

This text domain is just a unique string (usually your plugin’s repository slug). Well, many plugin developers see code like this:

<?php _e( 'Another Text String', 'my-plugin-name' ); ?>
<?php _e( 'Yet Another Text String', 'my-plugin-name' ); ?>
<?php _e( 'Gosh, So Many Text Strings!', 'my-plugin-name' ); ?>

And they think to themselves “hm, I sure am typing the 'my-plugin-name' string a lot. I’ll apply the DRY (Don’t Repeat Yourself) principle and throw that string into a variable or a constant!”

Stop! You’re being too clever! That won’t work!*

See, PHP isn’t the only thing that needs to parse out your translatable strings. GNU gettext also needs to parse out the strings in order to provide your blank translation file to your translators. GNU gettext is not a PHP parser. It can’t read variables or constants. It only reads strings. So your text domain strings needs to stay hardcoded as actual quoted strings. 'my-plugin-name'.

Happy coding!

* Well, it won’t break your plugin, but it could make it harder to be used with automated translation tools. And trust me, you don’t want to be manually managing your translation files… we have a better solution coming.

How to write a WordPress plugin that I’ll use

I tend to be very fastidious about the WordPress plugins that I’ll install. I’ll often write my own simple version of a plugin rather than install one from someone else that does a bunch of stuff I don’t need. Here is my philosophy behind writing WordPress plugins, best witnessed through the plugins I’ve written lately, like Markdown on Save, Login Logo, Monitor Pages, and WP Help.

Fewer features as a feature

There are diminishing returns as you add features. That is, the more you add, the more likely you’re adding something that X % of your plugin’s users won’t ever use. Stick to the basics. I’ll often release a “0.1” version of my plugin with really obvious features missing. When I get a flurry of “You should add Y!” messages, that validates my assumption that Y is necessary. Start with the smallest version that gets the core job done. Iterate as needed.

Code the hell out of it

The best part of starting small is that you can code the hell out of the plugin. Do it right. Make each line of code beautiful. Make sure you’re using WordPress APIs properly, and while you’re at it, add i18n support (WP Help 0.2 shipped with support for Bulgarian, German, Spanish, Mexican Spanish, Macedonian, Dutch, Brazilian Portuguese, and Russian!)

Reduce UI

If you can do without UI, don’t make it. Make every bit of UI prove its necessity. As an example, look at my Login Logo plugin. It has zero UI. It looks for the presence of a file named login-logo.png in the wp-content directory. The rest is “magic.” It measures the image, generates appropriate CSS, and gives you an instantly and easily customized login screen. The plugin is invisible. It’s completely out of sight, and out of mind. Finally, UI screens are generally where plugin authors make security mistakes. By skipping them, you make it much more likely that your plugin is secure.

Code it for the future

Don’t use deprecated APIs. Plan features in future-forward ways. Implement it in such a way that a site that is using the plugin doesn’t break if the plugin suddenly goes away. One example of this is my Markdown on Save plugin, which offers per-post Markdown formatting. First, I decided that for performance reasons, I wanted to parse Markdown then the post was updated, not on display. The obvious place to store the generated HTML was in the post_content_filtered column that WordPress provides (but does not use). But then I considered what would happen if someone deactivated the plugin or deleted the plugin. The code that accessed post_content_filtered would not work. Their blog would spit out raw Markdown. And any exports they made would export raw Markdown. What if they were exporting to which doesn’t support Markdown? So I decided to store the Markdown in post_content_filtered, and store the generated HTML in post_content. When you edit a Markdown-formatted post, it swaps in the Markdown, so you can edit that. But if you deactivated the plugin, it would fall back to the HTML. So you can feel free to use this plugin and know that if one day you wake up and you hate Markdown, all you have to do is deactivate the plugin and all of your posts are back to HTML.

Secure it

Writing secure WordPress plugins isn’t hard. It just takes awareness. Take the time to do your research and code a plugin that will be an asset to its users, not a liability.

Developing on WordPress using Git

WordPress uses Subversion (SVN) for revision management. Before Subversion, it used CVS. Right now, Git is a hot option in the SCM category. It offers really nice features such as decentralization, speed, fast and cheap local branching, better merging, more offline capabilities, staging of commits, and lots more. It’s premature to talk about moving WordPress core and plugins to another SCM system — we have a lot invested with Subversion and Trac. But be of good cheer. You can have your Git and commit to Subversion too! Here’s how I do it.

First, tools. You’ll need Git, obviously. But you’ll also need git-svn-diff, a Bash script that generates Subversion-compatible diffs.

Download git-svn-diff, put it somewhere in your path, and make it executable. Like this:

curl -L > /usr/local/bin/git-svn-diff
sudo chmod +x /usr/local/bin/git-svn-diff

Next, to enable you to do git svn-diff instead of git-svn-diff, edit ~/.gitconfig and add this:

	svn-diff = !git-svn-diff

This next step is going to take a while. You’re going to pull down WordPress’ SVN history using Git’s SVN support.

git svn clone -t tags -b branches -T trunk

You might want to let that run overnight. Really. It’s going to go through each changeset.

Once you’re done, you should be in the Git master branch, which corresponds to WordPress SVN’s trunk. WordPress’ branches are in remotes/{name}

To pull in the latest changes from SVN, use git svn rebase. Important rule: never modify the SVN branches (remotes/{name}). Instead, create a new topic branch.

For example, say that I’m going to work on a ticket for trunk. I’d create a new branch from remotes/trunk like this:

git checkout -b ticket-12345 remotes/trunk

That will create a new local Git branch called ticket-12345 based on SVN’s trunk, and then check it out (i.e. switch to it).

If you’re working on a WordPress SVN branch, you can do something like this:

git checkout -b ticket-12345 remotes/3.1

Do your work in the branch you created. You can make multiple local Git commits if you want, to break up your work into smaller chunks that make sense to you.

When you’re ready to submit your patch, use git-svn-diff to produce it.

git svn-diff > ~/12345.diff

If you have commit access, you can commit to Subversion from this topic branch. But be careful! First you should do git svn rebase to bring your patch up to date. Next, you should squash your local git commits, otherwise each one of them will be individually committed to SVN (hello, flood). So rebase your commits into one commit, like so:

git rebase -i remotes/trunk

Use “reword” on the first commit. Use “fixup” on the subsequent ones. That will roll the commits up into one. You’ll then be prompted to enter your amended commit message for that commit amalgam.

Ready? You can now commit to SVN using:

git svn dcommit

Git knows which remote SVN branch it came from when you checked out your topic branch. You can verify which one it is attached to by doing:

git svn info

A few tips:

Create a .gitignore file. This lists files or directories that you want Git to ignore. First, you want Git to ignore the .gitignore file itself! Next, you want Git to ignore your local wp-config.php Finally, you want to ignore any additional plugins, must-use plugins, themes, uploads, etc. Just do a git status and add anything that you don’t want to commit to WordPress or put in your patches.

I hope you found this helpful! Let me know if you have any questions.

Block-level comments trick

Block-level comments are useful for commenting out an entire block of code in PHP, CSS, and other code contexts.

$this = 'code is deactivated';
$and = 'so is this';

The only problem with this is that when you go to re-activate this code, you have to change both the opening and closing comment markers. That’s a pain.

While I was at WordCamp NYC last week, I saw Daisy Olsen using a very clever trick in her lightning round talk.

$this = 'code is deactivated';
$and = 'so is this';

See what she did there? The closing comment marker is preceded by another opening marker. Because comment blocks can’t be nested, this second opening comment marker is ignored. This enabled her to re-enable this code by removing the opening marker.

$this = 'code is reactivated';
$and = 'so is this';

Brilliant! I can’t believe I haven’t seen this before. The one downside to this is that you are deleting two characters and destroying the opening marker. Here’s an even better method.

$this = 'code is reactivated';
$and = 'so is this';

By adding a slash in front of the opening comment marker, I comment out the comment marker. It only takes one key press, and the corpse of the original opening marker is retained, allowing you to reinstate it with the deletion of a single character.

Props to Aleem Bawany for the second trick (he uses //*/ as the closing comment, which works pretty much the same way).

What other commenting tricks do you know?

Escaping API updates for WordPress 2.8

The WordPress escaping API functions have been updated. Escaping is a way of using untrusted text that “neuters” anything that could do damage. Escaping is used in WordPress to avoid SQL injection and cross-site scripting/script injection (XSS), among other things.

The old functions still work, so if you were using the old ones, you’re fine. The new ones just offer you an easier to remember, more concise, and more flexible way of securing your WordPress code.

Here’s an example. Say you wanted to translate a string, escape it for use in a quoted HTML attribute, and then echo it out. In WP 2.7, you’d have to do this:

<?php echo attribute_escape( __( 'Untranslated text' ) ); ?>

In WordPress 2.8, you can just do this:

<?php esc_attr_e( 'Untranslated text' ); ?>

Shorter, plus we killed the echo and the extra pair of parenthesis! But let’s break it down.

esc_ = 1, attr = 2, _e = 3

  1. esc_ is the prefix for the new WP escaping functions.
  2. attr is the context (in this case, attribute). The available contexts for 2.8 are attr, html, js, sql, url, and url_raw.
  3. _e is the optional translation suffix. The available suffixes for 2.8 are __, and _e. If you omit the suffix, no translation is performed, and your string is just returned.

More about contexts

  • attr means an HTML attribute.
  • html means text within an HTML node, but not within an attribute
  • sql is an alias to $wpdb->escape().
  • url means URLs for use in HTML attributes.
  • url_raw means URLs for use in redirects or storage in the database (does not entity-encode).
  • js means JS, for using PHP to populate a Javascript var.

More about suffixes

Suffixes work just like the function they’re named after.

  • __ translates the string and returns it.
  • _e translates the string and echoes it.
  • The suffix is optional. A blank suffix will just return.
  • Suffixes are currently only available for html and attr contexts.

Functions with translation suffixes also accept an option second parameter of a translation domain, for use in plugins.

Enjoy! Go forth and write more succinct code.

How to write a solid and stable WordPress plugin

The b5media tech team was having a discussion today about what criteria we use when reviewing a WordPress plugin for possible inclusion on one of our sites or across our network. It makes for a good list of what to do (or not do) when writing a WordPress plugin, something that might be generally useful to plugin authors. These things won’t make your plugin good — they’ll just help make it secure and stable.

Don’t ignore security

WordPress plugins have unlimited power, which is a blessing (it makes WordPress very flexible), but it can be a curse if your plugin is not code with security in mind. If things like CSRF, the prepare() method (SQL injection protection), XSS are not intimately known to you, it’s time for you to do some research. When someone installs their plugin, they are potentially putting their site’s security in your hands. Rise to the occasion.

Make proper use of the role/capability system

WordPress controls admin access using a roles/capabilities system. Make sure your plugin is checking against an appropriate capability. If it is the sort of thing that only “admins” should use, you should be checking against the manage_options capability. If this is something that an author should be able to do, you might use the publish_post capability. Do not check against roles. Roles are only containers for capabilities, and you can’t assume that an “author” on one blog means the same thing on all of them.

Use current API functions

WordPress has a lot of legacy code and deprecated functions. We try not to break stuff without good reason, so often times we’ll leave in compatibility for old API functions. If your plugin is using these functions, it tells me that you’re not up on current WordPress development. This means that your plugin might be more likely to break with a WordPress upgrade, and it suggests sloppiness that can lead to security issues.

Mind your performance

The number of queries that your plugin makes can affect its performance. We don’t care how cool your plugin is — if it uses 10 MySQL queries on every page, it’s not going in. You should work to minimize these queries as much as possible. Other performance issues like the efficiency of your PHP code should be taken into consideration as well.

Use existing WordPress data structures whenever possible

Don’t create your own tables unless you’re absolutely certain there is no way to accomplish your functionality within the WordPress data system.

I’m going to go into more detail on these topics at a later date. Maybe even with screencasts, which seem to be a great way of demonstrating these sorts of things.