Bi-Directional Language Applications in Iron Speed Designer
This article demonstrates a solution that sets user's respective reading directions when they log into the application.
- Gil Givati, CEO of Efficens Software Ltd.

April 4, 2007
Iron Speed Designer V4.2
Introduction
As the world becomes smaller, the applications we develop require us to support different communities in different areas of the globe. Iron Speed Designer does a tremendous work for you by generating multiple-language applications and also by creating the infrastructure for switching between languages using the Set Language page, automatically generated by Iron Speed Designer.

This article addresses the specific issue of bi-directional language display. You probably know languages such as Hebrew and Arabic are written from right to left, in contrast to American, European, and Asian languages which are written from left to right. This means for applications supporting both Hebrew and English, the Hebrew page labels should be displayed to the right of the field values, and the whole page layout should be a "mirror reflection" to its English version. This article demonstrates a solution that sets user's respective reading directions when they log into the application.

How do you support both targeted users?
The easiest option is to generate two versions of the application, one for the English language and another one for Hebrew. While easier to implement, it requires us to maintain two versions of the same application, create two copies for each web page, etc.

The option I'll present is not complex but requires some understanding of Iron Speed Designer's infrastructure. The small effort will eliminate duplicate work and could serve as infrastructure for multiple applications.

The basic and easiest way is to take a left-to-right web page and transform it to a right-to-left page by using the CSS attribute "direction: rtl;" for the <html> tag. Fortunately, Iron Speed Designer does this for us in its Style.RightToLeft.css style sheet file, available in every design theme. But, we have no easy way to dynamically decide which style sheet we want to use in our application since Iron Speed Designer’s Application Wizard requires us to choose the application’s text direction.

To provide this support we need to be able to:

    1. Dynamically change the style sheet used in an application
    2. Make this change globally for every page in our application
    3. Identify the requested environment per application user
Procedure
Step 1: Enable dynamic style sheet changing.

In order to programmatically change the name of the style sheet file, we need access to the "<link>" tag in the ASPX web page files. You cannot access this tag in the out-of-the-box design themes since the tag does not have an ID. Accordingly, the first step is to modify the page layout files. The page layout files are the actual containers for the different panels (such as menu panel, table panel etc.) and include the page attributes to be generated in the application.

All design themes are usually found in:

C:\Program Files\Iron Speed\Designer v4.2.0\Design Themes

(assuming you are using Iron Speed Designer V4.2.0 and you installed it in the default location).

The Design Themes folder contains a folder tree for each design theme, and each design theme contains a folder called “Pages” that includes these page layout files:

Empty Page.html
Mater Page.html
Scrolling Content Page.html

We will add the following <link> tag attribute in each of these files:

id="MainStyle" runat="server"

After this modification, the link attribute (for example in Master Page.html) should look like:

<link id="MainStyle" runat="server" rel="stylesheet" rev="stylesheet" type="text/css"
    href="../Styles/Style.css"/>

Step 2: Add a global code customization to enable bi-directional language display on every application page.

Now we create a code customization that will modify, during run time, the display direction of the page using the two style sheets provided by Iron Speed Designer. This code customization:

    1. Gets a reference to the <link> tag
    2. Removes the current style sheet reference
    3. Adds an appropriate new style sheet reference
If you intend to allow dynamic language change in each page and not use the Set Language Page provided by Iron Speed Designer, you'll most likely want to perform these actions every time the page is displayed. My example performs these actions only when the page is loaded the first time.

Here is my customized LoadData method, which I add to the Page class of every page:

Public Sub LoadData()
    ' LoadData reads database data and assigns it to UI controls.
    ' Customize by adding code before or after the call to LoadData_Base()
    ' or replace the call to LoadData_Base().
    LoadData_Base()
    ' The Following code will allow for BiDi Application to use the correct
    ' stylesheet Based on the value of a session variable called LangCode
    '
    If Not Page.IsPostBack Then
        Dim MyTopLinkControl As System.Web.UI.HtmlControls.HtmlGenericControl
        If Not IsNothing(Me.Page.Session("LangCode")) Then
            MyTopLinkControl = CType(Me.FindControl("MainStyle"), _
               System.Web.UI.HtmlControls.HtmlGenericControl)
            If Not IsNothing(MyTopLinkControl) Then
                If CType(Me.Page.Session("LangCode"), String) = "HEB" Then
                    MyTopLinkControl.Attributes.Remove("href")
                    MyTopLinkControl.Attributes.Add("href", _
                    "../Styles/sAyle.RightToleft.css")
                Else
                    MyTopLinkControl.Attributes.Remove("href")
                    MyTopLinkControl.Attributes.Add("href", _
                    "../Styles/style.LeftToRight.css")
                End If
            End If
        End If
    End If
End Sub

My code customization refers to a session variable called "LangCode" which stores the logged in user’s display language. In the next section I’ll show how to store it.

Now we need to apply this code customization to every page in our application. Here are several different options:

Option #1:
Create a code customization template to be used by the Code Customization Wizard. A detailed explanation for the steps needed to do this is provided in an article called "Using Iron Speed Designer as a code patterns repository". The customization template file (an XML file) will look like the following:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<CodeCustomization>:
  <Version>1.0</Version>
  <Type>Page</Type>
  <Description>
      <![CDATA[
       This customization shows how to add a BiDirectional Support for your page
      ]]>
  </Description>
  <FinishDescription>
      <![CDATA[
       The code customization you selected will be placed in the safe class of your page.
      ]]>
  </FinishDescription>
  <Substitutions>
  </Substitutions>
  <Customizations>
    <Code>
       <Class>Page</Class>
       <Type>Sub</Type>
       <Content>
        <![CDATA[
''' <summary>
''' The MyPage_Load sub handles load event for page.
''' </summary>
''' <param name="sender">The object that raised the load event.</param>
''' <param name="e">The object that contains the event data of the load event.</param>
Private Sub MyPage_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' The Following code will allow for BiDi Application to use the correct stylesheet
        ' Based on the value of a session variable called AppCultureUI set by the SetlanguagePage
        '
        ' This code will work for both Hebrew and Arabic
        '
        Dim MyTopLinkControl As System.Web.UI.HtmlControls.HtmlGenericControl
        Dim Thisculture As String
        If Not IsNothing(Me.Page.Session("AppCultureUI")) Then
            Thisculture = UCase(CType(Me.Page.Session("AppCultureUI"), String))
            MyTopLinkControl = CType(Me.FindControl("MainStyle"), _
                    System.Web.UI.HtmlControls.HtmlGenericControl)
            If Not IsNothing(MyTopLinkControl) Then
                If Thisculture.IndexOf("HE") > -1 or Thisculture.IndexOf("AR") > -1 Then
                    MyTopLinkControl.Attributes.Remove("href")
                    MyTopLinkControl.Attributes.Add("href", "../Styles/style.RightToleft.css")
                Else
                    MyTopLinkControl.Attributes.Remove("href")
                    MyTopLinkControl.Attributes.Add("href", "../Styles/style.LeftToRight.css")
                End If
            End If
        End If
End Sub
        ]]>
      </Content>
    </Code>
  </Customizations>
</CodeCustomization>

Option #2:
Modify the Iron Speed BaseClasses module. Add the code customization to either the BasePage.vb file or the GenericPage.vb file, which inherits from BasePage.vb. The best place to put this code customization is in the Page_Load_InitPageSettings method in the BasePage.vb file.

Option #3:
Modify the code generation template file. The page’s code generation template file is typically found in:

..\Program Files\Iron Speed\Designer v4.2.1\CodeGenerator\Templates

Modify the Page.Class.Methods.vb.xsl file.

Option Pros Cons
Add a code customization to every page
  • Does not require knowledge of the base classes

  • Does not require Visual Studio to create the modification

  • Can be applied to already existing pages
  • Can only be used on a page by page basis, hence requires manual activation for each page

  • The Code Customization Wizard template . Needs to be applied for every new release of Iron Speed Designer (using a simple copy and paste to the code customizations library)

  •  
Modify Base Classes DLL
  • Will automatically apply for every page in the application using the base classes DLL

  •  
     
  • Requires some knowledge of base classes and use of Visual Studio

  • Needs to be applied for every new release of Iron Speed Designer

  •  
Modify the code generation template
  • Will automatically apply for every page generated after the template was changed

  • Does not require Visual Studio

  • Does not require knowledge of Base Classes
  • Needs to be done for every new release of Iron Speed Designer

  •  
     

Since most of you either are familiar with base classes or do not want to touch them, I'll guide you on how to modify the code generation template file.

Open the Page.Class.Methods.vb.xsl code generation template file and locate the string "Public Sub LoadData()". This is the beginning of the code generated in every page as part of the code customization region. I chose to place my customization here in order to always know this is not a part of the original version but a part of my code customizations. Additionally, placing it on this region ensures that as long as the code customization model of Iron Speed Designer does not change, any migration of this page to new versions will not affect this behavior.

After the call to "LoadData_Base()", add the code listed above and you are ready. If you need the same functionality for C# applications, just repeat the process for the Page.Class.Methods.cs.xsl file.

Step 3: Identify the user's preferred language at run-time.

In an application we developed for the Israeli Opera, we needed to automatically provide the user with his default user language and page direction without asking him to specify it for us. We chose to do this by adding an attribute to the Users table called “default language” to directly relate this information to every user.

In order to set the language (called LangCode in the above example) we added a code customization into the LoadData method of signin_control.ascx.vb that accepts the user name either from the form or, if needed, using the silent login algorithm. It reads the Users table accordingly and sets this flag. Alternatively, you could replace the LangCode flag with the AppCultureUi session variable that is used to specify the current UI language and is also used by the SetLanguage.aspx page generated by Iron Speed Designer.

The customized LoadData method in the SignIn_Control class looks like:

Public Sub LoadData()
  LoadData_Base()
 
   If Not IsNothing(Me.Page.Session("LastPage")) Then
    Dim OriginalUrl As String = CType(Me.Page.Session("LastPage"), String)
    ' Check if UserName was transfered
    '     If Len(OriginalUrl) > 0 Then
      If InStr(OriginalUrl, "UserId") > 0 Then
        Dim MRMUserId As String = Mid(OriginalUrl, InStr(OriginalUrl, "UserId"), 9)
        If Not IsNumeric(Mid(MRMUserId, 9, 1)) Then
                MRMUserId = Left(MRMUserId, 8)
        End If         MRMUserId = Mid(MRMUserId, 8, 3)
        '         ' Validate if user is logged into another asp application
        Dim ExistingUser() As ExistingUsersViewRecord
        Dim UserDetails As ExistingUsersViewRecord
        Dim WC As WhereClause = New WhereClause
        Dim User As String
        Dim UserID As String
        Dim UserPassword As String
        Dim LangUser As String
        WC.iAND(ExistingUsersViewView.User_ID, _
          BaseFilter.ComparisonOperator.EqualsTo, MRMUserId)
        ExistingUser = ExistingUsersViewView.GetRecords(WC, Nothing, 0, 10)
        For Each UserDetails In ExistingUser
              User = UserDetails.UserName0
              UserID = CStr(UserDetails.User_ID)
              If Not IsNothing(UserDetails.sPassword) Then
                UserPassword = UserDetails.sPassword
              Else
                UserPassword = " "
              End If
              LangUser = UserDetails.DefaultLang ' This is the default user lang
              Me.Page.Session("LangCode") = LangUser
              Me.Page.Session("CurrentUserPermissionGroup") = _
                UserDetails.UserGroup_ID
              If UCase(LangUser) = "HEB" Then
               Me.Page.Session("AppCulture") = "he-IL"
               Me.Page.Session("AppCultureUI") = "he-IL"
              Else
               Me.Page.Session("AppCulture") = "en-US"
               Me.Page.Session("AppCultureUI") = "en-US"
              End If
        Next
        '
        ' Doing a silent login as a result of the fact user is already logged into
        ' another application using the user details.
           '
            Me.SystemUtils.SetLoginInfo(User, UserPassword, "Login Error")
 
        If (Len(Trim(Me.SignInControl.SuccessURL)) > 0) Then
            Response.Redirect(Me.SignInControl.SuccessURL)
        Else
            CType(Me.Page, BaseClasses.Web.UI.BasePage).RedirectBack(False)
        End If
      End If
    Else
      CType(Me.Page, BaseClasses.Web.UI.BasePage).RedirectBack(False)
    End If
  End If
End Sub

Summary
This article shows how you can support bi-directional applications, either by doing manual work on every page or as an infrastructure process that can be used for every application you generate The process includes adding an ID to the link tag in every page, add a few lines of code to the page load method and provide a mechanism to automatically set the application display direction for every user that logs into the application. Now it is up to you to choose between the different ways to implement it as needed and as time allows.
About the Author
Gil Givati
CEO of Efficens Software Ltd.

Gil has been working with IT systems for the past 17 years from both the infrastructure side of applications and the development side of it. During these years he has served as a database systems team leader for one of the Israeli defense forces software units and Chief technology officer. Gil has also served as the products group manager and CTO in a software house that is representing Sybase Inc., iAnywhere Solutions, Information Builders and other companies. As part of his job he was required to take active part in systems design and deployment. For the past two years Gil has managed Efficens Software and while also taking active part in its projects and products activities.

Gil earned a BA in Business Management from the Derby University in the United Kingdom.

Contact the author.



  Privacy Statement