= Localization Using RESX Files =
= WinForms Localization =
* Create WinForms Application (eg ''MyApp.exe'').
* Optionally, create an Resource-only Assembly (eg ''Acme.App.Localization.dll'') where all localized resources will be placed, so that other assemblies or applications can share it.
* Create Resource file in local application or resource-only assembly.
* Add new ''Resource.resx'' file to the project under ''Properties'' folder.
* Set Access Modifier to ''Public'' (top toolbar in resource document).
* Add strings to the Resource file. Eg: ''rsClose'' = ''Close''
* Autotranslate original English RESX strings using Google Translate and [[http://resxtranslatorbot.codeplex.com|Resx Translator Bot]].
* Create other resources files for other languages not supported in autotranslation. For example:
* ''Resources.es.resx'' for Spanish (es-ES)
* ''Resources.fr.resx'' for French Canadian (fr-CA), or potentially ''Resources.fr-CA.resx'' as well.
* Set Culture before calling anything else in the application. Modify ''Program.cs'' to include:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace LocalizedAppWinForms
{
static class Program
{
public static System.Resources.ResourceManager resmgr;
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
// Sets the UI culture
//System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("es"); // Spanish (Spain)
//System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en"); // English (US)
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr"); // French (France)
//System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("de"); // German (Germany)
// Load localized resources
resmgr = new System.Resources.ResourceManager(
"LocalizedAppWinForms.Properties.Resources",
System.Reflection.Assembly.GetExecutingAssembly()
);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}
}
}
* Localize controls. For example, using form ''frmMain'', modify file ''frmMain.cs'' to include:
private void frmMain_Load(object sender, EventArgs e)
{
LoadResources();
}
private void LoadResources()
{
// Optional method to load resources local to this form
//System.Resources.ResourceManager resmgrlocal = new System.Resources.ResourceManager(
// "LocalizedAppWinForms.Properties.Resources",
// System.Reflection.Assembly.GetExecutingAssembly()
//);
//lblFirstName.Text = resmgrlocal.GetString("rsFirstName");
//lblLastName.Text = resmgrlocal.GetString("rsLastName");
//lblPatient.Text = resmgrlocal.GetString("rsPatient");
//btnClose.Text = resmgrlocal.GetString("rsClose");
// Method to load global resources (defined in Program.cs file)
lblFirstName.Text = Program.resmgr.GetString("rsFirstName");
lblLastName.Text = Program.resmgr.GetString("rsLastName");
lblPatient.Text = Program.resmgr.GetString("rsPatient");
btnClose.Text = Program.resmgr.GetString("rsClose");
}
= WPF Localization =
* Create WPF Application (eg ''MyApp.exe'').
* Optionally, create an Resource-only Assembly (eg ''Acme.App.Localization.dll'') where all localized resources will be placed, so that other assemblies or applications can share it.
* Create Resource file in local application or resource-only assembly.
* Add new ''Resource.resx'' file to the project under ''Properties'' folder.
* Set Access Modifier to ''Public'' (top toolbar in resource document).
* Add strings to the Resource file. Eg: ''rsClose'' = ''Close''
* Create other resources files for other languages. For example:
* ''Resources.es.resx'' for Spanish (es-ES)
* ''Resources.fr.resx'' for French Canadian (fr-CA), or potentially ''Resources.fr-CA.resx'' as well.
* Localize controls.
* Add reference to ''Window'''s or ''UserControl'''s namespace in XAML. For example, if using resources within the application:
. . .
Or, if using a resource-only assembly instead:
. . .
* Add resource to a control. For example, assuming a resource string ''rsClose'' exists:
* Set ''Culture'' before calling anything else in the application. Modify ''App.xaml.cs'' to include:
public App()
{
MyApp.Properties.Resources.Culture = new CultureInfo("fr-CA");
}
However, reading the Culture setting from the Settings file is even better:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using MyApp.Properties; // for Culture
using System.Globalization; // for Culture
using System.Threading; // for Culture
using System.Windows.Markup; // for Culture
namespace MyApp
{
///
/// Interaction logic for App.xaml
///
public partial class App : Application
{
///----------------------------------------------------------------------------------------
///
/// Constructor
///
///----------------------------------------------------------------------------------------
public App()
{
InitializeCultures();
}
///----------------------------------------------------------------------------------------
///
/// Initialize cultures.
///
///----------------------------------------------------------------------------------------
public static void InitializeCultures()
{
if (!string.IsNullOrEmpty(Settings.Default.Culture))
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(Settings.Default.Culture);
}
if (!string.IsNullOrEmpty(Settings.Default.UICulture))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Settings.Default.UICulture);
}
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))
);
}
}
}
* Setup ''Settings'' file:
* Create (during application installation) the default settings file (''//MyApp//.exe.config''). In the project, the file is called ''App.config'':
* Create the user settings file. In the project, it is located in ''Properties/Settings.settings'':
* To change the user settings for the language, and perform this from the application itself, call a routine like this:
///----------------------------------------------------------------------------------------
///
/// Change language user setting to the specified language code.
///
/// Language Code
///----------------------------------------------------------------------------------------
public void ChangeLanguageUserSetting(string NewLanguageCode = "en-US")
{
MyApp.Properties.Settings.Default.Culture = NewLanguageCode;
MyApp.Properties.Settings.Default.UICulture = NewLanguageCode;
MyApp.Properties.Settings.Default.Save();
}
=== Deployment ===
* Copy application executable, and localization resource-only assembly, if using one. Eg: ''MyApp.exe'', ''Acme.App.Localization.dll''
* Copy language subfolders with localized resources or resource-only assemblies. Eg:
* ''..\de\MyApp.resources.dll'', or ''..\de\Acme.App.Localization.resources.dll''
* ''..\es\MyApp.resources.dll'', or ''..\es\Acme.App.Localization.resources.dll''
* ''..\fr\MyApp.resources.dll'', or ''..\fr\Acme.App.Localization.resources.dll''
* ''..\pt\MyApp.resources.dll'', or ''..\pt\Acme.App.Localization.resources.dll''
* Create an application settings file with default language setting. Eg: File ''//MyApp//.exe.config'': . . .
de-DEde-DE
. . .
== Localization Sample ==
Source: [[http://waf.codeplex.com|WAF Framework (Localization sample included)]]
=== WPF Application Framework (WAF) ===
==== Introduction ====
The LocalizationSample demonstrates a way to localize WPF applications. It uses the “old” ResX files for this task because they are well supported by the .NET Framework and Visual Studio.
**Remark**: Microsoft encourages you to use their tool “LocBaml” for localizing WPF applications (see [[http://msdn.microsoft.com/en-us/library/ms746621.aspx|MSDN: How to Localize an Application]]). Unfortunately, this tool is not production-ready and you don’t get the same comfortable support from Visual Studio.
The ''LocalizationSample'' is part of the [[http://waf.codeplex.com/|WPF Application Framework (WAF) download]].
==== Run the sample ====
1. Open the ''WpfApplicationFramework'' solution.
2. Set the ''LocalizationSample'' project as StartUp project and start it in Debug mode.
3. The application shows a window with two text boxes and a button. The text shown in the application is in English or German depending on the Windows language settings.
4. The birthday is formatted as short date time format. This formatting is defined in the Windows Regional and Language Options.
5. Close the application.
6. Open the ''App.config'' file. In this file you can define the cultures that should be used in the application. You can use one of these settings:
* '''': Use the culture defined in Windows
* ''en-US'': Define the culture English - US
* ''de-DE'': Define the culture German - Germany
* Just uncomment the culture you like to use and comment the other two culture settings. The xml example below shows the configuration for the ''German - Germany'' culture.
de-DEde-DE
7. Run the application again and see how the changes affect its appearance.
==== CurrentCulture and CurrentUICulture ====
The difference between ''CurrentCulture'' and ''CurrentUICulture'' is not that obvious. These two properties represent the “Regional and Language Options” which can be found in the Windows Control Panel.
* ''CurrentCulture'' represents the “Format” tab of the “Regional and Language Options”. This setting is responsible for formatting and parsing of values.
* ''CurrentUICulture'' represents the second part of the “Keyboards and Languages” tab, the “Display Language”. This property controls which language of the resources is loaded and shown to the user. **Remark**: Not every version of Microsoft Windows supports the “Display Language” option. When you don’t see this field then your Windows doesn’t support this option.
==== Code Walkthrough ====
# Open the ''WpfApplicationFramework'' solution.
# Expand the ''LocalizationSample'' project.
# Open the ''App.xaml.cs'' file.
* In the constructor we call the ''InitializeCulture'' method.
* In the ''InitializeCulture'' method we try to retrieve the ''Culture'' and ''UICulture'' property from the application settings file. When they exist then we set the ''Culture'' on the ''CurrentThread''.
* In the last line of the ''InitializeCulture'' method we set the WPF ''XmlLanguage'' with the ''CurrentCulture''. This line is necessary that WPF uses the ''CultureInfo.CurrentCulture'' settings.
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage( CultureInfo.CurrentCulture.IetfLanguageTag )
)
);
**Remark**: Windows Forms and Console applications use the ''CultureInfo.CurrentCulture'' setting by default. In my opinion, WPF should do the same so that we don’t have to write the previous line in our applications.
# Open ''Properties/Settings.settings'' file
* **Important:** Change the “Access Modifier” from ''internal'' to ''public'' (see top bar in document within VS2010 IDE).
# Open the ''Properties/Resources.resx'' file
* This file contains the string resources used in the user interface.
* **Important:** Note that the “Access Modifier” was changed from ''internal'' to ''public''. You find this setting in the Visual Studio Resources Designer toolbar. This is necessary so that you are able to access the resources within XAML.
* Besides the ''Resources.resx'' file you find the ''Resources.de.resx'' file in the same folder. The second resource file contains the resources for the German language.
# Open the ''Presentation/ShellWindow.xaml'' file
* The ''Title'' property of the Window uses the ''x:Static'' markup extension to access one of the Resources properties which are static.Title="{x:Static p:Resources.WpfLocalizationDemo}"
* The Window defines the ''SizeToContent'' property with the “WidthAndHeight” value. This allows the Window to automatically define its ''Width'' and ''Height'' in respect to the content. By example the words in the German language are longer than in English. With the ''SizeToContent'' setting you allow WPF to create a larger Window when it has to show its resources in the German language so that none of the text gets cut.
* The Grid in the ''MainWindow'' defines its ''ColumnDefinition.Width'' and ''RowDefinition.Height'' properties with the “Auto” value. This is done because of the same reason as the previous point defines the ''SizeToContent'' property. **Remark**: Try to avoid defining the ''Width'' and ''Height'' properties with concrete values. Let WPF calculate those values itself.
=== Code ===
App.config:
App.xaml.cs:
using System;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Markup;
using LocalizationSample.Domain;
using LocalizationSample.Presentation;
using LocalizationSample.Properties;
namespace LocalizationSample
{
public partial class App : Application
{
public App()
{
InitializeCultures(); // call here or before InitializeComponent()
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Person person = new Person()
{
Name = "Luke",
Birthday = new DateTime(2080, 2, 6) }
;
ShellWindow mainWindow = new ShellWindow();
mainWindow.DataContext = person;
mainWindow.Show();
}
private static void InitializeCultures()
{
if (!string.IsNullOrEmpty(Settings.Default.Culture))
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(Settings.Default.Culture);
}
if (!string.IsNullOrEmpty(Settings.Default.UICulture))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Settings.Default.UICulture);
}
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}
}
}
English (en/default) resources. File ''Properties/Resources.resx'':
text/microsoft-resx2.0System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089BirthdayCloseNameWPF Localization Demo
German (de) resources. File ''Properties/Resources.de.resx'':
text/microsoft-resx2.0System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089GeburtstagSchließenNameWPF Lokalisierungs Demo
User Settings. File ''Properties/Settings.settings'':
Main Window: Presentation/ShellWindow.xaml.cs:
using System.Windows;
namespace LocalizationSample.Presentation
{
public partial class ShellWindow : Window
{
public ShellWindow()
{
InitializeComponent();
}
}
}
Main Window: Presentation/ShellWindow.xaml
Data Model: Domain/Person.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LocalizationSample.Domain
{
internal class Person
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
}
= Reference Material =
* [[http://www.codeproject.com/Articles/37339/WPF-Localization.aspx?display=Mobile|WPF Localization (3 methods)]]
* [[http://waf.codeplex.com|WAF Framework (Localization sample included)]]
* [[http://msdn.microsoft.com/en-us/library/aa984332(v=vs.71).aspx|MSDN: Creating Resource Assemblies]]
* [[http://www.codeproject.com/KB/WPF/WPF_Resx_Localization.aspx?display=Mobile&fid=1538725&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=101|WPF Localization Using ResX Files]]
* [[http://www.codeproject.com/KB/WPF/WPF_Localization.aspx|Simple WPF Localization]]
* [[swdev:dotnet:localization:Editing RESX Files with SimpleResxEditor]]