Xamarin iOS localization using .NET

I've created a bit of an ugly solution, but it works. What I've done is made a folder called 'strings' in my Portable Class Library (PCL) and inside that created files called:

  • strings.resx
  • strings_fr.resx
  • strings_ja_JP.resx

I've set all of these as Embedded Resource with custom tool as ResXFileCodeGenerator. This means I have a separate resource DLL for each language.

In iOS I can then get the locale by calling:

string sLocale = NSLocale.CurrentLocale.LocaleIdentifier;

I would guess there's an Android equivalent using Locale but I don't know what it is.

This gives me a string like "ja_JP" or "fr_FR" or "en_GB" (note they're underscores, not dashes). I then use this with the following static class I created to retrieve an appropriate resource manager and get the string from it.

If given a locale aa_BB it first looks for strings_aa_BB, then for strings_aa, then for strings.

public static class Localise
{
    private const string STRINGS_ROOT = "MyPCL.strings.strings";

    public static string GetString(string sID, string sLocale)
    {
        string sResource = STRINGS_ROOT + "_" + sLocale;
        Type type = Type.GetType(sResource);
        if (type == null)
        {
            if (sLocale.Length > 2) {
                sResource = STRINGS_ROOT + "_" + sLocale.Substring(0, 2); // Use first two letters of region code
                type = Type.GetType(sResource);
            }
        }
        if (type == null) {
            sResource = STRINGS_ROOT;
            type = Type.GetType(sResource);
            if (type == null)
            {
                System.Diagnostics.Debug.WriteLine("No strings resource file when looking for " + sID + " in " + sLocale);
                return null; // This shouldn't ever happen in theory
            }
        }
        ResourceManager resman = new ResourceManager(type);
        return resman.GetString(sID);
    }
}

An example of how this would be used (referring to the above code) would be:

string sText = Localise.GetString("enter_movie_name", sLocale);
lblEnterMovieName.Text = sText;

A significant downside of this is that all views will need to have their text set programatically, but does have the upside that the translations can be done once and then reused on many platforms. They also remain separate from the main code in their own DLLs and therefore can be recompiled individually if necessary.


I created a similar solution to the accepted answer but using txt files instead of Resx and nuget ready to go: https://github.com/xleon/I18N-Portable. Blog post here.

Other improvements are:

  • "anyKey".Translate(); // from anywhere
  • Automatic detection of the current culture
  • Get a list of supported translations: List<PortableLanguage> languages = I18N.Current.Languages;
  • Support for Data Binding in Mvvm frameworks / Xamarin.Forms
  • etc