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:
SL apps:
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:
Store app:
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.
Jon Alza
Oct 4, 2016
Hello Nicolò:
I can see that you work a lot with Xamarin and localization. I have a question that is not about this topic, but it is related with localization. I would like to ask you if you know a way to refresh translations automatically. I want to give the option to the user to change the selected language when the app is running. But following Xamarin’s documentation about localization (you refer to it in the post) the translations are not refreshed to the new language when it is changed. I could do it in WPF using ObjectDataProvider, but it is not available in Xamarin.Forms. Do you know if this is posible?
I have this question explained in different ways in Stack Overflow and Xamarin forums:
http://stackoverflow.com/questions/39786730/refresh-ui-language-on-the-fly
https://forums.xamarin.com/discussion/79379/update-language-translation-on-the-fly-using-translateextension
I hope you can give me an answer.
Thank you.
George Ceaser
Oct 16, 2016
I have tried to implement code from your solution in my project but I am running into a challenge.
In the TranslateExtension class constructor I am not getting a reference to the resource manager.
I added some extra code into the method to see where things were going wrong and it appears the .GetValue function is not returning the value. The variable mylist contains the correct information and I can see the info for the resource manager. Things seem to be going awry on the GetValue function as after this is executed, myobject is null. Any ideas on what I am doing incorrectly?
public TranslateExtension()
{
cultureInfo = DependencyService.Get().GetCurrentCultureInfo();
var mylist = typeof(AppResources)
.GetRuntimeFields()
.First(m => m.Name == “resourceMan”);
var myobject = typeof(AppResources)
.GetRuntimeFields()
.First(m => m.Name == “resourceMan”)
.GetValue(typeof(AppResources));
resmgr = typeof(AppResources)
.GetRuntimeFields()
.First(m => m.Name == “resourceMan”)
.GetValue(typeof(AppResources)) as ResourceManager;
}
George Ceaser
Oct 17, 2016
Never mind I figured out the problem. I was missing the WinRTResManager.cs code in the Windows Phone app as well as the line that injected it from Launched event of the App.xaml.cs file. Sorry to bother you 🙂
George Ceaser
Oct 19, 2016
OK I am now having the same problem that I originally posted when running the Android or iOS version of the project. I cannot seem to find anything that I am missing for those projects. Any help would be greatly appreciated.