22

The goal is to offer a button/select drop-down or similar to switch the public interface1) language on the fly.

What I'm searching for

  • Plugin or Theme Code...
  • ... or Ideas
  • Using a work-around with load_textdomain() would be appreciated to leave translation to .po/.mo files
  • Ideas on how to parse the strings into the interface (ex. ajax/plain php/ini, json, xml files) when not relying on some textdomain function

Note:

1) It's not about publishing in different languages.

2) I don't need code to make the actual drop-down/button/whatever. It's only about the code/system delivering the strings for the UI.

Thanks!

5
  • 1
    If you can settle for a page refresh, redefining the WPLANG constant could be an option. I'm doing that on two sites with multilingual content where the multilinguality plugin fails to trigger UI translation. If it has to be an AJAX/JSON solution, I wouldn't know how to help. Commented Dec 5, 2011 at 19:01
  • @JohannesPille At which point/hook to do you switch? Btw: Could you move that to an answer. Already worth an up vote. Thanks.
    – kaiser
    Commented Dec 5, 2011 at 19:04
  • I am doing it in a plugin, no hooks involved. It's a pretty dirty fix and I'm not all that proud of it, but it got the job done at the time. You can see the plugin's code in this pastebin. What it does is self explanatory. The site I copied this from utilizes the last free version of WPML. Fair enuff, moved it to an answer. Commented Dec 5, 2011 at 19:33
  • The pastebin URL seems to be dead.
    – PapaFreud
    Commented Mar 20, 2012 at 14:04
  • @JohannesPille Would you mind posting your code as an update to your Q, or adding a pastebin that's not expiring?
    – kaiser
    Commented Mar 20, 2012 at 14:59

3 Answers 3

15
+100

By far the best (easiest) way is to use the locale filter (inside get_locale()).

First set up a quick function for retrieving a different language to use on the locale filter.

/**
 * A function returns with returns the user's selectd locale, if stored. 
 */
function wpse35622_get_new_locale($locale=false){
$new_locale = get_user_meta(get_current_user_id(), 'wpse_locale', true);
    if($new_locale)
        return $new_locale;

    return $locale;
}

I've left this as a global function, might be handy...? The rest of the plug-in I've wrapped inside a class.

It creates a drop-down of possible languages from which you can select.

Drawbacks

  • Searches wp-content/languages for files of the form xx_xx.mo. If it's not of that form (not all .mo files are) the plug-in won't pick up on it!
  • The drop down gives a list of the found locales to choose from, but these are displayed in the 'xx_xx` format of locales, not a human friendly way - any ideas??
  • Does $locale do anything other than language? If so it's still possible to get the locale the same, but provide alternative translations. It's a lot messier though...

The code

class SH_Pick_Lang{
    /**
     * A unique name for this plug-in
     */
    static $name ='sh_pick_lang';

    /**
     * Hook the functions
     */
    public function __construct(){

        if( isset($_POST[self::$name]) ){
            add_action('locale',array($this,'update_user'));
        }
        add_filter( 'locale', 'wpse35622_get_new_locale',20 );
        add_action( 'wp_before_admin_bar_render', array( $this, 'admin_bar') );
    }

    /**
     * Update the user's option just in time! Only once mind...
     */
    function update_user($locale){
        $save_locale = $_POST[self::$name];
        update_user_meta(get_current_user_id(), 'wpse_locale', $save_locale);
        remove_filter(current_filter(),__FUNCTION__);
        return $locale;
    }

    /**
     * Add a really horrible looking dropdown menu that I'm sure Kaiser will make look fantastic.
     */
    function admin_bar(){
        global $wp_admin_bar;

        $wp_admin_bar->add_menu( array(
            'id'        => 'shlangpick',
            'title'     => $this->wpse_language_dropown(),
        ) );
    }

    /**
     * Generates a list of available locales.
     * Searches the wp-content/languages folder for files of the form xx_xx.mo
     * 
     * TODO Not all locales are of the form xx_xx.mo - we might miss some.
     * TODO Better way of gettin gthe wp-content/languages folder without a constant?
     */
    function wpse_language_dropown(){
         $name = self::$name;
         $locale = get_locale();
         $langs = get_available_languages();
         $langs[] = 'en_US';

         //For the labels (format_code_lang)
         require_once( ABSPATH . 'wp-admin/includes/ms.php');

         $html = "<form method='post'> ";
         $html .= "<select name='{$name}'>";
         foreach($langs as $lang){
              $label = format_code_lang( $lang );
              $html .= "<option ".selected( $lang, $locale, false)." value='{$lang}'>{$label}</option>";
        }
         $html .= "</select>";
         $html .= get_submit_button('Change Language','secondary', 'submit', true);
         $html .= "</form >";

         return $html;
    }

} // END Class

//Initiate...
$sh_pick_lang = new SH_Pick_Lang();
5
  • 1
    Upvote done (loughing about all the inline comments as well). Bounty is set and will be awarded to you as soon, as I'm allowed (23h left - point me at it if I forget).
    – kaiser
    Commented Jul 6, 2012 at 11:36
  • 2
    Updated to reflect the answer to this question. Commented Jul 6, 2012 at 11:57
  • N1ce one. Bookmarked. +1 Commented Jul 6, 2012 at 16:50
  • I've noticed that just including 'wp-admin/includes/ms.php' triggers an admin notice for updating your network sites (when you're a single site!). It may be that format_code_lang() will need to be duplicated inside a custom function. Commented Jul 6, 2012 at 16:54
  • I added a Gist with my clean up - had no time testing it, as I'm messing around with plugin folders currently and this means that no other plugin works. Hope you can work with the provided gist and maybe fork/update. I can award the bounty in half an hour, but have to leave now, so I'll send it tomorrow. Thanks for all your work - should be in any default installation :)
    – kaiser
    Commented Jul 7, 2012 at 11:15
5

If you can settle for a page refresh, redefining the WPLANG constant could be an option. I'm doing that on two sites with multilingual content where the multilinguality plugin fails to trigger UI translation.

5
  • Hmm.. you could use ajax to replace the body or the whole html tag of the page for that matter, after executing the above code ( in the same ajax request ), onclick of your button or something like that. Commented Dec 5, 2011 at 19:44
  • +1. Just looked at your pastebin. This means your setup is http://example.com/language/whatever/, right?
    – kaiser
    Commented Dec 5, 2011 at 21:18
  • @kaiser Yup, that's the setup on the site where the code comes from. If you care about the UI language only, passing a URL query string or setting a $_SESSION variable to trigger the redefining of WPLANG upon the next pageload should do. Should you care to replicate this on a multilingual site, note that as the comment with the pastebin says, the code in it works with WPML, last free version. It cannot be replicated 1 to 1 with qTranslate, because those URI's are not physical but rewritten via the mod_rewrite apache module. I'm sure it can be adjusted, but won't work "as is". Commented Dec 5, 2011 at 22:40
  • I won't use qTranslate or WPML. It's really only about the UI language. Ad $_SESSION) My problem is that afaik they're pretty tough to handle and stuff between globals and the array can get mixed up easily. So, if I'd try to find a way to change WPLANG, then I need to find a solid way to give it from one request to another. Maybe add_query_arg/get_query_var will do the trick...
    – kaiser
    Commented Dec 6, 2011 at 1:19
  • I was just thinking about this again (solution still not built into the project): Could you wrap this into some ajax? I think this would be the best way to get around this. I want to close the Q and mark it as solution :) Thanks.
    – kaiser
    Commented Feb 4, 2012 at 3:21
2

http://www.qianqin.de/qtranslate/

is what you need ...

Edit I - after comment. First - thanks for all who contributed in the downvotes torrent. (this is what happens when one does not visit frequently enough :- ) )

now - The function that handles the switch is in qtranslate-core. (starts at line 80 more or less - depends on the version that you want.)

Since you can not look into a NOT OOP code , and I am now with nothing else to do - I took 10 minutes to synthesize , paraphrase compile that non-OOp with yet another non-OOP code for you .

(sorry, - primitive old skool here)

Assuming that I understood the question && Assuming you want the code to be for admin && assuming you will know how to port it to front end if you want && assuming you understand that the code is not optimal :

<?php
/*
Plugin Name: k99 language switcher
Plugin URI: http://www.krembo99.com
Description: Admin Language switcher proof of concept only - do not use in production.
Version: 0.0.0.0.0.0.0.1
Author: Krembo99
Author URI: http://www.krembo99.com
*/ 
?>
<?php function k99_add_language_menu() {
    //  k99_load_ajax_display_functions();
?>  
    <div class="mgmb_help_setting">
    <?php _e('Language:','your_text_domain'); ?>
    <select name="mgmb_language_setting_help" id="mgmb_language_setting_help" onChange="mgmb_set_language_cookies(this.value);" >
        <option value="en_US" <?php if($_COOKIE['k99_userLang']=="en_US"){echo "selected";} ?>><?php _e('English','your_text_domain');?></option>
        <option value="de_DE" <?php if($_COOKIE['k99_userLang']=="de_DE"){echo "selected";} ?>><?php _e('German','your_text_domain');?></option>
        <option value="zh_CN" <?php if($_COOKIE['k99_userLang']=="zh_CN"){echo "selected";} ?>><?php _e('Chinese','your_text_domain');?></option>
    </select>
    </div>
<?php   
}
// Now we set that function up to execute when the help action is called
add_filter('contextual_help', 'k99_add_language_menu');

// I guess this is your mysterious "browser stuff"
function k99_language_change($lang){ 
 global $locale;
 // wp_cache_set( "language", $lang, 'options' );
    if ( isset($_COOKIE['k99_userLang'])) {
      $lang = $_COOKIE['k99_userLang'];
    }
    define( 'WPLANG', $lang );
 if($locale!= $lang) {
    $locale = $lang;
    load_plugin_textdomain('your_text_domain', false, dirname( plugin_basename(__FILE__) ) . '/lang'); // sample use for plugins textdomain
    load_plugin_textdomain('your_text_domain2', false, dirname( plugin_basename(__FILE__) ) . '/lang');
    load_plugin_textdomain('your_text_domain3', false, dirname( plugin_basename(__FILE__) ) . '/lang');
 }
  return $locale;
}
add_filter('locale', 'k99_language_change',99);

////////// +++++++++++++++  START COOKIES CREATION +++++++++++++++//////////////////
// Javascript function set the language cookies
// access external
// @param lang - language code
// @return NULL
// or maybe this is the OP´s mysterious "browser stuff" ??
    function mgmb_print_script() {
    ?>
    <script type="text/javascript" >
    function mgmb_set_language_cookies(lang){   
        var Then = new Date(); 
        Then.setTime(Then.getTime() + 10000*60*60*1000 ); //set cookie expire to 10000 hours cookies (hour*minute*seconds*1000)
        document.cookie = "k99_userLang="+lang+";expires="+ Then.toGMTString();
        window.location.reload();
        } 
        </script>
<?php
} 
add_action('admin_print_scripts', 'mgmb_print_script');  // this is not the right way to do .. but no time now.
?>

REMARKS :

1 - This was built in 10 min. on local machine while at the airport . The reason why I say this is that the only ready available wordpress installation that I have here is quite old . (2.9 I think).

This means that probably the language menu will appear UNDER the contextual help div, due to the fact that a new div structure was recently introduced (with tabs) - but still it should work. (I promise to get back to this later if I have time)

If I had more time (and a new wordpress code here ) i would probably integrate this in the new admin-bar with add_action( 'admin_bar_menu', 'k99_add_language_menu_2', 1000 ); (I already promised to get back to this)

2 - you should have the listed languages files inside the "languages" folder in wp-content (better??) or wp-include. regarding this point - the code is only a proof of concept - and of course if one has more time the menu should be built in a dynamic way and not like done here.

3 - your WPLANG in config should be empty. I am not sure it will work on newer wp versions if a language is already set.

4 - it is a simple synthesized and paraphrased proof of concept built on what was seen in qtranslate code (also old version) - so most of the things are done the "quick" way, not necessarily the "right" way (like adding JS, no dedicated textdomain etc..).

EDIT II

someone is lucky today ! (Flight Delay )

Since you were looking for "ideas" for approaches , here is another famous one .

It will not fit in a plugin, or actually any other non-exclusive environment , but it might give you another direction as how to handle the "Browser stuff".

This will allow one to get the language with a GET like : <a href="index.php?lang=de">German</a> or <a href="whatever.php?lang=ml">my Language</a>

To use , you can create a code (let´s call it wp-langswitch.php )

 session_start();
 if ( isset( $_GET['lang'] ) ) {
    $_SESSION['WPLANG'] = $_GET['lang'];
    define ('WPLANG', $_SESSION[WPLANG]);
 } else {
    if(isset($_SESSION['WPLANG'])) {
        define ('WPLANG', $_SESSION['WPLANG']);
        $_GET['lang'] = $_SESSION['WPLANG'];
    } else {
        if ( isset( $_SERVER["HTTP_ACCEPT_LANGUAGE"] ) ) {
            $languages = strtolower( $_SERVER["HTTP_ACCEPT_LANGUAGE"] );
             $languages = explode( ",", $languages );
            $_SESSION['WPLANG'] = $languages[0];
            $_SESSION['WPLANG'] = str_replace("-", "_", $_SESSION['WPLANG']);
            $_GET['lang'] = substr($_SESSION['WPLANG'],0,2);
            define ('WPLANG', $_SESSION[WPLANG]);
        } else {
            define ('WPLANG', '');
        }
    }
 }

now, in wp-config, just before the WPLANG constant , we need to include our code .

 require_once(dirname(__FILE__).'/wp-langswitch.php');
 define ('WPLANG', ''); 

Now - obviously it would be better to add some cookies to the mix to store the languages .

EDIT III

I promised to get back to this later - here is how (in a very non-correct and primitive way ) it could be integrated inside the admin-bar (instead of the contextual-help-menu)

 function k99_add_language_menu_bar() {
    global $wp_admin_bar, $wpdb;
    if ( !is_super_admin() || !is_admin_bar_showing() )
        return;
// I am sure you can find a way alone to construct array from reading folder . If not , comment and I will update again.

if($_COOKIE['k99_userLang']=="en_US"){$sel==$_COOKIE['k99_userLang'];} 
if($_COOKIE['k99_userLang']=="de_DE"){$sel==$_COOKIE['k99_userLang'];}
if($_COOKIE['k99_userLang']=="zh_CN"){$sel==$_COOKIE['k99_userLang'];}

    $k99_lang = '';
    $k99_lang .= '<select name="mgmb_language_setting_help" id="mgmb_language_setting_help" onChange="mgmb_set_language_cookies(this.value);" >';
    $k99_lang .= '<option value="en_US" >English</option>';
    $k99_lang .= '<option value="de_DE" >German</option>';
    $k99_lang .= '<option value="zh_CN" >Chinese</option></select>';

    /* Add the main siteadmin menu item */
    $wp_admin_bar->add_menu( array( 'id' => 'Language', 'title' => __( 'language', 'your_text_domain3' ), 'href' => FALSE ) );
    $wp_admin_bar->add_menu( array( 'parent' => 'Language', 'title' => $k99_lang, 'href' => FALSE ) );
}
add_action( 'admin_bar_menu', 'k99_add_language_menu_bar', 999 );

Just replace this function (or add) to the old code (the original NON - OOP non-plugin) .

Like I said, I do not have a new wordpress installation here on local that features admin-bar - but it should work .

(and then again, it might not work...but I am sure you could fix that if you really want - even thought it is not OOP ) :-)

gotta run now . hope it helps somehow.

EDIT IV - changed edit III to working verion for admin menu bar (at least on my wp 3.4)

since I had no working install of 3.4 - and now I returned and I do - I have edited the Edit III solution - and it works for me .

About the upvotes, I could not care less . And I do not need any "bounties" (whatever those may be..) It was just for the sake of the exercise you shlould maybe give it to the author of qTranslate . :-) despite of the fact that it is not OOP - it is a genious plugin with a lot to learn from in a lot of areas . considering the "when" of this plugin release , it is even amazing. worth the indescribably effort of reading a NON OOP code - even if it is not - OOP . And even if it is not correctly indented (good god !).

5
  • 3
    Maybe there's a solution burried in the depth of this code, but it's close to impossible to figure all ways out, as it's not written in OOP. If you know where exactly the language detection sits (the browser stuff) and how it filters the gettext strings, please let me know.
    – kaiser
    Commented Dec 5, 2011 at 21:24
  • 1
    @kaiser - see edit I for "browser stuff".
    – krembo99
    Commented Jul 8, 2012 at 9:19
  • 1
    @kaiser ..and see edit II for more "browser stuff" - and edit III for how to incorporate the "browser stuff" inside the "browser (stuff)" .
    – krembo99
    Commented Jul 8, 2012 at 9:49
  • Ok, I upvoted it (not downvote to revert here), for your effort. The idea using a cookie, is nice. Anyway, here're some notes: Edit II will throw an error after the require(). You can't use define() on the same string twice. Edit III has some problems with the languages. Selecting en_US would do nothing(?), but de_DE would set it to English ;). To sum it up: I award bounties and give them out to (for example StephenHarris) for a complete and working answer. If you got something like that, please update your answer (cookie-approach interesting) and I may add and reward you a bounty.
    – kaiser
    Commented Jul 8, 2012 at 11:45
  • Please don't leave comments for me. Make edits to your answer, so everyone can read through your ideas. Sidenote: imho external code should always be added as Plugin/MU-Plugin/Theme/DropIn to make it moveable.
    – kaiser
    Commented Jul 8, 2012 at 15:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.