Archive for the ‘Rants’ Category.

Fun fact of the day: about 37% of WordPress downloads are for non-English, localized versions.

So as a plugin or theme author, you should be thinking of localization and internationalization (L10N and I18N) as pretty much a fact of life by this point.

Fun total guess of the day: based on my experience in browsing through the thing, roughly, ohh… all plugins and themes in the directory are doing-it-wrong in some manner.

Yes friends, even my code is guilty of this to some degree.

It’s understandable. When you’re writing the thing, generally you’re working on the functionality, not form. So you put strings in and figure “hey, no biggie, I can come back and add in the I18N stuff later.” Sometimes you even come back and do that later.

And you know what? You probably still get it wrong. I did. I still often do.

The reason you are getting it wrong is because doing I18N right is non-obvious. There’s tricks there, and rules that apply outside of the normal PHP ways of doing things.

So here’s the unbreakable laws of I18N, as pertaining to WordPress plugins and themes.

Note: This is not a tutorial, as such. You are expected to already be translating your code in some way, and to have a basic grasp on it. What I’m going to show you is stuff you are probably already doing, but which is wrong. With any luck, you will have much slapping-of-the-head during this read, since I’m hoping to give you that same insight I had, when I finally “got it”.

Also note: These are laws, folks. Not suggestions. Thou shalt not break them. They are not up for debate. What I’m going to present to you here today is provably correct. Sorry, I like a good argument as much as the next guy, but arguing against these just makes you wrong.

Basic I18N functions

First, lets quickly cover the two top translation functions. There’s more later, and the laws apply to them too, but these are the ones everybody should know and make the easiest examples.

The base translation function is __(). That’s the double-underscore function. It takes a string and translates it, according to the localization settings, then returns the string.

Then there’s the shortcut function of _e(). It does the same, but it echoes the result instead.

There’s several functions based around these, such as esc_attr_e() for example. These functions all behave identically to their counterparts put together. The esc_attr_e() function first runs the string through __(), then it does esc_attr() on it, then it echo’s it. These are named in a specific way so as to work with existing translation tools. All the following laws apply to them in the exact same way.

So, right down to it then.

Law the First: Thou shalt not use PHP variables of any kind inside a translation function’s strings.

This code is obviously wrong, or it should be:

$string = __($string, 'plugin-domain');

The reason you never do this is because translation relies on looking up strings in a table and then translating them. However, that list of strings to be translated is built by an automated process. Some code scans your PHP code, without executing it, and pulls out all the __()’s it finds, then builds the list of strings to be translated. That scanning code cannot possibly know what is inside $string.

However, sometimes it’s more subtle than that. For example, this is also wrong:

$string = __("You have $number tacos", 'plugin-domain');

The translated string here will be something like ‘You have 12 tacos’, but the scanning code can’t know what $number is in advance, nor is it feasible to expect your translators to translate all cases of what $number could be anyway.

Basically, double quoted strings in translation functions are always suspect, and probably wrong. But that rule can’t be hard and fast, because using string operations like ‘You have ‘.$number.’ tacos’ is equally wrong, for the exact same reason.

Here’s a couple of wrongs that people like to argue with:

$string = __('You have 12 tacos', $plugin_domain);
$string = __('You have 12 tacos', PLUGIN_DOMAIN);

These are both cases of the same thing. Basically, you decided that repetition is bad, so you define the plugin domain somewhere central, then reference it everywhere.

Mark Jaquith went into some detail on why this is wrong on his blog, so I will refer you to that, but I’ll also espouse a general principle here.

I said this above, and I’m going to repeat it: “that list of strings to be translated is built by an automated process“. When I’m making some code to read your code and parse it, I’m not running your code. I’m parsing it. And while the general simplistic case of building a list of strings does not require me to know your plugin’s text domain, a more complicated case might. There are legitimate reasons that we want your domain to be plain text and not some kind of variable.

For starters, what if we did something like make a system where you could translate your strings right on the wordpress.org website? Or build a system where you could enlist volunteer translators to translate your strings for you? Or made a system where people could easily download localized versions of your plugin, with the relevant translations already included?

These are but a few ideas, but for all of them, that text domain must be a plain string. Not a variable. Not a define.

Bottom line: Inside all translation functions, no PHP variables are allowed in the strings, for any reason, ever. Plain single-quoted strings only.

Law the Second: Thou shalt always translate phrases and not words.

One way people often try to get around not using variables is like the following:

$string = __('You have ', 'plugin') . $number . __(' tacos', 'plugin-domain');

No! Bad coder! Bad!

English is a language of words. Other languages are not as flexible. In some other languages, the subject comes first. Your method doesn’t work here, unless the localizer makes “tacos” into “you have” and vice-versa.

This is the correct way:

$string = sprintf( __('You have %d tacos', 'plugin-domain'), $number );

The localizer doing your translation can then write the equivalent in his language, leaving the %d in the right place. Note that in this case, the %d is not a PHP variable, it’s a placeholder for the number.

In fact, this is a good place to introduce a new function to deal with pluralization. Nobody has “1 tacos”. So we can write this:

$string = sprintf( _n('You have %d taco.', 'You have %d tacos.', $number, 'plugin-domain'), $number );

The _n function is a translation function that picks the first string if the $number (third parameter to _n) is one, or the second one if it’s more than one. We still have to use the sprintf to replace the placeholder with the actual number, but now the pluralization can be translated separately, and as part of the whole phrase. Note that the last argument to _n is still the plugin text domain to be used.

Note that some languages have more than just a singular and a plural form. You may need special handling sometimes, but this will get you there most of the time. Polish in particular has pluralization rules that have different words for 1, for numbers ending in 2, 3, and 4, and for numbers ending in 5-1 (except 1 itself). That’s okay, _n can handle these special cases with special pluralization handling in the translator files, and you generally don’t need to worry about it as long as you specify the plural form in a sane way, using the whole phrase.

You might also note that _n() is the one and only translation function that can have a PHP variable in it. This is because that third variable is always going to be a number, not a string. Therefore no automated process that builds strings from scanning code will care about what it is. You do need to take care than the $number in _n is always a number though. It will not be using that $number to insert into the string, it will be selecting which string to use based on its value.

Now, using placeholders can be complex, since sometimes things will have to be reversed. Take this example:

$string = sprintf( __('You have %d tacos and %d burritos', 'plugin-domain'), $taco_count, $burrito_count );

What if a language has some strange condition where they would never put tacos before burritos? It just wouldn’t be done. The translator would have to rewrite this to have the burrito count first. But he can’t, the placeholders are such that the $taco_count is expected to be first in the sprintf. The solution:

$string = sprintf( __('You have %1$d tacos and %2$d burritos', 'plugin-domain'), $taco_count, $burrito_count );

The %1$d and such is an alternate form that PHP allows called “argument swapping“. In this case, the translator could write it correctly, but put the burritos before the tacos by simply putting %2$d before %1$d in the string.

Note that when you use argument swapping, that single-quoted string thing becomes very important. If you have “%1$s” in double quotes, then PHP will see that $s and try to put your $s variable in there. In at least one case, this has caused an accidental Cross-Site-Scripting security issue.

So repeat after me: “I will always only use single-quoted strings in I18N functions.” There. Now you’re safe again. This probably should be a law, but since it’s safe to use double-quoted strings as long as you don’t use PHP variables (thus breaking the first law), I’ll just leave you to think about it instead. :)

Law the Third: Thou shalt disambiguate when needed.

When I say “comment” to you, am I talking about a comment on my site, or am I asking you to make a comment? How about “test”? Or even “buffalo”?

English has words and phrases that can have different meanings depending on context. In other languages, these same concepts can be different words or phrases entirely. To help translators out, use the _x() function for them.

The _x() function is similar to the __() function, but it has a comment section where the context can be specified.

$string = _x( 'Buffalo', 'an animal', 'plugin-domain' );
$string = _x( 'Buffalo', 'a city in New York', 'plugin-domain' );
$string = _x( 'Buffalo', 'a verb meaning to confuse somebody', 'plugin-domain' );

Though these strings are identical, the translators will get separated strings, along with the explanation of what they are, and they can translate them accordingly.

And just like __() has _e() for immediate echoing, _x() has _ex() for the same thing. Use as needed.

Finally, this last one isn’t a law so much as something that annoys me. You’re free to argue about it if you like. :)

Annoyance the First: Thou shalt not put unnecessary HTML markup into the translated string.

$string = sprintf( __('<h3>You have %d tacos</h3>', 'plugin-domain'), $number );

Why would you give the power to the translator to insert markup changes to your code? Markup should be eliminated from your translated strings wherever possible. Put it outside your strings instead.

$string = '<h3>'.sprintf( __('You have %d tacos', 'plugin-domain'), $number ).'</h3>';

Note that sometimes though, it’s perfectly acceptable. If you’re adding emphasis to a specific word, then that emphasis might be different in other languages. This is pretty rare though, and sometimes you can pull it out entirely. If I wanted a bold number of tacos, I’d use this:

$string = sprintf( __('You have %s tacos', 'plugin-domain'), '<strong>'.$number.'</strong>' );

Or more preferably, the _n version of same that I discussed above.

Conclusion

Like I said at the beginning, we’ve all done these. I’ve broken all these laws of I18N in the past (I know some of my plugins still do), only to figure out that I was doing-it-wrong. Hopefully, you’ve spotted something here you’ve done (or are currently doing) and have realized from reading this exactly why your code is broken. The state of I18N in plugins and themes is pretty low, and that’s something I’d really like to get fixed in the long run. With any luck, this article will help. :)

Disclaimer: Yes, I wrote this while hungry.

Shortlink:

Just a quick note to state this as a fact, in case anybody was wondering (and since I’ve had a few emails about it lately).

Under no circumstances will I ever implement Facebook’s “frictionless sharing” in Simple Facebook Connect. If you want such a thing, I recommend using another plugin.

Facebook’s frictionless sharing is a privacy invading, oversharing, useless-result creating nightmare. I block websites that use it from appearing in my News Feed, I remove Applications and services that implement it (hey Yahoo!, you’ve been axed from my life entirely because of your use of this crap), and I will not help anybody to implement it or even provide them with useful advice.

In my opinion, this is by far the worst thing Facebook has ever created. Even if you ignore the privacy implications entirely, Facebook has finally succeeded at doing something that they have been trying to do for ages: Make Facebook’s main feed almost completely worthless.

I will continue to add other features to SFC, but I use FB a lot less than I did before (and am now focusing more on Google+ anyway). However, this is one “feature” that I will not be adding.

Oh, and I’m not the only one who thinks this idea sucks rocks, BTW.

http://news.cnet.com/8301-31322_3-57324406-256/how-facebook-is-ruining-sharing/
http://www.theatlantic.com/technology/archive/2011/09/why-the-new-york-times-isnt-using-facebooks-frictionless-sharing/245880/
http://technorati.com/blogging/article/facebooks-frictionless-sharing-causes-friction-among/
http://news.softpedia.com/news/It-Took-Two-Months-but-People-Finally-Realize-that-Facebook-s-Auto-Sharing-Is-Creepy-235716.shtml

/rant

Shortlink:

I’ve been hosting my sites on GoDaddy for years, despite everybody saying that they suck and so forth. I’ve even defended them. Their interface is crap, but it’s not terrible once you get used to it. It works well enough. Their shared servers are indeed overloaded, but with a little super-caching they tended to work alright. Their new cloud hosting service is definitely faster.

But if there’s one thing I can not stand, it’s censorship.

I recently discovered that a couple of old posts of mine about decoding code used by hackers were no longer loading up. Everything else worked, but not those posts. I couldn’t even pull them up in the WordPress post editor.

After some trial and error and back and forth, I discovered that any HTTP or FTP request that contains the string “eval(base 64_decode(” or similar variants, is blocked. FTP just stops dead, as does HTTP requests, with a continual spinning loading icon. Apparently they have some form of filtering in the TCP stack somewhere that just stops those connections dead in their tracks.

(BTW, the irony here is thick. GoDaddy’s malicious code scanner was blocking my “Scanning for Malicious Code is Pointless” post.)

GoDaddy… guys, I loved your service in the past, but I have to tell you that this is a *shit* approach to security.

After some tweeting back and forth, I found out from the horse’s mouth that this is intentional and cannot be disabled.

And as much as they’d like to claim this isn’t censorship:

Guys, you’re wrong. It is censorship. I wrote that post content, and they’re refusing to serve it over HTTP. You can spin that any way you like, but GoDaddy hosting is now censoring me.

What’s more, this is a *new* problem. Those posts worked fine when I wrote them. What changed? I dunno. I did move to their 4GH hosting, but nowhere did I see in the documentation that they would be intentionally blocking my content.

Anyway, I’ve worked around the problem for now with a plugin to add spaces to the proper places in my HTML content, thus bypassing their filter. However, in the long run, this will not stand. GoDaddy thinks it’s okay to block my personal content. I disagree with them, and no amount of argument is going to make me change my mind on this topic. Blocking my own content from being served is NOT a security measure.

As you might be able to tell, I’m a bit angry.

Therefore, I am now looking for a new hosting service. Some requirements of mine:

  • Traffic-wise I serve about 6000 page views a day, all told. In terms of total HTTP requests, I’d say somewhere around 30,000 or so.
  • Bandwidth tends to be in the 1.5 GB per day range. So, 50 GB per month, say.
  • Obviously, any form of censorship or technical limitations is unacceptable.
  • SSH access is a must-have.
  • I don’t necessarily need dedicated hosting or virtual dedicated hosting, shared is fine if it can handle it.
  • Speed would be nice. GoDaddy has always sucked in terms of time-to-first-byte. Their cloud hosting made it better, but not great.
  • MySQL Databases. I need at least 10 of them.

So, not too heavy requirements, I’d say.

I’ve heard suggestions for DreamHost in the past, and A Small Orange has always gotten favorable reviews from people I’ve talked to, but what the heck, might as well solicit the opinions of the internets in general, yeah?

Suggestions are happily accepted. If you can provide estimated pricing or links, I’d love to take a look at them. :)

Shortlink:

Seen this a couple of times on various sites and had a couple people ask me on Twitter about it.

Starting October 1st, Facebook will start requiring two new things:

1. OAuth 2.0 support. SFC has this in version 1.1, which will be released shortly (tomorrow, probably).

2. Canvas and Page Tab Applications will require HTTPS/SSL support.

Both of these are true.

However, some people are interpreting that second one to mean that you need to buy an SSL certificate for your own domain to use Facebook Connect type of functionality, like my plugin Simple Facebook Connect provides. This is false.

A “Canvas” application is one that runs on your site, but shows up on http://apps.facebook.com. A “Page Tab” application is similar, but can show up anywhere on the facebook.com website, depending on how it’s written (it makes a “tab” for somebody’s “Page”). Both of those have something in common: Your website’s contents are actually showing up on *.facebook.com.

Simple Facebook Connect does neither of these things. SFC is a way for you to integrate Facebook connection functionality back into your own site. It can publish stories to Facebook, it can let users comment using Facebook, etc., but the whole point of it is to take things from FB and put them on your site. SFC does not enable your site to appear under the *.facebook.com domain name.

You do NOT need an SSL certificate to use SFC, and you will continue to not need one after October 1st.

You will, however, need to upgrade to SFC 1.1. Old versions, including 1.0, will cease to function.

SFC 1.1 will most likely be released tomorrow, and should be a painless upgrade from 1.0. Sorry for the delay.

Shortlink:

Have you ever looked at the add_action function in WordPress? Here it is:

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
	return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

I know, right? Some people’s minds just got blown.

What are Filters?

A filter is defined as a function that takes in some kind of input, modifies it, and then returns it. This is an extremely handy little concept that PHP itself uses in a ton of different ways. About half the string functions qualify as a ‘filter’ function.

Look at strrev(). It’s a simple-stupid example. It takes a string as an argument, and then returns the reverse of that string. You could use it as a filter function in WordPress, easily. Like, to reverse all your titles.

add_filter('the_title', 'strrev');

Some filters take more than one argument, but the first argument is always the thing to be modified and returned. PHP adheres to this concept too. Take the substr() function. The first argument is the string, the second and third are the start and optional length values. The returned value is the part of the string you want.

What are Actions?

An action is just a place where you call a function, and you don’t really care what it returns. The function is performing some kind of action just by being called. If you hook a function to the init action, then it runs whenever do_action(‘init’) is called.

Now, some actions have arguments too, but again, there’s still no return value.

So in a sense, a WordPress action is just a filter without the first argument and without a return value.

So why have them both?

Because there is still a conceptual difference between an action and a filter.

Filters filter things. Actions do not. And this is critically important when you’re writing a filter.

A filter function should never, ever, have unexpected side effects.

Take a quick example. Here’s a thread on the WordPress support forums where a person found that using my own SFC plugin in combination with a contact form emailer plugin caused the email from the form to be sent 3-5 times.

Why did it do this? Basically, because the contact form plugin is sending an email inside a filter function.

One of the things SFC does is to build a description meta from the content on the page. It also looks through that content for images and video, in order to build meta information to send to Facebook. In order for this to happen at the right time, the plugin must call the_content filter.

See, what if somebody puts a link to a Flickr picture on their page? In that case, oEmbed will kick in and convert that link into a nice and pretty embedded image. Same for YouTube videos. Or maybe somebody is using a gallery and there’s lots of pictures on the resulting page, but the only thing in the post_content is the gallery shortcode.

In order to get those images from the content, SFC has to do apply_filters(‘the_content’,$post_content). This makes all the other plugins and all the other bits of the system process that $post_content and return the straight HTML. Then it can go and look for images, look for video, even make a pretty 1000 character excerpt to send to Facebook.

But SFC can’t possibly know that doing apply_filters(‘the_content’,…) will cause some other plugin to go and send a freakin’ email. That’s totally unexpected. It’s just trying to filter some content. That would be like calling the strrev() function and having it make a phone call. Totally crazy.

Shortcodes

Shortcodes are a type of filter. They take in content from the shortcode, they return replacement content of some sort. They are filters, by definition. Always, always keep that in mind.

Also keep in mind that shortcodes are supposed to return the replacement content, not just echo it out.

Conclusion

So plugin authors, please, please, I’m begging you, learn this lesson well.

Filters are supposed to filter. Actions are supposed to take action.

When you mix the two up, then you cause pain for the rest of the world trying to interact with your code. My desk is starting to get covered in dents from me repeatedly banging my head into it because of this.

Shortlink:

Just upgraded to the beta of 3.2. I like the new admin interface overall. Really, I do. But relatively minor things tend to bug me sometimes.

For example, I don’t much care for the Site Title being so tiny and hidden at the top of the admin screens. I like the site’s name to be big and prominent, as it’s a link to the front end of the site. On multi-site, it’s awfully nice to see at a glance what site I’m on. I often click that link to go to the front end of the site easily. So trying to navigate to the front end became difficult and hit or miss with this title being so tiny.

I also don’t like seeing the Page Title being so big and having a big ol’ icon there beside it. The Page Title strikes me as kinda useless. I mean, I know what screen I’m on.

So I wrote a quick tweak plugin to fix it. I’m posting it in case it bugs you as much as it bugs me. On a side note, it’s a quick little demo of how to modify the WordPress admin CSS quickly and easily.

<?php 
/* 
Plugin Name: Embiggen Site Title for WordPress 3.2 beta
Description: Embiggen the Site Title in wp-admin. Debiggen the Page headers. Ditch the useless icon.
*/
add_action('admin_print_styles', 'big_site_title');
function big_site_title() {
?>
<style>
.wp-admin #wphead {
	height: 42px;
}
.wp-admin #wphead h1 {
	font-size: 28px;
	#font-family: "HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,sans-serif; #uncomment this if you want to go to the sans-serif font
}
.wp-admin #header-logo {
	background-image: url("images/logo.gif");
	background-size:32px 32px;
	width:32px;
	height:32px;
}
.wp-admin .wrap h2 {
	font-size:16px;
	padding-top: 1px;
	padding-bottom: 0px;
	height:20px;
}
.wp-admin .icon32 {
	display:none;
}
</style>
<?php
}

Feel free to tweak further as desired. Also, WordPress might change further before 3.2 is released, so this may stop working or need further tweaking.

Shortlink:

 

Internet PiracyI recently had an exchange with a commercial theme developer who changed his terms away from the GPL because of an experience with some rude person who was redistributing his themes for free. Ultimately, I wasn’t able to convince him to stick with it, but there was a clear misunderstanding of the GPL in the first place there (I suspect that language differences played some part), and I thought this might make for an interesting blog post.

(Note that I’m talking about themes, but this all applies to commercial plugins as well as any other code you’re selling online.)

It’s All About Redistribution

The main barrier to the GPL that a lot of theme developers have expressed is the right of redistribution. That is to say that if you sell me a GPL’d theme, then I can turn around and give that theme to anybody I want, for free, and you have no recourse.

This viewpoint is entirely correct, however it’s missing the big picture, I feel.

Why Would I Do That?

First off, why would I take something I paid for and then give it away to everybody else for free? I mean, it’s one thing to give a copy of something to a friend of mine for his use, but it’s wholly another to go to the effort of setting up a website to distribute your theme as some kind of “screw you” policy. Did you anger me in some way? What level of maliciousness would be necessary for me to want to do that? Seems a bit overboard, and most people are ultimately reasonable.

However, this ignores the existence of John Gabriel’s Greater Internet Fuckwad Theory. Which is to say that some people are just trolling bastards who will screw with you just because they can. So let’s say that somebody gets a copy of your themes, posts them online, then refuses to take them down despite your polite requests, and waves the GPL in your face for his right to redistribute them.

Technically, this sort of person is correct, he does have the right of redistribution. But that doesn’t really matter.

What Are You Selling, Anyway?

Let’s say I made a piece of code and sold it. No GPL, no license, just me selling code to people for their own use. They have no rights to the code whatsoever. So, somebody posts that code online, for free, at some pirate site. Somebody else downloads it, and uses it, without paying me. Straightforward software “piracy”.

What have I lost here? Well, I lost the cash that I could have made from an extra sale, true, assuming that said person would have bought the code instead of pirating it. If you know people who habitually pirate code, then you know that that is a rather dubious claim, at best.

More importantly, I’ve lost a contact point between me and the user of the code. When I sell something to somebody, then I now have a relationship with that person. I get their email address. They may contact me for support. Even paid support. I may have forums for purchasers of my software to talk amongst each other in a community support system. They may buy other things I wrote.

This is the real benefit to selling code, that relationship between me as a developer and them as a purchaser of what I develop. And I’m missing that connection, until they want support from me for my product. Then I may say “well, you’re using a pirated copy of my product, if you want to join my support forums and my community and get my help, then you have to buy the product from me”. Take note of the many times that software companies have offered “clemency” sales and such, to turn pirated copies into legitimate ones.

What it comes down to is simple:

You Can’t Stop Piracy, So Don’t Try.

Think about it, you’re selling a digital file here. Files can be copied. If I buy a copy of your software, strip out any identifying marks, then post it to a thousand torrent sites, what exactly can you do to stop me from doing that?

No matter what your terms and conditions are, people still can copy your files, distribute them, edit them, do whatever they want. Unless you’re actually enforcing your terms with (potentially expensive) legal actions, then your terms are really quite meaningless. Technical measures to stop piracy don’t work, as many game companies have found out over the years. DRM doesn’t (and technically cannot) work.

Instead of viewing people redistributing your code as a bad thing, view it as an opportunity. If somebody downloads a “pirated” copy of your code, and uses it, then clearly they have a use for it. And at some point, they’re going to want upgrades. They’re going to want support. They’re going to want modifications. So make sure that you are the person they come to, and then you have an opportunity to convert that pirated download into a real sale.

The GPL doesn’t screw the developer by allowing others to share his work. The GPL enables the developer to get more contacts (and potentially more sales) by allowing others to share his work along with his name, contact information, website, etc.

Don’t fight against the right of redistribution, make it work for you instead.

Shortlink: