Scott Hanselman

Making the ASP.NET Mobile Controls render RTL languages

November 17, 2006 Comment on this post [4] Posted in ASP.NET | Internationalization
Sponsored By

When you're rendering HTML for an Arabic or Hebrew speaking audience (or Farsi, or Divehi, or Urdu, or Syriac) you usually want to change the direction of the entire page, and that's done by adding dir="rtl" to a containing element of your page, typically the HTML outer element if you want the scrollbar to switch from the left to the right. RTL, of course, means Right To Left, as these languages are typically read right to left.

In ASP.NET 1.x, this was difficult programmatically, necessitating the BODY or HTML tag being runat="server" as you went hunting for it and manually added the dir="rtl" attribute yourself. I talked about this on the blog three years ago.

It gets a little easier in ASP.NET 2.0 with the addition of the ContentDirection enumeration/property that's added to the Panel and WebPart controls.

However, if you're working with the MobileControls, it appears to be quite a bit harder, as the whole "form" metaphor changes. The Mobile stuff in ASP.NET has the notion of multiple "forms" per page where only one is active. It is that active form that does the actual rendering of the <html> element.

That means even if you have "<html xmlns="http://www.w3.org/1999/xhtml">" in your ASPX markup, you might end up with just <html> because it is the mobile:Form control that does the rendering via a PageAdapter that inherits from MobileTextWriter.

The mobile stuff in ASP.NET is very powerful, but very obtuse. I wanted to add dir="rtl" programmatically, but there's no support for custom attributes on a mobile:Form, and the Alignment="right" attribute is semantically different from dir="rtl."

Here's a page with Hebrew on it that has dir="ltr," which is also the default if you include no dir= attribute. Notice that the there are three labels for Account Name, Number, and Amount, and the labels that are within strings that include numbers have the labels on the RIGHT with the colons (:) within the label to the LEFT of the label.

That is, NUMBER COLON LABEL.

While the line without numbers, or more specifically with English included has the Hebrew labels with a colon to the right.

That is LABEL COLON ENGLISHWORD.

This is because the browser, independent of dir= attribute (or lack of one) is trying to "do the right thing" with this mixed content page (that happens to be Unicode). You can override this behavior with markup like dir= or with CSS styles like unicode-bidi: bidi-override or unicode-bidi: embed. More details on that are in the very good W3C I18N FAQ on CSS vs. Markup for BIDI (BiDirectional) support. In this post, I'm just talking about markup.

I tried to do this:

this.ActiveForm.CustomAttributes.Add("dir","rtl")

but I got this funny error message. Why funny you ask? Because it's telling me that I can't set dir="rtl" but if you do a view source (or just notice where the scrollbar is!) you'll see that it DID in fact successfully apply dir="rtl". 

This error goes away if I add this to my web.config.

<mobileControls cookielessDataDictionaryType="System.Web.Mobile.CookielessData" allowCustomAttributes="true"/>

However, the CustomAttributes bag isn't the way to effectively manipulate the rendering of the mobile controls. 

Instead, I needed creating my own PageAdapter, specifically a DirectionAwareHtmlPageAdapter:

   1:  namespace Corillian.Web.Mobile
   2:  {
   3:      public class DirectionAwareHtmlPageAdapter : System.Web.UI.MobileControls.Adapters.HtmlPageAdapter
   4:      {
   5:          public override void RenderForm(System.Web.UI.MobileControls.Adapters.HtmlMobileTextWriter writer, System.Web.UI.MobileControls.Form form)
   6:          {
   7:              writer.BeginFile(this.GetFormUrl(form), "text/html", this.Page.Response.Charset);
   8:   
   9:              //Added by me to support RTL - sucks!
  10:              writer.WriteBeginTag("html");
  11:              if (Corillian.Voyager.Web.Globalization.CurrentCulture.IsRightToLeftCulture(Thread.CurrentThread.CurrentUICulture.Name))
  12:              {
  13:                  writer.WriteAttribute("dir", "rtl");
  14:              }
  15:              writer.Write(System.Web.UI.MobileControls.Adapters.HtmlMobileTextWriter.TagRightChar);
  16:   
  17:              form.RenderControl(writer);
  18:              if (this.Device.RequiresDBCSCharacter)
  19:              {
  20:                  writer.Write("<!--\u3000-->");
  21:              }
  22:              writer.WriteEndTag("html");
  23:              writer.EndFile();
  24:          }
  25:   
  26:      }
  27:  }

This kind of sucks, because the only CUSTOM code is between line 9 and 15. The rest is copy/pasted directly out of HtmlPageAdapter using Reflector. I really think the mobile guys (and the whole ASP.NET team) should have considered the need for dynamically changing page direction for both mobile and desktop HTML. Perhaps this adapter technique is what they recommend, or perhaps there's a hidden or simpler way that I haven't figured out yet. If so, I'll post that and retract all this silliness...but until then...

You'll notice the call to a custom method called "IsRightToLeftCulture." I couldn't find anywhere in the Globalization namespace where one could just ASK a CultureInfo object if it referred to a RTL or LTR language. I know this was the case in .NET 1.1, and I'm 99% sure it doesn't exist in .NET 2.0 either. I'd love to be told otherwise.

UPDATE: I can use System.Globalization.CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft() in .NET 2.0 so there's no need for this function under 2.0.

So, here is this little gem, but what price my immortal soul?

   1:  /// <summary>
   2:  /// Returns true if the supplied culture (or its parent) is read from Right-to-Left
   3:  /// </summary>
   4:  /// <returns>True if the supplied culture (or its parent) is read from Right-to-Left</returns>
   5:  public static bool IsRightToLeftCulture(string culture)
   6:  {
   7:      //Only look at the parent culture.
   8:      string myCulture = culture.Split(new char[]{'-'})[0];
   9:      switch (myCulture)
  10:      {
  11:          case "ar":        //Arabic
  12:          case "fa":        //Farsi
  13:          case "div":        //Divehi
  14:          case "syr":        //Syriac
  15:          case "he":        //Hebrew
  16:          case "ur":        //Urdu
  17:              return true;
  18:      }
  19:      return false;
  20:  }

Now that I have this new PageAdapter, I need to tell the world about it by editing my web.config:

<mobileControls cookielessDataDictionaryType="System.Web.Mobile.CookielessData" allowCustomAttributes="true">
    <device name="DirectionAwareHtmlDeviceAdapters" inheritsFrom="HtmlDeviceAdapters" pageAdapter="Corillian.Web.Mobile.DirectionAwareHtmlPageAdapter,App_Code"/>
</mobileControls>

Note the Assembly Qualified Name (QN) in the pageAdapter attribute. See how the ClassName,AssemblyName uses "App_Code" for the assembly name? That's because my DirectionAwareHtmlDeviceAdapter.cs file is in my App_Code directory. That's one of the well-known directories that automatically gets compiled when there's a .cs file in it. The assembly that's generated is App_Code.dll. A little confusing. Alternatively, I could have put the class in a separate and more manually-compiled assembly and referenced it from there.

Now that the dir="rtl" is added to the HTML element the whole page renders correctly with the labels and all page elements not only right aligned, but rendered right-to-left. The direction changes dynamically based on the browser's Accept-Language HTTP Header.

Thanks to Patrick Cauldwell and Travis Illig for their help debugging today!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

How to convert a text file with a specific Codepage to UTF-8 using Visual Studio .NET

November 16, 2006 Comment on this post [6] Posted in Internationalization
Sponsored By

A partner recently sent me a RESX (.NET Resource) text file in Hebrew for a project I'm working on. When I opened it, it looked really bad, as seen in the screenshot below.

I assumed this was a classic ANSI vs. UTF-8 problem, so I tried to do a quick conversion within Notepad2, but that resulted in nonsense East Asian characters from all over:

This means that the original file was in fact in ASCII, but just not using an English codepage. I've talked a little about codepages in the Hanselminutes Podcast on Internationalization in the past and regularly point folks to the Joel on Software article on Internationalization.

In a nutshell, a codepage is a "view" that someone can use to look

Ordinarily, I recommend that everyone (including Israelis) use Unicode/UTF-8 to represent their Hebrew characters, however, a lot of folks who write Hebrew use the Windows 1255 codepage for their encoding. (BTW, go here, scroll down, and bask in the glory of this reference site.)

So, how do I convert from one codepage to another, or in my case, from Hebrew 1255 to UTF8?

We start with Visual Studio.NET.

  • From the File menu, select Open to bring up the dialog. Note that you'll have to select a file before the Open button is un-grayed.
  • Select the tiny down-arrow on the right side of the Open button and select "Open With..."
    • -5 points for obscure UI design here
  • Next, select "Source Code (Text) Editor with Encoding." The With Encoding part is crucial, otherwise the next dialog won't appear.
  • From the Encoding Dialog, select the source document's encoding - in my case, that's Hebrew 1255.
    • -10 points for obscure UI design here.
  • At this point the document should appear correctly in Visual Studio.NET. Now, reverse the process by selecting File|Save As and clicking the tiny down arrow on the Save button, and selecting both the desired Encoding and Line Endings.

Now you've successfully normalized your document to UTF-8, and it should be usable as a RESX file or whatever you like. Here's that same document loaded into Notepad2.

And it was Good.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Best Outlook 2007 Improvement - Include Calendar

November 15, 2006 Comment on this post [6] Posted in Reviews
Sponsored By

Sometimes when you download multi-gig ISOs and have trouble installing, you look for little features that "make it all worthwhile." Little usabilty stuff, you know.

Everyone's raving about the Ribbon interface, and the Outlook "To-Do Bar" but my choice for the best new feature in Office, and Outlook in particular?

Check out the "Include" section of the Outlook New Message Ribbon...this makes it all worthwhile. It's bloodly brilliant, and it's about time. Click "Business Card" and you can add your VCard in one click. Click Signature and you can add any of your email sigs in replies or mesages where it was removed.

And the very best option? Include Calendar - a flexible little item that lets you include not only an ICS attachment with your free time, but also an HTML rendered schedule with your Availability and/or Details.

Sure, we all wish there was a cross-platform universal Free/Busy service, but until Google does it ;) we have this very clean, elegant and simple solution to one of life's daily irritations. Fabulous.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Office 2007 won't upgrade from a prerelease version of the 2007 Office System - Office 2007 Setup Spelunking

November 14, 2006 Comment on this post [21] Posted in Reviews | Tools
Sponsored By

I'm back to work and trying to install the Released version of Office 2007, and am greeted by this friendly dialog, announcing that

"Setup is unable to proceed due to the following error(s): The 2007 Microsoft Office system does not support upgrading from a prerelease version of the 2007 Microsoft Office system. You must first uninstall any prerelease versions of the 2007 Microsoft Office system products and associates technologies."

Hm...considering that I've never installed Office 2007 Beta on this machine, and I think this would qualify as a very poor error message that would totally stump my Mom.

According to the FAQ online, this is indicative of having the Office 2003 Web Components from a previous version is installed. Of course! Why didn't I think of that?

Well, this would be fine if it were true in my case. After removing the Office 2003 Web Components via Add/Remove Programs the problem persists. I doubt my OWC components are from a beta of Office.

OK, so what is the setup looking for, and finding, that is causing it to bork like this?

So, I fired up Filemon and Regmon from SysInternals and begin to dig.

FileMon indicated that the setup log file was here:

9:58:15 AM setup.exe:5000 IRP_MJ_WRITE C:\DOCUME~1\shanselm\LOCALS~1\Temp\SetupExe(200611140958151388).log SUCCESS Offset: 0 Length: 78

...and inside that file was

Catalyst beta product conflict check failed. Office Beta product conflict is detected on the computer, productcode={30120000-006E-0409-0000-0000000FF1CE}

...which resulted in this informative message:

...which is a shame, because looking in the registry for 30120000-006E-0409-0000-0000000FF1CE showed me that I some how got the "Office Shared MUI" on my system.

However, it doesn't show up in Add/Remove Programs:

But it does show up when I run the very fabulous MyUnInst from Nirsoft (a must-install tool) along with a few other things like "Microsoft Office Proof," which I assume is the Spellchecker and Grammar tools. I'm not sure WHO installed those or when it happened. Pressing Ctrl-U on these items within MyUnInst started the uninstall process.

Once I removed these strange little things, Office 2007 started to install as expected.

Not the best OOBE (Out Of Box Experience), but I think we can assume my Mom isn't running any Beta stuff like this, so hopefully she won't have a similar experience.

A few interesting asides, if you poke around on the Office 2007 installation media:

  • In the root is a folder called Rosebud - this is apparently the codename for Microsoft's new Web Folders implementation, apparently to better support WebDAV and SharePoint 2007. It seems to be an OLEDB Provider for Internet Publishing.
  • In one of the setup.xml files there's a line like: <Option Id="OneNoteFiles" DefaultState="Local" DisallowAbsent="no" DisallowAdvertise="no" PreReq="IsEnterprise; IsEnterpriseR; IsHomeStudentR; IsOneNoteHS; IsOneNote; IsOneNoteR; IsUltimate; IsUltimateR; IsMondo"/> and I wonder if "Mondo" is some version of Office that is "bigger and better" than Ultimate?

UPDATE: I got a note from a Microsoft Insider who clued me into why this might have happened to me. Seems I installed a beta of Microsoft Expression Web a while back, and it shares this MSI with other Office componentry. Good to know.

He also shares: "Mondo isn’t some super-secret better-than-best product. It’s an internal-only product we use for test purposes. All current and planned Office suites can be found online at http://office.microsoft.com/en-us/products/FX101635841033.aspx"

Interesting stuff.

UPDATE #2: Be sure to enter your Product Keys when they ask you. Even though you're not required to enter your Product Key until later, if you try to do anything that involves one of the Office 2007 programs accessing the Internet without first entering your Product Key, like downloading a template within Publisher, Word or Excel, you will see this dialog:

Clicking "Get Genuine" will launch your default browser (mine is Firefox, which this website doesn't support, and I got caught in a Javascript page-reload loop and the page reloaded over and over again, so I pasted the URL into IE manually) and tell you basically the same thing:

So, I went back into those two programs and entered my Product Key, which interestingly enough after entering, told me to "Install Now" again. I did, then visited the Genunie Validation page again.

Close, but looks like I need to activate first. So, I closed both apps, and launched them again and was prompted to activate:

...and I was told it was cool. Then I visted the page again:

...and now I'm told that my MSDN key "has been identifed as a false key used with counterfeit copies of Office." That's weird, since I just got it off MSDN and it was just used to activate Office and Visio. This isn't good. I tried again in OneNote:

Now I'm told that I'm not only counterfeit, but I've also used the activation key too many times. I'm not sure which it is, as I'm using the same key (the only key) for all these activations...Office, Visio, OneNote, InfoPath. I thought that one key in Office was one key for the suite, not each app outside of Office Professional. I'm still not able to get any templates off the Internet or download product updates.

Moral: Just enter your Product Key when they ask.

At this point, even though the website says that my copy of InfoPath is not activated and is, in fact, not legit, it does work fine. My copy of OneNote however, will not activate. According on the MSDN Product page, "This product key will activate 10 PCs" - I've only used this key on the one PC, and I just got it today. It's also confusing because MSDN lists two kinds of keys, one for Office 2007 Professional and one for "Office 2007 Desktop Programs."

I'm not sure what to do now. This sucks.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Surfing the Internet at 35,000 feet and the Death of Connexion by Boeing on Lufthansa

November 13, 2006 Comment on this post [7] Posted in Reviews
Sponsored By

The baby is fed, and he and Mo are asleep next to me. I'm in Row 24 on Lufthansa flying from Frankfurt, Germany to Portland, OR (we had a 14 hour layover in Frankfurt after flying in from Barcelona) and I'm blogging from the plane.

They've announced the end/death of this service but since October 1st, they are having a special deal right now where wireless 802.11 Internet from the plane is free. It's the last gasps of a doomed business model. The (at least, us) had a good run of nearly two years of wireless access on long-haul flights.

It's a fine service and a darned shame. It's a little slow on the turnaround time which makes sense given the satellites involved:

>ping www.hanselman.com

Pinging hanselman.com [66.129.71.242] with 32 bytes of data:

Reply from 66.129.71.242: bytes=32 time=713ms TTL=104
Reply from 66.129.71.242: bytes=32 time=731ms TTL=104
Reply from 66.129.71.242: bytes=32 time=729ms TTL=104
Reply from 66.129.71.242: bytes=32 time=731ms TTL=104

...but once you get a download started it's not bad at all. I'm off now to download Office 2007!

I wonder if cell phone technology would ever work up this high? Was Connexion doomed because of timing or pricing?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.