Topic: Plugin for Multilanguage support  (Read 40328 times)

Pages: [1] 2 3   Go Down

#1: 15-Jun-2005, 10:30 PM

Coding Team

sottwell
Posts: 10,503

WWW
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.

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

#2: 16-Jun-2005, 01:02 AM

Emeritus
xwisdom
Posts: 1,732

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
xWisdom
www.xwisdomhtml.com
The fear of the Lord is the beginning of wisdom:
MODx Co-Founder - Create and do more with less.

#3: 17-Jun-2005, 03:19 PM

Testers

Lammikko
Posts: 432

WWW
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/
QuickManager+ Support | Download

Mikko Lammi, Owner at Maagit

#4: 18-Jun-2005, 04:23 AM

Coding Team
Carsten
Posts: 572

WWW
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?
Tangent-Warrior Smiley

#5: 18-Jun-2005, 08:08 AM

Coding Team

sottwell
Posts: 10,503

WWW
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.
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

#6: 18-Jun-2005, 12:34 PM

Coding Team

sottwell
Posts: 10,503

WWW
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. 
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

#7: 19-Jun-2005, 10:51 AM

Emeritus
xwisdom
Posts: 1,732


Very nice susan. Very nice Smiley
xWisdom
www.xwisdomhtml.com
The fear of the Lord is the beginning of wisdom:
MODx Co-Founder - Create and do more with less.

#8: 19-Jun-2005, 10:43 PM

Coding Team

sottwell
Posts: 10,503

WWW
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. 
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

#9: 20-Jun-2005, 05:41 AM

Coding Team

sottwell
Posts: 10,503

WWW
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 1432 times.)
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

#10: 20-Jun-2005, 09:07 AM

Foundation

rthrash
Posts: 11,348

WWW
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?
MODx is a content managmeent framework that allows web professionals to turn over sites to end-users for daily maintenance without worrying. Please help us help you when asking for assistance and read the wiki. Searching the forums from the top level helps, too.
Ryan Thrash
MODx Co-Founder
Principal @ Collabpad
work productively.
work intelligently.
work together.

#11: 20-Jun-2005, 12:48 PM

Coding Team

sottwell
Posts: 10,503

WWW
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: 20-Jun-2005, 12:51 PM by sottwell »
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

#12: 13-Jul-2005, 01:46 AM

Coding Team

sottwell
Posts: 10,503

WWW
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;
}
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

#13: 13-Jul-2005, 01:58 AM

Coding Team

sottwell
Posts: 10,503

WWW
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'];

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

#14: 11-Sep-2005, 12:25 PM

Coding Team

sottwell
Posts: 10,503

WWW
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 -->
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

#15: 12-Sep-2005, 05:03 PM

Coding Team

sottwell
Posts: 10,503

WWW
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);
}
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

#16: 12-Sep-2005, 05:53 PM

Coding Team

sottwell
Posts: 10,503

WWW
I decided that I don't like that kludge of redirecting to sync the cookie with the new language choice, so I reworked the TV and the snippet.

Drop this snippet into each of your main language home page documents:

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);

That sets the cookie.

Then use this TV wherever you need the $lang variable to be set, such as in setting the ID for a menu snippet:

Default value:
Code:
@EVAL
if($modx->documentObject['parent'] == 0) {
$lang=$modx->documentIdentifier;
}
else
 if(isset($_COOKIE['Language'])) {$lang=$_COOKIE['Language'];}
else {$lang=$modx->config['site_start'];}
return $lang;

This first checks to see if the document is one of the main root documents (parent=0; where your language home pages/folders are); if so, it uses that document's id for the $lang variable.  If not, it gets the $lang variable from the cookie; if that's not available, then it uses the default site_start, which should be your default language's home page.  So even if the user has cookies disabled, he'll still get the whole site in the default language, he just won't be able to change languages.

The first check is necessary because the $lang variable is grabbed from the (old) cookie before the new cookie is set, causing the menus to be one step behind the user's new language selection.  This way, if the page is a new language selection, its ID is used, instead of the old cookie value.  The cookie is now up-to-date, so further menu selections will be from the correct language folder.
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

#17: 12-Sep-2005, 07:09 PM

Coding Team
Carsten
Posts: 572

WWW
Susan,

Is this something that could worked into the core?

I'd really like to see modx multilingual out of the box without having to revert to work arounds.
Tangent-Warrior Smiley

#18: 13-Sep-2005, 04:10 AM

Coding Team

sottwell
Posts: 10,503

WWW
I wouldn't call this a "workaround" any more than the news snippet is a "workaround".  It's just a very simple way of creating a mulitlanguage site, mostly taking advantage of the document tree system.

I chose the cookie method over another method I used before that carried the $lang variable in the URL query string and the SESSION because that method required modifying a number of existing snippets and the core parsing engine.  Plus, sessions can time out, losing the user's language choice.

This method only requires one snippet, to set the cookie, one TV to get the value from the cookie (which you would need anyway, to get the value from wherever you are storing it, and feed it to whatever needs it), and one chunk to add a Home link to each language's main menu (which you would also need in any case).  It works seamlessly with all menu snippets, the web login snippet, and anything else you might want to have. 
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

#19: 13-Sep-2005, 10:36 AM

Coding Team
Carsten
Posts: 572

WWW
coool!

sorry about the bad usage of "work around" but in a sense I still think that multi-lingual support should really be at a base level rather than at a snippet/plugin level.  PostNuke did a pretty good job of it especially with their use of boxes based on which language the user was using.

Tangent-Warrior Smiley

#20: 14-Sep-2005, 02:45 AM

mmjaeger
Posts: 579

I decided that I don't like that kludge of redirecting to sync the cookie with the new language choice, so I reworked the TV and the snippet.

Drop this snippet into each of your main language home page documents:

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);

That sets the cookie.

Then use this TV wherever you need the $lang variable to be set, such as in setting the ID for a menu snippet:

Default value:
Code:
@EVAL
if($modx->documentObject['parent'] == 0) {
$lang=$modx->documentIdentifier;
}
else
 if(isset($_COOKIE['Language'])) {$lang=$_COOKIE['Language'];}
else {$lang=$modx->config['site_start'];}
return $lang;

This first checks to see if the document is one of the main root documents (parent=0; where your language home pages/folders are); if so, it uses that document's id for the $lang variable.  If not, it gets the $lang variable from the cookie; if that's not available, then it uses the default site_start, which should be your default language's home page.  So even if the user has cookies disabled, he'll still get the whole site in the default language, he just won't be able to change languages.

The first check is necessary because the $lang variable is grabbed from the (old) cookie before the new cookie is set, causing the menus to be one step behind the user's new language selection.  This way, if the page is a new language selection, its ID is used, instead of the old cookie value.  The cookie is now up-to-date, so further menu selections will be from the correct language folder.


I might be stupid but I'm not sure how to set this stuff up - I'm kind of new to modx, maybe that's why. I created a snippet with the setcookie stuff above, but where do I go from here? Do I have to put this
Code:
@EVAL
if($modx->documentObject['parent'] == 0) {
$lang=$modx->documentIdentifier;
}
else
 if(isset($_COOKIE['Language'])) {$lang=$_COOKIE['Language'];}
else {$lang=$modx->config['site_start'];}
return $lang;
just into the template or how do I use it.

I would very much appreciate if somebody could line out some detailled instructions.

thank you in advance for your input.
Pages: [1] 2 3   Go Up
0 Members and 1 Guest are viewing this topic.