Wednesday, September 18, 2013

Localization in Xamarin + MVVMCross



Here is how I implement localization in my Xamarin MVVMCross (what a great pair!) app:
#1. In the Core PCL project, create resource (project properties \ Resources) and use editor to enter all localizable strings in the app (or, you might use localization tools which produce the .resx file).
     Make sure to select Public visibility for the members of the generated Resources class.image
     For the resource name, I usually use the actual string + 'Text':
         'Username' –> UsernameText. 
         'Change password' –> ChangePasswordText.
     For long strings, I try to give it a short distinctive name  + usage + 'Text':
         'NewAppVersionAvailableMessageText'  -> 'A new version of the application is currently available. Would you like to download it?'
         'DeleteUserConfirmationText'  ->  'Are you sure you want to delete selected user?'
#2. I have a BaseViewModel class derived from MvxViewModel serving as a base for all my view models. It uses an indexer to get the translated string value based on an index resource value  
public class ViewModelBase : MvxViewModel
{
    public string this[string index]
    {
        get
        {
            return Resources.ResourceManager.GetString(index);
        }
    }

#3.
(for Android, but the idea is similar to other platforms too) In the layout, I am using MVVMCross binding to bind the controls to the view model’s indexer and pass the resource name:

<TextView local:MvxBind="Text [UsernameText]"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content" />

At runtime, because of the MVVMCross binding between the TextView’s Text property and view model indexer, the Text property will call the view model’s indexer using the ‘UsernameText’ as index.
This in turn calls ResourceManager.GetString which returns the localized string.

This setup works great. I haven’t yet tried it on all the platforms but it makes sharing a single set of localized strings. Also, the resource strings can be referenced by the statically typed properties of the Resources class.

If the application needs to switch the language at runtime, it should be enough to tell the view model to notify its bound controls to refresh their values. I haven’t tried it yet, but viewModel.RaisePropertyChanged(string.Empty) should work.

There are different approaches to localization, here are few links:
N=21 - Internationalisation - i18n - N+1 Days of MvvmCros (using the MVMCross plugin) 
Using resx-files for localization in MvvmCross (mentioned in the video)
http://danielvaughan.org/post/Generating-Localized-Resources-in-Mono-for-Android-Using-T4.aspx (interesting ideas)

I am happy to hear your thoughts on this approach.

11 comments:

  1. awesome and just in time :)

    ReplyDelete
  2. Very nice work! How do you set which culture is selected, though?

    ReplyDelete
  3. Very nice work! How do do you set which culture is selected, though?

    ReplyDelete
  4. Very nice work! How do you set which culture is selected, though?

    ReplyDelete
  5. I think you can do that at the app initialization point.

    See
    http://stackoverflow.com/questions/468791/setting-currentculture-and-currentuiculture-of-an-application

    ReplyDelete
  6. Anonymous2:59 PM

    Exactly what I was looking for, thank you for posting.

    ReplyDelete
  7. Hi, the indexer binding seems not working anymore with MvvmCross 3.1.1, do you have any idea?

    ReplyDelete
  8. Indexer binding seems not working anymore with MvvmCross 3.1.1. Do you have any idea?

    ReplyDelete
  9. Anonymous11:18 AM

    You're the best !

    ReplyDelete
  10. Anonymous11:52 AM

    Any idea how to do this binding in the axml without mvvmcross? just with plain xamarin?
    Thx, Tom

    ReplyDelete
  11. @Tom it's not possible without MvvMCross.
    MvvmCross adds custom AXML elements to make it possible.

    ReplyDelete