Localizing Xamarin.Forms with Hindi Language

India is a very versatile country with 23 constitutionally recognized official languages 🙂 out of those 9 are can be found in the MS Global list of languages codes used in providing localization to applications. What can be the best way to provide example of localization in Xamarin forms than to provide an example to which all Indians can relate to 🙂 our national language Hindi as the default documentation of Xamarin Forms provides examples in all the other languages 🙂 except Hindi.
The built-in mechanism for localizing .NET applications uses RESX files and the classes in the System.Resources and System.Globalization namespaces. The RESX files containing translated strings are embedded in the Xamarin.Forms assembly, along with a compiler-generated class that provides strongly-typed access to the translations. The translation text can then be retrieved in code.
The implementation of the same in Xamarin Forms will have to done in the platform specific code and invoke the same in portable class using Dependency Service.
Lets start coding 🙂 create a new project in Xamarin/Visual Studio (see this article for steps). Add a new folder named ‘Resx’ by right clicking on the PCL project file and Selecting ‘Add’ –> ‘Add Folder’ Option. This folder will be used to save the resource files to used to implementing localization, you can name it something else also but giving the name ‘Resx’ will give more clarity in understanding the purpose of the folder.
Create a new resource file inside the ‘Resx’ folder with name ‘AppResources.resx’ by right clicking on the folder then selecting Add –> New Item –> Visual C# –> Resource File. This will be the default resource file which will be used by the application in absence of any other resource files and will contain the english language implementation for the resource keys.The file will look like following screenshot:
English Resource File
Create another resource file inside ‘Resx’ folder with name ‘AppResources.hi.resx’ by following the same above mentioned steps.This resource file will be used to store the resource keys values for Hindi language. This file will look like following Screen Shot:
Hindi Resource File

Similarly you can add resource files for other languages using the code provided in MS Global list of languages like ‘AppResources..resx’ For example Gujrati –> ‘AppResources.gu.resx’ or Sanskrit –> ‘AppResources.sa.resx’ or Punjabi — > ‘AppResources.pa.resx’ etc.

Create the interface whose methods will be implemented in platform specific codes and invoked in PCL using dependency service. The code of the interface will be like:

using System.Globalization;
namespace HindiXamApp
{
    public interface ILocalize
    {
        CultureInfo GetCurrentCultureInfo();
        CultureInfo GetCurrentCultureInfo(string sLanguageCode);
        void SetLocale();
        void ChangeLocale(string sLanguageCode);
    }
}

The First method of the interface ‘GetCurrentCultureInfo’ is for getting default culture info of the device, second one ‘GetCurrentCultureInfo(string sLanguageCode)’ is for getting culture info on the basis of provided language code, third method ‘SetLocale’ is used to set the default culture info on the basis of value got from ‘GetCurrentCultureInfo’ and ‘ChangeLocale(string sLanguageCode)’ is to change the culture info of application on the basis of provided language code.
I have named the implementation class of above interface as ‘LocaleService’ in all the platforms.The Code for iOS implementation for the interface methods will be like:

using System;
using System.Globalization;
using System.Threading;

using Foundation;
using Xamarin.Forms;

[assembly: Dependency(typeof(HindiXamApp.iOS.LocaleService))]
namespace HindiXamApp.iOS
{
    public class LocaleService : ILocalize
    {
        public CultureInfo GetCurrentCultureInfo()
        {
            var iosLocaleAuto = NSLocale.AutoUpdatingCurrentLocale.LocaleIdentifier;    // en_FR
            var iosLanguageAuto = NSLocale.AutoUpdatingCurrentLocale.LanguageCode;      // en
            var netLocale = iosLocaleAuto.Replace("_", "-");
            const string defaultCulture = "en";

            CultureInfo ci = null;
            if (NSLocale.PreferredLanguages.Length > 0)
            {               
                try
                {
                    var pref = NSLocale.PreferredLanguages[0];
                    var netLanguage = pref.Replace("_", "-");
                    ci = CultureInfo.CreateSpecificCulture(netLanguage);
                }
                catch
                {
                    ci = new CultureInfo(defaultCulture);
                }
            }
            else
            {
                ci = new CultureInfo(defaultCulture); // default, shouldn't really happen 🙂
            }
            return ci;
        }
        public CultureInfo GetCurrentCultureInfo(string sLanguageCode)
        {
            return CultureInfo.CreateSpecificCulture(sLanguageCode);
        }
        public void SetLocale()
        {
            var ci = GetCurrentCultureInfo();
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;
            Console.WriteLine("SetLocale: " + ci.Name);
        }
        public void ChangeLocale(string sLanguageCode) {
            var ci = CultureInfo.CreateSpecificCulture(sLanguageCode);
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;
            Console.WriteLine("ChangeToLanguage: " + ci.Name);
        }      
    }
}

The Code for Android implementation will be like:

using System;
using System.Globalization;
using System.Threading;
using Xamarin.Forms;

[assembly: Dependency(typeof(HindiXamApp.Droid.LocaleService))]
namespace HindiXamApp.Droid
{
    public class LocaleService : ILocalize
    {
        public CultureInfo GetCurrentCultureInfo()
        {
            var androidLocale = Java.Util.Locale.Default; // user's preferred locale
            var netLocale = androidLocale.ToString().Replace("_", "-");

            #region Debugging output
            Console.WriteLine("android:  " + androidLocale.ToString());
            Console.WriteLine("netlang:  " + netLocale);

            var ci = new CultureInfo(netLocale);
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;
            Console.WriteLine("thread:  " + Thread.CurrentThread.CurrentCulture);
            Console.WriteLine("threadui:" + Thread.CurrentThread.CurrentUICulture);
            #endregion

            return ci;
        }
        public CultureInfo GetCurrentCultureInfo(string sLanguageCode)
        {
            return CultureInfo.CreateSpecificCulture(sLanguageCode);
        }
        public void SetLocale()
        {
            var ci = GetCurrentCultureInfo();
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;
        }
        public void ChangeLocale(string sLanguageCode)
        {
            var ci = CultureInfo.CreateSpecificCulture(sLanguageCode);
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;
            Console.WriteLine("ChangeToLanguage: " + ci.Name);
        }
    }
}

Since we are also going to use the values of resource files in XAML, we will have to write XAML extension to read the values from from resource files. So create a new class by the name of ‘TranslateExtension’ and copy following code inside it.

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

namespace HindiXamApp
{
    [ContentProperty("Text")]
    public class TranslateExtension : IMarkupExtension
    {
        readonly CultureInfo ci;
        const string ResourceId = "HindiXamApp.Resx.AppResources";

        public TranslateExtension()
        {
            if (string.IsNullOrEmpty(App.CultureCode))
            {
                ci = DependencyService.Get<ILocalize>().GetCurrentCultureInfo();
            }
            else ci = DependencyService.Get<ILocalize>().GetCurrentCultureInfo(App.CultureCode);
        }

        public string Text { get; set; }

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

            ResourceManager temp = new ResourceManager(ResourceId , typeof(TranslateExtension).GetTypeInfo().Assembly);
            var translation = temp.GetString(Text, ci);
            if (translation == null)
            {
#if DEBUG
                throw new ArgumentException(string.Format("Key '{0}' was not found in resources '{1}' for culture '{2}'.", Text, ResourceId, ci.Name), "Text");
#else
				translation = Text; // HACK: returns the key, which GETS DISPLAYED TO THE USER
#endif
            }
            return translation;
        }
    }
}

This completes the translation related code changes of the application, now lets create the UI of the application. The application will have a Xamarin Forms ContentPage which will have picker containing list of languages and table containing some static values.The static values of the table will by default appear in english and when the user selects ‘हिन्दी’ from the picker the content will change to hindi. I want to give user the freedom to choose the language in app rather than changing the language from device settings thats the reason I added ‘GetCurrentCultureInfo(string sLanguageCode)’ and ‘ChangeLocale(string sLanguageCode)’ in the code. The XAML code of the ‘HomePage’ is as follows:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:lEx="clr-namespace:HindiXamApp;assembly=HindiXamApp"
             x:Class="HindiXamApp.HomePage">
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
  </ContentPage.Padding>
  <ContentPage.Content>
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="10"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="10"/>
      </Grid.ColumnDefinitions>
      <Label Grid.Row ="0" Grid.Column="1" Text="{lEx:Translate SelectLanguage}" />
      <Picker  Grid.Row="0" Grid.Column="2" x:Name="pkrLanguage" SelectedIndexChanged="OnPickerChanged" WidthRequest="50" >
        <Picker.Items>
          <x:String>English</x:String>
          <x:String>हिन्दी</x:String>
        </Picker.Items>
      </Picker>
      <Label Grid.Row ="1" Grid.Column="1" Text="{lEx:Translate Name}" />
      <Label Grid.Row="1" Grid.Column="2"  Text ="{lEx:Translate NameValue}" />
      <Label Grid.Row ="2" Grid.Column="1" Text="{lEx:Translate DateOfBirth}" />
      <Label Grid.Row="2" Grid.Column="2" Text ="{lEx:Translate DOBValue}"/>
      <Label Grid.Row ="3" Grid.Column="1" Text="{lEx:Translate Gender}" />
      <Label Grid.Row="3" Grid.Column="2" Text ="{lEx:Translate GenderValue}"/>
      <Label Grid.Row ="4" Grid.Column="1" Text="{lEx:Translate Address}"  />
      <Label Grid.Row="4" Grid.Column="2" Text ="{lEx:Translate AddressValue}" />
      <Label Grid.Row ="5" Grid.Column="1" Text="{lEx:Translate State}"  />
      <Label Grid.Row="5" Grid.Column="2" Text ="{lEx:Translate StateValue}" />
      <Label Grid.Row ="6" Grid.Column="1" Text="{lEx:Translate Country}"/>
      <Label Grid.Row="6" Grid.Column="2" Text ="{lEx:Translate CountryValue}" />
    </Grid>   
  </ContentPage.Content>
</ContentPage>

As it can be seen fron the above code that we are using ‘{lEx:Translate HomeButtonXaml}’ to set the text of the variables on basis of resource file values and lEx: is declared as local namespace xmlns:lEx=”clr-namespace:HindiXamApp;assembly=HindiXamApp” this basically let us use the above declared Translate XAML extension.
The code written in code behind of ‘HomePage’, which contains the implementation of language change is as follows:

using System;
using Xamarin.Forms;

namespace HindiXamApp
{
    public partial class HomePage : ContentPage
    {
        public HomePage()
        {
            InitializeComponent();
        }

        public void OnPickerChanged(object sender, EventArgs args)
        {
            var vSelectedValue = pkrLanguage.Items[pkrLanguage.SelectedIndex];
            if (vSelectedValue.Trim() == "हिन्दी")
            {
                DependencyService.Get<ILocalize>().ChangeLocale("hi");
                App.CultureCode = "hi";
            }
            else
            {
              DependencyService.Get<ILocalize>().SetLocale();
              App.CultureCode = string.Empty;
            }
            var vUpdatedPage = new HomePage();
            Navigation.InsertPageBefore(vUpdatedPage, this);
            Navigation.PopAsync();
        }
    }
}

As it can be seen from the above code, we are changing the culture of the application in ‘OnPickerChanged’ event using dependency service code ‘ DependencyService.Get().ChangeLocale(“hi”);’, after that we are setting the value of application level static variable ‘App.CultureCode’ so that translate extension should be able to get the respective culture info from platform and lastly we are creating a new object of home page and adding it to Navigation stack as updated culture info won’t work on the existing page object because it’s resources are already loaded. I have implemented an if statement as I am showing the example for only 2 languages if you are giving options for more languages I suggest to use switch statement.
This is how the application will look on execution:
iOS Simulator:
iOS Example


Android Simulator:

I have just implemented the one aspect of implementing localization .i.e. user Interface localization, but in order to make the application completely localized the database of the application should also support that which will change depending upon the need/requirements of the application. This application right now only supports Hindi, however I would request everyone who knows/understand the other nine Indian languages to fork the code on Github, add the resource file of those languages so that we could share the example containing all the Indian languages :).You can use this translator which I used to write hindi. let me know if I have missed anything or you have any queries/suggestions.

🙂 Happy Coding 🙂

Reference : Official Xamarin Forms Documentation.

7 Comments

  1. Hi Srkrathore,
    I had made a sample application in xamarin forms and i was developing localization based on your project code .
    So each time i am changing the language to Hindi but every time again it is returing as “English” itself, it is not changing to “HINDI”.

    If you don’t mind Can you please check my code and Let me know where i am making a mistake that would be helful for me.

    SourceCode Url: https://www.dropbox.com/s/kmre92sastkllo3/ChakriMultiLanguage.zip?dl=0

    Thanks,
    Subbu.

    • Hi Subbu,

      Sorry for late reply was busy in other community activities and paid projects so didn’t got time to look into your code. It may take some time to look into he issue as I am still busy with some paid projects.

      — S Ravi Kumar

  2. Hello Sir,

    This is amazing demo you have given us. I just want to know do I have to write code(language words) for each and every word I am using in the app in .resx file. I want to use Marathi language in whole app.

Leave a Reply

Your email address will not be published.


*