Oct 07, 2008, 12:01 AM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
modxcms.com web
  MODxCMS.com   Forums   Help Login Register  
News:Donate to MODx: Donations
Pages: [1] 2 3 4   Go Down
  Print  
Author Topic: Plugin for Multilanguage support  (Read 28042 times)
0 Members and 1 Guest are viewing this topic.
sottwell
Documentation Team
*
Posts: 8,137



WWW
« on: Jun 15, 2005, 10:30 PM »

I have been manually editing all snippets to add a value "&lang=$lang" to all links and link-creating code, such as menu snippets.  The $lang variable is determined at the top of every snippet with "$lang = isset($_GET['lang'] ? $_GET['lang'] : $modx->config['site_start'];". The $lang variable is initially determined by the docID of the root folder acting as home page and containing the content pages for that langauge, as selected from a menu generated by a simple list menu snippet using 0 as its base and that does have its links modifed to seed the $lang variable into the system.  The language files are also simply named after the same root docID.  If it's the initial visit to the site, or for some reason the query string value gets lost, it defaults to the site_start document ID.

This works well, but is really not desireable.  It seems to me that I could use a plugin on the DocumentPreRender event to add the value to the query string of all links on the page.  If I understand this correctly, I can do something like the "filter words" plugin example in the documentation.  I would use a regular expression to find all "<a href=.....> and add the query string value at the end of the text before the final ">".

Am I on the right track here?  And can somebody recommend a good site for a crash course on regular expressions?  I already tried hacking the core in the rewriteURLs function, and that worked, but it ended up with multiple "lang" fields in the query string, and while the last one was the desired one and it worked, it's sloppy, and besides, I don't want to have to have a hacked core for this to work.  This way, all I'd need is one modified simple list menu snippet and the plugin for instant multilanguage support.  Snippets written to use the feature would simply include their own "snippet/langs/$lang.php" language file as needed.

Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
xwisdom
Foundation
*
Posts: 1,732



« Reply #1 on: Jun 16, 2005, 01:02 AM »

Hi Susan,

You're on the right track. The OnDocumentPreRender event makes it very easy for you to append &lang to every <a> tag inside the document.

I'm not good at the regx stuff either but you can try this:

Quote
$pattern='/<a [^>]*href=([\\"\\\']?)([^>]*)\1[^>]*>/i';
preg_match_all($pattern,$x,$ahrefs);
echo $ahrefs[0][0]." Original "; //orig
echo $ahrefs[2][0]." Url "; // url

I borrowed th regx from the PNGAlpha4MSIE plugin Smiley
Logged

xWisdom
www.xwisdomhtml.com
The fear of the Lord is the beginning of wisdom:
MODx Co-Founder - Create and do more with less.
Lammikko
Full Member
***
Posts: 152



WWW
« Reply #2 on: Jun 17, 2005, 03:19 PM »

And can somebody recommend a good site for a crash course on regular expressions?

These two are very useful:
Excellent regular expressions tutorial: http://www.regular-expressions.info/
Regular expressions library:  http://www.regxlib.com/
Logged

Carsten
Coding Team
*
Posts: 546



WWW
« Reply #3 on: Jun 18, 2005, 04:23 AM »

Actually, a bit off topic.  But I've always been leary of using regex for replacements (ie, in regards to templates and such).  How much of hit is regex compared to say caching the language didies and just using a require?
Logged

Tangent-Warrior Smiley
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #4 on: Jun 18, 2005, 08:08 AM »

This isn't about substituting the text strings, I do use included language files for that.  This is about how to pass the $lang variable from page to page via the query string and recover it as needed from the GET array. 

Anyway, I've about given up on this idea; not all links on a page are to the local site, and friendly URLs would make it extremely difficult to determine which links were to local pages and which were not.  Looks like I'll be studying up on cookies after all. 

Session works great, and is the easiest, but if the user's session times out he gets dumped back into the site's default language.  Plus, sessions require fiddling the web login snippet core scripts, and I want this to be a stand-alone solution, a plug-in or a snippet that is easy to use.
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #5 on: Jun 18, 2005, 12:34 PM »

Ok, here's my take on a multilanguage site.  One snippet, one TV, a folder for language files.  That's it, no hacked core modules or anything nasty.

Have the language home page documents in the root.  i.e. "French", "Italian", "English".  These are the only published documents in the root.  These documents act as folders for the content for their respective languages.  Anything else goes into unpublished folders.

Have language files to replace placeholders for any generic text, such as login forms, contact forms, etc.  These kind of things are what would go in the unpublished folder and get called from all languages.  The language files are named after the ID of the language home page, 3.php, 45.php, etc.

Use this modifed CSS Dropdown Menu snippet to create the language menu and seed the system with the $lang variables:
Code:
// CSSDropMenu
// simple drop=down menu for ModX
// usage: [[DropDown?id=x&tt=y]]
// where x is the parent of the documents to be listed
// and y is the text you want for the top of the menu

$id = isset($id)?$id:0;
$toptext = isset($tt)?$tt:"Menu";
$children = $modx->getActiveChildren($id);
if($children == false) return;
$childrenCount = count($children);
$lang = $modx->documentIdentifier;
$dm = "";

  // a bit of javascript for those whose browsers aren't
  // CSS2 compliant (and we all know who they are)
$dm .= "<script type='text/javascript'>
function showList(thistag) {
   styleObj = document.getElementById(thistag).style;
   if (styleObj.display=='none') {styleObj.display = 'block';}
   else {styleObj.display = 'none';}
}
</script>";

  // set up the style for the list
$dm .= "<style type='text/css'>
  #CSSMenu {
      position:relative;
      width:100px;
      z-index:10;
      background:yellow;
  }
  #CSSMenu ul, #CSSMenu li {list-style:none;}
  #cmTopItem {display:block;cursor:pointer;}
  #cmTopItem ul {display: none;}
  #cmTopItem:hover ul {display: block;}
</style>";

  // generate the list
$dm .= "<div id='CSSMenu'>";
$dm .= "<ul class='cmList' id='cmTopList'>";
$dm .= "<li id='cmTopItem' class='cmItem' onClick='showList(\"cmList\");'>$toptext";
$dm .= "<ul class='cmList' id='cmList'";
  // individual items
for($x=0; $x<$childrenCount; $x++) {
   $dm .= "<li class='cmList'><a href='[~".$children[$x]['id']."~]&lang=".$children[$x]['id']."'>".$children[$x]['pagetitle']."</a></li>";
}
$dm .= "</ul></li></ul></div>";

return $dm;

Use this snippet to set the cookie; put it in each of your language home page documents:
Code:
// SetLangCookie snippet for ModX
// set cookie with language ID

$lang=$modx->documentIdentifier;
setcookie("Language",$lang,time()+604800, "/", "", 0);

And finally, this Template Variable to make the $lang variable available anywhere you like, such as the value for the id in the main menu generation snippet, or the logouthomeid in the web login snippet.
Code:
@EVAL
if(isset($_GET['lang'])) {$lang=$_GET['lang'];}
else if(isset($_COOKIE['Language'])) {$lang=$_COOKIE['Language'];}
else {$lang=$modx->config['site_start'];}
return $lang;

Now, the user stays in the language area he selects, until he selects another. 
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
xwisdom
Foundation
*
Posts: 1,732



« Reply #6 on: Jun 19, 2005, 10:51 AM »


Very nice susan. Very nice Smiley
Logged

xWisdom
www.xwisdomhtml.com
The fear of the Lord is the beginning of wisdom:
MODx Co-Founder - Create and do more with less.
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #7 on: Jun 19, 2005, 10:43 PM »

Dumb.  Don't need a modified menu generation snippet to put a lang variable into the URL query string.  The snippet in the language home page determines the lang by its document ID.  So any simple list menu would work. 
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #8 on: Jun 20, 2005, 05:41 AM »

Ok, here's the final (I do hope!  Roll Eyes ) simplified, actually functional version:

snippet SetLang (put in each of the language's homepage document/folders):

Code:
// SetLangCookie snippet for ModX
// set cookie with language ID

$lang=$modx->documentIdentifier;
setcookie("Language",$lang,time()+604800, "/", "", 0);
if($_COOKIE['Language'] != $lang) {
    $url = $modx->makeURL($lang);
    $modx->sendRedirect($url);
}

And the TV:
Code:

@EVAL
 if(isset($_COOKIE['Language'])) {$lang=$_COOKIE['Language'];}
else {$lang=$modx->config['site_start'];}
return $lang;

Put it wherever you need the language variable; for example in the main menu snippet : [[MenuBuilder?id=[*lang*]]]

Full instructions attached.

* SetLang.txt (4.35 KB - downloaded 1167 times.)
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
rthrash
Foundation
*
Posts: 9,269



WWW
« Reply #9 on: Jun 20, 2005, 09:07 AM »

Thanks for a great and elegant solution Susan!

Just a note for those that might not be aware, using this method requires duplicating the "language version(s)" of your site into a seperate folder, with each version being manually translated into the appropriate language(s). Probably the best way to procede is to build your site in your native language in a folder, then duplicate the folder and rename to the appropriate language(s) then start translating. Is that has what works best in your exprience, Susan?
Logged

MODx is a framework that allows web professionals to turn over sites to end-users for daily maintenance without worrying. Community participation and questions are encouraged, especially when you help us help you, read the wiki, and review snippet parameters – even if you have to look at the source. Searching the forums helps, too.
Ryan Thrash
MODx Co-Founder
Principal @ Collabpad
work productively.
work intelligently.
work together.
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #10 on: Jun 20, 2005, 12:48 PM »

Yes.  I created the first language (english, in my case ) then copied (this was before the lovely ModX "duplicate" feature; the next one will be sooo much easier!) everything into more folders.  Anyone interested can see the site at www.bmptrans.ch.   Their original site was just three directories with HTML files.  I mostly just copy/pasted the content from their old site; then they sent me all kinds of .doc files with new content.  They didn't have time to update it themselves...oh well, they're the ones paying for it!  I have updated the editor to FCKEditor, with a much cut-down menu.  Hopefully they'll find it easier to be able to paste from their Word documents.  <sigh>

They are also the ones for whom I wrote the DocMan system!  It is fully multilanguage as well.  The menu at the top is based on a simple list menu snippet called with id=0.  It uses an older method I worked out of passing the language ID in the URL, that involved hacking every script and snippet that generates a URL. 

This way is much better, although depending on cookies could be somewhat problematical, I suppose.  I'm not going to mess with their site any more; it's working great for them as it is.  But my next one will use the new method.

Now all I have to do is get them to take the time to give me all the translations that I need for all the langauge files!
« Last Edit: Jun 20, 2005, 12:51 PM by sottwell » Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #11 on: Jul 13, 2005, 01:46 AM »

Modified the "HomeAlone" snippet to hadle a multilanguage site.  It gets the $lang variable from the COOKIE, then checks to see if the page ID == $lang.  If true, this is the "home" page for that language. 

Every chunk or snippet you want processed by HomeAlone MUST be duplicated, with the $lang (the document ID of that language's home page) appended to the name: HomeChunk1, HomeChunk43; whatever the language's home page ID is.

You call the HomeAlone snippet with the base name of the chunk/snippet: HomeChunk

The HomeAlone snippet automatically appends the $lang to the base name to include the snippet/chunk for the language the page being processed belongs to. 

This way you can easily edit and translate the snippet/chunk being included to suit its language.  For example, the div ID of a chunk could be changed, and the CSS file would be able to use a different background image to suit the language, flags for example.

Code:
// --------------------
// Snippet: HomeAlone
// --------------------
// Version: 0.6a
// Date: 2004.09.02
// jaredc@honeydewdesign.com
//
// This snippet was designed to show other snippets or chunks
// that should appear on the home page only, or on all BUT
// the home page.
//
// Modifed 2004.07.13
// sottwell@sottwell.com
// for multilanguage sites
// use base snippet/chunk name in template
// you must have a separate snippet/chunk for each language used
// snippet/chunk name iteself uses basename + ID of main language homepage
// homepage check checks to see if page ID = $lang
// snippet/chunk names passed in call parameter
// have $lang appended while being loaded into array
// eg HomeBanner is used in call parameter
// if the $lang of the page being loaded is 1, the snippet/chunk name
// will be modified to HomeBanner1


// Configuration Settings

   // $HA_sHome and $HA_sNotHome [ set in snippet call parameter ]
   // These variables are pipe separated strings of snippet names to run
   // on the home page (in the case of $HA_sHome) or to run  on all BUT the
   // home page (in the case of $HA_sNotHome).
   // Call the snippet like this:
   // [[HomeAlone?HA_sHome=FirstSn|SecondSn&HA_sNotHome=third|fourth]]
   
   // $HA_cHome and $HA_cNotHome
   // These work exactly the same way but for chunks. You can use 1, 2, 3 or all 4
   // parameters in a single call if you wish, but this will tend to get confusing:
   // [[HomeAlone?HA_cHome=snippet1&HA_cNotHome=snippet2&HA_cHome=chunk1&HA_cNotHome=chunk2]]

   // Snippets will be processed first then chunks.
// for multilanguage sites, get the $lang value
// or use site_start
$lang = isset($_COOKIE['Language']) ? $_COOKIE['Language'] : $modx->config['site_start'];

// something has to be set for this to run
if (isset($HA_sHome) || isset($HA_sNotHome) || isset($HA_cHome) || isset($HA_cNotHome)){
  // decide if this is the home page - modified for multilang
  $atHome = ($modx->documentIdentifier == $lang) ? true : false ;
  // get the snippet/chunk for that language
 
  // find snippets
  $homeSnippets = isset($HA_sHome)? explode("|",$HA_sHome): '';
  $notHomeSnippets = isset($HA_sNotHome)? explode("|",$HA_sNotHome): ''; 
  // initialize output variable
  $output = '';
  //
  if ($atHome){
    if ($homeSnippets){
      foreach($homeSnippets as $snippetName){
        $output .= '[['.$snippetName.$lang.']]'; // add $lang to name
      }
    }
  } else { // other than home page
    if ($notHomeSnippets){
      foreach($notHomeSnippets as $snippetName){
     $output .= '[['.$snippetName.$lang.']]'; // add $lang to name
      }
    }
  }

  // find chunks
  $homeChunks = isset($HA_cHome)? explode("|",$HA_cHome): '';
  $notHomeChunks = isset($HA_cNotHome)? explode("|",$HA_cNotHome): ''; 
  // decide if this is the home page
  if ($atHome){
    if ($homeChunks){
      foreach($homeChunks as $chunkName){
        $output .= '{{'.$chunkName.$lang.'}}'; // add $lang to name
      }
    }
  } else { // other than home page
    if ($notHomeChunks){
      foreach($notHomeChunks as $chunkName){
     $output .= '{{'.$chunkName.$lang.'}}'; // add $lang to name
      }
    }
  }
 
  return $output;
}
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #12 on: Jul 13, 2005, 01:58 AM »

I've discovered (the hard way!) that the home pages for each language cannot be cached.  Apparently the cookie will not be refreshed on changing languages if the homepages are cached, even if the SetLang snippet is called non-cached ([!...!]).  At least this was an issue I seemed to run into; YMMV.

 I've also found that the [*lang*] TV does not seem to be universally available.  No big deal; it's easy enough to add a one-liner to any snippet or whatever that doesn't seem to get it:

Code:
$lang = isset($_COOKIE['Language']) ? $_COOKIE['Language'] : $modx->config['site_start'];

Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #13 on: Sep 11, 2005, 12:25 PM »

Just noticed that the main menu doesn't have a "home" page in this system.  Roll Eyes

Here's a chunk that can be inserted into the template immediately before calling the menu snippet:

Code:
<ul><li><a href="index.php?id=[*lang*]">Home</a></li></ul>

Remove the ul and li tags if you're using a menu snippet that doesn't create lists.

This will return the user to the main page for the language of his choice.

Inserted immediately before a menu snippet, inside the same block-level tags (such as <div>) it will be easy to control its display with the same CSS used on the snippet's menu items.  You would have to set the nodiv parameter in ListMenuX to "true", and enclose the whole menu in block-level tags in your template, for example:

Code:
<div id="topmenu" class="wide">
{{HomeMenuItem}}
[[ListMenuX?id=[*lang*]&depth=1&nodiv=true]]
</div> <!-- end top menu -->
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
sottwell
Documentation Team
*
Posts: 8,137



WWW
« Reply #14 on: Sep 12, 2005, 05:03 PM »

Found a nasty bug in the SetLangCookie snippet.  The site works fine, but "view source" or validating with the W3C tool would loop the redirect, since those don't set the cookie.  Here's the corrected snippet:

Code:
// SetLangCookie snippet for ModX
// set cookie with language ID
// 08-28-2005
// sottwell@sottwell.com

$lang=$modx->documentIdentifier;
setcookie("Language",$lang,time()+604800, "/", "", 0);

if(isset($_COOKIE['Language']) && $_COOKIE['Language'] != $lang) {
    $url = $modx->makeURL($lang);
    $modx->sendRedirect($url);
}
Logged

sottwell.com has moved to a lovely Solaris 10 server!
Log in username guest, password guestuser.
Templates are now becoming available at http://sottwell.com/templates.html
Pages: [1] 2 3 4   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP

Copyright © 2005-2008 MODxCMS, All rights reserved. Contact Us
Styles by ziworks.com

Powered by SMF 1.1.4 | SMF © 2005, Simple Machines LLC

Valid XHTML 1.0! Valid CSS!