Possible to add a language file to a plugin without adding .po/.mo files to plugin directory?

I need to add a language file to an existing plugin. Not for translation reasons, but because I’d like to change all occurrences of a certain word to something else. While I could just drop another .po/.mo file set into the plugin’s languages directory, I don’t want to lose the language file when the plugin is updated.

It appears if the plugin used load_textdomain to pull files from the WP_LANG_DIR in addition to load_plugin_textdomain then I could just drop in some .po files into /wp-content/languages/ and it would work.

BuddyPress does it this way, and has instructions for doing the exact sort of thing I’m trying to do: http://codex.buddypress.org/getting-started/customizing/customizing-labels-messages-and-urls/. I’ve changed every reference to “Group” to “Community”.

All I have to do is drop the correctly-named files into the /wp-content/languages/ directory and the files are loaded.

The plugin I’m trying to modify is: BuddyPress Group Email Subscription.

It loads the languages using this function:

function activitysub_textdomain() {
    load_plugin_textdomain( 'bp-ass', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
add_action( 'init', 'activitysub_textdomain' );

Solutions Collecting From Web of "Possible to add a language file to a plugin without adding .po/.mo files to plugin directory?"

Best way to do this is probably by using a second, custom-made, plugin.

Make a new plugin directory. Call it something like “example-com-custom-langs” or something unique to your site. In there, make a php file with a plugin header describing what the plugin does (for your own sanity), and do something like this:


function example_com_custom_langs_plugins_loaded() {
  remove_action('init', 'activitysub_textdomain'); // disable the old lang files
  add_action('init','example_com_custom_langs_init'); // load our new lang files

function example_com_custom_langs_init() {
  load_plugin_textdomain( 'bp-ass', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );

Now, you can put your language files into the /languages directory of this new plugin, and they will be loaded instead of the originals in the other plugin. And this plugin won’t get updated when the other plugin updates. So as long as they don’t change from “bp-ass” as the textdomain, yours get used instead.

I belive you can use a load_textdomain_mofile filter…

add_filter('load_textdomain_mofile', 'custom_load_textdomain_mofile', 10, 2);
function custom_load_textdomain_mofile( $mofile, $domain){
    if ($domain == 'bp-ass')
        $mofile = 'somepath/to/your/mo/file.mo';
    return $mofile;

Difference (comparing Otto) is you can actually specify your mo file for domain doesn’t metter how will be changed (triggered) localization.

The buddypress way is idiotic, you should not use translation to impact server and software behavior, translation should be only for labels (just spent an hour yesterday trying to figure why a CPT wasn’t working and it turned out because its type was translatable and I translated it).

Otto’s and Oleg’s answers are technically correct, but there is one issue they ignore – future upgrades. An upgrade of the plugin will eventually discard the strings you want to override and then your changes will disappear, and since this change is not part of the actual code no one will remember what you have done and will be able to do it again. In addition, depending on the type and amount of changes that you intent to make, you might make any documentation the plugin has unuseable.

In other words, you are forking the plugin’s code and an upgrade will kill your changes in any case, so why not to do them at code level where they will be easy to find, and then change the plugin name and slug to prevent it from being upgraded by mistake.

assume the original plugin has an <? _e('give me money!','text-domain')?> and you want that instead of printing “give me money!” you want that “give me your money please” will be printed and you use the methods described in the other answers to achieve that.

Some version later the author of the plugin decides to emphasize the sentence and replaces it with <? _e('GIVE ME THE MONEY!','text-domain')?>, but the new string is not in the modified translation table that was used as part of the solution and therefor instead of getting the expected “give me your money please” you get “GIVE ME THE MONEY!”.
This is a bug that will be hard to figure out because by the time it will happen you will forget what kind of a hack you have done originally.