Visual Studio LightSwitch Applications Localization

[Infragistics] Mihail Mateev / Sunday, October 10, 2010

In the modern software industry developers expect from each new framework to support localization.
This feature is a mandatory for the most of applications. Customers want a software that could be localizes easy and flexible.

This article describes how you can localize LightSwitch applications (using Visual Studio LightSwitch Beta 1).

In Visual Studio LightSwitch developers will be able to set the culture at the application level for all of the built in assets. For Beta 1 not all resources are localized, so you you likely need to wait until post beta 1 to really see that functionality. You can provide your own localized text/assets via the UI.

Sample application demonstrates how to solve localization issues for Visual Studio LightSwitch Beta 1.
In this demo is used German culture and are additionally localized strings in UI that are not supported in Beta 1.

Demo Application:

Demo application is based on the application used for article: “Introduction to Visual Studio LightSwitch”

Requirements:

  • SQL Server 2008 Express or higher license

 

Steps to reproduce:

 

LightSwitch Beta 1 provides localization at runtime for most of common used strings in applications. By default localization at design time is not supported.

At design time you can choose a single culture that you would like the application to run in. This will "brand" your application with this culture and all of the runtime strings provided by LightSwitch will honor that setting. For example you can set the application locale to "DEU" and all of the runtime strings will then appear in German. Any strings you provide could then be written to match the locale you chose for the application. For Beta 1 the runtime will support English, Japanese, German and Arabic.

Download  and open a sample application from the article: “Introduction to Visual Studio LightSwitch”

Select InfraOrders project and from the context menu select “Properties”.

In the General tab for Culture is selected English(United States).

Let look at the application files:

Under the [InfraOrders project path]\Client folders there are localized version of the  System.Windows.Data.resources assembly

Resources in .Net Reflector

 Under the [InfraOrders project path]\Common\Bin\Debug there are localized version of the  System.ComponentModel.Composition assembly

Resources in .Net Reflector

UI Descriptions (ApplicationDefinition.lsml) are included in InfraOrders.Client and InfraOrders.Common assemblies.


If you open the InfraOrders.xap file under [InfraOrders project path]\Bin\Debug\Web
there are included mentioned assemblies with resources for the specified culture.

Open EditableIGProductsGrid screen and look at the commands and properties.
(Later we will localize command “Refresh” and property “Product”).

Look at the ApplicationDefinition.lsml where are defined displayed names for these elements.

   1: <ContentItem Kind="Command"
   2:              Name="Refresh">
   3:     <ContentItem.Attributes>
   4:         <DisplayName Value="Refresh" />
   5:     </ContentItem.Attributes>
   6:     <ContentItem.DataSource>
   7:         <ScreenExpressionTree>
   8:             <ChainExpression NodeType="Chain">
   9:                 <CallExpression NodeType="Call"
  10:                                 Target="EditableIGProductsGrid/Methods[Refresh]" />
  11:             </ChainExpression>
  12:         </ScreenExpressionTree>
  13:     </ContentItem.DataSource>
  14: </ContentItem>
  15: ...
  16:  
  17: <ContentItem Kind="Command"
  18:              Name="IGProductsCollection_Refresh">
  19:     <ContentItem.Attributes>
  20:         <DisplayName Value="Refresh" />
  21:     </ContentItem.Attributes>
  22:     <ContentItem.DataSource>
  23:         <ScreenExpressionTree>
  24:             <ChainExpression NodeType="Chain">
  25:                 <MemberExpression Member="EditableIGProductsGrid/Properties[IGProductsCollection]"
  26:                                   NodeType="MemberAccess" />
  27:                 <CallExpression NodeType="Call"
  28:                                 Target="CollectionView$IGProducts/Methods[Refresh]" />
  29:             </ChainExpression>
  30:         </ScreenExpressionTree>
  31:     </ContentItem.DataSource>
  32: </ContentItem>
  33: .....
  34: <ContentItem Kind="Group"
  35:              Name="ProductGroup"
  36:              View="Microsoft.LightSwitch:VerticalStack">
  37:     <ContentItem.Attributes>
  38:         <DisplayName Value="Product" />
  39:     </ContentItem.Attributes>
  40:     <ContentItem Kind="Group"
  41:                  Name="DetailsGroup"
  42:                  View="Microsoft.LightSwitch:HorizontalStack">
  43:         <ContentItem.Attributes>
  44:             <DisplayName Value="Details" />
  45:         </ContentItem.Attributes>
  46:         <ContentItem DataType="Microsoft.LightSwitch:String"
  47:                      Kind="Value"
  48:                      Name="Product">
  49:             <ContentItem.Attributes>
  50:                 <Description Value="Product" />
  51:             </ContentItem.Attributes>
  52:             <ContentItem.DataSource>
  53:                 <ScreenExpressionTree>
  54:                     <ChainExpression NodeType="Chain">
  55:                         <MemberExpression Member="InfraOrders:IGProducts/Properties[Product]"
  56:                                           NodeType="MemberAccess" />
  57:                     </ChainExpression>
  58:                 </ScreenExpressionTree>
  59:             </ContentItem.DataSource>
  60:         </ContentItem>
  61:         <ContentItem DataType="Microsoft.LightSwitch:String"
  62:                      Kind="Value"
  63:                      Name="ProductCategory">
  64:             <ContentItem.Attributes>
  65:                 <Description Value="Product Category" />
  66:             </ContentItem.Attributes>
  67:             <ContentItem.DataSource>
  68:                 <ScreenExpressionTree>
  69:                     <ChainExpression NodeType="Chain">
  70:                         <MemberExpression Member="InfraOrders:IGProducts/Properties[ProductCategory]"
  71:                                           NodeType="MemberAccess" />
  72:                     </ChainExpression>
  73:                 </ScreenExpressionTree>
  74:             </ContentItem.DataSource>
  75:         </ContentItem>
  76:     </ContentItem>                    

Run the application to see the UI with a default culture.

 

Change the culture of the application.

In the project properties –>“General” tab set the culture to “German”.

Rebuild and run the application.
Most of resources are localized. Some strings are in English.
The reason is that for LightSwitch Beta 1 localization is not fully implemented.

Look at the resources using .NET Reflector.  Most of them are localized.

In the Config.xml file there is a definition of the application culture:
<ApplicationCulture>de</ApplicationCulture>

   1:  
   2: <Configuration>
   3:   <Manifests>
   4:     <Manifest>Microsoft.LightSwitch.Base.Client.Manifest.xml</Manifest>
   5:     <Manifest>Microsoft.LightSwitch.Client.Internal.Manifest.xml</Manifest>
   6:     <Manifest>Microsoft.LightSwitch.DebugOptions.Client.Internal.Manifest.xml</Manifest>
   7:   <Manifest>Microsoft.LightSwitch.RuntimeEditor.Internal.Manifest.xml</Manifest></Manifests>
   8:   <SubsystemPackages>
   9:     <SubsystemPackage>ModelLoader</SubsystemPackage>
  10:     <SubsystemPackage>RuntimeExtensionLoader</SubsystemPackage>
  11:     <SubsystemPackage>PresentationRuntimeLoader</SubsystemPackage>
  12:     <SubsystemPackage>ReportingLoader</SubsystemPackage>
  13:     <SubsystemPackage>RulesLoader</SubsystemPackage>
  14:     <SubsystemPackage>RuntimeServicesLoader</SubsystemPackage>
  15:     <SubsystemPackage>RuntimeShellLoader</SubsystemPackage>
  16:     <SubsystemPackage>ThemingRuntimeLoader</SubsystemPackage>
  17:     <SubsystemPackage>UtilitiesLoader</SubsystemPackage>
  18:     <SubsystemPackage>BaseServicesLoader</SubsystemPackage>
  19:     <SubsystemPackage>DiagnosticsLoader</SubsystemPackage>
  20:     <SubsystemPackage>RuntimeUserCodeLoader</SubsystemPackage>
  21:   <SubsystemPackage>RuntimeEditLoader</SubsystemPackage><SubsystemPackage>AppBridgeLoader</SubsystemPackage></SubsystemPackages>
  22:   <UserCodeAssemblies>
  23:     <UserCodeAssembly>InfraOrders.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</UserCodeAssembly>
  24:     <UserCodeAssembly>InfraOrders.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</UserCodeAssembly>
  25:     <UserCodeAssembly>InfraOrders.ClientGenerated, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</UserCodeAssembly>
  26:   <UserCodeAssembly>InfraOrders.Client, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null</UserCodeAssembly><UserCodeAssembly>InfraOrders.ClientGenerated, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null</UserCodeAssembly></UserCodeAssemblies>
  27:   <ExtensionAssemblies>
  28:     <ExtensionAssembly>Microsoft.LightSwitch.Extensions.Client, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35</ExtensionAssembly>
  29:     <ExtensionAssembly>Microsoft.Expression.Effects, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35</ExtensionAssembly>
  30:     <ExtensionAssembly>Microsoft.Expression.Interactions, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35</ExtensionAssembly>
  31:     <ExtensionAssembly>System.Windows.Interactivity, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35</ExtensionAssembly>
  32:     <ExtensionAssembly>Microsoft.LightSwitch.TouchFriendlyShell.Common, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</ExtensionAssembly>
  33:     <ExtensionAssembly>Microsoft.LightSwitch.TouchFriendlyShell.Client, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</ExtensionAssembly>
  34:     <ExtensionAssembly>Microsoft.LightSwitch.SdkProxy, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35</ExtensionAssembly>
  35:   <ExtensionAssembly>Microsoft.LightSwitch.Extensions.Design.Client, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</ExtensionAssembly></ExtensionAssemblies>
  36: <ApplicationCulture>de</ApplicationCulture></Configuration>

 

Localize specific strings in ApplicationDefinition.lsml for the selected culture

Copy the ApplicationDefinition.lsml  in different place to keep the default UI strings.

Change the “Display Name” attribute value for Refresh to “Aktualisieren” and for Product to “Produkt”.

   1: <ContentItem Kind="Command"
   2:              Name="Refresh">
   3:     <ContentItem.Attributes>
   4:         <DisplayName Value="Aktualisieren" />
   5:     </ContentItem.Attributes>
   6:     <ContentItem.DataSource>
   7:         <ScreenExpressionTree>
   8:             <ChainExpression NodeType="Chain">
   9:                 <CallExpression NodeType="Call"
  10:                                 Target="EditableIGProductsGrid/Methods[Refresh]" />
  11:             </ChainExpression>
  12:         </ScreenExpressionTree>
  13:     </ContentItem.DataSource>
  14: </ContentItem>
  15: ...
  16:  
  17: <ContentItem Kind="Command"
  18:              Name="IGProductsCollection_Refresh">
  19:     <ContentItem.Attributes>
  20:         <DisplayName Value="Aktualisieren" />
  21:     </ContentItem.Attributes>
  22:     <ContentItem.DataSource>
  23:         <ScreenExpressionTree>
  24:             <ChainExpression NodeType="Chain">
  25:                 <MemberExpression Member="EditableIGProductsGrid/Properties[IGProductsCollection]"
  26:                                   NodeType="MemberAccess" />
  27:                 <CallExpression NodeType="Call"
  28:                                 Target="CollectionView$IGProducts/Methods[Refresh]" />
  29:             </ChainExpression>
  30:         </ScreenExpressionTree>
  31:     </ContentItem.DataSource>
  32: </ContentItem>
  33: .....
  34: <ContentItem Kind="Group"
  35:              Name="ProductGroup"
  36:              View="Microsoft.LightSwitch:VerticalStack">
  37:     <ContentItem.Attributes>
  38:         <DisplayName Value="Produkt" />
  39:     </ContentItem.Attributes>
  40:     <ContentItem Kind="Group"
  41:                  Name="DetailsGroup"
  42:                  View="Microsoft.LightSwitch:HorizontalStack">
  43:         <ContentItem.Attributes>
  44:             <DisplayName Value="Details" />
  45:         </ContentItem.Attributes>
  46:         <ContentItem DataType="Microsoft.LightSwitch:String"
  47:                      Kind="Value"
  48:                      Name="Product">
  49:             <ContentItem.Attributes>
  50:                 <Description Value="Product" />
  51:             </ContentItem.Attributes>
  52:             <ContentItem.DataSource>
  53:                 <ScreenExpressionTree>
  54:                     <ChainExpression NodeType="Chain">
  55:                         <MemberExpression Member="InfraOrders:IGProducts/Properties[Product]"
  56:                                           NodeType="MemberAccess" />
  57:                     </ChainExpression>
  58:                 </ScreenExpressionTree>
  59:             </ContentItem.DataSource>
  60:         </ContentItem>
  61:         <ContentItem DataType="Microsoft.LightSwitch:String"
  62:                      Kind="Value"
  63:                      Name="ProductCategory">
  64:             <ContentItem.Attributes>
  65:                 <Description Value="Product Category" />
  66:             </ContentItem.Attributes>
  67:             <ContentItem.DataSource>
  68:                 <ScreenExpressionTree>
  69:                     <ChainExpression NodeType="Chain">
  70:                         <MemberExpression Member="InfraOrders:IGProducts/Properties[ProductCategory]"
  71:                                           NodeType="MemberAccess" />
  72:                     </ChainExpression>
  73:                 </ScreenExpressionTree>
  74:             </ContentItem.DataSource>
  75:         </ContentItem>
  76:     </ContentItem>                    

In designer you could see the changed strings. It is the same if you change the strings in ApplicationDefinition.lsml file or in designer.

Run the application. Localized strings are displayed now.

Conclusion:

In Visual Studio Beta 1 localization is not fully implemented.
Applications needs rebuild for specified locale. For some resources developers need to support localization in different ApplicationDefinition.lsml files.
I’m expecting better and easier localization in the post Beta 1 versions.