Friday, March 18, 2016

Microsoft 70-486: Plan and implement globalization and localization

Exam Objectives


Plan a localization strategy; create and apply resources to UI, including JavaScript resources; set cultures; create satellite resource assemblies

Quick Overview of Training Materials


Beginner's Tutorial on Globalization and Localization in ASP.NET MVC
ASP.NET MVC 5 Internationalization
MSDN - Globalization Step-by-Step
Wikipedia - Internationalization and localization
Simple ASP.Net MVC Globalization with Graceful Fallback

My example project on Github


Plan a localization strategy


Globalization is a pretty broad term, and though I found some consistency regarding it's meaning, and the meaning of related concepts, it was by no mean unanimous.  There are three concepts that appear pretty regularly in the literature I found:

  • Internationalization (I18N):  Making an application capable of supporting other cultures.
  • Localization (L10N): Actually implementing support for another culture
  • Globalization (G11N): The combination of the above concepts.
The exam ref, Scott Hanselman, and Wikipedia all seem to adopt the above definition, though the Code Project tutorial, Nadeem Afana's blog, and the Globalization Step by Step article on MSDN use "Globalization" in place of "Internationalization".  Even within the literature on MSDN, I found reference to Globalization being the process of I18N + localization, and to Localization being a combination of "World Readiness" (which includes Globalization) and Localization.  So the answer seems to be... tomato tomawto.  FFS.  The alternative hierarchy of concepts seems to be something like this:
  • Internationalization:  The overarching concept of making an application usable across many cultures
    • World Readiness: Generic coding and design issues (capability)
      • Globalization - developing a central code base that does not depend on a specific language and culture
      • Localizability - making the code base such that it can be localized to a specific culture without changes to source code.
    • Localization - Translating and otherwise customizing the product for a specific culture
Creating an Internationalized product is not a trivial endeavor.  It isn't just a matter of putting all the literal strings into a replaceable resource file.  The Wikipedia page offered me an interesting insight on this by pointing out all the ways, besides language, that cultures can differ:
  • Text directionality (English is left to right, Hebrew and Arabic are right to left)
  • Different character sets (Arabic script, Cyrillic, many Asian writing systems)
  • Text length (Chinese can be significantly shorter than English, while German and French can be significantly longer)
  • Capitalization and pluralization rules
  • Punctuation differences (English uses "quotes" while French uses «guillemets»)
  • Number, currency, and date formats
  • Telephone and address formats
  • Political subdivisions (The US has states, Canada provinces, UK... beats me...)
There are two approaches advocated in the materials I found.  The first keeps the usual project structure and uses resource files to externalize strings and settings that need to adjust according to culture.  I'll fully explain configuring and using resource files in a bit.

The second approach uses a separate view for each supported culture and switches between them by some mechanism (the Exam Ref outlines a method that changes the view path based on an override to Controller.OnActionExecuted(), whereas Brian Reiter article extends the view engine).

In addition to these structural approaches to i18n, it's worth noting that the MVC framework is not the only cog in your application that may require specific attention.  Scott Hanselman's blog post points out that for applications using a lot of client side jQuery, using the jQuery Global plugin will localize things like the DatePicker and UI elements.  He includes extensive examples of this in the post that I'm not going to reproduce here.


Create and apply resources


While not the only facet of i18n, translation of the strings in our application, whether dialog messages, tool tips, menu items, form labels, or whatever, is a big part of the process.  Using resources takes a couple of steps.

The first (shockingly) is creating the actual resource file.  This can be done a couple ways.  The simplest way is to just add a new resource file though Visual Studio.  VS supplies a handy interface to allow you to easily add key-value pairs to the file, which is stored as XML.  Another way is to create a text file contain lines in the format key=value and using the resgen utility to generate the resource xml file.  This is easily done from the Developer Command Prompt that comes with Visual Studio:


In order to create test using resources, I created a fake Polish resource file using the Pseudoizer utility which can be downloaded on Scott Hanselman's blog.  This replaces the string values in the resource file with similar text with lots of extra accents marks and punctuation.  Using the utility is simple enough:


Now that I have my two resource files, it's time to add them to the project.  I create a folder called "Resources" in the root of the project and add the files.


If we open up the files side by side, we can see what Pseudoizer did for us:


In order to use these in our project, we need to replace hard coded strings with references to the static class generated by Visual Studio.  On the properties for each resource, set a namespace (I used "Resources").  It may also be necessary to build the solution.  It took me a couple passes before I was able to see the class in code completion and honestly I'm not 100% certain which attempted fix actually worked...

I switched out all the copy in the default template home page, so the view looks like this now:

@{
    ViewBag.Title = "Home Page";
}
 
<div class="jumbotron">
    <h1>@Resources.Strings.header1</h1>
    <p class="lead">@Resources.Strings.jumbo</p>
    <p><a href="http://asp.net" class="btn btn-primary btn-lg">
        @Resources.Strings.learnMore</a></p>
</div>
 
<div class="row">
    <div class="col-md-4">
        <h2>@Resources.Strings.header2</h2>
        <p>
            @Resources.Strings.para1
        </p>
        <p><a class="btn btn-default" 
              href="http://go.microsoft.com/fwlink/?LinkId=301865">
            @Resources.Strings.learnMore</a></p>
    </div>
    <div class="col-md-4">
        <h2>@Resources.Strings.header3</h2>
        <p>@Resources.Strings.para2</p>
        <p><a class="btn btn-default" 
              href="http://go.microsoft.com/fwlink/?LinkId=301866">
            @Resources.Strings.learnMore</a></p>
    </div>
    <div class="col-md-4">
        <h2>@Resources.Strings.header4</h2>
        <p>@Resources.Strings.para3</p>
        <p><a class="btn btn-default" 
              href="http://go.microsoft.com/fwlink/?LinkId=301867">
            @Resources.Strings.learnMore</a></p>
    </div>
</div>

Running this code in Chrome with my language preference set to "Polish" (Settings -> Advanced Settings -> Language and Input Settings), my front page looks like this:


Resources have automatic fallback functionality.  If keys for certain text were not available in the preferred language (pseudolish), then the defaults from Strings.resx would be used instead. Cultures are separated into two parts: language and region.  So US English is designated en-US, Mexican spanish as es-MX, etc.  If a region is specified but not implemented, the first fallback is to a more general language resource (so if es-MX isn't present, ResourceManager will look for just es).  Failing this, the default is used.


Create satellite resource assemblies


It is possible to move the resource files into a separate project.  The biggest "gotcha" with this set up is that the access modifier on the resource must be changed from internal (the default) to public.  That done, simply add the project, reference the resource project from the consuming project, and voila:


Taking this process one step further, it is (allegedly) possible to compile resources into a satellite assembly.  The process is described in the MSDN article Creating Satellite Assemblies for Desktop Apps, but for the life of me I couldn't get it to work correctly.  I fought with MissingManifestResourceExceptions for a while until I got all the namespaces and properties aligned, then no matter what I did, I couldn't get the program to work as described in the article.  If I complied with Visual Studio with the resources in situ, it worked fine, but if I compiled from the command line the app wouldn't even run at all.  Generating the dll with assembly linker and adding to the corresponding folder didn't work either.  I may revisit later, but for now I'm cutting my losses (spent several hours on something I am never going to use... except maybe to pass this test lol).



4 comments:

  1. If you have an ASP.NET project and put your resources somewhere else than App_GlobalResources (e.g. /App_Start/String.resx and /App_Start/String.cs-CZ.resx), the satellite assemblies will be created automatically, but they won't contain the resources from the App_GlobalResources(= satellite assembly will contain only String.cs-CZ.resx).

    ReplyDelete
    Replies
    1. Thanks for the reply Honza! This was definitely an area I remember struggling with, it seems like I had a hard time finding good examples/tutorials etc. Seems to be a pretty advanced approach.

      Delete
  2. Thanks for this, your blogs are very helpful with the 70-486 exam.

    By the way, in the UK we have 'counties'.

    ReplyDelete
    Replies
    1. You're welcome! Lol yeah the Wikipedia article talked about county, parish, ward, constituency and I got a bit lost =P

      Delete