Smart fallback mechanism for loading text domains in WP

There’s a small difference between the way WordPress and a Java application are loading localization files. This difference always bugged me a little but never was an issue until I really started to localize my plugins and used Pootle to do so.

So I found a way to make WordPress smarter and load generic localization files when the exact localization files are not there (full explanation after the code). You can either add that snippet of code into your plugin or your theme, or make it a plugin on its own so you never have to choose what exact locale you support. Not only it saves me some stupid rename work but it also helps me actually cover more locales using only one localization file. It will not be ideal in some cases (French-speaking Canadians may choke on your French) but it is way better than falling back to English and it still lets anyone use a specific locale file if they wish.

function my_plugin_smarter_load_textdomain($mofile, $domain) {
    # Don't forget to change your domain name here
    if ($domain == 'my-plugin-domain' && !is_readable($mofile)) { 
        $pos = strrpos($filename, '_'); 

        if ($pos !== false) { 
            # cut off the locale part, leaving the language part only 
            $filename = substr($filename, 0, $pos); 
            $mofile = $dirname . '/' . $filename . '.' . $extension; 

    return $mofile; 
add_filter('load_textdomain_mofile', 'my_plugin_smarter_load_textdomain', 10, 2);

In Java, if a localization file for an exact locale does not exist (like say fr_CA) the system will fallback to a generic locale file (fr in that case). The principle is that if the local flavor of French is not there then it’s always better to fallback to generic French than let the user have English (or whatever is your default language).

Now, with WordPress, that fallback mechanism does not exist. Be it with WP itself, plugins or themes, if the file for the exact locale does not exist then WP falls back to English. That means that you either have to actually translate your plugin in fr_FR and fr_CA, duplicate your French localizations or let your Canadian users use English instead of French. It’s even worse with Spanish and that’s unsatisfactory.

So, for a short while, I was annoyed. It really started to itch when some of my Spanish-speaking friends offered to help translate my plugins in Spanish. One of them is Colombian (es_CO), another Argentinian (es_AR), one from Spain (es_ES) and one from California (so, Mexican Spanish, es_MX). I could have ask them to translate into their own flavor of Spanish, but frankly I don’t see the point (they don’t see it either) and it makes every one of them a critical resource. Having them all contribute to a unique flavor of Spanish made more sense. So I chose to call that flavor es_ES (that’s what the WordPress translation team does by the way). So far so good, I’d give those people a locale file in Spanish, they’d send it back to me and all is well. Except I have to merge the files and manage conflicts, which is not easy since I can’t speak Spanish.

Next, I setup a Pootle server. Pootle is a collaborative translation server that allows my friends to work together, see what’s left to do, make suggestions, etc. I just need to update the templates on the server when they change (the .pot files) and when I want to release my plugins I just have to download the work already done (the .po files, that I then compile into .mo files).

However, Pootle doesn’t care about es_ES. It knows only es and when I download the .mo files they are all named, which means I need to rename them to And this is the same for French, German, Italian and most of the languages. This is now a total annoyance for me because my tools, which should all work perfectly together are now forcing me to do mundane work. And I hate mundane work.

Hence the code above. Enjoy :)

One thought on “Smart fallback mechanism for loading text domains in WP

  1. Wilco

    It’s good but not good enough for me since i need a fallback on a “per string” basis.
    Not per file. It seems pretty difficult though..

    Thanks for the code and the explanation.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.