Another method of Multilingual CMSMS Page

In my last Project i was in need to create a CMSMS based Website with 5 languages and as we already know MLE Fork is no longer supported or better said it is declared as dead.
To be able delivering a functional Website that didn't bring any risk of not being able to update it if needed i decided not to use MLE Fork and go with the Core version, until there isn't a better solution for this.
There are already few ML Methods that you can find in CMSMS Forums like Rolf's or Alinome's and also a Module called Babel.

Unfortunately none of the Methods was real answer to my Problem, Rolf's Method links Language pages only to "Home" page, Alinome's Method requires Content Block for page linking and Babel has not been updated for over a year and not ideal for URL structure that was required by Client.

So let's start with another Method

As first step we need a logical Page structure, so we are able adding pages for each Language. The strucutre is tree based and compared to MLE Fork i prefer this structure as it gives us more power over URL structure and SEO.

  • Home
  • English
    • Home
    • English page two
    • English page three
  • Deutsch
    • Startseite
    • Deutsch page two
    • Deutsch page three
  • Francaise
    • Accueil
    • Francaise page two
    • Francaise page three

As you can see we have "Home" as root page, this page doesn't do much but rediercts Visitor to appropriate Page, "English", "Deutsch" and "Francaise" are simple "Internal Page Link" with alias like "en", "de" and "fr" which we will use later on for Language structure.

Detecting Browser Language and Redirecting

After we have our strucutre set we need to take care of redirecting our Website Visitors to appropriate page. This method was taken from Rolf's post or better said as RonnyK suggested.
We need a UDT named for example get_browserlang

// Read browser language 
$foo = $_SERVER['HTTP_ACCEPT_LANGUAGE']; 
// Only need the first two characters 
$lang = substr($foo,0,2); 
// Passing the parameter $lang to the template 

$smarty->assign('lang', $lang); 

Next step is to setup our root page for redirection, open the "Home" page and enter this chunk of code.

{get_browserlang} 

{if $lang == 'de'} 
    {redirect_page page="startseite"} 
{elseif $lang == 'fr'} 
    {redirect_page page="accueil"}
{else} 
    {redirect_page page="home-en"}
{/if}

As you can see, we call our UDT first to detect Browser Language and have parameter $lang available. The redirection works after this something like this, if Visitor has German language set he should get redirected to "startseite" which is our German start page with this Alias, else if Visitor is French he should be redirected to "accueil" which is start page in French and if none of both Visitor is redirected to "home-en" as Default.
Note: you can use here either Page Alias or Page ID.

Link Language between Pages

Now this is the part where this method is different from those i mentioned above. As required by client i was in need to have URL structure like www.domain.com/en/this-title-is-english and www.domain.com/de/dieser-titel-ist-deutsch and being able to switch between pages using Language Menu without jumping to "Home" page of each language.
Most bullet proof method i could think of was using page Hierarchy to link between pages, without worrying that Editor would break this structure as if i would use some method with page Aliases or Page ID's.
Unfortunately {cms_selflink} doesn't work with Hierarchy numbers, but it works with alias, so a solution to retrieve Alias of the page by Hierarchy number was needed.
And following UDT does exactly this (thanks to Peciura for great Help)

Create a UDT named hierarchy_position for example

/** 
 * Returns page_alias of valid [friendly] position 
 * 
 * @param string $params['friendly_position'] Mandatory. 
 * @param string $params['assign']
 */

$return = FALSE;
if (!empty($params[friendly_position])) {
    $delim                   = '.';
    $hierarchy_array         = array();
    $friendly_position_array = explode($delim, $params['friendly_position']);
    foreach ($friendly_position_array as $one) {
        $hierarchy_array[] = str_pad($one, 5, '0', STR_PAD_LEFT);
    }
    $hierarchy = implode($delim, $hierarchy_array);
    
    $query  = 'SELECT content_alias FROM ' . cms_db_prefix() . 'content WHERE hierarchy = ?';
    $db     = cmsms()->GetDB();
    $return = $db->GetOne($query, array(
        $hierarchy
    ));
}
if (!empty($params['assign'])) {
    $smarty = cmsms()->GetSmarty();
    $smarty->assign($params['assign'], $return);
} else {
    return $return;
}

Now we can move on to our single ML Template

As we have all needed functions set, we can move on building our Multilingual Template.
The first thing we need is CGSimpleSmarty Module.
Let's start with <head> part.

{strip}

{content assign="capturedcontent"} 
{if !isset($pagetitle)}
    {title assign='page_title'}
{/if}
{assign var='page_lang' value=$cgsimple->get_root_alias()}
{/strip}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{$page_lang}">
<head>    
    <title>{if !empty($pagetitle)}{$pagetitle}{else}{title}{/if} - {sitename}</title>
    {if isset($canonical)}
        <link rel="canonical" href="{$canonical}" />{elseif isset($content_obj)}
        <link rel="canonical" href="{$content_obj->GetURL()}" />
    {/if} 
    {metadata}
    <meta http-equiv="content-language" content="{$page_lang}" />
    {cms_stylesheet}
    {cms_selflink dir='start' rellink=1}
    {cms_selflink dir='prev' rellink=1}
    {cms_selflink dir='next' rellink=1}
</head>

So what have we done here, first we assigned content tag to capturedcontent, mostly i use content multiple times in my template and this prevents the error message in the backend about double content.
Next is the title part, used to display News module title instead fo regular page Title if we are viewing News detail page. Now the next thing is page_lang, as you can see we use here CGSimpleSmarty module to get our root alias which will result in "en", "de" or "fr".
In the doctype and metadata we use now {$page_lang} which returns values mentioned above.

Next part of Template is building our hierarchy_position UDT together so we can retrieve correct links in Language Menu.

<body>
{* language switching *} 
{capture assign="hierarchy_en"}2{$friendly_position|substr:'1'}{/capture} 
{capture assign="hierarchy_de"}3{$friendly_position|substr:'1'}{/capture} 
{capture assign="hierarchy_fr"}4{$friendly_position|substr:'1'}{/capture} 
{hierarchy_position friendly_position=$hierarchy_en assign="en_alias"} 
{hierarchy_position friendly_position=$hierarchy_de assign="de_alias"} 
{hierarchy_position friendly_position=$hierarchy_fr assign="fr_alias"}

As you can see, first we need our hierarchy position, with substr:'1' first value is removed so from 2.1.3 would result .1.3 and we add first number of each Language, this is the starting number of our Hierarchy structure.
Next part is hierarchy_position UDT where we call our assigned hierarchy_lang inside for each language and assign it again for later use in the Language Menu.
Simply said, we retrieve the Hierarchy number of current page, remove first numer and replace it with our Lanugage Hierarchy position and retrieve Page Alias from that number. 

In the next part we build our Language switch menu.

<!-- start pageWrapper -->
<div class="pageWrapper">
    <!-- start pageHeader -->
    <div class="pageHeader">
        <div class="pageLogo">
            {cms_selflink dir="start" imageonly="1" alt=$sitename image="uploads/template/pageLogo.jpg"}
        </div>
        <div class="langSelect">
            <ul>
                <li>
                    {cms_selflink page=$en_alias imageonly="1"  image="uploads/template/en.gif" title="English" alt="English"}
                </li>
                <li>
                    {cms_selflink page=$fr_alias imageonly="1"  image="uploads/template/fr.gif" title="Francaise" alt="Francaise"}
                </li>
                <li>
                    {cms_selflink page=$de_alias imageonly="1"  image="uploads/template/de.gif" title="Deutsch" alt="Deutsch"}
                </li>
            </ul>
        </div>
        <!-- end pageHeader -->
    </div>

So we have now the top part of our Template, a logo that links to start page, which will redirect us back to our Language and we have Language switch menu that is using above created parameters to link our current page correctly.  Next part would be building our Menu according to language like this. 

<!-- start pageNavigation -->
<div class="pageNavigation">
    {if $page_lang == 'de'}
    {menu  loadprops="0" template="pageNavigation" start_element="3.1" show_root_siblings="1"}
    {elseif $page_lang == 'fr'}
    {menu  loadprops="0" template="pageNavigation" start_element="4.1" show_root_siblings="1"}
    {else}
    {menu  loadprops="0" template="pageNavigation" start_element="2.1" show_root_siblings="1"}{/if}
    <!-- end pageHeader -->
</div> 

All we have left now is our Content area and Footer and we are done.

        <!-- start pageContent -->
        <div class="pageContent">
            {$capturedcontent}
            <!-- end pageContent -->
        </div>
        <!-- end pageWrapper -->
        </div>
        <!-- start pageFooter -->
        <div class="pageFooter">
            {if $page_lang ne ''}
            {global_content name=footer-$page_lang}
            {else}
            {global_content name='footer-en'}
            {/if}
            <!-- end pageFooter -->
        </div>
    </body>
</html>

Thats it, we are done, complete Template looks like this after we are done.

{strip}
 
{content assign="capturedcontent"} 
{if !isset($pagetitle)}
    {title assign='page_title'}
{/if}
{assign var='page_lang' value=$cgsimple->get_root_alias()}
{/strip}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{$page_lang}" >
    <head>
        <title>{if !empty($pagetitle)}{$pagetitle}{else}{title}{/if} - {sitename}</title>
        {if isset($canonical)}
            <link rel="canonical" href="{$canonical}" />
        {elseif isset($content_obj)}
            <link rel="canonical" href="{$content_obj->GetURL()}" />
        {/if}
        {metadata}
        <meta http-equiv="content-language" content="{$page_lang}" />
        {cms_stylesheet}
        {cms_selflink dir='start' rellink=1}
        {cms_selflink dir='prev' rellink=1}
        {cms_selflink dir='next' rellink=1}
    </head>
    <body>
        {* language switching *}
        {capture assign="hierarchy_en"}2{$friendly_position|substr:'1'}{/capture}
        {capture assign="hierarchy_de"}3{$friendly_position|substr:'1'}{/capture}
        {capture assign="hierarchy_fr"}4{$friendly_position|substr:'1'}{/capture}
        {hierarchy_position friendly_position=$hierarchy_en assign="en_alias"}
        {hierarchy_position friendly_position=$hierarchy_de assign="de_alias"}
        {hierarchy_position friendly_position=$hierarchy_fr assign="fr_alias"}
        <!-- start pageWrapper -->
        <div class="pageWrapper">
            <!-- start pageHeader -->
            <div class="pageHeader">
                <div class="pageLogo">
                    {cms_selflink dir="start" imageonly="1" alt=$sitename image="uploads/template/pageLogo.jpg"}
                </div>
                <div class="langSelect">
                    <ul>
                        <li>
                            {cms_selflink page=$en_alias imageonly="1"  image="uploads/template/en.gif" title="English" alt="English"}
                        </li>
                        <li>
                            {cms_selflink page=$fr_alias imageonly="1"  image="uploads/template/fr.gif" title="Francaise" alt="Francaise"}
                        </li>
                        <li>
                            {cms_selflink page=$de_alias imageonly="1"  image="uploads/template/de.gif" title="Deutsch" alt="Deutsch"}
                        </li>
                    </ul>
                </div>
                <!-- end pageHeader -->
            </div>
            <!-- start pageNavigation -->
            <div class="pageNavigation">
                {if $page_lang == 'de'}
                {menu  loadprops="0" template="pageNavigation" start_element="3.1" show_root_siblings="1"}
                {elseif $page_lang == 'fr'}
                {menu  loadprops="0" template="pageNavigation" start_element="4.1" show_root_siblings="1"}
                {else}
                {menu  loadprops="0" template="pageNavigation" start_element="2.1" show_root_siblings="1"}{/if}
                <!-- end pageHeader -->
            </div>
            <!-- start pageContent -->
            <div class="pageContent">
                {$capturedcontent}
                <!-- end pageContent -->
            </div>
            <!-- end pageWrapper -->
        </div>
        <!-- start pageFooter -->
        <div class="pageFooter">
            {if $page_lang ne ''}
            {global_content name=footer-$page_lang}
            {else}
            {global_content name='footer-en'}
            {/if}
            <!-- end pageFooter -->
        </div>
    </body>
</html>

Enjoy buliding your ML Template.

Comments