Written by 13:34 Languages & Coding

Another Way to Localize Application

I would like to introduce a simple way to localize applications. I do not like the standard mechanism with resource assemblies for the following reasons:

  1. When receiving a value of a localized string in the code, I would like to rely on OOP and compiler prompts. It is very unpleasant to generate a project on Friday evening, and on Saturday morning, to get a call from QAs working overtime that someone inattentive wrote GetResource (“asdf”) instead of GetResource (“assf”). Thus, now something crashes or is displayed incorrectly. In addition, the project is to be “put to print” on Monday…
  2. To write foo = language.Ui.PromtDialog.AdditionalQuestion instead of foo = Resources.GetResource(«Ui_PromtDialog_AdditionalQuestion») is nicer due to the compiler prompts in particular.
  3. Sometimes, it is necessary to localize the whole objects rather than strings. For example, a noun (a string plus gender male/female/neuter/plural) and an adjective (a string male plus string female plus string neuter plus string plural). To add a serialized string to resources, get and deserialize it each time is not convenient.
  4. A resource file is a flat list of strings. I would like the data to have a more complex hierarchy structure for which you do not need to use Ctrl+F.
  5. Creating a new language should be as simple as possible. The person who knows how to work with a computer and who knows the necessary languages should be able to localize the application. To do this, they do not need any Visual Studio or fuss with the creation of resource assemblies.

Another requirement is a possibility to easily bind UI elements to localization. It is desirable to use simultaneously WPF and WinForms.

The solution is very simple and is as follows:

  1. Create the Language class that will contain all the localized resources.
  2. Fill it with the properties of the string type and properties – objects with string properties (“categories”), and… Select depth of nesting to your preferences.
  3. Make the Language class, including all the nested classes, serialized with the method that a basic user may not like at all when trying to edit a file with a serialized language. Since I prefer XML, I select XmlType, XmlRoot, XmlElement, and XmlAttribute attributes. Those who like JSON can use it as well. If there is a comfortable wrapper, feel free to use it. Everything depends on your preferences.
  4. Extract the language to the form using the BindingSource (WinForms), {x:Static} or <ObjectDataProvider> (WPF) components or simple data binding.
  5. Create the «Languages» or «Localizations» subfolder in our folder with the application and add one or several files, in which the languages serialized by the selected method will be located.
  6. If it is necessary to localize more complex objects (for example, images) the language will store a relative path to the resource file. The file, in this case, will be located in a subfolder of the “Languages / Localizations” folder.
  7. When downloading the application through a standard deserializer, languages are loaded. The currently selected language depends on the stored settings and can be selected from the drop-down list of the dialog menu when launching the application (for example, if the application is started for the first time and there is nothing in the config) or set automatically from the available settings based on CultureInfo.CurrentCulture. It is possible to save this language in any object, access to which you can obtain from those places where localized resources are required. It does not matter how you receive the access either through singleton or dependency injection – it is up to you.

Anyone who wants to create a new locale needs to copy a file with the preferred language in the «Languages/Localizations» folder and translate available strings.

The example of the code can be found here. It seems to be created in a hurry and therefore has a bad design. For example, languages are loaded in the code of the main form, instead of the Main method. However, it works 100%.

Should you have any suggestions for the improvement of this method, feel free to let me know – I will be glad to discuss them.

Tags: , , Last modified: September 23, 2021