Localization is not a new task and there is a lot of documents and blog posts about it, but on my experience there is not a unique and comprehensive source about all the aspect related to strings and images localization on all the platforms covered by Xamarin.
In this post I’ll try to illustrate how did I solved it, starting from the Xamarin localization demo solution, adding some code from a Microsoft post and making some changes of the TraslateExtension code that I’ll describe in detail later on.
But first thing first, let’s see what I was trying to achieve: A Xamarin Forms Portable Class Library based solution, with a bunch of localized resources all contained on the PCL, possibly managed by MAT, the Microsoft Multilingual Application Toolkit.
Many of you can say that is easy, and surely it is if you want to build Android, iOS or Windows Phone app as explained on the Xamarin documentation page (Localizing Xamarin.Forms Apps with RESX Resource Files) and the GitHub solution by Craig Dunn (UsingResxLocalization).
But the true is that the Windows Phone app contained in that solution is the old Windows Phone Silverlight type. When and if you try to add a Windows Phone Store or a Windows Store project to the solution, you’ll soon find that the app is not working on these platforms.
For the sake of simplicity, from now on we will write:
– SL app to refer to Windows Phone Silverlight app (7, 8 and 8.1)
– Store app to refer to Windows Phone Store app (8 and 8.1) and Windows Store App (8 and 8.1)
Back to our problem, to understand why the solution is not working on the WinRT platform we need to look at the different kind of resource type used by SL vs. WinRT.
Let’s briefly describe them:
On SL apps we use resource files of type RESX that are easy to use by code because a Resource class is automatically generated:
So if we have an entry in the AppResources.resx file with a key of “MyMessage” and a value of “Hello World”:
we also will have an automatically generated AppResources class with a “MyMessage” read-only string property in the AppResources.Designer.cs:
and we can write this code:
On Store app we use resource files of type RESW that are easy to use by XAML, thanks to the resource key binding (the x:Id parameter) but without the automatically generated class.
So to get the value of “MyMessage” we need to do it by ourselves, using the ResourceLoader class:
Can you see the difference? On Store app we have to use a ResourceLoader.GetString(…) method while on Android, iOS and SL app we can use the ResourceManager.GetString(…) method. That’s why the Craig Dunn Localize Demo app do not works on Store app.
To solve the Store app problem, starting from the error message and searching on the web I did find a very useful blog post: MissingManifestResourceException when using Portable Class Libraries within WinRT.
To make it short, we need to “automagically” use the ResourceLoader.GetString(…) method when running on Store apps, while continuing to use the regular ResourceManager.GetString(…) method on all other platform. And because in the Xamarin Forms solution we use a resource file of type RESX, we also have the automatically generated resource class.
The super clever idea contained in the above linked post is to “hack” the resource class injecting a derived class of ResourceManager with an overridden GetString(…) method into the resource class “resourceMan” property (for a more detailed explanation of this hack, you can read the post).
To implement this hack, we need to add the derived from ResourceLoader class into the Store app project (if you have Windows and WindowsPhone, you’ll need to have it on both projects):
and call it from the app.xaml.cs (of both Windows and WindowsPhone projects) code:
Once done it, the code:
will work on all the platforms targeted by Xamarin.
But still we have another problem with the code of the TraslateExtension class that I used to get the resources values directly from XAML, because, as you can see from the code written by Craig Dunn, it doesn’t use the ResourceManager referenced by the “ResourceMan” property of the resource, instead it creates a fresh new instance of ResourceManager that for sure will never be the WinRTResourceManager needed in case of a Store app:
And we know that the ResourceManager.GetString(…) will not work on Store apps.
So after a very helpful Skype chat with my good friend Joost van Schaik, where he suggested me another hack especially suited for the TraslatedExtension code, I decided to take another way using the above injection hack and ended up with this code, that essentially retrieve the injected ResourceManager from the “ResourceMan” property of the resource and does it only one time, on the constructor of the class:
For this hack I also want to thanks Dan Ardelean that solved a strange Visual Studio behavior related to an experiment that I was doing inside the PCL. The short story is that declaring a ResourceLoader in the PCL produce tons of compiler errors. So when you’re in trouble, who you gonna call? Dan (a.k.a. Ghostbuster)! J He helped me nailing down the cause of those compiling error so that I ended removing the ResourceLoader from the PCL and took the other way I’ve presented here.
And this is all you need to create a Xamarin Forms Solution that enable you to have resources of type RESX in the Portable Class Library and to use them on all the platforms available with Xamarin.
You can clone my demo solution on GitHub and try it for yourself.
One more thing: during this post I didn’t mention the UWP (Universal Windows Platform) app, because even if it is also based on WinRT, his ResourceManager implementation do works well even without the injection hack.
On the next post, a step by step guide on all the tips and tricks of Localization.