Subscribe: RSS
  • Multilingual Websites with MODx

    After reading the solutions in this forum, none of them convinced me. I wanted that the user stays on the same page if he switches the language.

    But i didn´t want to assign the relation between the languages via a template variable where i have to put the corresponding ID of the actual page for every language.
    It should function automatic.

    So i found a solution for myself. It performs well, but surely has potential for improvements.
    Comments and improvements are welcome. Hope my solution helps someone.
    Update 02/17/2009
    I was asked how to redirect different TLDs to the corresponding language, so updated my code.
    Update 02/17/2009
    German version available at: http://www.modxcms.de/forum/comments.php?DiscussionID=1771
    Update 01/28/2009
    Now the connection between languages is made with a template variable called "languageIdentifier". You only need to write a unique name in this field once and then it will be duplicated when you duplicate your language folder.
    End Update

    Here we go:
    1. Modify your configuration under Tools/Configuration

    Use the following settings:
    friendly URLs: yes
    Use friendly aliases: yes
    Use friendly alias path: yes

    Create a new template variable with name "languageIdentifier"

    2. build Site Tree
    First make a new page and in the page settings tab click "container" and name the page with your main language as the title. For example „english“, „deutsch“, „français“.
    This is what a visitor sees to select his language.
    The alias for this folder is a two-letter ISO Country Code that you can find here: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm

    Now you create your pages as you would normally. Enter a languageIdentifier for every page. It could be the same as your alias for example.
    After that you duplicate the whole folder for your next language and change the title and the country code in the root folder of that language.

    Example:
    english / alias: gb / type: folder (1)
     |-> Welcome  (2)
    deutsch / alias: de / type: folder (3)
     |-> Willkommen (4)


    Be sure to check the option ’Show in menu’ and set the status to ’published’ for the folders!

    Now under configuration "set Site start": enter the id of the first page of your main language (2 in this example).
    You can also set the Site start to the id of the root folder (for example 1) and then use the [[FirstChildRedirect]] snippet on this page.

    3. create snippets
    Snippet Name: „language“.
    Description: creates placeholders and stores the actual language
    <?php
    $debugMsg = '$language is: '.$language.'<br />';
    $debugMsg = 'LanguageID from cookie: '.$_COOKIE['LanguageID'].'<br />';
    $params['topLevel'] =1;
    
        // Regular Page
        // find Ultimate Parent of this page. The alias of this page sets the language variable
        if ($modx->config['error_page'] != $modx->documentIdentifier){
            $up = $modx->runSnippet("UltimateParent", $params);
            $tv = $modx->getTemplateVar('alias', "", $up);
            $language = $tv['value'];
            setcookie("Language",$language,time()+604800, "/", "", 0);
            // LanguageID is needed for the 404 error page, to show the sitemap in the actual language
            setcookie("LanguageID",$up,time()+604800, "/", "", 0);
            $modx->setPlaceholder('LanguageID',$up);
    $modx->setPlaceholder('language',$language);
        }
        else{
            // If Error Page
            $debugMsg .= '<p>Error Page Start<br />';
    
            if ( !empty ($_COOKIE['LanguageID'])){
                $debugMsg .='$language is empty, try to read from cookie';
                $language = $_COOKIE['Language'];
                $LanguageID = $_COOKIE['LanguageID'];
                $params['id'] = $LanguageID;
                $up = $modx->runSnippet("UltimateParent", $params);
                //setcookie("LanguageID",$up,time()+604800, "/", "", 0);
                $modx->setPlaceholder('LanguageID',$up);
            }
            else {
                $debugMsg .= '<p>LanguageID Cookie is empty, get ID from Site start</p>'.$modx->config['site_start'];
                $params['id'] = $modx->config['site_start'];
                $up = $modx->runSnippet("UltimateParent", $params);
                //setcookie("LanguageID",$up,time()+604800, "/", "", 0);
                $modx->setPlaceholder('LanguageID',$up);
            }
        }
    
    $debugMsg .= 'LanguageID at finish: [+LanguageID+]<br />';
    
    $debugMsg .= '$language: '.$language.'<br />';
    $modx->setPlaceholder('debugMsg', $debugMsg);
    //echo $debugMsg;
    $modx->setPlaceholder('language', $language);
    if (file_exists("assets/languages/$language.php")) include_once "assets/languages/$language.php";
    ?>


    Snippet Name: „LanguageChooser“
    Description: „shortcut for the wayfinder“
    <?php
    $langMenu = $modx->runSnippet(
            "Wayfinder",
            array(
                "startId" => "0",
    "level"=>"1",
    "textOfLinks"=>"alias",
    "rowTpl"=>'flagAndLanguage',
    "outerTpl"=>'LanguageChooser'
            )
    );
    echo $langMenu;
    ?>


    Snippet name "link_to_language"
    Description: "gets link to actual page in other languages"
    <?php
    if (! function_exists('getdocs_with_same_values')){
    function getdocs_with_same_values($id = 'default', $field = 'description') 
    { 
    global $modx; 
    // Initialize ids 
    $ids = array (); 
    if ($id == 'default') 
    { 
    $id = $modx->documentIdentifier; 
    } 
    $table = $modx->getFullTableName('site_tmplvars'); 
    $result = $modx->db->select('id', $table, ' name = "'.$field.'"'); 
    $array_result = $modx->db->makeArray($result); 
    if (count($array_result) > 0) 
    { 
    $tvid = $array_result[0]['id']; 
    $table = $modx->getFullTableName('site_tmplvar_contentvalues'); 
    $result = $modx->db->select('*', $table, 'contentid = "'.$id.'" and tmplvarid = "'.$tvid.'"'); 
    $array_result = $modx->db->makeArray($result); 
    $fieldvalue = $array_result[0]['value']; 
    $result = $modx->db->select('contentid', $table, 'value = "'.$fieldvalue.'"'); 
    $array_result = $modx->db->makeArray($result); 
    foreach ($array_result as $value) 
    { 
    $ids[] = $value['contentid']; 
    } 
    } 
    else 
    { 
    $table = $modx->getFullTableName('site_content'); 
    $result = $modx->db->select($field, $table, ' id = "'.$id.'"'); 
    $array_result = $modx->db->makeArray($result); 
    $fieldvalue = $array_result[0][$field]; 
    $table = $modx->getFullTableName('site_content'); 
    $result = $modx->db->select($field, $table, ' id = "'.$id.'"'); 
    $array_result = $modx->db->makeArray($result); 
    $fieldvalue = $array_result[0][$field]; 
    $result = $modx->db->select('id', $table, $field.' = "'.$fieldvalue.'"'); 
    $array_result = $modx->db->makeArray($result); 
    foreach ($array_result as $value) 
    { 
    $ids[] = $value['id']; 
    } 
    } 
    return $ids; 
    }
    }
    
    $pages = getdocs_with_same_values('default','languageIdentifier');
    
    $langFolder = $modx->getDocumentObject('id', $id);
    $langFolderID = $langFolder['id'];
    $langFolderAlias = $langFolder['alias'];
    
    // set theURL to the root of the language
    $theURL = $modx->makeURL($id,'','','full');
    
    foreach ($pages as $page){
    //echo $page;
    // Now check if the id of the $value is a child of the UltimateParent
            $UltimateParent = $modx->runSnippet('UltimateParent', array('id' => $page));
    //echo "\n".$UltimateParent." ".$langFolderID;  
            //$checks[$counter]['id'] = $value['id'];
            
            // if the UltimateParent is equal to to folderID make link to the actual page in the other language
            if ($UltimateParent == $langFolderID) {
                $theURL = $modx->makeURL($page,'','','full');
            }
    }
    
    return $theURL;
    ?>



    create Chunks
    Chunk Name: „flagAndLanguage“
    Description: „prints a flag and the name of the language as a list item“
    Content:
    <li class="[+wf.classnames+]"><a href="[[link_to_language?id=`[+wf.docid+]`&link=`[+wf.link+]`&actualid=`[*id*]`]]"><img src="[(base_url)]assets/images/flags/gif/[+wf.linktext+].gif" alt="[+wf.linktext+]" /> [+wf.title+]</a></li>


    Chunk Name: "LanguageChooser"
    Description: "Outer template for the Language chooser"
    Content:
    <ul [+wf.classes+] id="sprachwahl">
    	[+wf.wrapper+]
    	</ul>
    


    4. modify Site-Template
    Now you insert the follwing code in your template:
    in the first row of your template insert the placeholder "[[language]]"
    <div id="languageselection">
    [[LanguageChooser]]
    </div>
    <div id="navigation">
    [!Wayfinder? &startId=`[+LanguageID+]`!]
    </div>
    

    This includes the languageChooser and a php-file "assets/languages/currentlanguage.php" (currentlanguage is the two-letter ISO Country Code) . And you get a navigation with only the pages in the current language.

    Add the placeholder [+language+] where you need it. For example if you have different images in your languages.
    Add the placeholder "[+debugMsg+]" for tracing the language snippet

    5. Download Icons

    This is an optional step. However, if you skip this step, you will need to change the "flagAndLanguage" snippet below.


    6. Forms
    Open the language file under assets/snippets/eform/lang and add your variables to it

    	$_lang['yourname'] = "Your Name";
    

    You can add as many variables as you need.

    Now you have to assign those variables to the fields array:
    Create a new snippet „setPrompts“
    <?php
    function setPrompts(&$fields) {
    global $_lang;
    $fields['yourname'] = $_lang['yourname'];
    return true;
    }
    ?>

    When you create your form chunk you have access to all the placeholders that you defined in the setPrompts snippet:

    [+validationmessage+]
    <form method="post" action="[~[*id*]~]">
        <input type="hidden" name="formid" value="feedbackForm" />
        <label for="cf[+yourname+]">[+yourname+]: </label>
    <input name="Your Name" id="cf[+yourname+]" class="required text" type="text" eform="[+yourname+]::1:" />
    

    Now insert your form on your contact page with the following code:
    [!setPrompts!]
    
    [!eForm? &formid=`feedbackForm` &to=`my@email.com`&eFormOnBeforeFormMerge=`setPrompts` &language=`german` &subject=`Web site feedback`!]
    


    Most important are the parameters „&language = `german`“ (name needs to be the same as in the assets/snippets/eform/lang folder) and „eFormOnBeforeFormMerge=`setPrompts`“ !

    Multilingual 404 Error Page
    Create a new folder in MODx that is unpublished.
    Then create and publish a new page with the title "[+errorpage+]" in it.
    Alias: "404"
    [[language]]
    [+errorpage+]
    [!Wayfinder? &startId=`[+LanguageID+]` &showDescription=`1`!]
    

    Now in your assets/languages/en.php (enter your 2 letter-language-code), add the following:
    <?php
    $modx->setPlaceholder("errorpage",'404 - Page not found');
    ?>

    Translate this message for all of your languages and insert it in the language files.

    Your 404 error page is done and provides a sitemap in the current language.

    Thats it. Please comment for suggestions.

    Redirect TLD to specific language
    Quote from: c-m at Jul 13, 2009, 08:10 AM

    Hi sorry to bother you. But I was wondering how your website redirects to different TLDs as I need to do this for a site at www.naturediet.co.uk
    So you want different TLDs, huh? There you go smiley
    $e = &$modx->event;
    $domains[] = array('url'=>'www.mydomain.de','id'=>72);
    $domains[] = array('url'=>'www.mydomain.ch','id'=>920);
    $domains[] = array('url'=>'www.mydomain.nl','id'=>242);
    $domains[] = array('url'=>'www.mydomain.fr','id'=>494);
    $umleitung = false;
    $host = $_SERVER['HTTP_HOST'];
    
    if($e->name=='OnWebPageInit') {
    	foreach ($domains as $domain){
    	// Wenn beim ersten Aufruf der Seite (site_start) der host == URL ist
    		if($host == $domain['url'] && $modx->documentIdentifier == $modx->config['site_start']){
    			$modx->sendRedirect($modx->makeUrl($domain['id'], '', '', 'full'));
    			exit;
    		}
    	}
    }


    In the array $domains enter your domainnames and the id of the corresponding parent folder and you have it.
    • all works! thx a lot, it’s a very usefull tip! but for me don’t work a last one thing: setPrompts

      do as u wrote... but maybe i’d not fully understand it... plz, if u can, write that steps im more detail, because i’m new to modx...

      Thanks a lot!
      • Hi folks. I will post updated code for multilingual forms later this day. I modified a lot of things for easier handling.
        • Looks like a great solution! Thanks.

          Will try it out and report back with any issues.
          • Updated version is online. Added support for a multilingual 404 page
            • How do I configure language selection for site root? For example, if I am at mysite/en root then the language menu points to: mysite/en/en, mysite/en/sk ... for other languages it is similar: mysite/sk/en, mysite/sk/sk ... How to fix this?

              thanks
              • Quote from: Stiivi at Nov 09, 2008, 06:55 PM

                How do I configure language selection for site root? For example, if I am at mysite/en root then the language menu points to: mysite/en/en, mysite/en/sk ... for other languages it is similar: mysite/sk/en, mysite/sk/sk ... How to fix this?

                thanks


                I think you created your language folders below your english folder. All language folders need to be in the site-root folder.
                Just like this:
                SiteRoot
                |-> english / alias: en / Type: folder
                |-> Welcome /alias: home (2)
                |-> deutsch / alias: de / Type: folder
                |-> Willkommen /alias: home (3)
                • yes, I have the structure like that. problem is, that I am using root/language as language home, instead of root/language/home. I had that structure before I tried to introduce the multilingual code. If you have root/language/home then the code works just fine, because parent of home is language, however for root/language the ultimate parent is the same: language.

                  how do I make root/language to redirect to root/language/home?
                  • Quote from: Stiivi at Nov 10, 2008, 08:28 AM

                    yes, I have the structure like that. problem is, that I am using root/language as language home, instead of root/language/home. I had that structure before I tried to introduce the multilingual code. If you have root/language/home then the code works just fine, because parent of home is language, however for root/language the ultimate parent is the same: language.

                    how do I make root/language to redirect to root/language/home?

                    Hi Stiivi, just try my updated code. I tried it as you explained it and it worked.
                    I hope it works for you too.

                    Greetings
                    Aceman
                    • hello, first of all thanks for giving us another solution for multilanguage sites.
                      Actually i need to redo a Typo3 website in Modx and the multilanguage part is my tricky part so i just try your new solution.

                      my structure is like yours but i need one sublevel more.. like :

                      |-> English / alias: en / Type: folder
                      |-> Welcome /alias: home (2)
                      |-> Formations /alias: formations(3)
                      |-> A /alias: A (4)
                      |-> B /alias: B (5)
                      |-> C /alias: C (6)

                      when i m in page A B or C the language link is http:/.... /fr/formations but should be http:/.... /fr/formations/A.HTML

                      something I’m doing wrong ?

                      I’m not a coder but i was thinking :
                      can’t we use the makeUrl function to travel the tree and just replace the /lang/ with your $language?



                      $docID =  isset ($id) && intval($id) ? intval($id) : $modx->documentIdentifier;
                      $myUrl = $modx->makeUrl($docID);
                      echo('URL :'.$myUrl ); 



                        CTRL+SHIFT+U - Clear Cache
                        CTRL+SHIFT+H - Hiding Heft Panel
                        CTRL+SHIFT+N - Fast Create Resource
                        CTRL+ALT+P - Preview Recource (in edit resorce window)
                        CTRL+ALT+S - Save