Monday, February 15, 2010

Localizing resources in a Silverlight Business Application

As a speaker of a foreign language and a former software tester for many localization projects, I have a continued interest in how to globalize and localize my applications. Fortunately, it has gotten much easier through the years as operating systems have become more accommodating to things like DBCS and bidirectional support. .NET has also continued to improve by adding better resource integration support and fortunately Silverlight is no exception in using this functionality.

Unlike with 3.0, Silverlight 4.0 provides a “ResourceWrapper.cs” class by default which encapsulates your resource references and binds the navigation elements out of the box. With only a few extra steps, you can fully globalize an application which will allow you to easily switch between languages on the fly.

image


Instructions

1. Open Visual Studio and create a “Silverlight Business Application.” (Note: If you are creating any other type of Silverlight application, I recommend seeing Tim Heuer’s blog entry mentioned below.)
2. Update the project file to support multiple languages.
i. Save the application and close Visual Studio.
ii. Open the Silverlight csproj file in notepad.
iii. Add as many culture names as will be supported. (See below for complete list.)
<SupportedCultures>en-US,ja-JP</SupportedCultures>
iv. Reopen the project in Visual Studio.
3. Add additional resource files for each culture name that you support.
i. Under the Assets –> Resources folder, copy and paste the ApplicationStrings.resx file and rename it using the appropriate culture name. For example, ApplicationStrings.ja-jp.resx would be used for Japanese.
ii. Double click on the resx file and ensure that the “No code generated” is picked for the Access Modifier. You can also delete the corresponding code behind file.
ii. Update the resource file with localized strings. In the following example, I have added a resource for Japanese and translated a few items.
image
iv. VERY IMPORTANT: If you add additional strings to your resource files, you will need to go into the original ApplicationStrings.Designer.cs file and ensure that the constructor is set to “public”. There is a bug in Visual Studio that changes it to “internal”. As of Visual Studio 2010 Beta 2, this is still a problem.

image
v. Binding is done in xaml with the following example.

Content="{BindingPath=ApplicationStrings.MyNewString, Source={StaticResource ResourceWrapper}}"

4. Update the ResourceWrapper.cs file to implement the INotifyPropertyChanged interface.

using
System.ComponentModel;
public sealed classResourceWrapper : INotifyPropertyChanged
{
private staticApplicationStrings applicationStrings = new ApplicationStrings();
private static SecurityQuestions securityQuestions = new SecurityQuestions();

public ApplicationStrings ApplicationStrings
{
get { return applicationStrings; }
set { OnPropertyChanged("ApplicationStrings"); }
}

public SecurityQuestions SecurityQuestions
{
get { return securityQuestions; }
set { OnPropertyChanged("SecurityQuestions"); }
}

public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

5. Add a UI element for changing languages. In this example, I us a ComboBox.
i. Copy and paste the following code in the MainPage.xaml file after the “About” button.

   <ComboBox Name="Language" SelectionChanged="Language_SelectionChanged">
<
ComboBoxItem Content="English" Tag="en-US" IsSelected="True" />
<
ComboBoxItem Content="日本語" Tag="ja-JP" />
</
ComboBox>

ii. Add the following code behind.

private void Language_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Thread.CurrentThread.CurrentCulture =
new CultureInfo(((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag.ToString());
Thread.CurrentThread.CurrentUICulture =
new CultureInfo(((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag.ToString());
((ResourceWrapper)App.Current.Resources["ResourceWrapper"]).ApplicationStrings =
new ApplicationStrings();
}
Run your application…
image 
image 
References
Tim Heuer - Silverlight and localizing string data - Although Tim’s example is for 3.0, I used it quite liberally in this post. I give Tim full credit for helping me understand how localization works in Silverlight.
Table of Language Culture Names, Codes, and ISO Values Method [C++]

Display Name Culture Name
Afrikaans - South Africa af-ZA
Albanian - Albania sq-AL
Arabic - Algeria ar-DZ
Arabic - Bahrain ar-BH
Arabic - Egypt ar-EG
Arabic - Iraq ar-IQ
Arabic - Jordan ar-JO
Arabic - Kuwait ar-KW
Arabic - Lebanon ar-LB
Arabic - Libya ar-LY
Arabic - Morocco ar-MA
Arabic - Oman ar-OM
Arabic - Qatar ar-QA
Arabic - Saudi Arabia ar-SA
Arabic - Syria ar-SY
Arabic - Tunisia ar-TN
Arabic - United Arab Emirates ar-AE
Arabic - Yemen ar-YE
Armenian - Armenia hy-AM
Azeri (Cyrillic) - Azerbaijan Cy-az-AZ
Azeri (Latin) - Azerbaijan Lt-az-AZ
Basque - Basque eu-ES
Belarusian - Belarus be-BY
Bulgarian - Bulgaria bg-BG
Catalan - Catalan ca-ES
Chinese - China zh-CN
Chinese - Hong Kong SAR zh-HK
Chinese - Macau SAR zh-MO
Chinese - Singapore zh-SG
Chinese - Taiwan zh-TW
Chinese (Simplified) zh-CHS
Chinese (Traditional) zh-CHT
Croatian - Croatia hr-HR
Czech - Czech Republic cs-CZ
Danish - Denmark da-DK
Dhivehi - Maldives div-MV
Dutch - Belgium nl-BE
Dutch - The Netherlands nl-NL
English - Australia en-AU
English - Belize en-BZ
English - Canada en-CA
English - Caribbean en-CB
English - Ireland en-IE
English - Jamaica en-JM
English - New Zealand en-NZ
English - Philippines en-PH
English - South Africa en-ZA
English - Trinidad and Tobago en-TT
English - United Kingdom en-GB
English - United States en-US
English - Zimbabwe en-ZW
Estonian - Estonia et-EE
Faroese - Faroe Islands fo-FO
Farsi - Iran fa-IR
Finnish - Finland fi-FI
French - Belgium fr-BE
French - Canada fr-CA
French - France fr-FR
French - Luxembourg fr-LU
French - Monaco fr-MC
French - Switzerland fr-CH
Galician - Galician gl-ES
Georgian - Georgia ka-GE
German - Austria de-AT
German - Germany de-DE
German - Liechtenstein de-LI
German - Luxembourg de-LU
German - Switzerland de-CH
Greek - Greece el-GR
Gujarati - India gu-IN
Hebrew - Israel he-IL
Hindi - India hi-IN
Hungarian - Hungary hu-HU
Icelandic - Iceland is-IS
Indonesian - Indonesia id-ID
Italian - Italy it-IT
Italian - Switzerland it-CH
Japanese - Japan ja-JP
Kannada - India kn-IN
Kazakh - Kazakhstan kk-KZ
Konkani - India kok-IN
Korean - Korea ko-KR
Kyrgyz - Kazakhstan ky-KZ
Latvian - Latvia lv-LV
Lithuanian - Lithuania lt-LT
Macedonian (FYROM) mk-MK
Malay - Brunei ms-BN
Malay - Malaysia ms-MY
Marathi - India mr-IN
Mongolian - Mongolia mn-MN
Norwegian (Bokmål) - Norway nb-NO
Norwegian (Nynorsk) - Norway nn-NO
Polish - Poland pl-PL
Portuguese - Brazil pt-BR
Portuguese - Portugal pt-PT
Punjabi - India pa-IN
Romanian - Romania ro-RO
Russian - Russia ru-RU
Sanskrit - India sa-IN
Serbian (Cyrillic) - Serbia Cy-sr-SP
Serbian (Latin) - Serbia Lt-sr-SP
Slovak - Slovakia sk-SK
Slovenian - Slovenia sl-SI
Spanish - Argentina es-AR
Spanish - Bolivia es-BO
Spanish - Chile es-CL
Spanish - Colombia es-CO
Spanish - Costa Rica es-CR
Spanish - Dominican Republic es-DO
Spanish - Ecuador es-EC
Spanish - El Salvador es-SV
Spanish - Guatemala es-GT
Spanish - Honduras es-HN
Spanish - Mexico es-MX
Spanish - Nicaragua es-NI
Spanish - Panama es-PA
Spanish - Paraguay es-PY
Spanish - Peru es-PE
Spanish - Puerto Rico es-PR
Spanish - Spain es-ES
Spanish - Uruguay es-UY
Spanish - Venezuela es-VE
Swahili - Kenya sw-KE
Swedish - Finland sv-FI
Swedish - Sweden sv-SE
Syriac - Syria syr-SY
Tamil - India ta-IN
Tatar - Russia tt-RU
Telugu - India te-IN
Thai - Thailand th-TH
Turkish - Turkey tr-TR
Ukrainian - Ukraine uk-UA
Urdu - Pakistan ur-PK
Uzbek (Cyrillic) - Uzbekistan Cy-uz-UZ
Uzbek (Latin) - Uzbekistan Lt-uz-UZ
Vietnamese - Vietnam vi-VN

8 comments:

  1. Thanks very much this was very helpful. The only thing I could add is that one needs to add a few reference at the top of MainPage.cs. In particular:
    System.Threading;
    System.Globalization;
    MyProject.Resources;

    Greg 'Hollywood' Gum

    ReplyDelete
  2. Turns out Brad Abrams ended up blogging on this too. If you get stuck, check out his post as well.

    http://blogs.msdn.com/brada/archive/2010/03/22/silverlight-4-ria-services-ready-for-business-localizing-business-application.aspx

    ReplyDelete
  3. Hi John:
    I just made this localization work but in the Brad Abrams link (provided by you) he tries to read the culture from the user browser and it is not working, any ints on this?
    Thanks a lot...

    ReplyDelete
  4. I had a similar problem before, but didn't find a resolution. I had changed my OS system locale to another language, but it didn't pick it up. Unfortunately I didn't have access to a fully localized OS at the time either in case that was the issue. Let me know if you find a solution.

    ReplyDelete
  5. Thanks John, will wait patiently.

    ReplyDelete
  6. Hello John and thank you for the great tutorial!

    The thing that I don't understand and don't know how to do, is when I have to change the text from code behind(c#).
    Let's say I have a textblock named txtWelcome. From xaml I'm saying text="Welcome".I have two resx, one for greek and one for english. They both have the txtWelcome parameter inside and each of them has the analogous text. What if I change the txtWelcome from the c# code? I mean will I write txtWelcome.text="WELCOME2" or txtWelcome.text="ΓΕΙΑΣΟΥ2"?

    I have some messageboxes also. How could I know which text to put? There are also some textblocks, that don't have their text filled in the beginning. The text is filled from code-behind.

    Can you help me please??

    ReplyDelete
  7. Hi there! I suggest trying https://poeditor.com to easily localize your app or other software product. Give it a try and see how it functions, maybe it's an useful localization tool. Cheers!

    ReplyDelete