Xamarin Android and iOS .resx localization with MvvmCross
Tags: csharp, xamarin, mvvmcross, localization
Published: 2021-01-23 00:10:21

Almost any mobile app published to the PlayStore or AppStore should be localizated in different languages in order to get more clients as possible from anywhere in the world.

MvvmCross is a 'MVVM framework' written exclusively to work with Xamarin and provides an easy way to share the localized resources among the different platforms using the same `.resx` files. In this post I will show you how to share localized resources between an Android and an iOS apps.

# Let's localize it!
Our starting point is the standard MvvmCross solution structure, with a separate project for each platform and the `Core` project with the common stuff. I created a single view called `HomeView` with its view model `HomeViewModel` on both platforms:

Solution structure:
<br />
![Solution](https://dev-to-uploads.s3.amazonaws.com/i/bfe4lpctefrjjpkj6sdu.png)

The `HomeView`s contain only a label in the middle of the screen with the text "Hello". Below the screenshots and the UI definition:

Android:
<br />
![Android](https://dev-to-uploads.s3.amazonaws.com/i/q1kie991qtipn62yhcdf.png)
<br />
```
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <androidx.appcompat.widget.AppCompatTextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Hello!"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
```
<br />
<br />

iOS:
<br />
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4e92xkv2r0hpc7jfhkfd.png)
<br />
```
public override void ViewDidLoad()
{
    View.BackgroundColor = UIColor.White;

    base.ViewDidLoad();

    var label = new UILabel { Text = "Hello!" };

    Add(label);

    View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
    View.AddConstraints(
        label.WithSameCenterX(View),
        label.WithSameCenterY(View));

    var set = CreateBindingSet();
    set.Bind(label).ToLocalizationId("Hello");
    set.Apply();
 }

```
<br />
<br />

*NOTE: For more information on how to setup MvvmCross, refer to the [official documentation](https://www.mvvmcross.com/documentation/getting-started/getting-started).*
<br />
<br />

At this point, we must install the NuGet package `MvvmCross.Plugin.ResxLocalization` to the `Core` project.
<br />
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ehh2swkluau27onl8pqr.png)
<br />

Then creates a folder `Localization` and add a resource file, let's call it `AppResources.resx`. This file will contain the main language of our app.
<br />
Open it and be sure to set the `Access Modifier` to `Public`:
<br />
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/10yin1hd5fape4873uju.png)
<br />
Let's add our first localized resource, as per in the screenshot below:
<br />
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fsfftxc2b6bi62iv7w6x.png)
<br />
<br />
*NOTE: the MvvmCross convention uses the string `{ViewModelName}.{ResourceName}` as key. In our case it is `HomeViewModel.Hello`.*
<br />
<br />
Now we can proceed adding another resource file containing the translations for another language of our choices - Italian in my case. To do this, add a file called `AppResources.it.resx` still under `Localization` folder:
<br />
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7kq7q2vsvgdw5awpla39.png)

Please, note that now the `Access Modifer` has been set to `No code generation`. In fact, it's only the main resx file that is going to generate the necessary code.

Close the resx files and go in `HomeViewModel` and:
- Implement the interface `IMvxLocalizedTextSourceOwner`
- Define the following property:
```
public IMvxLanguageBinder LocalizedTextSource =>
            new MvxLanguageBinder("", GetType().Name);
```
<br />
<br />

*NOTE: For demostration purposes I am doing this in the (only) page's view model, but in a real environment it would be better to add this property to a single base view model since almost every page in our application would need to localize resources.*

<br />
<br />
Register the plugin in the Mvx IoC container at the startup (`App.cs` in my example):
<br />

```
Mvx.IoCProvider.RegisterSingleton<IMvxTextProvider>(new MvxResxTextProvider(AppResources.ResourceManager));
```
<br />

Now it's time to apply the translation via the binding set exposed by MvvmCross. Even you can do this by UI too, I prefer to set all the resources by code: in case of refactoring or changes, it's definitely easier to keep track of all the changes. Let's see how to do it.

## Android
From the definition above, replace the `android:text` definition with `android:id`. You can see mine below:
<br />

```
<androidx.appcompat.widget.AppCompatTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/txtHello"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>

```
We need the control's `id` (`txtHello`) in the code behind in the next step.

Go in `HomeView.cs`. Write code to get the reference of the UI control (`txtHello`) and to bind it to the `MvxLanguageBinder` instance we defined into the view model through a built-in converter:
<br />
```
protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    SetContentView(Resource.Layout.home_view);

    var textView = FindViewById<AppCompatTextView>(Resource.Id.txtHello);

    var set = CreateBindingSet();

    set.Bind(textView)**.ToLocalizationId("Hello");**

    set.Apply();
}
```
<br />
Now your app is ready to rock! This is what you see if you press F5:
<br />

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d0n7cfenaectw67wm54k.png)

<br />

Then go to Android's settings and change the language to Italian. Then open the app again:
<br />

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0j02z0p5ifya2cwhws6s.png)

<br />

Urrah! 🎉🎉
<br /><br />
## iOS
In iOS everything works just as what we did on the Android side.

Creates the Mvx's binding set and apply the binding to the localized resource:
<br />

```
var set = CreateBindingSet();
set.Bind(label).ToLocalizationId("Hello");
set.Apply();
```
<br />

Change the iPhone language to Italian.. and here we go again 🎉
<br />
<br />
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o7eu18ygbs54u7stvuuj.png)

<br/>
<br/>

The source code is available on [my GitHub](https://github.com/Krusty93/MvvmCrossLocalization/tree/main)