XAML localization with Xamarin.Forms

Adding support for multiple languages in Xamarin.Forms is a relatively simple task. If you're using XAML to build your UI however, the solution might not be so obvious however. This is mainly because the XAML parser for Xamarin.Forms isn't as mature as implementation offered by Microsoft, and for that reason it does not offer a solution out of the box for binding translated text directly to your controls. For that reason, we have to hook into the pipeline in order to inject translated text into our controls.

Resource Files

If you're coming from a Microsoft based XAML world, you're probably used to working with resource files (*.resx). These files provide a simple way of managing translated values for different languages. Coupled with the Multilingual App Toolkit you easily translate your application into multiple languages. The goal is therefore to create a set of resource files, and hook them into our Xamarin.Forms application.

Generating Resource Files

In order to generate our resource files, we'll open up our solution in Visual Studio. The reason for this, is that Visual Studio generate some boilerplate code for us when we generate our first resource file.

For this example, I'm using a PCL Xamarin.Forms project:

Project layout 

We'll place our resource files in the project containing our UI. In this example, the views live in the Glider_Log project. Create a folder, Resources, in this project. Create a resource file in this folder:

 Create resource file

This resource file will be your default language file. I usually put english text in this. If a user loads up your application on a device with a language setting you do not provide translations for, this is the file the application will use. For each language you're going to support, you will need to add an aditional resource file with the name: "Resource.{culture name}.resx". You can find a list of culture names here. In this example, I'll add support for Danish. The project now looks like this:

 Languages

We'll now add some values to our resource file. I usually use the convention "view_value", but the only requirement is to use a value with no spaces:

English values

Since we're placing the resource files in the same assembly as the views, we can keep the default access modifier of Internal. If you need to reference the values in other assemblies, remember to change the access modifier for each resource file. Now we'll add the translated values to the Resource.da-DK.resx file:

 Danish values

Adding The Glue

At this point we need to add the glue that allows the engine to inject our values into our controls. For this we'll need to implement the IMarkupExtension interface. For this we'll add a new file, TranslateExtension.cs, to our Resources folder. In this file, we'll implement the interface:

using System;
using System.Globalization;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Glider_Log.Resources
{
    [ContentProperty("Text")]
    public class TranslateExtension : IMarkupExtension
    {
        public string Text { get; set; }

        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Text == null)
                return null;

            return Resource.ResourceManager.GetString(Text, CultureInfo.CurrentCulture);
        }
    }
}

This allows us to look up translated values at runtime using the ResourceManager (this is part of the boilerplate code generated by Visual Studio earlier. You can examine the code by opening the code-behind file of the Resource.resx file ). The resource manager will select the appropriate resource file, depending on the language used on the device. The text value to be translated will be provided by the XAML view.

Next, we'll create a Xamarin.Forms ContentPage ( Dashboard.xaml ) with a few controls:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
		     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"	
		     x:Class="Glider_Log.Dashboard"
		     xmlns:resources="clr-namespace:Glider_Log.Resources;assembly=Glider_Log">
	<ContentPage.Resources>
		<ResourceDictionary>
                     <LayoutOptions x:Key="horizontalOptions"
                                    Alignment="Center" />
		</ResourceDictionary>
	</ContentPage.Resources>
	<ContentPage.Content>
                 <StackLayout>
                       <Label Text="{resources:Translate Dashboard_Title}" HorizontalOptions="{StaticResource horizontalOptions}" />
                       <Button Text="{resources:Translate Dashboard_New}" />
                       <Button Text="{resources:Translate Dashboard_Browse}" />
                       <Button Text="{resources:Translate Dashboard_Settings}" />
		</StackLayout>
	</ContentPage.Content>
</ContentPage>

First we define the resources namespace at the ContentPage level. This will allow us to easily reference the Translate extension later on. We add a simple StackLayout with 4 controls: One Label instance, and three Button instances. On each control we'll bind the Text property to the translate extension (tip: you can leave out the Extension part of the type name). The value provided will be used by the ResourceManager to look up the value to be returned. For this reason, the value provided must be identical to the value in the resource files.

At this point, we're ready to test the application. If we load up the application on a device using English we get the following:

 Windows phone english

If we switch the language to Danish, we'll get the following result:

Windows phone danish

At this point we have a fully localized application in two languages, working across Android, IOS and Windows Phone, using a centralized solution. Adding support for more languages at this point, is easily achieved by adding more resource files.

Gotcha

 In order for languages to work on Windows Phone, you have to add support for the specific language first. This is easily done by accessing the projects settings ( Right-Click on the project -> Settings ). From here, you can add the languages you support in the Supported Cultures section: 

Windows phone supported languages

comments powered by Disqus