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 providean 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 be done in the platform specific code and invoke the same in portable class using Dependency Service.
Let's 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 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 which 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.The file will look like following screenshot:
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:
Similarly you can add resource files for other languages using the code provided in MS Global listof 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;
- var iosLanguageAuto = NSLocale.AutoUpdatingCurrentLocale.LanguageCode;
- 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);
- }
- 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;
- 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 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;
- #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 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 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 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:
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.
Reference : Official Xamarin Forms Documentation
Read more articles on Xamarin: