[Update 2/Actualización 2: He publicado una versión en español de la guía final de implementación. I've posted a Spanish version of the final implementation guide.]
[Update 3: You can see a working example of this method in a bilingual personal site of mine: http://alien.alinome.net.]
[Update 4 (2009-08-26): Pierre-Luc has written a module called Babel, based on this method, easier to use and with more features.]
[Update 5 (2009-09-12): After almost three years, I have decided not to use CMSMS any more. It has become too complex for my needs, version after version, and latest upgrades have broken many things in my code. I ported the site I mentioned in "Update 3" (and others) to a custom Website Revision System of my own, much simpler and productive (wiki-like, no database, straight PHP programming...). The website look is exactly the same than with CMSMS, though.]
Hi all,
Until now I have built only one site with CMSMS, monolingual; but some of my new projects will be in several languages. I could use other tools I used before, but I like CMSMS so much that I decided to give it a chance. I don't reject the idea to test the CMSMS MLE fork, but firstly I wanted to try to squeeze what I need out of the regular version.
I searched the forum for tips on this question and collected some interesting ideas, mainly about the same solution I already had in my mind at first: separated menu branches for the different languages. (By the way, it is really hard to find useful information with the provided search tool, so finally I googled with the "site:cmsmadesimple.org" option). All the approachs I found were (at first sight) too complex and hard to adapt one way or another. I wanted to find a solution as much simple and configurable as possible.
My goals were these:
- Only one page template for all pages in all languages (though of course you can use more if you like).
- The language bar buttons should link to the current page translation, not to the home page.
- Easily configurable for any number of languages.
- Easily configurable buttons in the language bar.
Menu organization
I created two root section headers for the two languages needed. Their names are the language codes. All pages in every language are created in the corresponding branch:
- es (Section Header)
- [li]page1
- page2
-
eo (Section Header)
- [li]page1
- page2
Get the language of the current page
To have only one template for all pages I needed a Smarty variable to keep the current language and use it to select the corresponding parts of the template (the other way would be to use a different template for every language, what is worse to maintain).
My first provisional approach, just to be able to start programming the language bar, was a bit fooly: The user had to choose the language of every page by filling a content block. I put this at the top of the template:
Code: Select all
{content block="Language code" oneline="true" assign="page_lang"}
{if $page_lang == ""} {assign var="page_lang" value="es"}{/if}
the message Re: Using the Global variables from CMS. Too late, I said, because I had already thought a simpler solution:
The left char of the template var friendly_position was all I need! The new code at the top of the template was:
Code: Select all
{if $friendly_position|truncate:1:"":true == "1"}
{assign var='page_lang' value='es'}
{else}
{assign var='page_lang' value='eo'}
{/if}
to use an array with the languages codes, indexed by the menu root positions. Si I decided to write an User Defined Tag called {assign_page_lang}:
Code: Select all
/*
Assign the Smarty variable page_lang depending on the menu branch the current page belongs to.
*/
global $gCms;
$language_codes = array(
"1" => "es",
"2" => "eo")
;
$menu_branch = substr($gCms->variables['friendly_position'],0,1);
$smarty = &$gCms->GetSmarty();
$smarty->assign('page_lang', $language_codes[$menu_branch]);
Code: Select all
{assign_page_lang}
The Smarty var $page_lang can now be used in the template and its blocks to select the content depending on the current language, e.g.:
Code: Select all
<__html xmlns='http://www.w3.org/1999/xhtml' xml:lang='{$page_lang}'>
Code: Select all
{if $page_lang == "es"}
<h1>Título en castellano</h1>
{else}
<h1>Esperanto-titolo</h1>
{/if}
Or the main menu itself, to show only the corresponding language branch:
Code: Select all
<div id='menu'>
{if $page_lang == "es"}{menu template='menu_1' start_element='1' number_of_levels='3'}
{else}{menu template='menu_1' start_element='2' number_of_levels='3'}
{/if}
</div>
The language menu
Now the language menu. I wrote a second UDT called... {language_menu}. In its code you'll see the simple trick to get every page related with its translation. Nevertheless I will explain that later.
The tag has two optional parameters:
- show_current = Show an inactive link to the current page in the menu? (true/false)
- show_title = Use the page titles instead of the defined buttons? (true/false)
Code: Select all
/*
Create a language menu.
2008-01-31 Por/Far/By Marcos Cruz (alinome.net)
*/
global $gCms;
/* Parameters
*/
$show_current = $params['show_current']; /* Show an inactive link to the current page in the menu? true/false */
$show_title = $params['show_title']; /* Use the page titles instead of the defined buttons? true/false */
/* Buttons to be used (if $show_title !== true). They could be images too.
I think the text "in language" in the language itself is the most accessible button.
(In my opinion flags are not a good option because they represent countries or regions, not languages).
*/
$language_buttons = array(
"es" => "<span xml:lang='es'>en castellano</span>",
"eo" => "<span xml:lang='eo'>en esperanto</span>")
;
$current_page = $gCms->variables['page_name'];
/* I explored how to get the page title and found this:
http://forum.cmsmadesimple.org/index.php/topic,1258.msg6831.html#msg6831 (too old)
http://forum.cmsmadesimple.org/index.php/topic,3778.msg93098.html#msg93098
But I haven't explored much yet because I don't use the show_title parameter now...
*/
$page_title = $gCms->variables['page_title']; /* ...so meanwhile this doesn't work :-) Someone can help? */
$page_lang = $gCms->smarty->get_template_vars('page_lang');
$base_page_name = ereg_replace("..$", "", $current_page ); /* take off the language code */
function smarty_cms_selflink($page,$text) {
/* Call Smarty {cms_selflink}
Doc found in:
http://wiki.cmsmadesimple.org/index.php/User_Handbook/Admin_Panel/Extensions/User_Defined_Tags#How_to_Execute_Smarty_Tags_from_A_User_Defined_Tag_.28UDT.29
*/
global $gCms;
if ( $text == "" ) { $smarty_data = "{cms_selflink page=" . $page . "}"; }
else { $smarty_data = '{cms_selflink page=' . $page . ' text="' . $text . '"}'; }
/*
echo "<p><strong>Debug info:</strong></p>";
echo "<p>page=$page</p>";
echo "<p>text=$text</p>";
echo "<p>smarty_data=$smarty_data</p>";
*/
$smarty = &$gCms->GetSmarty();
$smarty->_compile_source('temporary template', $smarty_data, $_compiled );
@ob_start();
$smarty->_eval('?>' . $_compiled);
$_contents = @ob_get_contents();
@ob_end_clean();
echo $_contents;
}
/* Create the menu
*/
/*
echo "<p><strong>Debug info:</strong><br/>";
echo "page_lang=$page_lang<br/>";
echo "show_title=$show_title<br/>";
echo "show_current=$show_current<br/>";
echo "base_page_name=$base_page_name<br/>";
echo "page_title=$page_title</p>";
*/
echo "<ul>";
foreach ($language_buttons as $language => $button ) {
if ($language != $page_lang) {
echo "<li>";
if ($show_title == true ) { smarty_cms_selflink($base_page_name.$language,""); }
else { smarty_cms_selflink($base_page_name.$language,$button); }
echo "</li>";
}
elseif ( $show_current == true ) {
echo "<li class='currentpage'>";
if ($show_title == true) { echo $page_title; } /* this doesn't work yet */
else { echo $button; }
echo "</li>";
}
}
echo "</ul>";
- es (Section Header)
- [li]contact_es
- products_es
-
eo (Section Header)
- [li]contact_eo
- products_eo
You create the language menu in your template like this:
Code: Select all
<div id='langmenu'>
{language_menu}
</div>
Code: Select all
<div id='langmenu'>
{language_menu show_current=true}
</div>
The code is working fine in a new personal site of mine I'm building these days. I'll post the URL later because the site has no content yet and looks really weird .
Future migration to CMSMS 2.0 or whatever
I use Mod Rewrite to get pretty URLs in the format "/contact_es.html" redirected to "/index.php?page=contact_es".
Maybe useful for someone, the lines I use for this in the file .htaccess are:
Code: Select all
Options +FollowSymLinks
RewriteEngine on
RewriteRule ^([a-z]+[_]{1}[a-z]{2})\.html$ index.php?page=$1 [NC,L]
That way, if in the future I migrate to other multilingual system whith a different URL format I will easily create those fake HTML files with PHP code to redirect them (that would be easier for me than messing about Mod Rewrite). E.g. the file products_es.html would be:
Code: Select all
<?php
header( 'HTTP/1.1 301 Moved Permanently' );
header( 'Status: 301 Moved Permanently' );
header('Location: http://whateveritis.com/futuresystem/es/products/');
exit(0);
?>
I'm not very used to PHP (the language I use the most is Forth, what is a extremely different thing!). If you find something that should be corrected or could be improved, please post. I suspect some if structures and conditions could be simpler (when I come back to PHP after a long time, I always forget about && and similar things ). The same about the relation beetwen PHP and Smarty, and the inner workings of CMSMS: all that is a new thing for me.
As the code comments explain, the page title option still does not work, but I will ask for help in the corresponding forum board.
Hope all this helps.
Cheers,
Marcos
PS: After implementing my solution I found this interesting message in the forum:
Starting homepage based on users browser language. I think I will add that feature in my site.