Soluling home   Document home

Windows Forms Localization and Internationalization

Windows Forms Localization and Internationalization

Windows Forms (Wikipedia) is the classic framework to build GUI applications in .NET. The more recent framework is WPF. Windows Forms uses primary .resx files to store resource data. Soluling localization tool and service support all Windows Forms application from .NET Framework 1.0 to the newest version of .NET, including .NET Core 3, .NET 5, .NET 6, .NET 7, and later.

The following chapters describe Windows Forms localization step by step.

The end of the document contains links to get the full source code of the samples. After reading this document, we recommend reading a tutorial about how to use Soluling.

Localization Process

You can localize Windows Forms applications using the following methods:

1. Visual Studio project or solution file localization method

The first way to localize a .NET project is to add a Visual Studio project file (.csproj, .vbproj, .fsproj, or .vcxproj) into a Soluling project. Soluling reads the project file to locate all the resource files it uses (.resx, .xaml, images, etc.). You don't have to manually select all the resource files, but adding one single project file is enough. There is one output file, the satellite assembly (.resources.dll), for each language.

If you have multiple project files, you can add a Visual Studio solution file (.sln) instead of individual project files. Suppose you later add new projects to your solution. In that case, you don't have to add anything to your Soluling project because Soluling will automatically find the latest items from the solution file and adds them to the project. The result you will get by using a Visual Studio solution file instead of individual project files is the same. Your Soluling project will contain the same sources, you can configure them in the same way, and the output files will be the same. Support for the Visual Studio solution file is to make things easier for you.

This is the recommened way to localize all .NET projects.

2. Assembly file localization method

Another way to localize a .NET project is to add an assembly file into a Soluling project. Soluling reads the assembly to locate all the resource data it uses. When building, Soluling creates satellite assembly files. There is one output file, the satellite assembly (.resources.dll), for each language. You don't need to have the source code files. The only file you need is the original assembly file.

In addition to creating satellite assembly files, Soluling can also create localized assembly files. The difference is that the localized assembly file is an identical copy of the original assembly. It contains the same code segments as the original. You can replace the original assembly with localized assembly without breaking anything. You deploy the localized assembly file without the original assembly file. The satellite assembly files, on the other hand, contain only resources. They do not contain any code. They do not run alone but must always be deployed with the original assembly file. By default, Soluling creates satellite assembly files. You can change this by using the Output sheet of Source dialog.

3. Resource file localization method

The last localization method is to localize each resource file (.resx). Soluling reads the original resource file and, when building, creates localized resource files. There is one output file for each language. Although this method seems easy, it has two drawbacks. First, you can not use localized resource files directly, but you have to put them inside an assembly file. If you use resource file localization, it is up to you to compile either the localized assembly file(s) or localized satellite assembly file(s). Second, a typical .NET project contains dozens of resource files, and in many cases, they are scattered around several folders. You have to add them all to your Soluling project. Even using wildcards, the process may be complicated. Also, every time you add a new resource file, you may require to add it to the project.

If you use this method, you can make Visual Studio to compile your satellite assembly files.

Internationalization

Internationalization is a process where you enable your application for localization.

Remove hard coded strings

The most important part is to remove hard coded strings. .NET has a built-in feature resource string. Let's have an example. The following line contains a hard coded string.

private void Form1_Load(object sender, EventArgs e)
{
  label1.Text = "Hello World"
}

First, we need to add the string to the resource file. Add Resources.resx file to your project. Then double click the file and press + button.

Add resource string

If you use an older Visual Studio or you prefer the legacy resource editor, you will see a resource string grid where you can add the string.

Legacy resource string editor

Try to give a meaningful name for the resource string name. By default, the translator can only see the string name and the value. So HelloWorld is a better name than String1. If you want to pass more information to the translator, you can add comments to resource strings.

Finally, refactor your code.
private void Form1_Load(object sender, EventArgs e)
{
  label1.Text = Resources.HelloWorld;
}

That is all. You just resourced a hard coded string.

Creating a Soluling project

Note! Soluling needs the compiled assemblies during the build process. Before starting localization, make sure you have built your Visual Studio project. If not, start Visual Studio and choose Build | Build Solution menu to build.

Start by creating a new Soluling project. Drag and drop a Visual Studio project file (.csproj, .vbproj, .fsproj, or .vcxproj), a Visual Studio solution file (.sln), or a .NET assembly file (.exe or .dll) to Soluling. Project Wizard appears, showing first the Resources page. If you use .NET Core or .NET 5 or later and use the binary localization, the main assembly is a .dll file. The .exe file with the same name is an unmanaged wrapper app that needs no localization.

Windows Forms resources

Select the resource types you want to localize. In most cases, the default types (ResX and Form) are all you need to localize. Click Next. If you have previously localized your application, you might have localized .resx files. In this case, Soluling finds the existing localized file. If at least one file is found, Project Wizard shows the following page.

Existing files

Click Next. The Languages page appears. If no existing languages were found, the language list is initially empty. If existing languages were found, the list contains them. In either case, you can add any number of target languages.

Languages

Finally, click Finish to complete Project Wizard. Your new Soluling project is ready.

Windows Forms project

Scan Rules

Soluling uses scan rules to specify what components and properties are localized and how they are localized. Form resources are the most important resource type of Windows Forms applications. You use Visual Studio to create and edit forms. Each form is stored in form files (.resx and .Design.cs/Design.vb). The .resx file uses the standard resx format to store most of the properties of the component on the form. Some of the properties are not written into .resx, but they are hardcoded into *.Design.cs or *.Design.vb files. Only data in .resx can be localized. Fortunately, pretty much all properties that could need localization are stored in .resx instead of *.Design.cs. In some rare cases, you might want to localize a property that is stored in *.Design.cs (e,g. a color property). This cannot be done, but you have to add the property value to a custom .resx and on the run time, read the value from .resx, and assign it to the property.

String typed properties

The rules work in such a way that by default, all string typed properties are localized, and all other properties are not localized. This method picks up the following string properties:

Property Value
Text Form
label1.Text This is a sample
button1.Text &Get
folderBrowserDialog1.SelectedPath C:\MyDataFiles

All three Text properties should be translated. However, the SelectedPath property should not be localized. To make this possible, Windows Forms scan property rules can contain exception rules. They specify those string type properties that are not localized (remember by default, all string typed properties are localized). By default, the rules contain exceptions for all those Windows Forms properties that should not be localized. For example,

Component name Property name
System.Windows.Forms​.FolderBrowserDialog SelectedPath

The above rule excludes the SelectedPath property of FolderBrowserDialog.

Note! The component name is the component type name. For example, Windows Forms label control's type name is System.Windows.Forms.Label.

Other properties

It is rare that you need to localize other kinds of properties than strings. If you do, Soluling can handle it. Other property types such as enumerations and numbers are not localized unless you add them into the scan property rules or turn the specific property type scanning on.

Localizable property

To localize a .NET Windows Forms application, each form or user control of the application has to have Localizable property set true. This makes Visual Studio to store the properties of the form in the .resx file instead of hard coding them to the source code. Having the resources in the .resx file makes localization possible. To change the Localizable property true open your project, select each form and user control, and make sure that the Localizable property is set to true.

Properties

Visual form editor

The visual form editor for Windows Forms is a full editor that has two purposes. First, it gives you the visual context of the strings. Secondly, it can be used to move and resize the controls on the form.

If you want to move or resize a control, use the mouse to select it. Then use either the mouse to move or resize the selected item. Drag the component to move it. To resize, drag one of its trackers. If the snap to grid is turned on, you can temporaly disable it by holding the Shift key down when moving or resizing the control.

By default the coordinate rows are hidden in the edit grid. Turn them visible either by settings the filter in Filter | Edit, or turning the right side filter panel on (View | Filter Panel), and checking Coordinate point and Coordinate size. Now you can edit the coordinate values in the grid. If you want to revert to the original coordinate value, just select the translation and clear it.

Properties

Note! To enable the editor or preview, you need to have a .NET 4.x runtime installed. Windows 10 and later have pre-installed .NET Framework (4.0-4.8.1).

3rd party controls

Soluling's Windows Forms editor uses the native Windows Forms components to show forms. Soluling does not contain the code for 3rd party components. Soluling contains code for standard Windows Forms controls. This means if you use 3rd party controls or your own controls, Soluling first tries to load the assemblies you form uses. If that fails then the editor tries to map them to the nearest standard Windows Forms controls. If there is no rule for a specific component and the mapping cannot be automatically detected from the component class name, the editor shows a yellow box at the place of the component. If a control have not been mapped the editor shows only the type of control and the position and size of the control. For example, the following form contains a custom MyLabel that is shown as a red box.

Unknown component

This lets you move and resize all 3rd party controls even the editor does not fully render them.

Some 3rd party assemblies such as ComponentOne require a license data. In that case Soluling's visual editor does not load the assembly.

Runtime language change

Soluling extends the .NET framework by providing classes to perform a runtime language change. It is possible to start an application in one language and later change the language on run time as many times as needed. Soluling's runtime language change is speedy and flicker-free.

Soluling.Forms namespace contains classes and functions that let you change the language of the .NET application on run time. <data-dir>\Library\NET\Translator\Forms contains the source code of the C# library project that contains the classes. If you compile this project, you will have Soluling.Translator.Forms.dll library assembly file. It contains all the classes.

You can get the Soluling API as a NuGet package.

PM> Install-Package Soluling

Adding the above package to your project adds several Soluling assembly references to your project. You can remove references that you don't need.

Reference Description
Soluling Soluling library. Keep this always.
Soluling.Translator.Forms Windows Forms translator. Keep this if you use Windows Forms.
Soluling.Translator.WPF WPF translator. Keep this if you use WPF.

Instead of the NuGet package, you can add the following to projects into your solution:

Project Description
<data-dir>\Library\NET\​Standard\Soluling.csproj Soluling library. Add this always.
<data-dir>\Library\NET\​Translator\Forms\Soluling​TranslatorForms.csproj Windows Forms translator. Add this if you use Windows Forms.
<data-dir>\Library\NET\​Translator\Forms\Soluling​TranslatorForms.csproj WPF translator. Add this if you use WPF.

Then in your main project, add references to the above projects.

You can read the API documentation from <data-dir>\Library\NET\NET.chm file. The API source code is in GitHub.

Soluling.Forms.Translator is a class that translates forms. Soluling.Resources is a class that works with satellite assembly files. Finally Soluling.Forms.SelectLanguage contains a dialog that shows available languages and lets the user select a new language.

Adding runtime language change is very easy. At a minimum, it only requires that you add one line of code into your application.

private void languageToolStripMenuItem_Click(object sender, EventArgs e)
{
  SelectLanguage.Select();
}

It is simple like that!

Form translating requires more explanation. Soluling.SelectLanguage.Select calls Soluling.Forms.Translator.SetUserInterfaceLanguage function that performs resource loading and calls Soluling.Forms.Translator.Translate function that translates existing forms. It is very important to understand what happens here. Soluling reads the localized form data from a satellite assembly file. This means that all the components and properties of the form that exist in the localized form data will be reassigned. However, if you changed any property value from code, for example, in Load or Show events, the property values do not change. If these so-called dynamic property values need to be localized, you have to set them again after language change. Let's have an example.

<data-dir>\Samples\WindowsForms\LanguageSwitch contains a simple application using runtime language change. Form1_Load set the initial value of the label2.Content.

private void Form1_Load(object sender, EventArgs e)
{
  UpdateItems();
}

private void UpdateItems()
{
  label2.Text = Properties.Resources.String1;
}

When the application has loaded, the form is not the same as in design time. The design time state is the state that exists in the .resx file (e.g., form data). The Text property of the label2 control has been changed on run time. When a language change occurs, Soluling automatically updates the static properties (e.g., those that exist on the form data) but does not change these dynamic properties. This is why you have to set them again. The following code changes the language and translates dynamic properties.

private void languageButton_Click(object sender, EventArgs e)
{
  if (SelectLanguage.Select())
    UpdateItems();
}

SelectLanguage.Select changes the language, and UpdateItems resets the dynamic properties using the new language.

Now our application works. It might seem a bit complicated that you have to remember to reset any dynamic properties after a language change. However, in most cases, your language change routine is on the main menu or the main window. In a situation like that, you only have one window existing at that moment, so you only have to take care of that form's dynamic properties. All the other forms that you create after language change will automatically use the new language.

Suppose you want to save the language selection so the next time the application starts, it uses the language selected previously. Save that to permanent storage, such as application settings. If you want, you can use other storages such as system registry, INI/XML/JSON/YAML file, or database. The following example uses the standard application settings to store the language.

private void languageButton_Click(object sender, EventArgs e)
{
  if (SelectLanguage.Select())
  {
    Properties.Settings.Default.Language = Language.Culture.Name;
    Properties.Settings.Default.Save();
  }
}

The Soluling.Language class contains a static property, Culture, that contains the culture info of the current language. Finally, in your Program.cs file, you read the stored language and set the application's initial language.

static void Main()
{
  Soluling.Language.SetInitial(Properties.Settings.Default.Language);
  ...
}

If you use WPF, read this.

Compile satellite assembly files

If you use assembly localization or Visual Studio project or solution localization method, Soluling will automatically compile the satellite assemblies or localized assemblies during the build process. If you use the resource file localization method, you have to make Visual Studio to compile the satellite assemblies. Fortunately, this is very easy. All you have to do is to add all localized .resx files into your Visual Studio project. Next time Visual Studio builds the assembly, it also builds the satellite assemblies.

  1. In Visual Studio's Solution Explore, click Show All Files. Now all files are shown in Explorer.
    Show all
  2. Underneath each form file, you can see the localized form files (if any). Right-click a localized resource file and choose Include In Project. Visual Studio adds the localized resource file into the project.
  3. Repeat #2 for each resource file you have.
  4. Finally, click Show All Files again to hide files that are not included.

The following image shows a project that has two original resource files: Properties\Resources.resx and Form1.resx. In addition the project contains Finnish version of those resource files: Properties\Resources.fi.resx and Form1.fi.resx.

Resources

Next time you build your project, Visual Studio not only builds the main assembly, VisualStudioCompile.exe, but also the Finnish satellite assembly, fi\VisualStudioCompile.resources.dll. Whenever you add new languages or new resource files, remember to include the new files into the project.

3rd party components

3rd party components are just like the standard Windows Forms components shipped with .NET. All components are ultimately inherited from Control or Component, and they contain properties that are stored in the form file (.resx) of the owner form. Soluling has been designed to localize any kind of forms. Applications use the scan rules to select what properties to scan. Even the built-in scanning mechanism can properly parse all form data, some 3rd party components might have some issues.

The first potential issue is that the component does not write the property data into a form resource file. If it is not in the form resource file, Soluling can not localize it. If a property value does not appear in your project, even you have a proper scan rule, check that your form resource file contains the value. Then check that the scanning rules include the property and do not exclude it.

Another potential issue is that the property is correctly written, but it does not use the standard data type but uses a proprietary binary format. Soluling can correctly read many binary format but if the format is unknown to Soluling, it can not parse it but ignorers the property. You have two choices here. The first one is to contact us and provide a sample that we can use. If the component is widely used and we can have full source code of the component (e.g., the component vendor has sent it to us), we can implement a parser for that specific format. Another solution is not to set the property value on design time but runtime. That way, you can store data into standard string resources and use that data to set the proprietary property. Soluling can localize the standard string resources.

Some 3rd party components have issues with certain locales. They might work correctly when the locale is English but may fail in Japanese. So we want to test all major 3rd party components. The following list contains the 3rd party component libraries we have tested and found localizable.

Component library Notes
DevExpress WinForms Components  

Even if the above list does not contain the component library, you can still use Soluling to localize your application. If your component library is not on the above list, please contact the library vendor and ask them to join our 3rd party component program.

Soluling's Windows Forms editor uses the native Windows forms components to show the forms. Soluling does not contain the assembly files for 3rd party components. This means that the editor tries to emulate the 3rd party components using the mappings rules. If there is no rule for a specific component and the mapping cannot be automatically detected from the component class name, the editor shows a red box at the place of the component.

Files that need to be deployed

When you deploy a multilingual application, you need to deploy the satellite assembly files. There is one assembly file for each language.

Each satellite assembly must locate on a culture id specific subdirectory of the directory where the main assembly exists. If your main assembly is C:\Files\Application.exe, then German satellite assembly must be in C:\Files\Application\de\Application.resources.dll. If the path or file name is different, .NET runtime does not find the assembly. Soluling always creates satellite assemblies with the right file name and to the right subdirectory. Do not rename or move them to a different directory. You can move them if the relative path to the main assembly remains the same.

Take these output satellite assembly files and deploy them with your other files.

Samples

GitHub and <data-dir>\Samples\WindowsForms contains following Windows Forms (.NET) samples:

Directory Description Notes
Driving Shows how to internationalize, localize and add a runtime language switch for a Windows Forms application.
Original Original English driving application that has not been internationalized.
Localized Internationalized and localized driving application.
Multilingual As above but with a runtime language switch.
Try this first!
Tutorial A tutorial sample.
BadSample Original application. Contains lots of hard-coded strings, code that is not internationalized, and a user interface that requires reworking.
GoodSample The above application is internationalized and localized properly.
Try this first!
     
BiDi Shows how to localize to bi-directional languages or from bi-directional languages. Contains the following projects:
BiDiArabic A sample application that uses originally Arabic and the right-to-left layout. The application is localized to English.
BiDiEnglish As above, but uses originally English and left to right layout. The application is localized to Arabic.
 
Comment Shows how to add comments for the resource string.  
Core Shows how to localize a Windows Forms application that uses .NET Core 3, .NET 5 or later.  
Custom Shows how to localize custom controls such as 3rd party or your own controls. This sample implements SampleLabel that extends the standard Label control adding new Custom* properties. Contains the following projects:
Application A sample application that uses SampleLabel control.
Control A project that implements SampleLabel control
 
Database Shows how to implement a multilingual application that uses a multilingual database. Runtime language switch
Encodings Shows how to use various encodings in .resx files.  
FileDialog Shows how to localize Filter property of OpenFileDialog and SaveFileDialog dialogs.  
HardCoded Shows how to localize hardcoded strings without internationalizing the application.  
Ignore​Satellite​Assembly Shows how to disable loading of satellite assembly files.  
Image Shows how to localize images.  
Ime Shows how to localize input method editor values.  
Image​List Shows how to localize an image list.  
Inherited Shows how to localize an application containing inherited forms.  
Language​Switch Shows how to perform a runtime language switch. Runtime language switch
Links Shows how to localize various resource items.  
ListView Shows how to localize a ListView component.  
Platform​Files Shows how to localize platform files.  
Plural Show how to use plural messages. Contains the following projects:
CustomPlural Uses custom plural patterns for zero and two.
MixedPlural Shows various ways to solve plural forms such as to ignoring the whole problem, using a home brew solution, and finally using the proper Soluling solution.
MultiPlural Uses two plural enabled variables.
SinglePlural Uses one plural enabled variable.
 
Resources Shows how to localize various embedded resource types.  
Resource​String​Location Shows how Soluling can locate the places where your application uses resource strings.  
ResxData Shows how to localize various .resx resource types.  
Shared Shows how to localize a application using shared projects.  
Signed Show how to localize signed assemblies. Contains the following projects:
Unsigned An unsigned sample.
Snk A sample that has been signed with an SNK key (Key.snk)
Pfx A sample that has been signed with a PFX key (Key.pfx). The password for the key file is "password".
Delay A sample that has been delay signed with a PFX key (Key.pfx).
You can not run the application without first signing it. Sign.bat is a command line batch file that signs the assembly and the satellite assembly files.
The password for the key file is "password".
Satellite​Contract A sample where the versions of main assembly and the satellite assemblies can be different by using SatelliteContractAttribute.
 
Sport Shows how to localize a grid. Runtime language switch
Text Shows how to localize text and binary resource items.  
TreeView Shows how to localize a TreeView component.  
VisualStudio​Compile Shows how to localize only .resx files and let Visual Studio compile satellite assembly files.  

Samples.sln is a Visual Studio 2019/2022 solution file that contains all the above samples. Samples are written in Visual Studio 2019/2022, but the files are compatible with the older Visual Studio version. Each project directory contains a Soluling project file that localizes the assembly found in the release directory of the project (i.e., bin\Release).

All samples work with .NET Framework 2.0 or higher except Image that requires .NET Framework 3.0 or higher. Most projects contain two Soluling projects: <projectname>.ntp uses the Visual Studio project localization, <projectname>Exe.ntp uses the assembly file localization.

How to configure localization

Project Wizard asks you some options when creating a new project. However, Soluling lets you configure the localization at a very detailed level. Once you have created a project, you can view and modify the configuration of each file of the project. Select a file, right-click, and choose Options.

Configuring Visual Studio project file localization

You can configure how to localize your Visual Studio project file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Configuring Visual Studio solution file localization

You can configure how to localize your Visual Studio solution file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Configuring assembly file localization

You can configure how to localize your .NET assembly file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.

Configuring resource file localization

You can configure how to localize your Windows Forms resource file by selecting the item in the project tree, right-clicking, and choosing the Options menu. A source dialog appears that lets you edit the options. This source uses the following option sheets.