Globalizing Xamarin.Forms code
• Adding and using string resources in Xamarin.Forms PCL app
• Enabling language (culture) detection
Localizing XAML
• Localizing XAML using IMarkupExtension
• Enabling the markup extension in the native apps, it is Windows (UWP & WP8.1)
in this article
Let’s create simple login page as below and bind all the strings from resource file.
Create a Xamarin.Forms project and add the resource .RESX file to PCL. This resource
file contains the default text, then add additional RESX files for each language
we wish to support.
When you create RESX file, the class and properties are internal by default
for the assembly. To make them Public, you must manually change the resource
file property CustomTool to PublicResXFileCodeGenerator.
Since globalization relies on device culture, create a dependency service
to get the culture (language) of user device as below.
Create an interface in PCL and declare a function as below.
public interface IPlatformService
{
CultureInfo GetCurrentCultureInfo();
}
A Class implementation in native app and register with Xamarin.forms dependency as
below.
[assembly: Xamarin.Forms.Dependency(typeof(PlatformService))]
namespace PageCustomRenderer.UWP.DependencyServices
{
public class PlatformService : IPlatformService
{
public CultureInfo GetCurrentCultureInfo()
{
return CultureInfo.CurrentUICulture;
}
}
}
If you want to localize or bind the resource strings in XAML, create a class to inherit
from iMarkupExtension. This class is being used to get the resource string value
by key from resource file based on user device culture.
[ContentProperty("Key")]
public class ResourceExtension : IMarkupExtension
{
private const string ResourceId = "PageCustomRenderer.Resources.Resource1";
private readonly CultureInfo cultureInfo;
private readonly ResourceManager resmgr;
public ResourceExtension()
{
this.cultureInfo = DependencyService.Get<IPlatformService>().GetCurrentCultureInfo();
this.resmgr = typeof(Resource1)
.GetRuntimeFields()
.First(m => m.Name == "resourceMan")
.GetValue(typeof(Resource1)) as ResourceManager;
}
public string Key { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (this.Key == null)
{
return string.Empty;
}
var translation = this.resmgr.GetString(this.Key, this.cultureInfo);
if (translation == null)
{
#if DEBUG
throw new ArgumentException(
string.Format("Key '{0}' was not found in resources '{1}'.", this.Key, ResourceId),
"Text");
#else
translation = Key; // HACK: returns the key, which GETS DISPLAYED TO THE USER
#endif
}
return translation;
}
}
From above, ResourceExtension class has been added with attribute ContentProperty of type Key, so pass the Key
from XAML to get respective string value from resource manager, hence Implemented
the ProvideValue function.
ResourceId, is being used to identify the resource fileEach resource file contains ResourceManager
which will be used to retrieve the resource strings. Just open the resource designer
file, you can find the resource id and resource manager with all resource keys.
Now, open the MainPage.Xaml and add the xmlns namespace of the ResourceExtension
class as below.
xmlns:res="clr-namespace:PageCustomRenderer.Resources"
XAML design with resource keys binding as below.
<StackLayout Margin="10">
<Label Text="{res:Resource SignIn}" Margin="10" FontSize="25"></Label>
<Label Text="{res:Resource UserName}" Margin="5" FontSize="21"></Label>
<Entry Margin="5" Placeholder="User Name"></Entry>
<Label Text="{res:Resource Password}" Margin="5" FontSize="21"></Label>
<Entry Margin="5" Placeholder="Password" IsPassword="True"></Entry>
<Button Text="{res:Resource Login}" Margin="10"></Button>
</StackLayout>
From above, res is xmlns namespace and Resource is considered from ResourceExtension
classs name.
If you want to use the resource file in code, you can directly use the resource file
as below.
var text = Resource1.Password;
Since windows (UWP and WP8.1) apps has the limitation of loading the resource files
from Xamarin.Forms PCL project, create a ResourceManager class in windows project
and ensure to load the resource file from Xamarin.Forms PCL project as below.
public class WinRtResourceManager : ResourceManager
{
private ResourceLoader resourceLoader;
private WinRtResourceManager(string baseName, Assembly assembly) : base(baseName, assembly)
{
this.resourceLoader = ResourceLoader.GetForViewIndependentUse(baseName);
}
public static void InjectIntoResxGeneratedApplicationResourcesClass(Type resxGeneratedApplicationResourcesClass)
{
resxGeneratedApplicationResourcesClass
.GetRuntimeFields()
.First(m => m.Name == "resourceMan")
.SetValue(
null,
new WinRtResourceManager(
resxGeneratedApplicationResourcesClass.FullName,
resxGeneratedApplicationResourcesClass.GetTypeInfo().Assembly));
}
public override string GetString(string name, CultureInfo culture)
{
return this.resourceLoader.GetString(name);
}
}
Finally, use this ResourceManager class in app.xaml.cs OnLaunched event as below.
WinRtResourceManager.InjectIntoResxGeneratedApplicationResourcesClass(typeof(Resources.Resource1));
Please read through the link for more documentation on Localization.
Download the working sample.