Tag Archives: roller-weblogger

Roller Weblogger Related Entries and Blog Post code

Do you have lots of blog postings, possibly over a number of years? And do you suspect that despite embedding search and tag clouds into your blog that your readers are still not finding content related to that they enjoy? In fact do you have evidence of that very problem from your web analytics data but don’t know what to do about it? Yes? So did I, so I created this roller weblogger code to generate a list of the most related blog entries based upon tag and category relationships.

To show you what I mean, here’s examples for a couple of blog entries:

Messaging Sub-Systems in the UK Government

and

My banana yellow Yamaha V-Max

The way this works is that for any given individual blog post it attempts to match both category and tags to genuinely find the most related entries (first it cycles though entries related by the same category for tag matches, then though an aggregate of all categories for tag matches, and finally, just those related by category alone).

So this has to be my favourite roller weblogger functionality to date; simply because it offers up the most pertinent content to that which the reader is currently looking at, based upon tag affinity, without forcing them to search or link endlessly though tag clouds (which often contain large number of posts that have very little related contextual meaning).

Here’s the code:


## Related Entries
## Cycles through 100 articles of the same category - look for entries with tag matches - order by no. of matches
## Cycles through  20 articles of any category (ignoring the previous category) - look for entries with tag matches - order by no. of matches
## If still not enough items in the menu adds the most recent entries from the same category

    #if ($model.permalink)

        ## number to display (in menu) 
        #set ($dispFlag = 10)
        
        ## Cycles through 100 articles of the same category
        #set ($cycleEntriesTotal = 100)
        
        ## Go through same category first - should get most hits 
        #set ($cycleEntries = $model.weblog.getRecentWeblogEntries($model.weblogEntry.category.name, $cycleEntriesTotal))

        ## Set up other variables
        #set ($theseTags = $entry.tags)
        #set ($sawEntries = [] )
        #set ($sawCount = [] )

        #foreach ($cycleEntry in $cycleEntries)

            #if (($entry.title != $cycleEntry.title) && ($model.weblogEntry.locale == $cycleEntry.locale))

                #set ($matchBun = 1)
                #set ($bunTags = $cycleEntry.tags)

                #foreach ($thisTag in $theseTags)
                    #foreach ($bunTag in $bunTags)
                        #if ($thisTag.name == $bunTag.name)
                            #set ($matchBun = $matchBun + 1)
                        #end
                    #end
                #end

                #if ($matchBun >= 2)

                    #set ($bunEntry = $cycleEntry)
                    #set ($entryCounter = 0)
                    #set ($bunFlag = 0)

                    #foreach ($sawNum in $sawCount)
                        #if (($matchBun > $sawNum) || (($bunFlag == 1) && ($matchBun == $sawNum)))
                            #if ($bunFlag == 0)
                                #set ($bunFlag = 1)
                            #end
                            #set ($bunEntry = $sawEntries.set($entryCounter, $bunEntry))
                            #set ($matchBun = $sawCount.set($entryCounter, $matchBun))
                        #end
                        #set ($entryCounter = $entryCounter + 1)
                    #end

                    #if ($entryCounter <= $dispFlag)
                        #if ($sawEntries.add($bunEntry))
                        #end
                        #if ($sawCount.add($matchBun))
                        #end
                    #end

                #end

            #end

        #end
       
        ## Cycles through  20 articles of any category (ignoring the previous category)
        #set ($cycleEntriesTotal = 20)
        
        ## Go through all categories next - should get fewer hits 
        #set ($cycleEntries = $model.weblog.getRecentWeblogEntries("", $cycleEntriesTotal))

        ## Reset other variables
        #set ($theseTags = $entry.tags)

        #foreach ($cycleEntry in $cycleEntries)

            #if (($entry.title != $cycleEntry.title) && ($entry.category.name != $cycleEntry.category.name) && ($model.weblogEntry.locale == $cycleEntry.locale))

                #set ($matchBun = 0)

                #set ($bunTags = $cycleEntry.tags)
                #foreach ($thisTag in $theseTags)
                    #foreach ($bunTag in $bunTags)
                        #if ($thisTag.name == $bunTag.name)
                            #set ($matchBun = $matchBun + 1)
                        #end
                    #end
                #end

                #if ($matchBun >= 1)

                    #set ($bunEntry = $cycleEntry)
                    #set ($entryCounter = 0)
                    #set ($bunFlag = 0)

                    #foreach ($sawNum in $sawCount)
                        #if (($matchBun > $sawNum) || (($bunFlag == 1) && ($matchBun == $sawNum)))
                            #if ($bunFlag == 0)
                              #set ($bunFlag = 1)
                            #end
                            #set ($bunEntry = $sawEntries.set($entryCounter, $bunEntry))
                            #set ($matchBun = $sawCount.set($entryCounter, $matchBun))
                        #end
                        #set ($entryCounter = $entryCounter + 1)
                    #end

                    #if ($entryCounter <= $dispFlag)
                        #if ($sawEntries.add($bunEntry))
                        #end
                        #if ($sawCount.add($matchBun))
                        #end
                    #end

                #end

            #end

        #end

        <ul class="rEntriesList">

        ## Set up count variables
        #set ($noneFlag = 0)
        #set ($entryCounter = 0)

        ## Output related entries
        #foreach ($sawEntry in $sawEntries)

            #if ($noneFlag < $dispFlag)        
                #set ($noneFlag = $noneFlag + 1)
                #set ($matchBun = $sawCount.get($entryCounter))
                <li class="recentposts"><a href="$url.entry($sawEntry.anchor)" title="relationships: $matchBun" name="$utils.encode($sawEntry.anchor)" id="$utils.encode($sawEntry.anchor)">» $sawEntry.title</a></li>
            #end

            #set ($entryCounter = $entryCounter + 1)

        #end

        ## If still not enough items in the menu adds the most recent entries from the same category
        #if ($noneFlag < $dispFlag)

            ## Cycle though the number to be displayed (plus one including the 'calling' entry itself) 
            #set ($cycleEntriesTotal = ($dispFlag + 1))

            ## Reset variables
            #set ($cycleEntries = $model.weblog.getRecentWeblogEntries($model.weblogEntry.category.name, $cycleEntriesTotal))
  
            ## Cycle though the number to be displayed (plus one including the 'calling' entry itself) 
            #foreach ($cycleEntry in $cycleEntries)
  
                #set ($addFlag = 0)
                
                #foreach ($sawEntry in $sawEntries)
                    #if ($sawEntry.title == $cycleEntry.title)
                        #set ($addFlag = 1)
                    #end
                #end        

                #if ($addFlag == 0)
                    #if ($noneFlag < $dispFlag)        
                        #if (($entry.title != $cycleEntry.title) && ($model.weblogEntry.locale == $cycleEntry.locale))
                            #set ($noneFlag = $noneFlag + 1)
                            <li class="recentposts"><a href="$url.entry($cycleEntry.anchor)" title="relationships: 1" name="$utils.encode($cycleEntry.anchor)" id="$utils.encode($cycleEntry.anchor)">» $cycleEntry.title</a></li>
                        #end
                    #end
                #end

            #end

        #end

        </ul>

    #end

After writing this article I got a mention by Dave Johnson, which inspired me to change the code based on his comments so it only grabs the last 100 entries of the same category and then the last 20 entries of the other categories to analyse. Obviously you can change this by editing #set ($cycleEntriesTotal = 100) to whatever you feel appropriate.

I’ve also removed all redundant variables, fixed a bug due to non-allocation of variables, and changed the sort code so that it keeps date and time based precedence (so entries with the same number of matches will remain in date and time sequence).

Rather than repost another article I’ve retconned the code example into this article; the code displayed is the newer, healthier code; the old code is still there, but hidden in a non-displayed “div” code block at the end (just after this text in fact).

Links for this article:

Roller Weblogger Archive Menu Macro

Whenever I read my friend’s blogs who host on Blogger I always admire the archive menu that comes as standard; it’s pretty neat and really helps when you have a large number of blog posts over a number of years (something that bothers me because if a reader enjoys a blog post I want to make it as easy as possibly for them to find other, similar and related posts, but more on this later).

So I mocked up this simple, yet effective, version which produces a similar, self-generating, archive menu list.

Here’s an example:

And here’s the code (and don’t forget to change the $startYear and $startMonth variables to the year and month you started your blog as well as using your blog handle instead of ‘eclectic’):


## Blog Start Year
#set ($startYear  = 2007)

## Blog Start Month
#set ($startMonth = 4)

## Get the current Year and Month
#set ($countYear  = $now.getYear() + 1900)
#set ($countMonth = $now.getMonth() + 1)

## Text Adjust

#set ($zero = 0)

## Total number of years to cycle through
#set ($numberYear  = $countYear - $startYear)

<ul class="rEntriesList">

#foreach ($number in [0..$numberYear])

  #if ($countYear >= $startYear)

    ## Months in a year
    #foreach ($number in [1..12])

      #if  (($countMonth != 0) && ((($countYear > $startYear) || (($countYear == $startYear) && ($countMonth >= $startMonth)))))

        <li class="recentposts">

        #if ($countMonth     ==  1)
          #set ($nameMonth = "January")
        #elseif ($countMonth ==  2)
          #set ($nameMonth = "February")
        #elseif ($countMonth ==  3)
          #set ($nameMonth = "March")
        #elseif ($countMonth ==  4)
          #set ($nameMonth = "April")
        #elseif ($countMonth ==  5)
          #set ($nameMonth = "May")
        #elseif ($countMonth ==  6)
          #set ($nameMonth = "June")
        #elseif ($countMonth ==  7)
          #set ($nameMonth = "July")
        #elseif ($countMonth ==  8)
          #set ($nameMonth = "August")
        #elseif ($countMonth ==  9)
          #set ($nameMonth = "September")
        #elseif ($countMonth == 10)
          #set ($nameMonth = "October")
        #elseif ($countMonth == 11)
          #set ($nameMonth = "November")
        #elseif ($countMonth == 12)
          #set ($nameMonth = "December")
        #end

        #if ($countMonth > 9)
          <a href="http://web.archive.org/web/20100123170540/http://blogs.sun.com/eclectic/?date=$countYear$countMonth">» $nameMonth, $countYear</a>
        #else
          <a href="http://web.archive.org/web/20100123170540/http://blogs.sun.com/eclectic/?date=$countYear$zero$countMonth">» $nameMonth, $countYear</a>
        #end

        </li>

        #set ($countMonth = $countMonth - 1)

      #end
 
    #end

  #set ($countMonth = 12)

##</ul>

  #end

#set ($countYear = $countYear - 1)

#end

</ul>

Links for this article:

Roller Weblogger alternative Next Previous function

Another one of the standard roller weblogger functions I replaced: the next / previous function. Basically I just wanted this to be a little more informative, and host it as a menu list in the sidebar.

Here’s a couple of examples:

and

And here’s the code (don’t forget to change the blog handle from ‘eclectic’):


<ul class="rEntriesList">

#if ($model.results)

<li class="recentposts">» Currently viewing search results... </li>

#else

	#if($model.permalink)
		<li class="recentposts">» Reading <a href="$url.entry($model.weblogEntry.anchor)">$model.weblogEntry.title</a> </li>
                #if ($pager.prevLink)
                <li class="recentposts">» Backward: <a href="$pager.prevLink">$pager.prevName</a> </li>
                #end
                #if ($pager.prevCollectionLink)
                <li class="recentposts">» Backward: <a href="$pager.prevCollectionLink">$pager.prevCollectionName</a> </li>
                #end
                #if ($pager.nextLink)
                <li class="recentposts">» Forward: <a href="$pager.nextLink">$pager.nextName</a> </li>
                #end
                #if ($pager.nextCollectionLink)
                <li class="recentposts">» Forward: <a href="$pager.nextCollectionLink">$pager.nextCollectionName</a> </li>
                #end
                <li class="recentposts">» Return to <a href="http://web.archive.org/web/20090416190738/http://blogs.sun.com/eclectic/">main site</a>. </li>
  	#elseif($model.weblogCategory)
		<li class="recentposts">» Reading entries related to the "<a href="$url.entry($model.weblogCategory.anchor)">$model.weblogCategory.name</a>" category </li>
                <li class="recentposts">» From <a href="$url.entry($entryFirst.anchor)">$entryFirst.title</a> </li>
                <li class="recentposts">» To: <a href="$url.entry($entryLast.anchor)">$entryLast.title</a> </li>
                #if ($pager.prevLink)
                <li class="recentposts">» Backward: <a href="$pager.prevLink">$pager.prevName</a> </li>
                #end
                #if ($pager.prevCollectionLink)
                <li class="recentposts">» Backward: <a href="$pager.prevCollectionLink">$pager.prevCollectionName</a> </li>
                #end
                #if ($pager.nextLink)
                <li class="recentposts">» Forward: <a href="$pager.nextLink">$pager.nextName</a> </li>
                #end
                #if ($pager.nextCollectionLink)
                <li class="recentposts">» Forward: <a href="$pager.nextCollectionLink">$pager.nextCollectionName</a> </li>
                #end
                <li class="recentposts">» Return to <a href="http://web.archive.org/web/20090416190738/http://blogs.sun.com/eclectic/">main site</a>. </li>
	#else
		<li class="recentposts">» Reading the <a href="http://web.archive.org/web/20090416190738/http://blogs.sun.com/eclectic/">main</a> site </li>

                <li class="recentposts">» From <a href="$url.entry($entryFirst.anchor)">$entryFirst.title</a> </li>
                <li class="recentposts">» To: <a href="$url.entry($entryLast.anchor)">$entryLast.title</a> </li>
                #if ($pager.prevLink)
                <li class="recentposts">» Backward: <a href="$pager.prevLink">$pager.prevName</a> </li>
                #end
                #if ($pager.prevCollectionLink)
                <li class="recentposts">» Backward: <a href="$pager.prevCollectionLink">$pager.prevCollectionName</a> </li>
                #end
                #if ($pager.nextLink)
                <li class="recentposts">» Forward: <a href="$pager.nextLink">$pager.nextName</a> </li>
                #end
                #if ($pager.nextCollectionLink)
                <li class="recentposts">» Forward: <a href="$pager.nextCollectionLink">$pager.nextCollectionName</a> </li>
                #end
	#end

#end

</ul>

Links for this article:

Integrating Disqus and Roller Weblogger on blogs.sun.com

I’ve recently updated my site to use Disqus the blog comment hosting and conversation site.

Done this for two reasons:

  1. Firstly my usually frustration with any status quo means I want more functionality delivered yesterday, and although I’d started to have a look at the functionality I wanted and how I might add it as a Roller macro / velocity code I didn’t want to spend a huge amount of time coding it out (the functionality I specifically wanted was the separation of comments and trackbacks, as well as comment ‘threads’).
  2. Secondly to gain readership and comments from the sizeable blog comment audience that Disqus have built up (Disqus is estimated to be running on over 30,000+ servers).

I’ve already had a couple of comments from Disqus members, and I’ll have to see how it goes before I start heralding it as an unprecedented success, but I’m very pleased with the results (both aesthetic and functional).

Sadly the Disqus comment import function was initially provided for WordPress and Blogger, but apparently wasn’t fully functional; subsequently an update is due out soon that will hopefully include Roller Weblogger. See this Disqus forum entry, and it’s threads for more info: How do I import comments?

Given this was the case I wanted to make sure my blog supported my new Disqus commenting system, but would still show my old comments if there where any for an entry. Here are a few examples:

The code I developed, which has to be separated into two components (number of comments associated with a blog entry, and comment entry form and comment display), is below, but if you use or copy it please note that you need to replace the Disqus supplied JavaScript for my site with your Disqus comments hosted sites JavaScript code. ¨C18C

Combined Roller Weblogger and Disqus Number of Comments code

For comment numbers I’ve broken it down into displaying “n Comments” for Disqus on it’s own, whilst “x Comments (new, via Disqus) and y Comments (legacy, via Roller)” for comments hosted on both systems.

This replaces the code in the Roller Weblogger “_day” template which displays the number of comments per blog entry.

Don’t forget to replace occurrences of ‘eclectic’, my blog handle, with yours (just one, about the fifth line from the end).

## Number of Comments

<a href="$url.entry($entry.anchor)#disqus_thread">View Comments</a>

#set($commentCount = $entry.commentCount)
#if ($commentCount &gt; 0)
    (new, via <a href="https://web.archive.org/web/20090830081824/http://www.disqus.com/" target="_blank">Disqus</a>) and 
    #if ($commentCount == 1)
        <a href="$url.comments($entry.anchor)">$commentCount Comment</a> (old, via <a href="https://web.archive.org/web/20090830081824/http://rollerweblogger.org/" target="_blank">Roller</a>) 
    #else
        <a href="$url.comments($entry.anchor)">$commentCount Comments</a> (old, via <a href="https://web.archive.org/web/20090830081824/http://rollerweblogger.org/" target="_blank">Roller</a>) 
    #end
#end

<script type="text/javascript">
//<[CDATA[
(function() {
		var links = document.getElementsByTagName('a');
		var query = '?';
		for(var i = 0; i < links.length; i++) {
			if(links[i].href.indexOf('#disqus_thread') >= 0) {
				query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&';
			}
		}
		document.write('<script type="text/javascript" src="https://web.archive.org/web/20090830081824/http://disqus.com/forums/eclectic/get_num_replies.js' + query + '"></' + 'script>');
	})();
//]]>
</script>

Combined Roller Weblogger and Disqus Comment entry and Comments display code

This basically displays the Disqus commenting system, along with any Disqus hosted comments, however if any ‘legacy’ Roller Weblogger hosted comments are found it displays those too.

It replaces the code in the Roller Weblogger “permalink” template which displays comments themselves (the same changes may need to be made to the “weblog” and “searchresults” templates too).

## Comments

<h2>Comments (new, via <a href="https://web.archive.org/web/20090830081824/http://www.disqus.com/" target="_blank">Disqus</a>)</h2>

<div id="disqus_thread"></div><script type="text/javascript" src="https://web.archive.org/web/20090830081824js_/http://disqus.com/forums/eclectic/embed.js"></script><noscript><a href="https://web.archive.org/web/20090830081824/http://eclectic.disqus.com/?url=ref">View the forum thread.</a></noscript><a href="https://web.archive.org/web/20090830081824/http://disqus.com/" class="dsq-brlink">blog comments powered by <span class="logo-disqus">Disqus</span></a>

##showWeblogEntryComments($model.weblogEntry)
##showWeblogEntryCommentForm($model.weblogEntry)

<br></br>

#set($commentCount = $entry.commentCount)
#if ($commentCount &gt; 0)
    <h2>Comments (old, via <a href="https://web.archive.org/web/20090830081824/http://rollerweblogger.org/" target="_blank">Roller</a>)</h2>
    #showWeblogEntryComments($model.weblogEntry)
    <br></br>
#end

Additional benefits that I’ve picked up by implementing Disqus include:

  • Following commentators.
  • Having my, and my sites, comments followed.
  • Being able to easily ‘reblog’ my comments and make blog entries out of them (looking forward to trying this, although I haven’t yet).

Finally here’s my Disqus profile for you to have a look at: http://www.disqus.com/people/wayne_horkan/

Links for this article:

Roller Weblogger improved blogroll display code for use with icons and images

Here’s some code that improves on the default Roller Weblogger blogroll code to ensure that if you include an image / icon for your blogroll entries it displays the image and the blogroll entry name (good if your using icons).

Using the default roller macro for displaying your blogroll means that no text is displayed if an image / icon URL is given. The default behaviour leaves rather empty lists of course, so I see this as an improvement.

So this code is effectively a replacement for “#showBookmarkLinksList($rootFolder false false)”, although it still uses the same CSS notation for lists, so as not to disturb your design.

It works with specific blogroll folders, so you do need to dictate which one your going to display (in the example below it’s “/My Blog Roll”). However this could be improved further to pick up the current folder name if you were to cycle through the sub folders (but I didn’t need it to do that, so I haven’t).


#set($rootFolder = $model.weblog.getBookmarkFolder("/My Blog Roll"))
#if ($rootFolder.bookmarks.size() > 0)

<h2>My Blog Roll</h2>



    #foreach ($bookmark in $rootFolder.bookmarks)
  • $bookmark.name  $bookmark.name
  • #end
#end

Roller Weblogger blog post tag link code for blogs.sun.com, technorati and del.icio.us

Here’s some code that produces tag links to four popular tag destinations, your blog, blogs.sun.com, technorati and del.icio.us (for your Roller Weblogger based blog, as the code is velocity).

It also ensures that the links are marked as tags, so that crawlers that look for and index tags and tag data will pick them up (microformat and semantic web focused applications, like the ‘Operator’ plug in for Firefox also pick them up of course).

I only add it to individual entries, rather than collections, which is what “#if ($model.permalink)” checks for.

Best to add just before or just after the Comments section in your main Roller template.

The code also includes some functionality to replace ‘-‘ and ‘+’ with ‘ ‘, so as to make the text fit in the table (this does not affect the tag).

You can see this functionality here, on my blog, and on Bill Vass’ blog, however you will need to look at an individual blog post to see it.

If your on bsc you’ll need to replace ‘eclectic’ with your bsc blog name, or if your not on bsc you’ll need to replace “http://blogs.sun.com/eclectic/” with your entire blog URL.

Here’s the code:


#if ($model.permalink) <!-- BEGIN TAGS: $entry.title -->

<div class="comments-head">Tags</div>



This blog

Sun Blogs

Technorati

Del.icio.us

#end

Roller Weblogger language translation with Google using JavaScript and Velocity

So here’s the latest version of my Google Translation code for Roller Weblogger, as used to host blogs.sun.com (or ‘bsc’ as we in Sun call it).

It’s much improved over the original and Google versions.

First off it checks for JavaScript, if there then it uses it to make sure the page hasn’t been translated before as well as get the current URL to translate (Google generates translation glitches if it tries to translate pre-translated pages).

If not it still generates the language translation by using Velocity / Roller JSP code.

If your on bsc then $baseURL needs the ‘eclectic’ text replaced with whatever your bsc site is called, otherwise $baseURL needs to be your blog URL.

If your not on bsc you’ll also need to change $iconURL to where you are hosting the flag images (which I got from FamFamFam), unless of course you leech off mine.

So far this code is being used on Bill Vass’ blog and Glenn Brunette’s Security Weblog (as well as this one of course).

Here’s the code for you to have a look at and cut and paste if you’d like to use it. It needs to be embedded in your Roller template code, probably best to put it in the sidebar. You can also use it to translate from other source languages other than English, more on this at the bottom of the page.



<div style="margin:0px; padding:1px;"> <!-- BEGIN _MOD_TRANSLATE --> ## Current Language
#set ($langCur="en") ## Lang Text
#set ($langText_ar="Arabic")
#set ($langText_bg="Bulgarian")
#set ($langText_zh-CN="Chinese (Simplified)")
#set ($langText_zh-TW="Chinese (Traditional)")
#set ($langText_hr="Croatian")
#set ($langText_cs="Czech")
#set ($langText_da="Danish")
#set ($langText_nl="Dutch")
#set ($langText_en="English")
#set ($langText_fi="Finnish")
#set ($langText_fr="French")
#set ($langText_de="German")
#set ($langText_el="Greek")
#set ($langText_hi="Hindi")
#set ($langText_it="Italian")
#set ($langText_ja="Japanese")
#set ($langText_ko="Korean")
#set ($langText_no="Norwegian")
#set ($langText_pl="Polish")
#set ($langText_pt="Portuguese")
#set ($langText_ro="Romanian")
#set ($langText_ru="Russian")
#set ($langText_es="Spanish")
#set ($langText_sv="Swedish") ## Lang Code
#set ($langCode_ar="ar")
#set ($langCode_bg="bg")
#set ($langCode_zh-CN="zh-CN")
#set ($langCode_zh-TW="zh-TW")
#set ($langCode_hr="hr")
#set ($langCode_cs="cs")
#set ($langCode_da="da")
#set ($langCode_nl="nl")
#set ($langCode_en="en")
#set ($langCode_fi="fi")
#set ($langCode_fr="fr")
#set ($langCode_de="de")
#set ($langCode_el="el")
#set ($langCode_hi="hi")
#set ($langCode_it="it")
#set ($langCode_ja="ja")
#set ($langCode_ko="ko")
#set ($langCode_no="no")
#set ($langCode_pl="pl")
#set ($langCode_pt="pt")
#set ($langCode_ro="ro")
#set ($langCode_ru="ru")
#set ($langCode_es="es")
#set ($langCode_sv="sv") ## Other variables
#set ($tranText01="<a style="padding:0px" title='") #set ($tranText02="' href="%22)%20%20%20%0A#set%20(%24tranServer=%22http://66.102.9.104%22)%20%20%20%0A#set%20(%24tranCommand=%22/translate_c?hl=%22)%20%20%20%0A#set%20(%24tranText03=%22&#038;langpair=%22)%20%20%20%0A#set%20(%24tranText04=%22%7C%22)%20%20%20%0A#set%20(%24tranText05=%22&#038;u=%22)%20%20%20%0A#set%20(%24tranText06=%22"><img decoding="async" alt='") #set ($tranText07="' style="border:0px; padding:0px 8px 8px 0px;" src="/web/20090226230923im_/http://blogs.sun.com/eclectic/entry/%22)%20%20%20#set%20(%24iconURL=%22http://blogs.sun.com/eclectic/resource/%22)%20%20%20#set%20(%24iconTLA=%22.png"></a>" ) ## specific blog entry ? #if ($model.permalink) #set ($baseurl="http://blogs.sun.com/BVass/entry/$utilities.encode($model.weblogEntry.anchor)" ) #else #set ($baseurl="http://blogs.sun.com/BVass/" ) #end <script language="JavaScript"><br/><!--<br/>{ /* Allocate memory */<br/>strLangPage = new String (location.href); /* Catch pages translated externally and re-assign */<br/>if ((strLangPage.indexOf("/translate_c?")!=-1)) { /* Catch pages with the new google tracking code and strip */ if ((strLangPage.indexOf("&usg=")!=-1)) { strLangPage = strLangPage.slice((strLangPage.lastIndexOf("u=") + 2),(strLangPage.lastIndexOf("&usg=") )); } else { strLangPage = strLangPage.slice((strLangPage.lastIndexOf("u=") + 2)); } } document.write("<div style='margin:0px; padding:0px;'>");<br/>document.write("$tranText01$langText_ar$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ar$tranText05",strLangPage,"$tranText06$langCode_ar$tranText07$iconURL$langCode_ar$iconTLA$tranText08");<br/>document.write("$tranText01$langText_bg$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_bg$tranText05",strLangPage,"$tranText06$langCode_bg$tranText07$iconURL$langCode_bg$iconTLA$tranText08");<br/>document.write("$tranText01$langText_zh-CN$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_zh-CN$tranText05",<br/>strLangPage,<br/>"$tranText06$langCode_zh-CN$tranText07$iconURL$langCode_zh-CN$iconTLA$tranText08");<br/>document.write("$tranText01$langText_zh-TW$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_zh-TW$tranText05",<br/>strLangPage,<br/>"$tranText06$langCode_zh-TW$tranText07$iconURL$langCode_zh-TW$iconTLA$tranText08");<br/>document.write("$tranText01$langText_hr$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_hr$tranText05",strLangPage,"$tranText06$langCode_hr$tranText07$iconURL$langCode_hr$iconTLA$tranText08");<br/>document.write("$tranText01$langText_cs$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_cs$tranText05",strLangPage,"$tranText06$langCode_cs$tranText07$iconURL$langCode_cs$iconTLA$tranText08");<br/>document.write("$tranText01$langText_da$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_da$tranText05",strLangPage,"$tranText06$langCode_da$tranText07$iconURL$langCode_da$iconTLA$tranText08");<br/>document.write("$tranText01$langText_nl$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_nl$tranText05",strLangPage,"$tranText06$langCode_nl$tranText07$iconURL$langCode_nl$iconTLA$tranText08");<br/>document.write("$tranText01$langText_en$tranText02",strLangPage,"$tranText06$langCode_en$tranText07$iconURL$langCode_en$iconTLA$tranText08");<br/>document.write("$tranText01$langText_fi$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_fi$tranText05",strLangPage,"$tranText06$langCode_fi$tranText07$iconURL$langCode_fi$iconTLA$tranText08");<br/>document.write("$tranText01$langText_fr$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_fr$tranText05",strLangPage,"$tranText06$langCode_fr$tranText07$iconURL$langCode_fr$iconTLA$tranText08");<br/>document.write("$tranText01$langText_de$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_de$tranText05",strLangPage,"$tranText06$langCode_de$tranText07$iconURL$langCode_de$iconTLA$tranText08");<br/>document.write("$tranText01$langText_el$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_el$tranText05",strLangPage,"$tranText06$langCode_el$tranText07$iconURL$langCode_el$iconTLA$tranText08");<br/>document.write("$tranText01$langText_hi$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_hi$tranText05",strLangPage,"$tranText06$langCode_hi$tranText07$iconURL$langCode_hi$iconTLA$tranText08");<br/>document.write("$tranText01$langText_it$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_it$tranText05",strLangPage,"$tranText06$langCode_it$tranText07$iconURL$langCode_it$iconTLA$tranText08");<br/>document.write("$tranText01$langText_ja$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ja$tranText05",strLangPage,"$tranText06$langCode_ja$tranText07$iconURL$langCode_ja$iconTLA$tranText08");<br/>document.write("$tranText01$langText_ko$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ko$tranText05",strLangPage,"$tranText06$langCode_ko$tranText07$iconURL$langCode_ko$iconTLA$tranText08");<br/>document.write("$tranText01$langText_no$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_no$tranText05",strLangPage,"$tranText06$langCode_no$tranText07$iconURL$langCode_no$iconTLA$tranText08");<br/>document.write("$tranText01$langText_pl$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_pl$tranText05",strLangPage,"$tranText06$langCode_pl$tranText07$iconURL$langCode_pl$iconTLA$tranText08");<br/>document.write("$tranText01$langText_pt$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_pt$tranText05",strLangPage,"$tranText06$langCode_pt$tranText07$iconURL$langCode_pt$iconTLA$tranText08");<br/>document.write("$tranText01$langText_ro$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ro$tranText05",strLangPage,"$tranText06$langCode_ro$tranText07$iconURL$langCode_ro$iconTLA$tranText08");<br/>document.write("$tranText01$langText_ru$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ru$tranText05",strLangPage,"$tranText06$langCode_ru$tranText07$iconURL$langCode_ru$iconTLA$tranText08");<br/>document.write("$tranText01$langText_es$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_es$tranText05",strLangPage,"$tranText06$langCode_es$tranText07$iconURL$langCode_es$iconTLA$tranText08");<br/>document.write("$tranText01$langText_sv$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_sv$tranText05",strLangPage,"$tranText06$langCode_sv$tranText07$iconURL$langCode_sv$iconTLA$tranText08");<br/>document.write("</div>"); }<br/>// --><br/></script> <noscript>

<div style="margin:0px; padding:0px;">
$tranText01$langText_ar$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ar$tranText05$baseURL$tranText06$langCode_ar$tranText07$iconURL$langCode_ar$iconTLA$tranText08
$tranText01$langText_bg$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_bg$tranText05$baseURL$tranText06$langCode_bg$tranText07$iconURL$langCode_bg$iconTLA$tranText08
$tranText01$langText_zh-CN$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_zh-CN$tranText05$baseURL$langCode$tranText06$langCode_zh-CN$tranText07$iconURL$langCode_zh-CN$iconTLA$tranText08
$tranText01$langText_zh-TW$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_zh-TW$tranText05$baseURL$langCode$tranText06$langCode_zh-TW$tranText07$iconURL$langCode_zh-TW$iconTLA$tranText08
$tranText01$langText_hr$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_hr$tranText05$baseURL$tranText06$langCode_hr$tranText07$iconURL$langCode_hr$iconTLA$tranText08
$tranText01$langText_cs$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_cs$tranText05$baseURL$tranText06$langCode_cs$tranText07$iconURL$langCode_cs$iconTLA$tranText08
$tranText01$langText_da$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_da$tranText05$baseURL$tranText06$langCode_da$tranText07$iconURL$langCode_da$iconTLA$tranText08
$tranText01$langText_nl$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_nl$tranText05$baseURL$tranText06$langCode_nl$tranText07$iconURL$langCode_nl$iconTLA$tranText08
$tranText01$langText_en$tranText02$baseURL$langCode$tranText06$langCode_en$tranText07$iconURL$langCode_en$iconTLA$tranText08
$tranText01$langText_fi$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_fi$tranText05$baseURL$tranText06$langCode_fi$tranText07$iconURL$langCode_fi$iconTLA$tranText08
$tranText01$langText_fr$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_fr$tranText05$baseURL$tranText06$langCode_fr$tranText07$iconURL$langCode_fr$iconTLA$tranText08
$tranText01$langText_de$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_de$tranText05$baseURL$tranText06$langCode_de$tranText07$iconURL$langCode_de$iconTLA$tranText08
$tranText01$langText_el$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_el$tranText05$baseURL$tranText06$langCode_el$tranText07$iconURL$langCode_el$iconTLA$tranText08
$tranText01$langText_hi$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_hi$tranText05$baseURL$tranText06$langCode_hi$tranText07$iconURL$langCode_hi$iconTLA$tranText08
$tranText01$langText_it$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_it$tranText05$baseURL$tranText06$langCode_it$tranText07$iconURL$langCode_it$iconTLA$tranText08
$tranText01$langText_ja$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ja$tranText05$baseURL$tranText06$langCode_ja$tranText07$iconURL$langCode_ja$iconTLA$tranText08
$tranText01$langText_ko$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ko$tranText05$baseURL$tranText06$langCode_ko$tranText07$iconURL$langCode_ko$iconTLA$tranText08
$tranText01$langText_no$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_no$tranText05$baseURL$tranText06$langCode_no$tranText07$iconURL$langCode_no$iconTLA$tranText08
$tranText01$langText_pl$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_pl$tranText05$baseURL$tranText06$langCode_pl$tranText07$iconURL$langCode_pl$iconTLA$tranText08
$tranText01$langText_pt$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_pt$tranText05$baseURL$tranText06$langCode_pt$tranText07$iconURL$langCode_pt$iconTLA$tranText08
$tranText01$langText_ro$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ro$tranText05$baseURL$tranText06$langCode_ro$tranText07$iconURL$langCode_ro$iconTLA$tranText08
$tranText01$langText_ru$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_ru$tranText05$baseURL$tranText06$langCode_ru$tranText07$iconURL$langCode_ru$iconTLA$tranText08
$tranText01$langText_es$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_es$tranText05$baseURL$tranText06$langCode_es$tranText07$iconURL$langCode_es$iconTLA$tranText08
$tranText01$langText_sv$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_sv$tranText05$baseURL$tranText06$langCode_sv$tranText07$iconURL$langCode_sv$iconTLA$tranText08
</div>

</noscript> <!-- END _MOD_TRANSLATE --></div>

As I mentioned above it can also be used to translate from other source languages other than English.

By changing #set ($langCur=”en”) it’ll translate from other source languages. For instance set $langCur to ‘fr’ to use French as a the source language. You’ll also need to transpose the ‘en’ output strings with that of the source language you want to use, because Google doesn’t like trying to translate a page to and from the same language! For instance, still using French as an example:


$tranText01$langText_en$tranText02$baseURL$langCode$tranText06$langCode_en$tranText07$iconURL$langCode_en$iconTLA$tranText08
needs to become
$tranText01$langText_fr$tranText02$baseURL$langCode$tranText06$langCode_fr$tranText07$iconURL$langCode_fr$iconTLA$tranText08
and reciprocally
$tranText01$langText_fr$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_fr$tranText05$baseURL$tranText06$langCode_fr$tranText07$iconURL$langCode_fr$iconTLA$tranText08
needs to become
$tranText01$langText_en$tranText02$tranServer$tranCommand$langCur$tranText03$langCur$tranText04$langCode_en$tranText05$baseURL$tranText06$langCode_en$tranText07$iconURL$langCode_en$iconTLA$tranText08

Remember that there are two versions of this string though, one encapsulated in JavaScript (using ‘document.write’) and the other free standing in the ‘NOSCRIPT’ element, just in case JavaScript isn’t used.

Internationalizing a Roller Weblogger based Blog

So I’ve been spending time lately providing better international support to the blog.

In fact check out the variants I’ve put together:

For the translator I used the Yahoo Babelfish translation service, rather than Google Translate (which I use to produce on demand translations of the site, at the time of posting this it should be at the top of the right hand sidebar), because I didn’t want to become tied in to a single Translation Service Supplier.

During translation I switched to using “blog” rather than “weblog” for the title, as many of the languages would translate blog but not weblog (possibly a weakness of the translation service).

I was alluding to the new multi-language pages and the new multi-lingual nature of the blog in the post on St. Patrick’s Day, however I’d only translated some of the posts, not internationalised the site itself, and so it wasn’t really time to go live, but I did want the posts to start being spidered (and the post “Weblog language translator – blog translation on the fly with Roller specific functionality” explains why).

iron l10n zion – or how I did it…

There are a number of ways to internationalize a blog running over Roller Weblogger, for instance at the Aquarium, another Sun Blog, they use multiple blogs instances, like the Japanese Aquarium, I didn’t go for this approach as I wanted to keep to a single blog instance (due to maintainability basically).

I approached the problem by having a language resource file which loads as the session begins based upon the locale determined in the URL.

At run time this is done dynamically like this:

  1. Decide which locale the user is loading the page from
  2. Load the language specific resource file / pack (from a repository of language resource files, of which there is one resource file / pack per language)
  3. Variables are already allocated and populated with language specific data
  4. Use the above variables throughout the Roller Weblogger template code (HTML mainly) to create the page
  5. Present the page to the user requesting it

A number of language resource files were needed, all of which I populated with text based named variables (obviously the name of the variable stays the same, just the content per language resource file is different).

Then I replaced all the specific uses of text across my roller templates with calls to those variables.

This is a code snippet example of the code which decides which language resource file to load, and yes, before you say it, it’s not aesthetically pleasing, but I’m the only person who’ll be debugging it, so I’ll let myself off on that one. As you can see it checks which locale the page is being called from (based on the URL, but you can’t see that bit), once it finds a positive it loads the language specific resource file (notice I also ensure to load a default at the end if a match can’t be found).





  #if ($model.locale == "en")





      #includeTemplate($model.weblog "_lang_en")





  #elseif ($model.locale == "zh")





      #includeTemplate($model.weblog "_lang_zh")





  #else





      #includeTemplate($model.weblog "_lang_en")





  #end




And here’s an example of a specific language resource file, in this case this is the start of “_lang_de”, one of the files that would have been loaded based on the logic in the above piece. As you can see it has my (string) variables allocated and populated.





  #set ($gtTitle = "Blog Wayne-Horkans: eklektisch")





  #set ($gtMostPopTags = "Die meisten populären Umbauten")





  #set ($gtSitePrefs = "Aufstellungsort-Präferenzen")




Here’s an example use of the $gtTitle (string) variable from above within the Roller Weblogger template, which Roller builds dynamically at run time, obviously if the page was being


  $gtTitle

Probably the worst part of this was being adversely effected by Roller timezone and localization sensitivity issues as documented in ROL-1337 “all components involved in weblog rendering need to be locale & timezone sensitive”.

For instance when generating blog specific URLs in my templates, not all of the Roller Weblogger functions, macros and variables are timezone / localisation safe, and so for a number of them I’ve had to step though the templates modding the code to be timezone and localization safe as I go.

This meant that I had to bodge parts of the code with temporary ‘fixes’ to make up for the incomplete coverage, but it will do for now.

A couple of the most obvious issues was one with dates, as “$utils.formatDate” only produces day and month names in English, and another with “$entry.permalink” as it produces a non-locale specific URL





  ## Replaced instances of $entry.permalink with $entryLSP (Locale Specific Permalink)


  #set ($entryLSP = 

  "http://blogs.sun.com/eclectic/$model.locale/entry/$utilities.encode($entry.anchor)")




I may also have to write alternate macros which are locale specific, including the one that generates a list of recent entries:





  #set ($rEntries = $model.weblog.getRecentWeblogEntries($chosenCat, $rEntriesTotal)) 




  ## Have to use this as locale settings don't yet effect "getRecentWeblogEntries"


  #showWeblogEntryLinksList($rEntries)




I don’t want to give the impression that non of the Roller Weblogger timezone / locale specfic functionality works. In fact a lot more than I assumed would, did. Including the menu (content) functions and Tag URL functions, and I was very pleased that there was the level of support in Roller that there is for internationalization.

I’ll be providing more multi-lingual content, specifically the content rich, article like posts I’ve been doing, so far I’ve translated three posts of this ilk:

Weblog language translator – blog translation on the fly with Roller specific functionality

Finally got round to upgrading my ‘Weblog language translator‘ from beta.

Key to improving it was removing the roll over based banner I had implemented (the Google translation service, which I piggy-back off of, only translates circa 3k characters, so the banner header, full of links was using up the majority of the translation).

Obviously this points out a few of the flaws of the implementation, namely reliance on Google to provide the service (and of course a dependency on the call syntax not changing), and all of the weaknesses that follow on from relying on the Google service, not least the translatable character limit.

This time round I’m much happier with the implementation – and I’ve done a fair bit of testing to ensure it’s fit for purpose.

Unlike the other implementations out on the web I’ve added Roller specific functionality, implemented in JavaScript, creating a ‘main’ (or rather ‘weblog’) page for each language.

I did this because I wanted to tailor the service to be language specific, and because the major search engines outside of the English speaking, Google dominated, Internet, often verify that there is actual language specific content (and I want these search engines to be able to index my site, even if that’s only a couple of pages).

The code uses Roller Weblogger specific URL notations to provide the matching ‘weblog_xx’ (where xx stands for the two character country code – five characters when looking at Traditional and Simplified Chinese) to the target language to be translated to.

Currently it works for the generic weblog URL, all ‘entry’ variants, all ‘date’ variants, and all ‘page’ variants. It doesn’t work for ‘tags’ or ‘category’ variants (mainly because I haven’t had time to research the URL notation), but I hope to get this done soon. I’ll research and code up the other, alternative Roller URL formations when I next revisit the code. I find this acceptable, as it still provides a translation, however without accessing the language specific ‘weblog’ page.

The JavaScript is available via the page source – and you’re welcome to have a look and re-use if you wish (it’s nowhere near the nicest bit of JavaScript available – if you’d like to tidy it up at all you’re more than welcome).

I’ve also added Dutch and Greek to the list of languages that can be translated to, as these have been recently added to Google’s translation service (still no Hindi or Bengali though). That makes a total of fourteen languages, including the already implemented Simplified Chinese, Traditional Chinese (Taiwanese), English, Spanish, Arabic, Portuguese, Russian, Japanese, German, Korean, French and Italian. Plus I’ve replaced the language text with flag icons – which improves the look and feel too.

The icons are “available for free use for any purpose with no requirement for attribution” (although I thought it would be nice to credit the originating site) from FamFamFam, by fellow ‘Brummie‘ Mark James, available at http://www.famfamfam.com/lab/icons/flags/

Previously, after the initial implementation in beta, I found a variety of resources in a similar vein, none of which are Roller specific though, here’s a few examples for you to have a look at if you’re interested:

Have to admit I’m really glad I’ve tidied this up as I was starting to feel as though it was in danger of genuinely being in ‘permanent beta’, and however fashionable that is, in the apocryphal words of Steve Jobbs: “real artists ship”.