.NET Localization and Internationalization |
.NET (Wikipedia) is a software framework developed by Microsoft. It is an extensive framework. It contains several technologies and programming methods. Soluling localization tool and service support every part of .NET.
Platform specific information about .NET localization
The list describes different files that .NET uses.
The following list describes .NET localization step by step.
.NET is an extensive group of technologies. It contains several implementations, APIs, and programming methods. You can use .NET to create desktop applications, command-line applications, server applications, mobile applications, web pages, web services, single-page applications, console games, etc. .NET contains APIs such as ASP.NET, Blazor, Windows Forms, WPF and Universal Windows Platform. The common thing for all .NET projects is that the output file is an assembly file. An assembly file is a compiled file that contains compiled code and compiled resources. Sometimes some of the resources are in external files. However, in most cases, the assembly contains resource files embedded as resource data. The file extension of an assembly file is either .exe or .dll, and assembly files use Windows PE file format. When you deploy your .NET application, you deploy the assembly files and optional resources files that your .NET development tool created.
The following table contains a list of .NET implementations:
Implementation | Description | APIs | Notes |
---|---|---|---|
.NET | A cross-platform .NET implementation for Windows, Mac, and Linux | WPF, Windows Forms, Blazor, ASP.NET Core | |
.NET Framework | Classic .NET implementation for Windows desktop and server |
|
|
Universal Windows Platform | .NET API for Windows 10 and later. | Universal Windows Platform | This is called WinRT on Windows 8.x |
Mono/Xamarin/MAUI | New cross platform .NET implementation for iOS, Android and browsers. | Xamarin | |
,NET Compact Framework | A subset of .NET Framework implemented for the Windows Embedded Compact platform | Windows Forms | Not compatible to .NET Standard. |
Soluling supports all the above .NET implementations plus .NET Standard.
.NET platform contains several APIs. The main APIs are:
API | Description | Resource types | Files to localize |
---|---|---|---|
ASP.NET Core | .NET API for web applications and services. | Resx, templates, other | Visual Studio project file |
ASP.NET Framework | Classic .NET API for web applications and services. | Resx, templates, other | Visual Studio project file, or Assembly files, template files and script files |
Blazor | API for single-page web applications. | Resx, other | Visual Studio project file |
WPF | .NET API for desktop applications. | XAML, Resx, other, unmanaged | Visual Studio project file or Assembly files |
Windows Forms | Classic .NET API for desktop applications. .NET Compact Framework uses its own version that is a subset of Windows Forms. |
Resx, other, unmanaged | Visual Studio project file or Assembly files |
Universal Windows Platform | .NET API for desktop and mobile applications for Windows 10. | Visual Studio project file or Appx files | |
Silverlight * | A subset of WPF that works on web browsers. | XAML, Resx, other | XAP file |
Windows Phone * | A subset of WPF that works on cell phones. Based on Silverlight. | XAML, Resx, other | XAP file |
Soluling supports all the above .NET APIs. *) However, Silverlight and Windows Phone have been deprecated, and so our support for them.
.NET projects can contain several different resource types. The following table describes different resource types.
Resource type | Description |
---|---|
Standard resources | This is the basic .NET resource type. It contains one or more resource items. Each item represents a resource value. The original resource file is a .resx file. In most cases, these items contain string data such as message strings or images. However, they can include other kinds of data too. |
Windows Forms resources | Windows Forms stores form data into .resx files. However, unlike the standard resource, a form resource contains additional data such as property type and component where the property belongs to. |
XAML resources | Universal Windows Platform, WPF, Xamarin, Windows Phone, and Silverlight all use XAML files to store resources. In addition to XAML, they also use standard resource files (.resx). |
Images and other files | It is possible to store images and any other files as resource data. |
Unmanaged resources | In addition to the managed resources above, .NET assemblies also contain unmanaged resources. These resources contain application icon and version information. |
Soluling can localize all resource types of .NET.
Visual Studio project file is the project file that all .NET projects use. In most cases, you need to add this file to your Soluling project. Soluling supporta C# (.csproj), Visual Basic (.vbproj), F# (.fsproj) and C++ (.vcxproj) project files.
Visual Studio solution file (.sln)is a container for one or more Visual Studio project files. If you have several projects in a solution file, you can add the solution file instead of the Visual Studio project file.
Resx is the basic resource format of .NET and UWP. It is based on XML, but files are converted to a binary format when added into assemblies. All .NET platforms use .resx file format. UWP uses the same file format, but .resw file extension is used instead of .resx.
Windows Forms uses .resx files to store the properties of the form. Such a .resx file is called the form file. Soluling uses a dedicated parser to scan form files because not all properties should be extracted. Scan rules are used to determine what properties are extracted.
XAML (Wikipedia) is a declarative XML-based language created by Microsoft that is used for structured data and objects. UWP, WPF, and Xamarin use XAML.
Use scan rules to select what properties are parsed and localized. You can optionally also use id attributes to control what to localize. If you check Localize only components with an id attribute checkbox in the XAML options sheet, Soluling scans only those elements with an id.
XAML files use the binding feature a lot. For example, the following XAML has bound a formatted text box value into the text block.
<TextBlock Name="textBlock1" Text="{Binding ElementName=textBox, Path=Text, StringFormat=Distance is {0} meters.}" />
The raw text value would be
{Binding ElementName=textBox, Path=Text, StringFormat=Distance is {0} meters.}
This would be too difficult (translator might not know what part to translate) and too dangerous (translator might break the format) to extract as a complete string value. Instead, Soluling parses the Binding value and extracts only the actual string part that in this case is
Distance is {0} meters
This will ensure that the translator gets strings as a simple plain string and cannot accidentally break the binding format.
Unfortunately, all localization tutorials and how-to-do papers from the web recommend that you do not store string values into the XAML file. Instead, they recommend to store the values into a .resx file and use those ResX items from XAML using markup extensions (e.g. {x:Static Resources.String1}). Do not apply. Those documents were written by people that do not know that modern localization tools can easily localize XAML. Tools and services they use can only localize .resx resources. When you use Soluling, you can always store all strings into your XAML file and let Soluling localize the XAML file. Use .resx to store only the strings that are used in your C#/VB code. If you already have used the method described above, you don't have to remove the ResX items from XAML files. You can mix and match the plain string and .resx strings.
Learn more about this here.
BAML is a binary XAML. It is Microsoft's proprietary format to store XAML data in a binary format. All XAML files for the WPF application are compiled into BAML before adding into the assembly resource. The BAML format cannot be converted back to the original XAML. Localization must be done using .NET localization API. This makes localization of BAML data more limited. However, if properly written (i.e., includes x:Uid attributes) BAML data can also be localized. XAML files in UWP, Windows Phone, Silverlight, and Xamarin assemblies are kept in standard XML based XAML files or in a binary format that can be converted back to text XAML.
Template files define the HTML markup. Page and view files are templates files. Read more about how to internationalize and/or localize them in ASP.NET and ASP.NET Core.
Page and view files can contain client-side script files. Scripts are generally localized in the same way as the templates. However, if you have standalone script files, you can localize the files.
Besides the templates and string resource files, Soluling can localize images, audio, and other file types.
Assembly file is a building block of .NET platform and applications. An assembly file contains both compiled code and compiled resource data.
No matter what .NET implementation or .NET language you use, your compiler creates an assembly file. Assembly files are the executable files in .NET. They contain both compiled code and resources. If your source code is properly internationalized, then all the items that need to be localized are localized in the resource blocks of your assembly files. If you have not internationalized your application, there might be some hard coded strings in the compiled code. In this case, you have two options. Either you replace hardcoded strings with resource string (i.e., internationalize code. This is recommended) or your turn on Soluling's hardcoded string localization.
One type of assembly is the satellite assembly. It contains only resource data but no code. .NET uses satellite assemblies for localization. If you use satellite assemblies instead of localized assemblies, you should make sure that .NET can load them correctly.
When using Windows Forms, WPF, or ASP.NET, you deploy assembly files.
The original assembly file is the assembly file that the compiler creates. It contains the compiled code and original resources. Original resources do not have a language id. Even the resources do not have a language id the text in the resources is, of course, written in a human language. This so-called original language is the language that is used in the development tool when entering Text values. The original language is very often English, but it can be any language. The following picture contains an original assembly file that contains original resources written in English.
The file extension of an assembly file is either EXE or DLL. Applications use EXE, and library files use DLL. In Soluling, the original assembly file is the source file that you add to the Soluling project. Soluling creates either localized assembly files or satellite assembly files.
The localized assembly file is just like the original assembly file, but the resource data has been localized from the original language to the target language. The following picture contains the German assembly file.
If you start the above assembly file, it always uses the German resources that are embedded in the assembly file. The French assembly file would look like this.
Each resource in a localized assembly file uses an empty language id. Only the resource data itself has been localized.
The satellite assembly file contains only resource data. It does not contain any code. The resource data has been localized, and each resource uses the language id. Finally, the satellite assembly file must locale a locale-specific relative subdirectory of the original assembly file.
The following picture shows a configuration where there are an original (English) assembly file and three satellite assembly files: German, French, and Japanese. If the original assembly file name is C:\Sample\Application.exe, then the German satellite assembly file must be C:\Sample\de\Application.resources.dll. The French satellite assembly file is C:\Sample\fr\Application.resources.dll. The Japanese satellite assembly file is C:\Sample\ja\Application.resources.dll.
When you start the above application, it has four language choices. Either it uses the original (English) resources found the original assembly file, or it uses resources found from one of the satellite assembly files. If you do not have any special code in your application .NET framework will choose a language that matches the user interface of your operating system. This means that if you run the application on English OS the original (English) resources are used. If you run the application on German OS, the resources from the German satellite assembly file are used. You can override this default feature by setting the user interface culture of the current thread. For example the following code makes .NET framework to use the resources matching the default locale of the operating system.
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture;
Note! You must set the culture before the application loads any resources. A good place to set the culture is the Main procedure before Application.Run
is called. Learn more about satellite assembly loading.
The most common unmanaged resource in .NET assembly is the version resource. If you need to localize it, turn on the assembly info localization. Right-click you .NET project file in the project tree, right-click, and choose Options. Select Resources and check Assembly info.
Soluling reads the Visual Studio project file, Visual Studio solution file, or original assembly file to extract resource items into the Soluling project. Translators use Soluling to translate the items. Finally, when building Soluling creates satellite assembly files. There is one output file (.dll) for each language. If you use Visual Studio solution or project file, you need to have the full source code. If you use an assembly file, you don't need to have the source code files. The only file you need is the original assembly file.
If you use the original assembly as a source, Soluling can also create localized assembly files. The localized assembly file is an identical copy of the original assembly, except the strings in it have been replaced. The assembly contains exactly 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 assemblies. You can change this by using the Output sheet of Source dialog.
If you project uses either Windows Forms form resources or XAML resources that have string it is recommended to use Visual Studio project file or Visual Studio solution file localization.
Soluling reads the original resource files to extract resource items into the Soluling project. Translators use Soluling to translate the items. Finally, when building Soluling creates localized resource files. There is one output file (.resx) for each language. At the end of the process, you have the localized resource files, but you can not use them directly unless you 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).
This is similar to adding a single assembly file (.exe/.dll). Instead you add a .NET project file (.csproj, .vbproj, .fsproj, .vcxproj). Soluling reads the project file locating all the resource files (.resx, forms, and XAMLs) it uses and extracts items from them.
When you start localizing your .NET project, you have to select what files you add to the Soluling project. In most cases, this is easy, and you just add your project or assembly file(s). However, in some cases, you might have several project files, or you might have some other file types that are hard to locate. This is why Soluling has made it easy for you to localize your complete .NET project. Instead of manually adding all necessary project or assembly files, you just add one file: your Visual Studio solution file (.sln). Soluling reads the solution file and locates all the necessary project and resource files that need to be localized. If you add new projects into your solution, you don't have to add anything to your Soluling project because Soluling will automatically find the new 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 or assembly files is exactly 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 Visual Studio solution file is just to make things easier for you.
Internationalization is a process where you enable your application for localization.
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.
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.
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.
private void Form1_Load(object sender, EventArgs e)
{
label1.Text = Resources.HelloWorld;
}
That is all. You just resourced a hard coded string.
Composite format strings are string that are composed on runtime by combining static and dynamic text. The following lines compose a hello message for two people.
private void button1_Click(object sender, EventArgs e) { label1.Text = "Hello " + name1 + " and " + name2; }
Add the following item to the resource file.
It is a good practice to add a comment that describes what the placeholders will contain. Finally, refactor your code to use String.Format
function and the resource string.
private void button1_Click(object sender, EventArgs e) { label1.Text = String.Format(Resources.HelloNames, name1, name2); }
Visual Studio lets you share code and resources in many different ways. It helps to reduce unnecessary code duplication but also make localization more challenging.
This is the most comment method. You create a shared library. It has its own project and output assembly. If the library contains any resources, you need to localize the library. As a result, you will have satellite assemblies for the library, and you need to deploy them as well.
Sometimes if you have one file or two and you don't want to create a separate library, you can share the files by adding them as links. You only have one copy of the file. If you change it, the project using the file changes. If you share a resource this way, the resource will be included in all assemblies that linked it, and you have to localize the resource in all projects using it.
This is a very rare case. .NET lets you create a shared project that is linked to a project. It is a bit like linked files, but instead of linking a single file, you link a collection of files. Soluling detects shared projects in your project file and lets you localize them too.
Most .NET assemblies are signed. They are signed for two reasons. One is to prevent hacking of assemblies (digital signature), and another is to guarantee that every assembly has a unique name (strong name).
Unfortunately, strong name signing makes localization harder. If the main assembly is a strong name signed, the localized satellite assembly must be signed with the same key(s). If you are localizing your own assemblies, the signing is no problem because you have the key. However, 3rd party vendors hardly ever release their keys. This means that if you want to localize an assembly of a 3rd party vendor, you or Soluling can not completely sign the satellite assemblies Soluling creates. Instead, Soluling will delay-sign the assemblies. Such assemblies contain only public key and empty space for the sign data but no actual sign data. You must use Sn.exe to complete the signing. To do that, you need the key file. If you do not have the key file, you have few choices.
If the vendor does not provide the alternative localization method, does not give you the key, or won't sign your assemblies, there is nothing you can do except to keep pressuring the vendor. More clients there are demanding a proper localization, more likely the vendor either provides localized satellite assemblies, signing services, or even give you the strong name key file.
Not having a digital signature in your localized satellite files may not be a show stopper. However, not having a strong name in your localized satellite assemblies is a show stopper because .NET runtime will not load a satellite assembly files if it is not a strong name signed in the same way as the original assembly.
Two types of key files exist. One is an SNK file that contains a public/private key pair, and you can make your key file yourself. Another is PFX that is a key file provided by certificate providers such as Verisign. Using the key files differs. If you use SNK, then you use the key file when signing the files. If you use PFX, then you install the key into the key container and give the key a name. When singing, you don't use the key file but the key name. Soluling can handle both key types. If you use SNK, then you enter the key file name in the sign dialog. If you use PFX, you enter either the key file or the key name.
The reference assembly directory is a directory that contains satellite assemblies in language-specific subdirectories. The directory is usually installed together with the library. In other words, it contains ready to be used localized satellite assembly files made by the original assembly vendor. The following directory is a reference assembly directory of Expression Blend. It contains German (de), English (en), Spanish (es), French (fr), Italian (it), Japanese (ja), Korean (ko), Simplified Chinese (zh-Hans) and Traditional Chinese (zh-Hant) satellite assemblies.
Each satellite assembly must be in the right subdirectory where the name of the directory matches the .NET culture id of the language. If your satellite assemblies are in some other directories, you must rename the directories to use proper .NET naming.
If you don't have the key file and you don't have existing satellite assemblies in the reference directory, then your choice is to use delay signing. In this process, Soluling does not completely sign the assemblies but inserts the public key and reserves a space for sign data. You must complete the signing before deploying your files. However, you can test your localized assemblies without completing signing by registers assemblies for verification skipping. Use the Sn.exe tool for that. Sn.exe is a part of the .NET SDK. The following command registers the German satellite assembly, de\MyApplication.resources.dll, for verification skipping.
sn.exe -Vr de\MyApplication.resources.dll
You must run the command as an administrator. You can let Soluling handle registering by checking Register created assemblies for verification skipping checkbox of sign sheet, sign methods dialog, or sign method dialog.
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\SolulingTranslatorForms.csproj | Windows Forms translator. Add this if you use Windows Forms. |
<data-dir>\Library\NET\Translator\Forms\SolulingTranslatorForms.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.
Standard .NET and UWP source code uses String.Format function. It is not plural-enabled, but fortunately, Soluling contains a plural enabled Format function for .NET and Windows Runtime. If you use standard String.Format function you might have something like this
str = String.Format("{0} files", count);
Replace "{0} files"
with "{plural, one {{0} file} other {{0} files}}"
and add that into the Resources.resx and give it MessagePlural name. Replace String.Format with Soluling's Format function. The modified .NET code is
str = Soluling.Plural.Format(Properties.Resources.MessagePlural, count, count);
The modified Windows Runtime code is
var rl = new ResourceLoader();
str = Soluling.Plural.Format(rl.GetString("MessagePlural"), count, count);
Soluling.Plural.Format function is found from <data-dir>\Library\Net\Plural.cs. The same code works with .NET, ASP.NET, and UWP. The code is also available at GitHub.
See the following samples:
Technology | Sample directory |
---|---|
ASP.NET | <data-dir>\Samples\ASP.NET\Framework\MVC\Driving |
ASP.NET Core | <data-dir>\Samples\ASP.NET\Core\RazorPages\Driving |
UWP | <data-dir>\Samples\UWP\Plural |
Windows Forms | <data-dir>\Samples\WindowsForms\Plural |
WPF | <data-dir>\Samples\WPF\Plural |
Both Windows Forms and WPF contain file dialogs that you can use to select a file to be opened and specify the file to be saved. The dialog contains Filter property that specifies file typed shown in the file type combo box.
OpenFileDialog dialog = new OpenFileDialog(); dialog.Title = "All supported files (*.xml;*.ini)|*.xml;*.ini|XML files (*.xml)|*.xml|Ini files (*.ini)|*.ini";
The format of the string is quite complicated. It contains the description text separator with file mask(s). There can be several of these pairs. If you place the above string into a string resource, the translator will see it as it is
All supported files (*.xml;*.ini)|*.xml;*.ini|XML files (*.xml)|*.xml|Ini files (*.ini)|*.ini
It may be that the translator will miss one | character or enter one extra. Either one is fatal, and your application will crash if the invalid localized file string is used. This is why it is better to use Soluling's file dialog filter format class.
OpenFileDialog dialog = new OpenFileDialog(); dialog.Filter = new Soluling.DialogFilter() .Supported("All supported files") .Add("XML files", "xml") .Add("Ini files", "ini") .ToString();
This will expose translator three plain strings: "All supported files," "XML files," and "Ini files." They are easy to translate, and a translator cannot enter invalid values. If you don't want to use or cannot use the file dialog filter format class, use the file dialog filter validation to check if any of the filter translations are invalid.
Soluling.DialogFilter class is found from <data-dir>\Library\Net\DialogFilter.cs. See following samples:
Technology | Sample directory |
---|---|
Windows Forms | <data-dir>\Samples\WinForms\FileDialog |
WPF | <data-dir>\Samples\WPF\FileDialog |
.NET framework has a built-in support to load a satellite assembly file. When .NET assembly (application or library) is started the framework looks if there are any available satellite assembly files for the main assembly. Each satellite assembly file must be in a culture (.NET's term for locale) specific sub directory of the main assembly. If the main assembly is Application.exe then German satellite assembly is de\Application.resources.dl and German (Germany) satellite assembly file is de-DE\Application.resources.dll.
The method how a .NET application chooses this initial satellite assembly file file is shown in following.
In addition it is possible to disable satellite assembly files.
When a .NET application starts, the framework tries to look for the right satellite assembly file. This depends on the settings and operating system. If you have not specified the culture that should used framework uses the default culture.
Default culture is always the same culture the same as the user interface language of your operating system. If you have an English (US) operating system, the default culture is en-US. The secondary culture is en. This means that the application tries to first look for an en-US satellite assembly file. If found, the application loads it. If not found application tries to find an en satellite assembly file.
The file .NET runtime looks for depends on the user interface language of your operating system. If you have an English (US) OS, the runtime will look satellite assembly file from en-US or en. On German OS, the runtime looks for de-DE or de. This method to select is a bit limited because the user has no control over the initial language except for installing a multilingual OS or reinstall OS with a different language. This seems way too heavy to just select a different initial language for your application. Fortunately, there is an easier way. By adding just one line of code to your application, you can make the initial language selection smarter.
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture;
This line of C# code makes .NET runtime to look for a satellite assembly file that matched the default locale of the user. Not the user interface language of the operating system itself. Changing the default locale of the user is easy using the control panel.
You can change the default culture. This can be done by setting the CurrentUICulture property of the current thread. The following line sets the default culture to English.
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en");
If you want that the application uses the default culture of the user, use the following code.
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture;
Note that this is not necessarily the same as the user interface culture of the operating system. The user might have an English operating system, but the default locale in the screenshot below is Finnish (Finland). The user can change the default locale from the Control Panel.
You can read more about how to make the application to use the user default language.
If the application can not found any of the above satellite assembly files, it will use the original resources of the main assembly. Our sample application will use the resources of application.
If you want your application not to use the satellite assembly file, the easiest way is to remove the satellite assembly file(s). If you want to keep the files but not to use them, you can disable the satellite assembly file by setting a null default culture.
using System.Threading;
using System.Globalization; ... Thread.CurrentThread.CurrentUICulture = new CultureInfo("");
See <data-dir>\Samples\Windows Forms\IgnoreSatelliteAssembly sample.
When you use Visual Studio project file localization method, Soluling reads your Visual Studio project file (.csproj, .vbproj, .fsproj, or .vcxproj) or Visual Studio solution file (.sln). Most projects have two or more configurations and/or platforms. These let you specify a different set of properties for different purposes. For example, the debug configuration contains the debug information. One of the main properties of each configuration is the output directory. It specifies the directory where the output assembly is created. By default, Debug configuration uses bin\Debug
sub folder and Release configuration uses bin\Release
subfolder.
When Soluling compiles XAML, ResX, or satellite assembly files, it needs to know where the original assembly and the assemblies it depends on are located. To find this, Soluling read the output path from the project file and uses that path. When you create a new Visual Studio project file source, you can specify the configuration and platform that are used. For example, the following same uses Debug configuration and AnyCPU platform.
Before you add such a source, make sure that you compiled the project using that configuration. That will ensure that the output directory contains the assembly file, and it is up to date. Whenever you rescan the Soluling project, make sure that the Visual Studio project has been fully rebuilt so that the assembly is up to date.
If you use a command line, use the following command:
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\15.0\Bin\MsBuild.exe" Simple471.csproj /property:Configuration=Debug
By default, .NET runtime uses a satellite assembly matching the language of the operating system itself. This means that on the English operating system, the runtime uses an English assembly file. In most cases, this is the right thing to do. However, a better choice is to use the satellite assembly matching the default culture of the operating system. Many users use the English operating system, but their default localize is not English but some other. To use the default culture instead of the language of the operating system itself, you have to add one line of code. Set Thread.CurrentThread.CurrentUiCulture property to change the user interface culture. The CultureInfo.CurrentCulture property contains the default culture of your system. This setting is specified in the Regional and Language Options of Control Panel.
The above image contains a case where an English Windows 8.1 has Finnish as default format (e.g. locale or culture in .NET). Without modification, a .NET application will start up in English. If you want it to start up using the default culture (in this case, Finnish), follow the instructions below.
The following examples show how to set the user interface culture. Add lines written in bold typeface.
Note! Even Soluling can localize your application properly; it can not run (Project | Run Localized) in the desired language if you do not set the user interface culture to match the default culture.
Set the culture in the Main function (Program.cs).
using System.Globalization;
using System.Threading; static void Main() { // Add this line of code Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
Set the culture in the constructor of the main form.
Imports System.Globalization Imports System.Threading Public Class Form1 ... Public Sub New() ' Add this line of code Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture ' This call is required by the Windows Form Designer. InitializeComponent() End Sub ... End Class
Add contructor in the App class (App.xaml.cs) and set the culture there.
using System.Globalization; using System.Threading; public partial class App : Application { // Add these lines of code public App() { Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture; } }
Set the culture in the constructor of the main form.
Imports System.Globalization Imports System.Threading Class Application
// Add these line of codes Public Sub New() Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture End Sub End Class
Sometimes you might get an error when performing the build process. Most common two reasons are:
The assembly files do not exist. Soluling looks for the assemblies on the output directory of the selected configuration (e.g. bin\Debug\). If you haven’t compiled that configuration, the assembly file does not exist and that is likely to cause an error. To fix that make sure you have compiled your project on the configuration you have selected in your Soluling project file.
Soluling cannot locate the assembly even that exist. In that case turn on the detailed output logging and send us the output. To turn on the detailed log, right mouse click on the Output sheet, choose Customize sheet, and then check Detailed in the dialog that appears. The output log usually contains "Please send "<yourprojectfile>.zip" file to the support team." Get the zip file Soluling created and send it to our support.
If you use satellite assemblies instead of localized assemblies, it may be that your application does not load the satellite assembly you think it should load. There are a few reasons why the loading might fail. The first three ones, incorrect path, signing, and version, are fatal. They totally prevent .NET runtime to even load the assembly.
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.
If your main assembly is a strong name signed, then all the satellite assemblies that are loaded must also be signed with the same key as the main assembly. Make sure that you specify your key in the Signing sheet of Source dialog.
The version number of the main assembly and its satellite assemblies must match. If they are not the same .NET runtime does not load the satellite assembly. When Soluling creates localized satellite assembly files, it, by default, gives them the same version numbers as the original assembly has. Every time you change the version of your main assembly, use Soluling or its SoluMake command-line tool to build new satellite assemblies that has matching version numbers.
.NET Core 1.x used a dependencies file with the main assembly. It is located on the same directory as the application assembly: <appname>.deps.json. This file must contain the satellite assembly files specified in the resources section. Either edit the dependencies file manually or let Soluling update it.
The above issues are the main reason why .NET runtime can not load a satellite assembly at all. There is one more reason that makes .NET to load a different satellite assembly that you though. The initial language is the language that .NET runtime tries to load once the application starts. By default, .NET runtime tries to load a language matching the language of the operating system. This means that an English satellite assembly on English Windows, and a German satellite assembly on German Windows. If you want to load a satellite assembly matching the current language specified in the Region and Language sheet of Control Panel, you have to add some code. Here is the main function of a sample Windows Forms application.
static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
Add the following lines of code in Program.cs:
using System.Threading; using System.Globalization; ... static void Main() { Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
Now your application tries to load a satellite assembly matching the regional settings instead of the operating system's language itself.
On WPF application you have to do the following modifications to App.xaml.cs.
public partial class App : Application { public App() { Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture; } }
There is one more thing that might make .NET to load different satellite assembly file. It is the case where you have hardcoded a specific culture. For example, you might have the following code
static void Main() { Thread.CurrentThread.CurrentUICulture = new Culture("en"); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
This will always make .NET to load English satellite assembly no matter the language of the operating system or settings in Control Panel.
.NET has a very good resource string mechanism: .rexs files. Each resource strings item has an id and value. In addition, they can contain an optional comment that is a good place to give extra information for a translator to properly translate the string. When using .resx localization or project/solution file localization, then Soluling automatically reads also comments. However, the compiled assemblies no longer have comments. They only have the id and resource value. If you use the assembly file localization Soluling has to read them original resource source files (.resx). To do this, Soluling needs to know the Visual Studio project for the selected assembly file. In most cases, Soluling automatically detects the localization of the project file (if it exists). If detection fails, you can use the File sheet to select the files. You need to have resource file source files (.resx). A recommended way to localize a .NET project is to use the project or solution file localization.
See <data-dir>\Samples\WindowsForms\Comments sample to see how to use comments.
In .NET, resource strings are stored in a specific resource file (.resx) that is compiled into a resource item (.resources). The user interface of the application uses those resource items. It is very useful for a translator to know where a specific resource string is used. This is why the context of the string should be available. This relation is not stored anywhere in the resource file or compiled assembly. However the location where a resource string is used can be found by analyzing the source code. Soluling can do that if you turn the option on by using Options sheet. In order to do this Soluling must know where the source code files are located. Soluling needs to know the Visual Studio project file to find the source code. Use File sheet to specify the project file. You need to have full sources files (.cs or .vb).
If Soluling finds resource strings that are used in a form, Soluling links them into the form node. If you select the node, the rows are shown along with node's own rows. Each row that is a linked row shows a chain icon in the info column.
The above image shows Form1 form of ResourceStringLocation application. The form has only one row of its own, $this.Text, but in addition it uses three resource strings: MyName, String2 and String3.
If you select the resource string node you can see only the resource strings.
See <data-dir>\Samples\WindowsForms\ResourceStringLocation sample to see how to use resource strings locations.
A hardcoded string is a string inside your source code that contains a string value that should be localized. For example, you might have the following code
private void Form1_Load(object sender, EventArgs e) { label2.Text = "Hello World"; }
Here "Hello World" is a hardcoded string. The right way to deal with these is to remove the hardcoded strings and replace them with resource strings. This process is called resourcing. You first add the above string into a string resource files (.resx) and replace the string literal with the resource string variable.
private void Form1_Load(object sender, EventArgs e) { label2.Text = Properties.Resources.HelloWorld; }
Now the string value is in a resource file, and it can be easily localized without any change into the source code. This is the right way to deal with hardcoded strings. Removing them is the most important part of internationalization.
Note! Use the following feature only as of the last option if you absolutely have no possibility to modify the original source code by removing the hardcoded strings.
If you do not have any possibility to modify your application, then your option is to let Soluling localize hard coded strings. To do that, you have to turn that option on.
Normally Soluling changes only the resource data of .NET assemblies. It does not modify the actual code. A localized assembly file has a 100% identical code compared to the original assembly. If you use satellite assembly files, then the localized file does not contain any code because satellite assembly is always used together with the original assembly. By localizing the hardcoded strings, Soluling starts "localizing" code too. This causes satellite assembly output to be disabled. Soluling can only create localized assemblies that contain localized resources and localized code. Most often, code contains many strings that must not be localized. For example, most SQL statements should remain intact. When you use hardcoded string localization, you have to exclude those strings that must not be localized carefully. If you localize (e.g., change) such as a string, it may be that your code does not work anymore.
If you need to localize the version info resources of your assembly, check Assembly info. This makes Soluling scan your assembly info files. Those files contain copyright text, application name, and various application version numbers. By default Soluling only scans the string data from assembly info. If you need to get version data (e.g., you need to change the version number), select the assembly info file in the project tree, right-click, and choose Options. Then check Scan string numbers.
The default localization method of Windows Forms and WPF applications is to create localized satellite assembly files. If you use that, Soluling can localize all .NET files as long as your computer has a matching .NET framework installed. If you can run the application, then Soluling can localize it. If you can not run the application, the reason is most likely a missing framework or a missing assembly. If you don't have the .NET framework that the application uses, download it from Microsoft's site and install it. If you don't have an assembly that the application uses, run the application setup again or get the missing assemblies.
In addition to satellite assembly files, Soluling can also create localized assembly files. If you choose that method, you must also have a matching .NET SDK or Visual Studio installed. The framework alone is not enough. If you don't have the SDK, download it from Microsoft's site and install it. If you have Visual Studio installed, you also have .NET SDK.
Localization method | Framework needed |
SDK needed |
Visual Studio needed |
---|---|---|---|
Project files | Yes |
Yes |
WPF |
Assembly files, creating satellite assembly files | Yes |
-* |
- |
Assembly files, creating localized assembly files | Yes |
Yes |
- |
*) When creating satellite assembly files, SDK is needed on the cases the original assembly is signed with a container key, assembly uses delay signing, or you want to delay-sign the localized satellite assembly files.
The following table shows what .NET frameworks each operating system contains by default.
Operating system | .NET 2.0 | .NET 3.0 | .NET 3.5 | .NET 4.0 | .NET 4.5 | .NET 4.6 | .NET 4.6.1 | .NET 4.6.2 | .NET 4.7 | .NET 4.7.1 | .NET 4.7.2 | .NET 4.8 | .NET 4.8.1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Windows 7 | Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
Windows 7 + Visual Studio 2010 | Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
- |
- |
- |
Windows 7 + Visual Studio 2012 | Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
- |
- |
Windows 8 | Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
- |
- |
Windows 10, Windows 8 + VS 2015 | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
- |
Windows 10 Novermber Update | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
- |
Windows 10 Anniversary Update | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
- |
Windows 10 Creators Update | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
- |
Windows 10 Fall Creators Update | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
- |
Windows 10 April 2018 Update | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
- |
Windows 10 May 2019 Update and later | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
- |
Windows 11 2023 Update and later | Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
For example, if you want to localize an application that uses .NET 4.0 and you use Windows 7, you have to install .NET 4.0 framework if you have not already done it or you do not have Visual Studio 2010 or later installed. The following list contains links where you can download .NET frameworks.
There are two versions of the .NET 4.0 framework: full and client profile. You don't need a full framework, but a client profile framework is enough.
The following list contains samples for each .NET technologies: