[
Update/Actualización: He publicado una
versión en español de este mensaje. I've posted a
Spanish version of this message.]
[
Update (2009-08-26): Pierre-Luc has written
a module called Babel, based on this method, easier to use and with more features.]
Hi all,
These days this proposal of mine has been improved several times, mainly following the Karolis' useful suggestions. To help people implement this multilingual model, I repeat here all the steps needed, with the last version of the code.
I think this model is very easy to implement and to use for a normal user. You don't need to be a programmer, but just a bit acquainted with CMS Made Simple. Nevertheless I'll try to explain everything very easily.
Features
- * Only one template needed for all pages in all languages (though of course you can use more if you want).
- * Only two User Defined Tags needed.
- * Suitable for any number of languages.
- * No need to have all language versions ready at the same time.
- * The alias of the language versions can be completely different.
- * Default page can be specified for every language, to be linked when no translation is avalaible.
- * Link buttons can be specified for every language (any HTML can be used, also images).
- * Language menu fully configurable with parameters:
- [li] - Use the page titles or the configurable HTML buttons as the link text.
- - Show the current page as an inactive link or show nothing.
- - Link the language default page when no translation is avalaible, or create no link.
- - Show an inactive button when no default page has been specified, or nothing.
[/li]
- * Fully commented code. Easy to understand and adapt.
Step 1: Create the content hierarchy
Create a root section header for every language of your site following this steps:
1) Give it the name you like (the name of the language is a good option).
2) The alias must be the language code (two letters).
3) Delete the tick "Show in menu". We don't want the section header in the menu.
All your content (pages, sections headers...) in a language must be under the corresponding root section header.
Step 2: Get the language of every page
Create a new User Defined Tag and name it
assign_page_lang.
Copy and paste this code into it:
Code: Select all
/*
Assign the Smarty variable $page_lang depending on the root parent of the current page.
The root parent alias must be the language code (two letters).
2008-02-02 Marcos Cruz (http://alinome.net)
Second version, without array
Reference:
http://wiki.cmsmadesimple.org/index.php/Share_your_tags_here#Get_a_page.27s_root_parent.27s_alias
*/
global $gCms;
global $smarty;
$manager =& $gCms->GetHierarchyManager();
$result = '??';
$thisPage = $gCms->variables['content_id'];
$currentNode = &$manager->sureGetNodeById($thisPage);
while( isset($currentNode) && $currentNode->getLevel() >= 0 )
{
$currentContent =& $currentNode->getContent();
$result = $currentContent->Alias();
$currentNode =& $currentNode->getParentNode();
}
$smarty->assign('page_lang',$result);
Then put the tag at the top of your template(s), like this:
Step 3: Select content depending on the page language
Now you can use the Smarty variable $page_lang in your templates and pages
to select content.
Example 1:
Code: Select all
<__html xmlns='http://www.w3.org/1999/xhtml' xml:lang='{$page_lang}'>
Example 2:
Code: Select all
{if $page_lang == 'es'}<h1>Título en castellano</h1>
{elseif $page_lang == 'eo'}<h1>Esperanto-titolo</h1>
{else}<h1>English title</h1>
{/if}
Step 4: Create the content menu
How do you show in your navigation menu only the pages in the current language?
You can use the
{menu} tag with the parameter
start_element, to select the root section header of the language. This is an example:
Code: Select all
<div id='menu'>
{if $page_lang == "es"}{menu template='my_menu' start_element='1.1'}
{else}{menu template='my_menu' start_element='2.1'}
{/if}
</div>
Another method is to use the parameter
start_level. Then only one
{menu} is needed:
Code: Select all
<div id='menu'>
{menu template='my_menu' start_level='2'}
</div>
Read the help of the menu module to learn how to use all its parameters.
Step 5: Specify the language translations of every page
Put this at the top of your template:
Code: Select all
{content block='Other languages' oneline='true' assign='other_languages' wysiwyg='false'}
That will create a new content block for every page. Its content will be asigned to the Smarty variable
other_languages. When you edit a page, you must fill this block with the names of its translations, using the following format:
en=alias-of-the-corresponding-english-page;ru=alias-of-the-corresponding-russian-page
No spaces are allowed. Language codes must have two letters. Use semicolon to apart the languages. You can specify as many languages as you like, in any order.
Step 6: Create the language menu
Create an User Defined Tag and name it
language_menu.
Copy and paste this code into it (it has a lot of comments to help understand it):
Code: Select all
/*
Create a language menu (version 5).
2008-02-06 Por/Far/By Marcos Cruz (http://alinome.net)
New features in this version 5:
- New parameter show_default.
- New parameter show_inactive.
Changes in the code in this version 5:
- Some forgotten doble quotes has been changed to single quotes for better perfomance.
- Doble and single quotes changed in the example HTML buttons.
- New constants DEFAULT_PAGE and BUTTON to make the code more readable without using keys in the language array.
- Language array and other variables renamed to make the code more readable.
- More comments to help beginners understand the code.
New features in version 4:
- Every language has a default page to be used when no translation has been specified for the current page,
or when the specified page doesn't exist.
- The HTML attribute hreflang is added to the links.
- The UDT parameter show_title now works in inactive links to the current page too.
Changes in the code in version 4:
- The function smarty_cms_selflink has been factored to make it easier to reuse the code.
- The conditional operator "?:" used instead of several if-structures.
- Code revised according to the PHP coding guidelines (spaces, string quotes, braces...)
- Optional debug points homogenized and marked with the word "DEBUG". Delete them if you want.
- New comments for beginners.
Possible future improvements:
- Spaces allowed in the template variable $other_languages (syntactic sugar to make it error-proof).
*/
global $gCms;
// ----------------------------------------------------------------
// Parameters
// Show the page title in the links? (true/false)
// (if false, show the defined buttons)
$show_title = isset($params['show_title']) ? $params['show_title'] : false;
// Show an inactive link to the current page in the menu? (true/false)
// (if false, don't create a menu entry for the current page)
$show_current = isset($params['show_current']) ? $params['show_current'] : false;
// If there's no valid translation, then show a link to the default page? (true/false)
// (if false, don't create a menu entry for that language)
$show_default = isset($params['show_default']) ? $params['show_default'] : true;
// If $show_default==false or there's no valid default page, then show an inactive link? (true/false)
// (if false, don't create a menu entry for that language)
$show_inactive = isset($params['show_inactive']) ? $params['show_inactive'] : true;
// ----------------------------------------------------------------
// Constants
define ("DEFAULT_PAGE",0);
define ("BUTTON",1);
// ----------------------------------------------------------------
// Language data common to all pages
/*
The array $languages keeps information about all languages of the site.
It is indexed by the language code.
Every item is an array that contains two items:
0) The optional default page for the language (used when there's no valid translation, if the parameter show_default is true). It can be empty (and then the parameter show_inactive will decide what to do).
1) The HTML code of the link button (used when the parameter show_title is false or unset).
To make the code more readable, these items are accessed with the constants DEFAULT_PAGE and BUTTON in the main loop.
*/
// Example data
$languages = array(
'en' => array('home', '<span xml:lang="en">English</span>'),
'es' => array('inicio', '<span xml:lang="es">Castellano</span>'),
'eo' => array('komenco', '<span xml:lang="eo">Esperanto</span>'),
'lt' => array('pradinis', '<span xml:lang="lt">Lietuviškai</span>'),
'ru' => array('glavnaja', '<span xml:lang="ru">Pусский</span>') );
// ----------------------------------------------------------------
// Variables about the current page
$page_lang = $gCms->smarty->get_template_vars('page_lang'); // assigned in the template by the User Defined Tag {assign_page_lang}
// ----------------------------------------------------------------
// Language versions of the current page
/*
The template variable $other_languages is assigned with a content block in the template. eg.:
{content block='Other languages' oneline='true' assign=other_languages wysiwyg='false'}
For every page, it has to be filled with a string in the following format:
"c1=page1;c2=page2;c3=page3" (no spaces allowed),
where c(n) are language codes (two letters);
any number of languages can be specified in any order.
That string is converted into the array $language_versions.
*/
$other_languages = $gCms->smarty->get_template_vars('other_languages');
foreach ( explode(';', $other_languages) as $other_language )
{
$language_codes[]=substr($other_language, 0, 2);
$language_pages[]=substr($other_language, 3);
}
$language_versions = array_combine($language_codes, $language_pages);
/*
echo '<p><strong>DEBUG</strong>:';
echo "<br/>language_codes="; print_r($language_codes);
echo "<br/>language_pages="; print_r($language_pages);
echo "<br/>language_versions="; print_r($language_versions);
echo '</p>';
*/
// ----------------------------------------------------------------
function smarty_tag ($smarty_tag)
{
// Call a smarty tag.
// Reference:
// 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;
/*
echo '<p><strong>smarty_tag DEBUG</strong>:';
echo "<br/>smarty_tag=$smarty_tag";
echo '</p>';
*/
$smarty = &$gCms->GetSmarty();
$smarty->_compile_source('temporary template', $smarty_tag, $_compiled);
@ob_start();
$smarty->_eval('?>' . $_compiled);
$_contents = @ob_get_contents();
@ob_end_clean();
echo $_contents;
}
// ----------------------------------------------------------------
function smarty_cms_selflink ($page, $text, $more)
{
// Call the CMSMS tag {cms_selflink}.
/*
echo '<p><strong>smarty_self_link DEBUG</strong>:';
echo "<br/>page=$page";
echo "<br/>text=$text";
echo "<br/>more=$more";
echo '</p>';
*/
// smarty_tag ("{cms_selflink page=$page" . ( (empty($text)) ? '' : ' text="' . $text .'"' ) . ' more="' . $more . '"}');
smarty_tag ("{cms_selflink page=$page" . ( (empty($text)) ? '' : " text='$text'" ) . ' more="' . $more . '"}');
}
// ----------------------------------------------------------------
function valid_page($page_alias)
{
// Is a page alias valid?
// References:
// http://forum.cmsmadesimple.org/index.php/topic,3778.msg20921.html#msg20921
global $gCms;
$manager =& $gCms->GetHierarchyManager();
$node =& $manager->getNodeByAlias($page_alias);
return ( $node != null );
}
// ----------------------------------------------------------------
function current_page_title()
{
// Return the title of the current page.
// This is needed only to print and inactive link to the current page when $show_title==true.
// References:
// http://forum.cmsmadesimple.org/index.php/topic,3778.msg20921.html#msg20921
// http://forum.cmsmadesimple.org/index.php/topic,3778.msg21051.html#msg21051
global $gCms;
$this_page = $gCms->variables['content_id'];
$manager =& $gCms->GetHierarchyManager();
$node =& $manager->GetNodeById($this_page);
$content =& $node->getContent();
return $content->Name();
}
// ----------------------------------------------------------------
// Create the menu
/*
echo '<p><strong>DEBUG</strong>:';
echo "<br/>page_lang=$page_lang";
echo "<br/>show_title=$show_title";
echo "<br/>show_current=$show_current";
echo "<br/>page_title=$page_title";
echo "<br/>language_versions="; print_r($language_versions);
echo "<br/>language_buttons="; print_r($language_buttons);
echo '</p>';
*/
echo '<ul>';
foreach ( $languages as $language_code => $language ) // explore all possible languages of the site
{
// $language_code = code of the language to create a link for
// $language[DEFAULT_PAGE] = the default page used when no alternative has been defined for the language
// $language[BUTTON] = HTML used as button for the link, when $show_title==false.
/*
echo '<p><strong>menu DEBUG</strong>:';
echo "<br/>language_code=$language_code";
echo "<br/>language="; print_r($language);
echo '</p>';
*/
if ( $language_code != $page_lang )
{
$alternative_page = $language_versions[$language_code];
// echo "alternative_page=$alternative_page"; // DEBUG
if ( valid_page($alternative_page) )
{
echo '<li>';
// echo "ALTERNATIVE: $alternative_page"; // DEBUG
smarty_cms_selflink($alternative_page,($show_title == true ) ? "" : $language[BUTTON], "hreflang='".$language_code."'");
echo '</li>';
}
else // no alternative page is valid for this language
{
if ( $show_default == true and valid_page($language[DEFAULT_PAGE]) )
{
echo '<li>';
// echo 'NO ALTERNATIVE! DEFAULT: '; // DEBUG
smarty_cms_selflink($language[DEFAULT_PAGE],( ($show_title == true ) ? "" : $language[BUTTON] ) , "hreflang='".$language_code."'");
echo '</li>';
}
else
{
if ( $show_inactive == true )
{
echo '<li class="inactive">';
// echo 'DEFAULT CAN NOT BE USED! INACTIVE BUTTON: '; // DEBUG
echo $language[BUTTON] . '</li>';
}
}
}
}
elseif ( $show_current == true )
{
// echo 'SHOW CURRENT'; // DEBUG
echo '<li class="currentpage">' . ( ($show_title == true) ? current_page_title() : $language[BUTTON] ) . '</li>';
}
}
echo '</ul>';
Then configure the code: Change the content of the array
$languages with the languages you use, their default home page (if any) and their default HTML code for buttons. The code is clear enough and it's fully commented.
Then put the new tag
{language_menu} in the desired place of your template, e.g.:
Code: Select all
<div id='langmenu'>
{language_menu}
</div>
That code must be always after the "Other languages" content block (that's why in
Step 5[/i ]we put that content block at the top of the template).
You can use optional parameters (they are fully explained in the code itself), e.g.:
Code: Select all
<div id='langmenu'>
{language_menu show_title='false' show_current='true' show_default='true' show_inactive='false'}
</div>
I kept some comments marked with the word "DEBUG" because they are useful while modifing the code. If you need, uncomment them; if you have no interest, delete them.
The end
That's all. I hope you find it useful. I'm using it in a new personal site of mine.
I'll post the URL when there's enough content. It's a bilingual site. Its URL is
http://alinome.net/alien.
Marcos