I18N-Portable
Simple and cross platform internationalization/translations for Xamarin and .NET
- 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
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 |
JsonListReader | See sample | 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.