Simple and cross platform internationalization/translations for Xamarin and .NET
Clone or download
Latest commit 5d729c2 Jun 19, 2018

README.md

I18N-Portable

Simple and cross platform internationalization/translations for Xamarin and .NET

NuGet NuGet AppVeyor Codecov

  • Cross platform
  • Simple to use: "key".Translate().
  • Simple and fluent initialization setup.
  • Readable locale files (.txt with key/value pairs).
  • Support for custom file formats (json, xml, etc)
  • Light weight
  • No dependencies.
  • Well tested

https://cloud.githubusercontent.com/assets/145087/24824462/c5a0ecce-1c0b-11e7-84d3-4f0fa815c9da.png

Install

Install it on your PCL and platform projects. From nuget package manager console:

PM> Install-Package I18NPortable

Setup locales

  • In your PCL/Core project, create a directory called "Locales".
  • Create a {languageCode}.txt file for each language you want to support. languageCode can be a two letter ISO code or a culture name like "en-US". See full list here.
  • Set "Build Action" to "Embedded Resource" on the properties of each file

Locale content sample

# key = value (the key will be the same across locales)
one = uno
two = dos
three = tres 
four = cuatro
five = cinco
  
# Enums are supported
Animals.Dog = Perro
Animals.Cat = Gato
Animals.Rat = Rata
Animals.Tiger = Tigre
Animals.Monkey = Mono
 
# Support for string.Format()
stars.count = Tienes {0} estrellas
 
TextWithLineBreakCharacters = Line One\nLine Two\r\nLine Three
 
Multiline = Line One
    Line Two
    Line Three

Other file formats (including custom) supported

Fluent initialization

I18N.Current
    .SetNotFoundSymbol("$") // Optional: when a key is not found, it will appear as $key$ (defaults to "$")
    .SetFallbackLocale("en") // Optional but recommended: locale to load in case the system locale is not supported
    .SetThrowWhenKeyNotFound(true) // Optional: Throw an exception when keys are not found (recommended only for debugging)
    .SetLogger(text => Debug.WriteLine(text)) // action to output traces
    .SetResourcesFolder("OtherLocales") // Optional: The directory containing the resource files (defaults to "Locales")
    .Init(GetType().GetTypeInfo().Assembly); // assembly where locales live

Usage

string one = "one".Translate();
string notification = "Mailbox.Notification".Translate("Diego", 3); // same as string.Format(params). Output: Hello Diego, you´ve got 3 emails
string missingKey = "missing".Translate(); // if the key is not found the output will be $key$. Output: $missing$
string giveMeNull = "missing".TranslateOrNull(); // Output: null

string dog = Animals.Dog.Translate(); // translate enum value (Animals is an Enum backed up in the locale file with "Animals.Dog = Perro")

List<string> animals = I18N.Current.TranslateEnumToList<Animals>(); 

List<Tuple<Animals, string>> animals = I18N.Current.TranslateEnumToTupleList<Animals>();
string dog = animals[0].Item2; // Perro

Dictionary<Animals, string> animals = I18N.Current.TranslateEnumToDictionary<Animals>();
string dog = animals[Animals.Dog]; // Perro

// List of supported languages (present in the "Locales" folder) in case you need to show a picker list
List<PortableLanguage> languages = I18N.Current.Languages; // Each `PortableLanguage` has 2 strings: Locale and DisplayName

// change language on runtime
I18N.Current.Language = language; // instance of PortableLanguage

// change language on runtime (option 2)
I18N.Current.Locale = "fr";

Data binding

I18N implements INotifyPropertyChanged and it has an indexer to translate keys. For instance, you could translate a key like:

string three = I18N.Current["three"]; 

With that said, the easiest way to bind your views to I18N translations is to use the built-in indexer by creating a proxy object in your ViewModel:

public abstract class BaseViewModel
{
    public II18N Strings => I18N.Current;
}

Xaml sample

<Button Content="{Binding Strings[key]}" />

Xamarin.Forms sample

<Button Text="{Binding Strings[key]}" />`

Android/MvvmCross sample

<TextView local:MvxBind="Text Strings[key]" />

iOS/MvvmCross sample

var set = this.CreateBindingSet<YourView, YourViewModel>();
set.Bind(anyUIText).To("Strings[key]");

Supported formats

The library ships with a single format reader/parser that is TextKvpReader. Any other reader will be isolated in a different nuget/plugin to keep the library as simple as possible.

Reader Format Source
TextKvpReader See sample I18NPortable
JsonKvpReader See sample I18NPortable.JsonReader I18NPortable.JsonReader
JsonListReader See sample I18NPortable.JsonReader I18NPortable.JsonReader

To use any non-default format, it needs to be added on initialization:

I18N.Current
    .AddLocaleReader(new JsonKvpReader(), ".json") // ILocaleReader, file extension
    // add more readers here if you need to
    .Init(GetType().Assembly);

Creating a custom reader for another file format:

It´s very easy to create custom readers/parsers for any file format you wish. For instance, lets take a loot at the above mentioned JsonKvpReader:

Given this en.json file

{
  "one": "uno",
  "two": "dos",
  "three": "tres"
}

Creating a custom reader is as simple as implementing ILocaleReader:

public interface ILocaleReader
{
    Dictionary<string, string> Read(Stream stream);
}
public class JsonKvpReader : ILocaleReader
{
    public Dictionary<string, string> Read(Stream stream)
    {
        using (var streamReader = new StreamReader(stream))
        {
            var json = streamReader.ReadToEnd();

            return JsonConvert
                .DeserializeObject<Dictionary<string, string>>(json)
                .ToDictionary(x => x.Key.Trim(), x => x.Value.Trim().UnescapeLineBreaks());
        }
    }
}

Contributing new readers

If you implemented a new reader for another file format and you want to contribute, feel free to make a pull request. Any new reader will live in their own project in the solution and will produce a different nuget as a plugin to I18NPortable.