Localizing Xamarin.Forms With Hindi Language

India is a very versatile country with 23 constitutionally recognized official languages. Out of those nine can be found in the MS Global list of language codes used in providing localization to applications. What can be a better way to provide an 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 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 be done in the platform-specific code and invoke the same in portable class using Dependency Service.

Let's start coding and create a new project in Xamarin/Visual Studio. Add a new folder named ‘Resx’ by right-clicking on the PCL project file and Selecting the ‘Add’ –> ‘Add Folder’ Option. This folder will be used to save the resource files to be used to implement 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 that will be used by the application in the absence of any other resource files and will contain the English language implementation for the resource keys.

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 key values for the Hindi language. 

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 this:

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 the above interface as ‘LocaleService’ in all the platforms. The Code for iOS implementation for the interface methods will be like this:

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 <img draggable="false" class="emoji" alt="" src="https://s.w.org/images/core/emoji/72x72/1f642.png">  
            }  
            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 this:

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 extensions to read the values from resource files. To create a new class by the name of ‘TranslateExtension’ and copy the 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 let's create the UI of the application. The application will have a Xamarin Forms ContentPage which will have a picker containing a list of languages and a 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 users the freedom to choose the language in the app rather than changing the language from device settings that's 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 TranslateXAML 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 the translate extension should be able to get the respective culture info from the platform and lastly we are creating a new object of the homepage 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.

I have just implemented 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, and 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 if you have any queries/suggestions.

Reference: Official Xamarin Forms Documentation

Read more articles on Xamarin:


Similar Articles