Localization #4: Getting rid of Singleton classes in localized multi-platform Apps

As promised in Localization #3, this post presents an alternative approach of preparing localized resource files for the usage in Portable Class Libraries, aiming at multi-platform App development. The idea of implementing the LocalizedStrings helper class as Singleton is comfortable because it is available from anywhere within the App – when developing a multi-platform App consisting of several projects, it is even available from each project that references the Portable Class Library. However, since many developers don’t feel comfortable with intense usage of Singleton classes, here is another solution!

This approach is based on the fact that our AppResoures (or however it’s called, depending on the resource files’ name) only contains read-only properties – none of the localized key-value pairs contained within the resource files will be changed at runtime. This means that it is not really necessary to maintain one single reference to these resources. Instead, what about creating a separate instance of LocalizedStrings class for each Viewmodel?

Accessing localized resources from XAML code:

The idea is to create a read-only property that represents a LocalizedStrings instance in each Viewmodel, which provides access to the global AppResources class:

private readonly LocalizedStrings _strings = new LocalizedStrings();
public LocalizedStrings Strings { get { return _strings; } }

The LocalizedStrings class itself looks like in part #2, we don’t need the Singleton implementation any more:

public class LocalizedStrings : INotifyPropertyChanged
{
	private static AppResources _localizedResources = new AppResources();
	public AppResources LocalizedResources { get { return _localizedResources; } }

	// Update language code:
	public event PropertyChangedEventHandler PropertyChanged;
	public void RefreshLanguage()
	{
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs("LocalizedResources"));
	}
}

Actually, that’s it – we are already done here! Each View that has one of these Viewmodels declared as its DataContext can access the AppResources directly through this Strings property defined above:

<TextBlock Text="{Binding Strings.AppResources.String1} " />

Instant language switching:

In the Viewmodels of those pages that allow the user to switch between languages (e.g., the App’s settings page), add a call to the current Viewmodel’s LocalizedStrings instance to update the language:

Thread.CurrentThread.CurrentUICulture = new CultureInfo(LANGUAGE_CODE);
this.Strings.RefreshLanguage();

In contrast to the previously discussed approach of using one central Singleton reference to AppResources, this affects only the current page, notifies it that the display language has changed and forces it to update its user controls – all the other, currently inactive pages, will not be notified. But since those other pages are inactive, they don’t need to instantly update. As soon as one of the other pages is re-activated, it will update its layout anyway, automatically switching to the new display language.

Pros & cons:

Compared to the Singleton approach discussed in Localization #3, this solution has the following drawbacks:

  • Additional code is necessary within each Viewmodel class, because the LocalizedStrings property is needed separately in each of those classes
  • Creating a separate instance of the LocalizedStrings class for each Viewmodel consumes slightly more memory than maintaining one global Singleton instance (however, since LocalizedStrings is only a provider class, this should not leed to massive performance issues – the AppResources class which holds a lot of data still exists only once)

On the other hand, it

  • reduces the necessary code within both the LocalizedStrings class (because no Singleton pattern and no separate LocalizedStringsProvideer class is needed), and in each page’s XAML code (because binding to one of the Viewmodel’s properties is easier and produces shorter code than referencing static Singleton instances)
  • and eliminates the Singleton pattern from the code, bringing about better maintainability of the whole project

One final improvement:

Disadvantage #1 of the above list (the necessity for additional code within each Viewmodel class) can even be eliminated by a small trick: Instead of maintaining a separate property in each Viewmodel implementation, create a base Viewmodel class that defines the necessary LocalizedStrings property, and let all your actual Viewmodels inherit from this base class!

public class BaseViewmodel : INotifyPropertyChanged
{
	private readonly LocalizedStrings _strings = new LocalizedStrings();
	public LocalizedStrings Strings { get { return _strings; } }
}

Each actual Viewmodel implementation that derives from BaseViewmodel has direct access to the Strings property, as shown above!

public class MySampleViewmodel : BaseViewmodel
{
	...

(Of course, if you use any MVVM framework that defines a base Viewmodel class, you can still create an additional layer between this base Viewmodel and your Viewmodel implementations. Let the generic class that contains the LocalizedStrings property inherit from the MVVM framework’s base Viewmodel class, and your actual Viewmodels again inherit from this generic class – through this inheritance chain, it is guaranteed that no inheritance dependency is lost along the way.)