Localizing your app isn’t the most fun thing to do. In the last couple of years I’ve seen and tried various ways of implementing multi language support.
In this blogpost I would like to share how I recently set up localization in a Xamarin Froms app. With this approach, I am able to bind to items in a RESX Resource file dynamically. What do I mean with dynamically? Well, as soon as the user changes his preferred language in the app, the value of all bound resource items are instantly updated to the value of the chosen langue.
If you don’t know what RESX resource files are, I recommend you quickly go through this: Localizing Xamarin.Forms Apps with RESX Resource Files.
Let’s take a look at dynamically binding RESX Resources
It is very simple! Not only the implementation itself, but also the usage of it.
I’ve created a class LocalizedResources that looks like this:
public class LocalizedResources : INotifyPropertyChanged { const string DEFAULT_LANGUAGE = "en"; readonly ResourceManager ResourceManager; CultureInfo CurrentCultureInfo; public string this[string key] { get { return ResourceManager.GetString(key, CurrentCultureInfo); } } public LocalizedResources(Type resource, string language = null) : this(resource, new CultureInfo(language ?? DEFAULT_LANGUAGE)) { } public LocalizedResources(Type resource, CultureInfo cultureInfo) { CurrentCultureInfo = cultureInfo; ResourceManager = new ResourceManager(resource); MessagingCenter.Subscribe<object, CultureChangedMessage>(this, string.Empty, OnCultureChanged); } private void OnCultureChanged(object s, CultureChangedMessage ccm) { CurrentCultureInfo = ccm.NewCultureInfo; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item")); } public event PropertyChangedEventHandler PropertyChanged; }
The first thing to point out, is the ResourceManager field. This class can be used to look up culture-specific resources. In other words, if I have all my culture specific resources in place, I can use this class to retrieve a string for a particular culture. Let’s say I have some resource files and I want to get the French value of a particular key, I could do the following:
ResourceManager = new ResourceManager(resource); ResourceManager.GetString("welcomelabel", new CultureInfo("FR"));
The resource that I pass into to constructor of the ResourceManager is a Type. It is the type from which the resource manager derives all information for finding the correct resource. More information can be found on Micrososft Docs.
Back to my LocalizedResources class. Now that we know what the ResourceManager can do, it should be clear in the above code that we use the CurrentCultureInfo field to retrieve culture-specific resources from my RESX Resource files. We do this in the class’ indexed property:
public string this[string key] { get { return ResourceManager.GetString(key, CurrentCultureInfo); } }
This is the glue between my Resources and my UI! In Xamarin forms we can bind to indexed properties. Whatever value we put between the square brackets in our Binding statement in XAML is passed as key in our indexed property.
One more thing: in my LocalizedResources class, I subscribe to a Message CultureChangedMessage. When this message is sent, I will set the CurrentCultureInfo field and trigger a PropertyChanged event, passing “item” as PropertyName (see my previous blogpost). All bindings on the index property are re-executed, resulting in getting the resource values of the newly set CultureInfo.
The only thing left to do is adding an instance of the LocalizedResources class to a ViewModel.
public class ViewModelBase : INotifyPropertyChanged { public LocalizedResources Resources { get; private set; } public ViewModelBase() { Resources = new LocalizedResources(typeof(LocalizationDemoResources)); } public void OnPropertyChanged([CallerMemberName]string property = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); } public event PropertyChangedEventHandler PropertyChanged; }
So at a high level, it all comes down to this:
Example
This sample app has two views: Main and Settings. On the Settings view, users are able to change their language. The labels in the app will be updated instantly.
public class ViewModelBase : INotifyPropertyChanged { public LocalizedResources Resources { get; private set; } public ViewModelBase() { Resources = new LocalizedResources(typeof(LocalizationDemoResources), App.CurrentLanguage); } public void OnPropertyChanged([CallerMemberName]string property = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); } public event PropertyChangedEventHandler PropertyChanged; }
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:LocalizationDemo" x:Class="LocalizationDemo.MainPage"> <StackLayout> <Label Text="{Binding Resources[Welcome]}" VerticalOptions="Center" HorizontalOptions="Center" /> <Button Text="{Binding Resources[Settings]}" HorizontalOptions="Center" Clicked="Button_Clicked" /> </StackLayout> </ContentPage>
public partial class MainPage : ContentPage { public MainPage() { this.BindingContext = new MainPageViewModel(); InitializeComponent(); } private void Button_Clicked(object sender, EventArgs e) { Navigation.PushAsync(new SettingsPage()); } }
(MainPageViewModel only inherits ViewModelPage, there is nothing special to show there)
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LocalizationDemo.SettingsPage" Title="{Binding Resources[Settings]}"> <StackLayout> <Label Text="{Binding Resources[PickLng]}" /> <Picker ItemsSource="{Binding Languages}" SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}" /> </StackLayout> </ContentPage>
public partial class SettingsPage : ContentPage { public SettingsPage() { BindingContext = new SettingsViewModel(); InitializeComponent(); } }
public class SettingsViewModel : ViewModelBase { public List<string> Languages { get; set; } = new List<string>() { "EN", "NL", "FR" }; private string _SelectedLanguage; public string SelectedLanguage { get { return _SelectedLanguage; } set { _SelectedLanguage = value; SetLanguage(); } } public SettingsViewModel() { _SelectedLanguage = App.CurrentLanguage; } private void SetLanguage() { App.CurrentLanguage = SelectedLanguage; MessagingCenter.Send<object, CultureChangedMessage>(this, string.Empty, new CultureChangedMessage(SelectedLanguage); } }
Full source-code can be found on my GitHub.
Remarks: of course this is a bit of a naïve implementation as I just keep a CurrentLanguage field on the App class. This demo is just to show you how easy it is to implement localization of your app this way. If you want to do a ‘full’ implementation of localization, I would like to refer back to this guide on the Xamarin website and take a look at the ILocalize interface and the implementations of it. Using that abstraction works great together with my solution of localization.
If you have any remarks or questions, please feel free to drop a comment below!
June 15, 2017 at 1:45 pm
I was looking on how to do something like this, thanks for this post, it was really helpful!!
I’ve got one question though, why do you create the CultureChangedMessage? You could do something like:
MessagingCenter.Send(this, “CultureChanged”, new CultureInfo(“en”));
MessagingCenter.Subscribe(this, “CultureChanged”, OnCultureChanged);
Cheers!
July 1, 2017 at 8:17 am
Hi,
you are absolutely right, the approach you suggests works as well. However, I like to use dedicated classes for my messages, because strings are a lot more error prone as you could easily mistype the name of your message. Of course you can fix this by referencing to strings in a static messages class or something like that.
Both ways work perfectly. I think it is a matter of opinion and I suggest you use the approach you feel most comfortable with. 🙂
July 31, 2017 at 11:11 am
I want to implement the Indian languages, I added the new resx files in your sample, however only Hindi is working no other language like Tamil or Gujrati or Marathi working
July 31, 2017 at 11:33 am
Hi Raj. Interesting case. Could you please provide me your code? I’d love to take a look at it.
October 1, 2017 at 5:34 pm
HI, Is it possible to edit RESX files at runtime?
Actually I will be getting the translated strings from webservice. so how can I edit resx files dynamically?
October 1, 2017 at 5:46 pm
Hi Payal. If your translations are dynamic, resx isn’t the right tool for the job.
You can, however, use most of the code in my sample. But instead of going to look a specific string up in the resource file, look it up in your custom translation service which uses the (cached) data you retrieved from your backend service.
Kind regards,
Pieter
October 2, 2017 at 7:14 am
Thanks alot 🙂
It is possible for you to share sample app?
October 10, 2017 at 1:24 pm
This technique working fine in XAML, but what if i have a string in code behind.
October 12, 2017 at 7:09 am
Hi! I don’t know exactly what you mean… Do you want to access your resources in code behind? Because that is really easy to do. For example (in code behind):
string labelText = (this.BindingContext as MyViewModel).Resources["TheResourceYouWant"];
Please let me know if this is the answer to your question, else you can give me some information about what you want to do.
April 26, 2019 at 10:11 am
Hi, I’ve just found your great solution, but I have a problem with resource access from code as well. To be precise, I have no problem with accessing them, but they dont update dynamicaly.
XAML binding updates without any problem, but this code access doesn’t update when language changes.
Do you have any suggestions?
Thank you 🙂
November 7, 2017 at 7:23 pm
When you bind to viewmodel, which loads resources with some logic this method does not work 🙁 Do you know any elegant workaround?
December 19, 2017 at 10:31 pm
This is a great solution, I’ve been looking for it for a very long time!
There is one incomprehensible moment. I, like Rajnikant, want to use this solution in code, in a typical application with MasterDetailPage. I have a MasterPageItem class:
public class MasterPageItem : BindableObject
{
public static BindableProperty TitleProperty = BindableProperty.Create(
propertyName: “Title”,
returnType: typeof(string),
declaringType: typeof(MasterPageItem),
defaultValue: string.Empty,
defaultBindingMode: BindingMode.OneWay,
propertyChanged: TitlePropertyChanged);
}
I create the ObservableRangeCollection () in the ViewModel:
FirstPageItem = new MasterPageItem() { Icon = “learning.png”, TargetType = typeof(FirstPage) };
FirstPageItem.SetBinding(MasterPageItem.TitleProperty, new Binding(path: “Resources[FirstPageMenuItemText]”, source: this));
MainMenuItems = new ObservableRangeCollection()
{
FirstPageItem
};
And I assign in the MainPage constructor (MainPage.xaml.cs):
navigationDrawerList.ItemsSource = vModel.MainMenuItems;
But it does not work. There are items on the menu (I add more than I gave here), but they do not contain text. How to fix it?
October 11, 2018 at 7:41 am
Thank you Sir!
January 29, 2019 at 7:39 pm
Nice implementation! Thank you
March 27, 2019 at 11:00 am
Probably the best implementation I’ve seen so far, works really well, however I am very interested in if you can access the resource array from an other static resource (like a class of static String ID), any idea this could be accomplished?
Thanks a lot for the guide
May 27, 2019 at 8:49 pm
I like this post. If i change the language from language drop down then immediately constrol lable changed to selected language but I have one problem master detail page xamarin forms not changing language
I have added below come in constrator
MenuItems.Add(new Models.MenuModel()
{
Icon = “Home”,
PageName = nameof(HomePage),
Title = Resources[“titleHome”]
});
I think master detail page calling only once coz of that is it not changing.
do you have any suggestion to update menu page language
July 10, 2020 at 4:08 am
Pieter, thank you so much for sharing this clever implementation! It’s been super hard to find good Forms localization content. I’m using a modified version of your system in my current project, and it works great. It’s baffling that dynamic resource binding isn’t already a built-in Forms option! I wonder why that is.
July 2, 2021 at 7:58 am
Thanks a lot for posting this Article. I downloaded and run it. It working fine. The same article kept in csharpcorner but they didn’t kept any sample code or they didn’t tell fully. So very very much thank you so much for keeping the sample code. I love it. Keep it up like this only.