MyApp.exe
).Acme.App.Localization.dll
) where all localized resources will be placed, so that other assemblies or applications can share it.Resource.resx
file to the project under Properties
folder.Public
(top toolbar in resource document).rsClose
= Close
Resources.es.resx
for Spanish (es-ES)Resources.fr.resx
for French Canadian (fr-CA), or potentially Resources.fr-CA.resx
as well.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; /// <summary> /// The main entry point for the application. /// </summary> [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()); } } }
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"); }
MyApp.exe
).Acme.App.Localization.dll
) where all localized resources will be placed, so that other assemblies or applications can share it.Resource.resx
file to the project under Properties
folder.Public
(top toolbar in resource document).rsClose
= Close
Resources.es.resx
for Spanish (es-ES)Resources.fr.resx
for French Canadian (fr-CA), or potentially Resources.fr-CA.resx
as well.Window
's or UserControl
's namespace in XAML. For example, if using resources within the application: <UserControl . . . xmlns:properties="clr-namespace:MyApp.Properties"> . . . </UserControl>
Or, if using a resource-only assembly instead:
<UserControl . . . xmlns:localization="clr-namespace:Acme.App.Localization; assembly=Acme.App.Localization"> . . . </UserControl>
rsClose
exists: <Button Name="btnClose" Content="{x:Static properties:Resources.rsClose}">
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 { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { ///---------------------------------------------------------------------------------------- /// <summary> /// Constructor /// </summary> ///---------------------------------------------------------------------------------------- public App() { InitializeCultures(); } ///---------------------------------------------------------------------------------------- /// <summary> /// Initialize cultures. /// </summary> ///---------------------------------------------------------------------------------------- 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)) ); } } }
Settings
file:MyApp.exe.config
). In the project, the file is called App.config
: <?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="MyApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/> </sectionGroup> </configSections> <userSettings> <MyApp.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> <setting name="UICulture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> </MyApp.Properties.Settings> </userSettings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/> </startup> </configuration>
Properties/Settings.settings
: <?xml version='1.0' encoding='utf-8'?> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MyApp.Properties" GeneratedClassName="Settings"> <Profiles /> <Settings> <Setting Name="Culture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> <Setting Name="UICulture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> </Settings> </SettingsFile>
///---------------------------------------------------------------------------------------- /// <summary> /// Change language user setting to the specified language code. /// </summary> /// <param name="NewLanguageCode">Language Code</param> ///---------------------------------------------------------------------------------------- public void ChangeLanguageUserSetting(string NewLanguageCode = "en-US") { MyApp.Properties.Settings.Default.Culture = NewLanguageCode; MyApp.Properties.Settings.Default.UICulture = NewLanguageCode; MyApp.Properties.Settings.Default.Save(); }
MyApp.exe
, Acme.App.Localization.dll
..\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
MyApp.exe.config
: . . . <userSettings> <MyApp.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--Pick only one value--> <value>de-DE</value> </setting> <setting name="UICulture" serializeAs="String"> <!--Pick only one value--> <value>de-DE</value> </setting> </MyApp.Properties.Settings> </userSettings> . . .
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 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 WPF Application Framework (WAF) download.
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:
<value/>
: Use the culture defined in Windows <value>en-US</value>
: Define the culture English - US <value>de-DE</value>
: Define the culture German - GermanyGerman - Germany
culture.<WpfLocalizationDemo.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--<value>en-US</value>--> <value>de-DE</value> <!--<value />--> </setting> <setting name="UICulture" serializeAs="String"> <!--<value>en-US</value>--> <value>de-DE</value> <!--<value />--> </setting> </WpfLocalizationDemo.Properties.Settings>
7. Run the application again and see how the changes affect its appearance.
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.WpfApplicationFramework
solution.LocalizationSample
project.App.xaml.cs
file.InitializeCulture
method.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
.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.
Properties/Settings.settings
fileinternal
to public
(see top bar in document within VS2010 IDE).Properties/Resources.resx
fileinternal
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.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.Presentation/ShellWindow.xaml
fileTitle
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}"
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.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.App.config:
<?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <section name="LocalizationSample.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/> </sectionGroup> </configSections> <userSettings> <LocalizationSample.Properties.Settings> <setting name="Culture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> <setting name="UICulture" serializeAs="String"> <!--Pick only one value--> <!--<value>en-US</value>--> <!--<value>de-DE</value>--> <value/> </setting> </LocalizationSample.Properties.Settings> </userSettings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/> </startup> </configuration>
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
:
<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema Version 2.0 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the various data types are done through the TypeConverter classes associated with the data types. Example: ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> <value>[base64 mime encoded serialized .NET Framework object]</value> </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> There are any number of "resheader" rows that contain simple name/value pairs. Each data row contains a name, and value. The row also contains a type or mimetype. Type corresponds to a .NET class that support text/value conversion through the TypeConverter architecture. Classes that don't support this are serialized and stored with the mimetype set. The mimetype is used for serialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: Note - application/x-microsoft.net.object.binary.base64 is the format that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="metadata"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" /> </xsd:sequence> <xsd:attribute name="name" use="required" type="xsd:string" /> <xsd:attribute name="type" type="xsd:string" /> <xsd:attribute name="mimetype" type="xsd:string" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="assembly"> <xsd:complexType> <xsd:attribute name="alias" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="Birthday" xml:space="preserve"> <value>Birthday</value> </data> <data name="Close" xml:space="preserve"> <value>Close</value> </data> <data name="Name" xml:space="preserve"> <value>Name</value> </data> <data name="WpfLocalizationDemo" xml:space="preserve"> <value>WPF Localization Demo</value> </data> </root>
German (de) resources. File Properties/Resources.de.resx
:
<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema Version 2.0 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the various data types are done through the TypeConverter classes associated with the data types. Example: ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> <value>[base64 mime encoded serialized .NET Framework object]</value> </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> There are any number of "resheader" rows that contain simple name/value pairs. Each data row contains a name, and value. The row also contains a type or mimetype. Type corresponds to a .NET class that support text/value conversion through the TypeConverter architecture. Classes that don't support this are serialized and stored with the mimetype set. The mimetype is used for serialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: Note - application/x-microsoft.net.object.binary.base64 is the format that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="metadata"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" /> </xsd:sequence> <xsd:attribute name="name" use="required" type="xsd:string" /> <xsd:attribute name="type" type="xsd:string" /> <xsd:attribute name="mimetype" type="xsd:string" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="assembly"> <xsd:complexType> <xsd:attribute name="alias" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="Birthday" xml:space="preserve"> <value>Geburtstag</value> </data> <data name="Close" xml:space="preserve"> <value>Schließen</value> </data> <data name="Name" xml:space="preserve"> <value>Name</value> </data> <data name="WpfLocalizationDemo" xml:space="preserve"> <value>WPF Lokalisierungs Demo</value> </data> </root>
User Settings. File Properties/Settings.settings
:
<?xml version='1.0' encoding='utf-8'?> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="WpfLocalizationDemo.Properties" GeneratedClassName="Settings"> <Profiles /> <Settings> <Setting Name="Culture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> <Setting Name="UICulture" Type="System.String" Scope="User"> <Value Profile="(Default)" /> </Setting> </Settings> </SettingsFile>
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
<Window x:Class="LocalizationSample.Presentation.ShellWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:LocalizationSample.Properties" Title="{x:Static p:Resources.WpfLocalizationDemo}" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize"> <Grid Margin="0,0,11,11"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Content="{x:Static p:Resources.Name}" Grid.Column="0" Grid.Row="0" Margin="11,10,0,0"/> <TextBox Text="{Binding Name}" Grid.Column="1" Grid.Row="0" Width="100" Margin="11,11,0,0"/> <Label Content="{x:Static p:Resources.Birthday}" Grid.Column="0" Grid.Row="1" Margin="11,6,0,0"/> <TextBox Text="{Binding Birthday, StringFormat=d}" Grid.Column="1" Grid.Row="1" Width="100" Margin="11,7,0,0"/> <Button Content="{x:Static p:Resources.Close}" Grid.Column="1" Grid.Row="2" Width="73" HorizontalAlignment="Right" Margin="11,22,0,0"/> </Grid> </Window>
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; } } }