Updates from February, 2015 Toggle Comment Threads | Keyboard Shortcuts

  • Thorsten Frommen 2:53 pm on 2015/02/11 Permalink | Reply

    How to get translations programmatically 

    The most important API in MultilingualPress for you is probably the Language API. This API has a method get_translations() that you can use to get a prepared set of translations for posts of any post type, terms of any taxonomy, a search term, the front page or a blog page.

    You can access that API with a filter:

    $mlp_language_api = apply_filters( 'mlp_language_api', NULL );

    MultilingualPress will transform that NULL value now into an instance of the class Mlp_Language_Api. In other words: The variable $mlp_language_api is an object now. But you should still test that, just in case the user has deactivated MultilingualPress:

    $mlp_language_api = apply_filters( 'mlp_language_api', NULL );
    if ( ! is_a( $mlp_language_api, 'Mlp_Language_Api_Interface' ) )

    As you can see, you should test against the Interface Mlp_Language_Api_Interface, not against the concrete class. This enables other plugins to replace our implementation with a custom translation handler.

    Today, we are looking just at $mlp_language_api->get_translations( $args );

    Arguments for Mlp_Language_Api::get_translations()

    $args is an array, we can pass some options here to tweak the results.

    Name Type Description
    site_id int Base site. Usually the current site.
    content_id int post or term_taxonomy ID, not term ID.
    type string Either post, term, post_type_archive, search or front_page.
    strict bool When TRUE (default) only matching exact translations will be included.
    search_term string If you want to translate a search.
    post_type string For post type archives.
    include_base bool Include the base site in returned list.

    All parameters are optional. MultilingualPress will try to find proper values for them. We recommend to set the content_id for terms and posts though, because that is not always available, at least not in a reliable way.

    Now let’s see how our code could look like:

    $mlp_language_api = apply_filters( 'mlp_language_api', NULL );
    if ( ! is_a( $mlp_language_api, 'Mlp_Language_Api_Interface' ) )
    $args = array (
    	'strict'               => TRUE,
    	'include_base'         => TRUE
    /** @var Mlp_Language_Api_Interface $mlp_language_api */
    $translations = $mlp_language_api->get_translations( $args );
    if ( empty ( $translations ) )

    Note that $mlp_language_api->get_translations( $args ) will return an empty array if there are no translations even when we set include_base to TRUE.

    Now, let’s say the translations are not empty. We get an array of objects, each an instance of Mlp_Translation which implements the Mlp_Translation_Interface. That sounds complicated, but it just means that we have a set of methods on each object to get information about the translation.

    Methods for Mlp_Translation

    Method Return type Description
    get_source_site_id() int The site ID the translation is based on.
    get_target_site_id() int The ID of the site where the translation can be found.
    get_page_type() string Either post, term, post_type_archive, search or front_page.
    get_icon_url() Mlp_Url_Interface An object, an instance of a class implementing the Mlp_Url_Interface. It has a magic method __toString(), so we can cast it to a string and get an escaped URL.
    get_target_title() string The title of the translation, for example the post title or the term name.
    get_target_content_id() int The term_taxonomy_id or the post id. This is empty for other translation types like post type archives or search.
    get_remote_url() string The URL for the translation.
    get_language() Mlp_Language_Interface An object, an instance of a class implementing the Mlp_Language_Interface.

    The Mlp_Translation::get_language() object deserves an explanation. It has three public methods.

    Methods for Mlp_Language

    Method Return type Description
    get_priority() int A number between 0 and 10. See the post about Language negotiation for an explanation.
    is_rtl() bool Whether the translation is in a right-to-left language (like Hebrew) or not.
    get_name( $name ) string Different representations of the language. Default is the language in its native writing, eg. Deutsch for German. We strongly recommend to use that, because that’s most easily to recognize for your readers.
    Other allowed parameters are english to get the English name, http to get the HTTP value (for example de-AT) or custom to get the custom name you have set in the site properties.
    You can also use language_short to get just the first part of a language code with subsets, eg. just de.

    Example: Add translation links to the post content

    Let’s see what we can do with all this code. The following example adds very simple translation links to the post content. It uses the first part of the language code and sets it to uppercase. The images are used too, if they are available.

    add_filter( 'the_content', function( $content ) {
        if ( ! is_singular() )
            return $content;
        $mlp_language_api = apply_filters( 'mlp_language_api', NULL );
        if ( ! is_a( $mlp_language_api, 'Mlp_Language_Api_Interface' ) )
            return $content;
        $args = array (
            'strict'               => TRUE,
            'include_base'         => TRUE
        /** @var Mlp_Language_Api_Interface $mlp_language_api */
        $translations = $mlp_language_api->get_translations( $args );
        if ( empty ( $translations ) )
            return $content;
        $links = array();
        /** @type Mlp_Translation_Interface $translation */
        foreach ( $translations as $translation ) {
            $current = $img = '';
            if ( $translation->get_target_site_id() === get_current_blog_id() )
                $current = ' class="current"';
            $img_url = $translation->get_icon_url();
            if ( '' !== (string) $img_url )
                $img = "<img src='$img_url' alt=''> ";
            $text = $translation->get_language()->get_name( 'language_short' );
            $text = mb_strtoupper( $text, 'UTF-8' );
            $links[] = sprintf(
                '<a href="%1$s" title="%2$s" %3$s>%4$s</a>',
                esc_attr( $translation->get_target_title() ),
                $img . $text
        $links = '<p class="translations">'
            . join( ' <span class="separator">|</span> ', $links )
            . '</p>';
        return $content . $links;

    The result should look like this: Screenshot

    Theme integration

    You can use such a function in other places too, of course. In a theme you should add a custom action wherever you need it and assign a callback handler to that action. This way, your theme will not break when the user deactivates MultilingualPress.

    So in a template file add this line:

    do_action( 'translation_box' );

    And in your functions.php create a callback function and register it for that action:

    add_action( 'translation_box', 'show_mlp_translation' );
    function show_mlp_translation() {
        // find and print translation links

    Any questions or suggestions? Or do you have used this tutorial successfully? Please let me know.

  • Thorsten Frommen 1:17 pm on 2014/10/28 Permalink | Reply  

    How to disable broken save_post callbacks 

    Many plugins are not multisite aware. This is rarely a problem, because each site in a network works almost like a normal site in a single-site installation. But sometimes … things go really wrong.

    There is a hook, the action save_post, that can be called multiple times when a post is saved: one time for each site. Many plugins are not aware of this, they run their own code on every call to save_post without a check for the site context. The result is that they are either deleting user data on the other sites, or they overwrite existing data.

    This happens when MultilingualPress updates the post translations.

    Site A save_post - MultilingualPress {
        creates or updates translations:
        - switch_to_blog( Site B) -> save_post
        - switch_to_blog( Site C) -> save_post
        - switch_to_blog( Site D) -> save_post
        return to Site A

    MultilingualPress removes all POST data before the other save_post actions are called, and it restores it when it switches back. Normal plugins use a nonce as a basic security check before they try to save anything. Since we remove the nonce along with the POST data, they will not do anything. So far, so good.

    Unfortunately, some plugin author don’t use nonces. They just try to save their data, without proper context checks. There is nothing we can do about that.

    But you can. You have to find the code (function or class method) that is registered for the save_post action and then remove it earlier. There is another hook that runs right before save_post: the filter wp_insert_post_data. You can hook your own custom callback to that filter, check if the current context is in a switched site and then remove the “evil” callback. Don’t forget to return the filtered value, that’s necessary for filters.

    Here is an example for the Custom Sidebars plugin:

    add_filter( 'wp_insert_post_data', function( $data ) {
        if ( is_multisite()
            && ms_is_switched()
            && class_exists( 'CustomSidebarsEditor' )
        ) {
            $cse = CustomSidebarsEditor::instance();
            remove_action( 'save_post', array( $cse, 'store_replacements' ) );
        return $data;

    If you are a plugin or theme author and want to be sure that your code is safe to run in a multisite: please contact me. I,or one of my colleagues, will look at it and tell you what needs to be changed.

  • Thorsten Frommen 9:37 am on 2014/10/20 Permalink | Reply  

    A basic plugin for MultilingualPress 

    Plugins for MultilingualPress should wait until MultilingualPress is actually loaded. That means they should use a hook provided by the main plugin to be sure that MultilingualPress is running and ready to be used. There are three main entry hooks:

    1. inpsyde_mlp_init: equals to plugins_loaded, 0 plus MultilingualPress loaded. Happens before scripts and stylesheets are registered. The only parameter is an instance of the class Inpsyde_Property_List which holds useful plugin information.
    2. inpsyde_mlp_loaded: Almost the same, but after the scripts are registered.
    3. mlp_and_wp_loaded: equals to wp_loaded, 0 plus MultilingualPress loaded. The first parameter is again the same Inpsyde_Property_List. When in doubt use this action.
      wp_loaded happens after WordPress has checked if the current blog was marked as spam. Unless you want to run your code on spam blogs, wait for this action.

    When we use type hints in our callback function, we should declare the dependency for the first parameter against the interface Inpsyde_Property_List_Interface, not against a concrete class.

    First example

    (All code examples here require at least PHP 5.3.)

        function( Inpsyde_Property_List_Interface $mlp_data ) {
        // your code here

    The class Inpsyde_Property_List

    This class is very similar to a stdClass from the PHP core: just some key-value pairs. There are two important differences: an Inpsyde_Property_List can be made immutable, and it can have a parent class.

    To make an instance of this class immutable, we call its method freeze(). Once that is done, there is no way to change any data of this class. Any attempt to do that will return an instance of WP_Error. We can test that with the method is_frozen() which returns true or false, depending on the current state.
    The instance we get from MultilingualPress is always frozen. We can rely on its data, because no matter how many plugins access it, they cannot change it.

    What should we do if we want an instance with almost the same data, but some of them with different values? We create a new instance and use the existing one as its parent. Now we can change the values for our internal use and use the other values as if they were properties of our new instance.

    Usage example for Inpsyde_Property_List

        function( Inpsyde_Property_List_Interface $mlp_data ) {
            $ours = new Inpsyde_Property_List;
            $ours->set_parent( $mlp_data );
            $ours->plugin_url = plugins_url( '/', __FILE__ );
            $ours->css_url    = "{$ours->plugin_url}css/";
            $ours->js_url     = "{$ours->plugin_url}js/";
            $ours->image_url  = "{$ours->plugin_url}images/";

    This approach avoids common problems with classic inheritance, like the fragile base class and statically linked dependencies. We could call it delayed inheritance. The concept is explained in Steve Yegge’s article The Universal Design Pattern.

    We will look at the various default properties from MultilingualPress in a separate article. For custom code, we need just one important property:

    The autoloader

    The property loader holds an instance of the class Inpsyde_Autoload, a very simple Collection Pipeline for instances of the Inpsyde_Autoload_Rule_Interface which handle the actual class loading. Sounds complicated, but it is dead simple.

    Let’s say our plugin is organized like this:

    - plugin.php // the main file
    - css/ // stylesheets
    - js/ // scripts
    - php/ // PHP classes
        - Plugin_Name_Controller.php
        - Plugin_Name_Model.php
        - Plugin_Name_View.php

    Now we want set up an autoloader to get our classes when we need them. There is one existing class for that, we can reuse it to create a new auto-load rule: Inpsyde_Directory_Load. It takes a path to a directory, and we pass its instance to the Inpsyde_Autoload instance provided by $mlp_data->loader.

    Usage example for Inpsyde_Autoload

        function( Inpsyde_Property_List_Interface $mlp_data ) {
            $load_rule = new Inpsyde_Directory_Load( __DIR__ . '/php' );
            $mlp_data->loader->add_rule( $load_rule );
            // We can just use our classes now, no 'require' needed.
            $model      = new Plugin_Name_Model( 'foo' );
            $view       = new Plugin_Name_View( $model );
            $controller = new Plugin_Name_Controller( $model, $view );

    All we have to take care of is that the class names match the file names. This works for interfaces and traits too. Inpsyde_Directory_Load works with flat directories only, it doesn’t search in child directories. It is very fast, because the complete directory is read just once. There is no separate file_exists() check for every class name.

    And that’s all we need for a start: Hook into mlp_and_wp_loaded, register the directory for the auto-loader with the help of the property list – and write awesome code!

  • Thorsten Frommen 11:03 am on 2014/10/17 Permalink | Reply  

    How to add icons to the language nav menu items 

    By default, our language navigation menus show the language name in its native spelling. This intended: We want to discourage the use of flags for languages. But we do offer the option to use a custom image for each site, and if you want to use that, you can add it to the link text.

    The hook for that is mlp_prepare_nav_menu_item_output. You get two variables as input:

    1. $item: The nav menu item, an instance of the class WP_Post with some extra properties.
    2. $translation: An instance of an implementation of the Mlp_Translation_Interface. If we couldn’t find a translation for some reason, this variable is NULL, so you have to test it before you work with it.

    The $translation has a method get_icon_url() which is again an object: an instance of the Mlp_Url_Interface. It can be casted to a string to get the URL. Another way is calling the method __toString() directly. If there is an icon, the escaped URL is returned. You get an empty string otherwise.

    Here is how you can use it:

     * Adds an icon to the menu text
     * $item is passed as an object handle, so this function can change it directly
     * and doesn't have to return anything.
     * We still return a string value to make unit tests easier.
     * @link    http://make.multilingualpress.pro/?p=167
     * @wp-hook mlp_prepare_nav_menu_item_output
     * @param   WP_Post                        $item
     * @param   Mlp_Translation_Interface|NULL $translation Might be NULL
     *                       if there is no translation for the item or
     *                       the other site is not linked to the current
     *                       site.
     * @return  string  When and why we stopped.
    function mlp_add_icons_to_menu( WP_Post $item, $translation = NULL ) {
    	if ( ! is_object( $translation ) )
    		return 'no translation object';
    	if ( ! class_implements( $translation, 'Mlp_Translation_Interface' ) )
    		return 'invalid translation object';
    	if ( FALSE !== strpos( $item->title, '<img' ) )
    		return 'icon already present';
    	/* $translation->get_icon_url() returns an instance of
    	 * Mlp_Url_Interface, so we have to cast it to a string
    	 * before we check it with empty().
    	 * @see Mlp_Url_Interface
    	$icon_url = (string) $translation->get_icon_url();
    	if ( empty ( $icon_url ) )
    		return 'no icon available';
    	$item->title = "<img src='$icon_url' alt=''> $item->title";
    	return 'success';
    	'mlp_prepare_nav_menu_item_output', // hook
    	'mlp_add_icons_to_menu',            // callback
    	10,                                 // priority
    	2                                   // number of accepted arguments

    Download as plugin: MultilingualPress Addon: Nav Menu Icons. Activate it as a network plugin, and it will just work without further configuration.
    You might have to adjust the image position in your stylesheet. Example:

    .mlp-language-nav-item img {
        vertical-align: middle;

    Here is a screenshot of such a changed menu with our built-in flags. Again: please do not use these flags. :)

    Navigation menu with icons

    Always add an alt attribute, but leave its value empty if you still have text, so users with screen readers don’t have to waste their time with it. The item text is good enough.

    • Jeroen 9:07 am on 2014/11/11 Permalink | Reply

      Hi Thomas. I posted a question on the plugin support page of MLP. The question was about adding a language button in my menu. They directed me to this post you wrote. I succeeded in installing the plugin. But where in my WordPress dashboard can I edit something to make this work? And what editing is needed? Some help would be awesome ;-).

      • Jeroen 9:14 am on 2014/11/11 Permalink | Reply

        I’m so sorry. I just now see that network activate already did the job! It picks the flag-url from the site settings and adds the image to the top menu without needing to edit anything. Awesome add-on!!!
        Thank you so very much :-)

        • Thomas Scholz 10:17 am on 2014/11/11 Permalink | Reply

          Thanks for the feedback. The activation of the plugin deserved indeed a short explanation. I have added one, plus a suggestion for the theme’s stylesheet.

    • Gus 10:09 pm on 2014/11/20 Permalink | Reply


      I am trying to insert the language selector plugin in a template monster header but it seems impossible…

      • Thomas Scholz 1:23 am on 2014/11/21 Permalink | Reply

        Please use our support forum to explain the problem, add your ode and the error messages that you get. I’m sure that we can find a way to solve it. :)

    • roberto 8:51 pm on 2015/02/10 Permalink | Reply

      Hi, i’ve tried many things but i’m getting confused. Please could you help me???. what I’m trying to do is to move both language buttons (with their icons) away from the “Primary Menu”, i would like to put it in the header or somewhere else.

      Here is the URL of my web project: http://vidaurrereisen.com/plasticwebs/

      Sorry if my english is a little poor, please try to explain it as simple as you can.


  • Thorsten Frommen 11:03 pm on 2014/07/14 Permalink | Reply  

    How to get a post from another site in the network 

    Let’s say we have a site id and a post id. The source doesn’t matter, it could be a database, user input, whatever. Now we want to get a WP_Post object from that site.

    A first idea might look like this:

    switch_to_blog( $site_id );
    $post = get_post( $post_id );

    But this is not necessary, there is already a function in WordPress for that:

    function get_blog_post( $blog_id, $post_id ) {
        switch_to_blog( $blog_id );
        $post = get_post( $post_id );
        return $post;

    Nice, but there is an important, subtle bug in that function: it is using get_post( $post_id ) without a check on the post id. If $post_id is 0, NULL or "", get_post() will use an existing global post object or id. But the current global post is from the source site, not from the target site! The id on site 1 references a completely different post than on site 2.
    WordPress doesn’t care. So we have to do that check:

    $post = NULL;
    if ( ! empty ( $post_id ) )
        $post = get_blog_post( $site_id, $post_id );
  • Thorsten Frommen 6:17 am on 2014/04/02 Permalink | Reply
    Tags: ,   

    Create a custom language switcher 

    We offer a widget and the “quick links” to show links to translations, but many users need a custom solution at other places or in a formatting we just don’t give them (yet).

    There is a handy helper function to get all the links as an array: mlp_get_interlinked_permalinks(). It returns an array with the text (human readable language name), the permalink to the other site’s translation and an entry lang (the language code) – for each single translation.

    You can use that to create your own language switcher. The following function creates a string that might look like this:

    EN | DE | RU

    Add it to your theme’s functions.php.

     * Create a navigation between translations
     * @param  string $between  Separator between items
     * @param  string $before   HTML before items
     * @param  string $after    HTML after items
     * @return string
    function mlp_navigation(
        $between = ' | ',
        $before  = '<p class="mlp-lang-nav">',
        $after   = '</p>'
        $links = (array) mlp_get_interlinked_permalinks();
        if ( empty ( $links ) )
            return '';
        $items = array ();
        foreach ( $links as $link ) {
            if ( isset ( $link['text'] ) ) {
                $text = $link['text'];
            else {
                // take just the main code
                $first = strtok( $link['lang'], '_' );
                $text  = mb_strtoupper( $first );
            $items[] = sprintf(
                '<a href="%1$s" hreflang="%2$s" rel="alternate">%3$s</a>',
                esc_url( $link['permalink'] ),
                esc_attr( $link['lang'] ),
        return $before . join( $between, $items ) . $after;

    Now you can call the function wherever you need it without any arguments:

    echo mlp_navigation();

    Don’t forget the closing semicolon!

    If you want to get a list, you can change the parameters:

    echo mlp_navigation(
        '</li><li>', // between items
        '<ul class="mlp-lang-nav"><li>', // before
        '</li></ul>' // after

    We will add support for custom menus in one of the next versions of Multilingual Press, so you can create your own menus with drag and drop.

    • Piet 4:31 am on 2014/07/02 Permalink | Reply

      Thanks for the tutorial, I will try to implement this. I noticed though that although Posts and Pages are perfectly linked together across languages, Categories are not. If I am looking at a Category Archive the language switcher links to the homepage of the resp. languages. Is there a way to fix this?

      • Thomas Scholz 5:47 am on 2014/07/02 Permalink | Reply

        This part is not completed – yet. We will add taxonomy support in version 2.1.

    • Jorge Díaz 11:39 pm on 2014/09/26 Permalink | Reply

      Hi, I think that the answer to this question is going to be pretty obvios, but when you want to print the code in a page you have to write the code using if that is correct I used it but I don’t know what is happening because is not showing the language switcher.

      • Thomas Scholz 12:00 am on 2014/09/27 Permalink | Reply

        Is there something missing in your comment? I’m not sure what exactly you are asking. :)

        • Jorge Díaz 1:20 am on 2014/09/28 Permalink | Reply

          Hi, thanks for your answer, actually yes the PHP tags are missing, but I finally did it, now I have other question. Multilingual Press works with multisite, I have multisite setup and 2 sites the second is cloned from the first one so I have the same data in both (pages and post) Priority of first language (site for first site) is 10 and second language(site) is 9. But how do I create the relationships between the pages from the first language to the pages in second language, because when I click the check button it creates a page in the same site as draft, so I don’t know what I am missing.

          • Thomas Scholz 1:46 am on 2014/09/28 Permalink | Reply

            Use the button Change Relationship to find the other post or page. It is right below the editor for the translation in MultilingualPress Pro. See our docs for screenshots.

            • Jorge Díaz 2:25 am on 2014/09/28 Permalink

              Hi, thanks for you answer, I am using the free version, I think that custom relationships are not available for the free version, now I managed to create a widget are as you can see here http://prntscr.com/4qzg2n it looks like the same widget from social icon, now my question is, is it possible at least to leave the widget as it is (because if I uncheck the “trasnlate this post option” it disappear) but linking to home page, because now it links to a page that doesn’t exist. Sorry to bother you. :(

            • Thomas Scholz 2:05 pm on 2014/09/28 Permalink

              If it links to the wrong page, it’s a bug. You should uncheck the option Show widget for translated content only. in the widget settings to get it on all pages.

              I have extended our language API in version 2.1, so you can create a much more granular language switcher now. I hope to find the time for a post about that next week.

            • Jorge Díaz 7:21 pm on 2014/09/28 Permalink

              Actually that option is unchecked :(

            • Jorge Díaz 7:29 pm on 2014/09/28 Permalink

              I have unchecked “show current site” and “show widget for translated content only” and now it appears just the flag for the other site in both site, but it’s linking to a page that don’t exist :S for example if the page is the home (domain.com) it’s linking to domain.com/lang/home-2/ and if I go to domain.com/about it links to domain.com/lang/about-2 so I don’t really understand what is happening.

  • Thorsten Frommen 12:04 pm on 2014/02/20 Permalink | Reply
    Tags: .htaccess, installation, wp-config.php   

    How to install multi-site 

    This is meant as a very short form of the overly long Codex post.

    Start with a clean regular single-site installation in the server document root. Do not install WordPress in a subdirectory, this will break.

    Be an admin.

    Make sure your salts are complete in your wp-config.php.

    Give PHP at least 128MB memory limit. More is better, less might break your site. Make sure your PHP version is up to date: version 5.6 is good, 5.4 is OK for a few months, 5.3 is too old, and 5.2 is hacked the moment you go online.

    Add the constant that enables multi-site:

    define( 'WP_ALLOW_MULTISITE', TRUE );

    Make sure that require_once(ABSPATH . 'wp-settings.php'); is always the last line in that file, put custom code above of it.

    Then go to Tools/Network Setup, use Sub-directories or Sub-domains if your are prepared for that (see Multisite on Windows with wildcard subdomains).

    Now you get the new code for wp-config.php (remove WP_ALLOW_MULTISITE) …

    define( 'MULTISITE',            true       );
    // Set this to true for sub-domain installations.
    define( 'SUBDOMAIN_INSTALL',    false      );
    define( 'DOMAIN_CURRENT_SITE',  'mstest.wp');
    define( 'PATH_CURRENT_SITE',    '/'        );
    define( 'SITE_ID_CURRENT_SITE', 1          );
    define( 'BLOG_ID_CURRENT_SITE', 1          );

    … and .htaccess, replace the default WordPress rules with this:

    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    # add a trailing slash to /wp-admin
    RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
    RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
    RewriteRule . index.php [L]

    Log in again, and you are done.

    • Daniele Federico 7:31 pm on 2014/09/28 Permalink | Reply

      I purchased Hamburg Theme and using it, then I got MultilingualPress PRO. I do not have coding knowledge and this is my very first Ecommerce (that’s why I trusted MarketPress a solid tool). But this Instal tutorial is quite difficult for me. Can you provide me please with a beginner guide?
      It would be very appreciated.

      Ex.: should I set a child theme before starting all of this?
      What should I make sure with my hosting provider before starting this? Some hosting requirements..

      Thank you!

      • Thomas Scholz 7:49 pm on 2014/09/28 Permalink | Reply

        Hi Daniele,
        all necessary requirements are listed in this guide. There is really nothing else. I am not aware of a simpler guide. In fact, most guides I have seen are incomplete or even more confusing, that’s why I wrote this one. :)
        If you have any further questions, please use our support forum.

    • Alejandro 2:36 pm on 2014/09/29 Permalink | Reply

      Hi Thomas

      Since you are a developer, you easily understand the description above (which is a copy of the codex of wordpress)

      Daniele on the other hand IS NOT a developer, it means, he does not know a code editing app, or how it looks inside.

      He needs a more detailed information, like: go into the server to the folder … then download the config.php file and open it with an editor, then look for the line…..

      Daniele needs an actual walk-thru all the step necessary, the explanation above is far from that.

      If you have limited time to attend this types of customers, then send him to your support team, but your answer is for more advanced users, Daniele already bought a theme and plugin from you guys.

      I believe, as a paying customer Daniele should get things working to be able to use the things one has paid for.

      I am evaluating the possibility of purchasing from you guys but now I have my doubts

      Kind regards

      • Thomas Scholz 2:42 pm on 2014/09/29 Permalink | Reply

        I talked about that in our team meeting today, and we planned a tutorial for beginners, with screenshots and the details I can’t see anymore, because I’m too close. :)

        There is also this tutorial on our blog already.

      • Daniele Federico 5:56 pm on 2014/10/27 Permalink | Reply

        Hi Alejandro,
        thanks for you message, you totally got my point.
        Ad a beginner I donno how much I can ask. Obviously this tutorial by Thomas is not enough for me and I’m struggling to understand if this is something I can do by myself and with a step-by-step guide or it’s something requiring me to study coding.
        Hopefully market press guys will help me more, having spent quite a lot of money on their products.


        • Thomas Scholz 9:26 am on 2014/10/28 Permalink | Reply

          As you can see, we did write a tutorial especially for beginners. If you have any further questions that are specific to MultilingualPress, please use our support forum.

    • jf 6:23 pm on 2014/10/26 Permalink | Reply

      Hi Thomas!

      Please add that the wp-config.php code has to be the first block, before erverything else. Otherwise nothing happens!

      Kind regards

      • Thomas Scholz 8:47 pm on 2014/10/26 Permalink | Reply

        Thanks for that, I have added an explanation now. It doesn’t have to be the first, but it should be set above of the require statement.

    • Tim 7:05 pm on 2014/11/27 Permalink | Reply

      Could you please do this for me through Team Viewer? I really don’t know how to do this… it would mean the world to me please!

      • Thomas Scholz 7:17 pm on 2014/11/27 Permalink | Reply

        That’s a little out of this blog’s scope, and I really don’t have time for that currently. :)

        But I like your idea. A video that shows a multisite installation and explains the most important settings should be possible. I will talk with our team about that.

    • paris 4:29 pm on 2015/08/10 Permalink | Reply

      I come here from wordpress admin editor. But don’t understand any things! this blog is not usefull for beginner so it will be nice to remouve the link from wordpress admin editor!
      best regards

      • Thomas Scholz 9:20 am on 2015/08/11 Permalink | Reply

        This blog is for users who want to dive into the code a little bit. We want to keep access to this resource as easy as possible.

    • Joseph 5:17 pm on 2015/09/21 Permalink | Reply

      After I run this I cannot add new plugins.
      Also the plugins I upload through FTP does not show in the plugins list ?

      • Thomas Scholz 8:19 pm on 2015/09/21 Permalink | Reply

        Plugins are installed on the network page /wp-admin/network/plugin-install.php in multisite. You still can activate them per site or for the whole network.

    • Ed 9:37 am on 2015/10/08 Permalink | Reply

      Hi. I have an issue with Sub-directories install. I’ve done all steps and tried to log in. There’s nothing happens, log in page appears again and that’s all.

      • Ed 10:15 am on 2015/10/08 Permalink | Reply

        The problem was solved after i’ve cleared cookies and cache. Add this to the instruction please.

    • Samathey 5:28 am on 2015/11/05 Permalink | Reply

      Hi, That’s right I have just bought the multilingual Press 78 €, I thought it will be like others plugin just click and go ! But in fact there are somme things to do first… multi-site to enable and so on….Since the begining of the conversation 2014, have you write something easier for beginners ? For instance I don’t know how to find and to modify a php file. Thanks Sam

    • Samathey 2:52 am on 2015/11/06 Permalink | Reply

      Yes in fact when I had understood how to open the two files wp-config.php and .htaccess and upgrade them on the serveur it was ok.(filezilla to transefrt and notepad++ to write into the files, I write this for beginers like me) My wordpress is now multi-site. (I just try now to activate the plugin…)

    • Luci 8:01 pm on 2015/11/07 Permalink | Reply

      Hello thanks do you have the nginx config please ?

    • ihtxam 4:17 pm on 2016/05/17 Permalink | Reply


      i am dummy for programming, could you please tell where to put

      ”Add the constant that enables multi-site:

      define( ‘WP_ALLOW_MULTISITE’, TRUE );
      Make sure that require_once(ABSPATH . ‘wp-settings.php’); is always the last line in that file, put custom code above of it.”

      in which file, i have to put this code. Sorry for my stupid question. reply soon.

      • Thorsten Frommen 4:24 pm on 2016/05/17 Permalink | Reply

        This goes into your wp-config.php file.

Compose new post
Next post/Next comment
Previous post/Previous comment
Show/Hide comments
Go to top
Go to login
Show/Hide help
shift + esc