Skip to content

Instantly share code, notes, and snippets.

@Alphapage
Created November 20, 2012 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alphapage/4119823 to your computer and use it in GitHub Desktop.
Save Alphapage/4119823 to your computer and use it in GitHub Desktop.
MvvmCross localization plugin dynamic key searching + sublanguage support + EmbeddedResource support in PCL + Grid resource format support
using System.Windows.Input;
using Cirrious.MvvmCross.Commands;
using Cirrious.MvvmCross.Localization.Interfaces;
using Cirrious.MvvmCross.Localization;
namespace Cirrious.Conference.Core.ViewModels
{
public class AboutViewModel
: BaseConferenceViewModel
{
public LocalizationBag CustomBag { get { return new LocalizationBag() { LanguageBinder = TextSource, StringToFind = "StuartLinkText" }; } }
public ICommand ContactSlodgeCommand
{
get
{
return
new MvxRelayCommand(
() =>
ComposeEmail("me@slodge.com", "About MvvmCross and the SQL Bits app", "I've got a question"));
}
}
public ICommand MvvmCrossOnGithubCommand
{
get
{
return
new MvxRelayCommand(
() =>
ShowWebPage("http://github.com/slodge/mvvmcross"));
}
}
public ICommand ShowSqlBitsCommand
{
get
{
return
new MvxRelayCommand(
() =>
ShowWebPage("http://sqlbits.com"));
}
}
public ICommand MonoTouchCommand
{
get
{
return
new MvxRelayCommand(
() =>
ShowWebPage("http://ios.xamarin.com"));
}
}
public ICommand MonoDroidCommand
{
get
{
return
new MvxRelayCommand(
() =>
ShowWebPage("http://android.xamarin.com"));
}
}
}
}
using Cirrious.MvvmCross.Application;
using Cirrious.MvvmCross.ExtensionMethods;
using Cirrious.MvvmCross.Interfaces.ServiceProvider;
using Cirrious.MvvmCross.Interfaces.ViewModels;
using Cirrious.MvvmCross.Localization.Interfaces;
namespace Cirrious.Conference.Core
{
public abstract class BaseConferenceApp
: MvxApplication
, IMvxServiceProducer<IMvxStartNavigation>
, IMvxServiceProducer<IMvxTextProvider>
, IMvxServiceProducer<IConferenceService>
, IMvxServiceProducer<ITwitterSearchProvider>
, IMvxServiceProducer<IErrorReporter>
, IMvxServiceProducer<IErrorSource>
{
protected BaseConferenceApp(string Language)
{
InitialisePlugins();
InitialiseText(Language);
InitialiseServices();
InitaliseErrorSystem();
}
private void InitialisePlugins()
{
Cirrious.MvvmCross.Plugins.File.PluginLoader.Instance.EnsureLoaded();
Cirrious.MvvmCross.Plugins.JsonLocalisation.PluginLoader.Instance.EnsureLoaded();
Cirrious.MvvmCross.Plugins.ResourceLoader.PluginLoader.Instance.EnsureLoaded();
// these don't really need to be loaded on startup, but it's convenient for now
Cirrious.MvvmCross.Plugins.Email.PluginLoader.Instance.EnsureLoaded();
Cirrious.MvvmCross.Plugins.PhoneCall.PluginLoader.Instance.EnsureLoaded();
Cirrious.MvvmCross.Plugins.Share.PluginLoader.Instance.EnsureLoaded();
Cirrious.MvvmCross.Plugins.Visibility.PluginLoader.Instance.EnsureLoaded();
Cirrious.MvvmCross.Plugins.WebBrowser.PluginLoader.Instance.EnsureLoaded();
}
private void InitaliseErrorSystem()
{
var errorHub = new ErrorApplicationObject();
this.RegisterServiceInstance<IErrorReporter>(errorHub);
this.RegisterServiceInstance<IErrorSource>(errorHub);
}
private void InitialiseServices()
{
var repository = new ConferenceService();
Cirrious.MvvmCross.Plugins.File.PluginLoader.Instance.EnsureLoaded();
this.RegisterServiceInstance<IConferenceService>(repository);
this.RegisterServiceInstance<ITwitterSearchProvider>(new TwitterSearchProvider());
}
private void InitialiseText(string Language)
{
var builder = new TextProviderBuilder();
// TODO - could choose a language here: builder.LoadResources(whichLanguage);
//builder.LoadResources("fr-ca");
//builder.LoadResources(Language);
builder.LoadEmbeddedResources(Language,false);
System.Diagnostics.Debug.WriteLine("Language:" + Language);
this.RegisterServiceInstance<IMvxTextProvider>(builder.TextProvider);
}
protected abstract void InitialiseStartNavigation();
}
public class ConferenceApp
: BaseConferenceApp
{
public ConferenceApp(string Language):base(Language)
{
InitialiseStartNavigation();
}
protected sealed override void InitialiseStartNavigation()
{
var startApplicationObject = new StartApplicationObject(true);
this.RegisterServiceInstance<IMvxStartNavigation>(startApplicationObject);
}
}
public class NoSplashScreenConferenceApp
: BaseConferenceApp
{
public NoSplashScreenConferenceApp(string Language)
: base(Language)
{
InitialiseStartNavigation();
}
protected sealed override void InitialiseStartNavigation()
{
var startApplicationObject = new StartApplicationObject(false);
this.RegisterServiceInstance<IMvxStartNavigation>(startApplicationObject);
}
}
}
#region Copyright
// <copyright file="IMvxJsonDictionaryTextLoader.cs" company="Cirrious">
// (c) Copyright Cirrious. http://www.cirrious.com
// This source is subject to the Microsoft Public License (Ms-PL)
// Please see license.txt on http://opensource.org/licenses/ms-pl.html
// All other rights reserved.
// </copyright>
//
// Project Lead - Stuart Lodge, Cirrious. http://www.cirrious.com
#endregion
using System.Collections.Generic;
namespace Cirrious.MvvmCross.Plugins.JsonLocalisation
{
public interface IMvxJsonDictionaryTextLoader
{
void LoadJsonFromResource(string namespaceKey, string typeKey, string resourcePath);
void LoadJsonFromText(string namespaceKey, string typeKey, string rawJson);
void LoadJsonFromEmbeddedResource(string namespaceKey, string typeKey, string resourcePath, bool IsFolderStructure, string whichLocalisationFolder);
List<string> GetSupportedLanguagesFromPackedEmbeddedResource(string namespaceKey, string typeKey, string resourcePath);
}
}
#region Copyright
// <copyright file="IMvxTextProviderBuilder.cs" company="Cirrious">
// (c) Copyright Cirrious. http://www.cirrious.com
// This source is subject to the Microsoft Public License (Ms-PL)
// Please see license.txt on http://opensource.org/licenses/ms-pl.html
// All other rights reserved.
// </copyright>
//
// Project Lead - Stuart Lodge, Cirrious. http://www.cirrious.com
#endregion
using System.Collections.Generic;
namespace Cirrious.MvvmCross.Plugins.JsonLocalisation
{
public interface IMvxTextProviderBuilder
{
MvxJsonDictionaryTextProvider TextProvider { get; }
void LoadResources(string whichLocalisationFolder);
void LoadEmbeddedResources(string whichLocalisationFolder,bool IsFolderStructure);
List<string> GetSupportedLanguagesFromPackedEmbeddedResource();
}
}
using System;
using Cirrious.MvvmCross.Exceptions;
using Cirrious.MvvmCross.ExtensionMethods;
using Cirrious.MvvmCross.Interfaces.ServiceProvider;
using Cirrious.MvvmCross.Localization.Interfaces;
namespace Cirrious.MvvmCross.Localization
{
public class LocalizationBag
{
public IMvxLanguageBinder LanguageBinder { get; set; }
public string StringToFind { get; set; }
}
}
#region Copyright
// <copyright file="MvxDictionaryBaseTextProvider.cs" company="Cirrious">
// (c) Copyright Cirrious. http://www.cirrious.com
// This source is subject to the Microsoft Public License (Ms-PL)
// Please see license.txt on http://opensource.org/licenses/ms-pl.html
// All other rights reserved.
// </copyright>
//
// Project Lead - Stuart Lodge, Cirrious. http://www.cirrious.com
#endregion
using System.Collections.Generic;
using Cirrious.MvvmCross.Platform.Diagnostics;
namespace Cirrious.MvvmCross.Plugins.JsonLocalisation
{
public class MvxDictionaryBaseTextProvider : MvxBaseTextProvider
{
private readonly Dictionary<string, string> _entries = new Dictionary<string, string>();
private readonly bool _maskErrors;
public MvxDictionaryBaseTextProvider(bool maskErrors)
{
_maskErrors = maskErrors;
}
protected void AddOrReplace(string namespaceKey, string typeKey, string name, string value)
{
var key = MakeLookupKey(namespaceKey, typeKey, name);
_entries[key] = value;
}
#region Implementation of IMvxTextProvider
public override string GetText(string namespaceKey, string typeKey, string name)
{
var key = MakeLookupKey(namespaceKey, typeKey, name);
string value;
if (_entries.TryGetValue(key, out value))
return value;
MvxTrace.Trace("Text value missing for " + key);
if (_maskErrors)
{
#if DEBUG
return key;
#else
return "";
#endif
}
throw new KeyNotFoundException("Could not find text lookup for " + key);
}
#endregion
}
}
#region Copyright
// <copyright file="MvxJsonDictionaryTextProvider.cs" company="Cirrious">
// (c) Copyright Cirrious. http://www.cirrious.com
// This source is subject to the Microsoft Public License (Ms-PL)
// Please see license.txt on http://opensource.org/licenses/ms-pl.html
// All other rights reserved.
// </copyright>
//
// Project Lead - Stuart Lodge, Cirrious. http://www.cirrious.com
#endregion
using System.Collections.Generic;
using System.IO;
using Cirrious.MvvmCross.ExtensionMethods;
using Cirrious.MvvmCross.Interfaces.Platform;
using Cirrious.MvvmCross.Interfaces.ServiceProvider;
using Cirrious.MvvmCross.Plugins.Json;
using Cirrious.MvvmCross.Plugins.ResourceLoader;
using System.Reflection;
using System;
using System.Linq;
namespace Cirrious.MvvmCross.Plugins.JsonLocalisation
{
public class MvxJsonDictionaryTextProvider
: MvxDictionaryBaseTextProvider
, IMvxJsonDictionaryTextLoader
, IMvxServiceConsumer<IMvxResourceLoader>
, IMvxServiceConsumer<IMvxJsonConverter>
{
public MvxJsonDictionaryTextProvider(bool maskErrors)
: base(maskErrors)
{
}
private IMvxJsonConverter JsonConvert
{
get
{
return this.GetService<IMvxJsonConverter>();
}
}
#region IMvxJsonDictionaryTextLoader Members
public void LoadJsonFromResource(string namespaceKey, string typeKey, string resourcePath)
{
var service = this.GetService<IMvxResourceLoader>();
var json = service.GetTextResource(resourcePath);
if (string.IsNullOrEmpty(json))
throw new FileNotFoundException("Unable to find resource file " + resourcePath);
LoadJsonFromText(namespaceKey, typeKey, json);
}
public void LoadJsonFromText(string namespaceKey, string typeKey, string rawJson)
{
var entries = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson);
foreach (var kvp in entries)
{
AddOrReplace(namespaceKey, typeKey, kvp.Key, kvp.Value);
}
}
public void LoadJsonFromEmbeddedResource(string namespaceKey, string typeKey, string resourcePath, bool IsFolderStructure, string whichLocalisationFolder)
{
var json = getTextFromEmbeddedResource(namespaceKey,resourcePath);
if (string.IsNullOrEmpty(json))
throw new FileNotFoundException("Unable to find resource file " + resourcePath);
LoadJsonFromText(namespaceKey, typeKey, json,IsFolderStructure,whichLocalisationFolder);
}
public void LoadJsonFromText(string namespaceKey, string typeKey, string rawJson, bool IsFolderStructure, string whichLocalisationFolder)
{
if (IsFolderStructure)
LoadJsonFromText(namespaceKey, typeKey, rawJson);
else
{
var entries = JsonConvert.DeserializeObject<Dictionary<string,List<string>>>(rawJson);
if (entries.Count <2)
return;
List<string> languageColumn = null;
entries.TryGetValue(whichLocalisationFolder, out languageColumn);
if (languageColumn == null)
return;
var textColumn = entries.ElementAt(0).Value;
for (int i = 0; i < textColumn.Count; i++)
{
AddOrReplace(namespaceKey, typeKey, textColumn[i], languageColumn[i]);
}
//foreach (var kvp in entries)
//{
// if (kvp.Key==whichLocalisationFolder)
// AddOrReplace(namespaceKey, typeKey, kvp.Key, kvp.Value);
//}
}
}
public List<string> GetSupportedLanguagesFromPackedEmbeddedResource(string namespaceKey, string typeKey, string resourcePath)
{
var json = getTextFromEmbeddedResource(namespaceKey, resourcePath);
List<string> supportedLanguages = new List<string>() ;
if (string.IsNullOrEmpty(json))
return supportedLanguages;
//throw new FileNotFoundException("Unable to find resource file " + resourcePath);
var entries = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(json);
if (entries.Count < 2)
return supportedLanguages;
for (int i = 1; i < entries.Count; i++)
{
supportedLanguages.Add(entries.ElementAt(i).Key);
}
return supportedLanguages;
}
string getTextFromEmbeddedResource(string namespaceKey, string resourcePath)
{
string path = namespaceKey+"." + resourcePath.Replace("/", ".");
try
{
string text = null;
Stream stream = Assembly.Load(namespaceKey).GetManifestResourceStream(path);
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
text = textReader.ReadToEnd();
}
return text;
}
//#if !NETFX_CORE
// catch (ThreadAbortException)
// {
// throw;
// }
//#endif
catch (Exception ex)
{
throw ex.MvxWrap("Cannot load resource {0}", path);
}
}
#endregion
}
}
#region Copyright
// <copyright file="MvxLanguageBinderConverter.cs" company="Cirrious">
// (c) Copyright Cirrious. http://www.cirrious.com
// This source is subject to the Microsoft Public License (Ms-PL)
// Please see license.txt on http://opensource.org/licenses/ms-pl.html
// All other rights reserved.
// </copyright>
//
// Project Lead - Stuart Lodge, Cirrious. http://www.cirrious.com
#endregion
using System;
using System.Globalization;
using Cirrious.MvvmCross.Converters;
using Cirrious.MvvmCross.Localization.Interfaces;
namespace Cirrious.MvvmCross.Localization.Converters
{
public class MvxLanguageBinderConverter
: MvxBaseValueConverter
{
#region Implementation of IValueConverter
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string key = null;
var binder = value as IMvxLanguageBinder;
if (binder != null)
{
if (parameter != null)
{
key = parameter.ToString();
return binder.GetText(key);
}
}
else
{
var binderBag = value as LocalizationBag;
if (binderBag != null)
{
if (binderBag.StringToFind !=null)
{
key = binderBag.StringToFind;
return binderBag.LanguageBinder.GetText(key);
}
}
}
return null;
}
#endregion
}
}
#region Copyright
// <copyright file="MvxTextProviderBuilder.cs" company="Cirrious">
// (c) Copyright Cirrious. http://www.cirrious.com
// This source is subject to the Microsoft Public License (Ms-PL)
// Please see license.txt on http://opensource.org/licenses/ms-pl.html
// All other rights reserved.
// </copyright>
//
// Project Lead - Stuart Lodge, Cirrious. http://www.cirrious.com
#endregion
using System;
using System.Collections.Generic;
using Cirrious.MvvmCross.ExtensionMethods;
using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics;
using Cirrious.MvvmCross.Platform.Diagnostics;
namespace Cirrious.MvvmCross.Plugins.JsonLocalisation
{
public abstract class MvxTextProviderBuilder
: IMvxTextProviderBuilder
{
private readonly string _generalNamespaceKey;
private readonly string _rootFolderForResources;
protected MvxTextProviderBuilder(string generalNamespaceKey, string rootFolderForResources)
{
#warning Error masking turned on by default - check this is OK
_generalNamespaceKey = generalNamespaceKey;
_rootFolderForResources = rootFolderForResources;
TextProvider = new MvxJsonDictionaryTextProvider(true);
LoadEmbeddedResources(string.Empty,false);
}
protected abstract IDictionary<string, string> ResourceFiles { get; }
#region IMvxTextProviderBuilder Members
public MvxJsonDictionaryTextProvider TextProvider { get; private set; }
public void LoadResources(string whichLocalisationFolder)
{
loadResourcesSelector(whichLocalisationFolder, false,true);
}
public void LoadEmbeddedResources(string whichLocalisationFolder,bool IsFolderStructure)
{
loadResourcesSelector(whichLocalisationFolder, true,IsFolderStructure);
}
void loadResourcesSelector(string whichLocalisationFolder, bool EmbeddedResource, bool IsFolderStructure)
{
bool isSubLanguage = whichLocalisationFolder.Contains("-");
string standardLocalisationFolder = isSubLanguage ? whichLocalisationFolder.Substring(0, whichLocalisationFolder.IndexOf("-")) : whichLocalisationFolder;
loadResources(standardLocalisationFolder,EmbeddedResource,IsFolderStructure);
if (isSubLanguage)
{
loadResources(whichLocalisationFolder, EmbeddedResource, IsFolderStructure);
}
}
void loadResources(string whichLocalisationFolder, bool EmbeddedResource, bool IsFolderStructure)
{
foreach (var kvp in ResourceFiles)
{
try
{
if (EmbeddedResource)
TextProvider.LoadJsonFromEmbeddedResource(_generalNamespaceKey, kvp.Key, GetResourceFilePath("", kvp.Value), IsFolderStructure, whichLocalisationFolder);
else
TextProvider.LoadJsonFromResource(_generalNamespaceKey, kvp.Key, GetResourceFilePath(whichLocalisationFolder, kvp.Value));
}
#if !NETFX_CORE
//catch (ThreadAbortException)
//{
// throw;
//}
#endif
catch (Exception exception)
{
MvxTrace.Trace(MvxTraceLevel.Warning, "Language file could not be loaded for {0}.{1} - {2}", whichLocalisationFolder, kvp.Key, exception.ToLongString());
}
}
}
public List<string> GetSupportedLanguagesFromPackedEmbeddedResource()
{
List<string> supportedLanguages = new List<string>();
foreach (var kvp in ResourceFiles)
{
List<string> languages = TextProvider.GetSupportedLanguagesFromPackedEmbeddedResource(_generalNamespaceKey, kvp.Key, GetResourceFilePath("", kvp.Value));
foreach (var language in languages)
{
if (!supportedLanguages.Contains(language))
supportedLanguages.Add(language);
}
}
return supportedLanguages;
}
#endregion
protected virtual string GetResourceFilePath(string whichLocalisationFolder, string whichFile)
{
if (string.IsNullOrEmpty(whichLocalisationFolder))
return string.IsNullOrEmpty(_rootFolderForResources) ? string.Format("{0}.json", whichFile) : string.Format("{0}/{1}.json", _rootFolderForResources, whichFile);
else
return string.IsNullOrEmpty(_rootFolderForResources) ? string.Format("{0}/{1}.json", whichLocalisationFolder, whichFile) : string.Format("{0}/{1}/{2}.json", _rootFolderForResources, whichLocalisationFolder, whichFile);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/com.cirrious.conference.sqlbits"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/transparent">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:orientation="vertical">
<TextView
android:text="hello"
style="@style/AboutPageTitle"
android:id="@+id/textViewAboutPageTitle"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;CustomBag&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;SQLBitsXApp&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;AboutSQLBitsXApp&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_email"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;StuartLinkText&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;ContactSlodgeCommand&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;Disclaimer&apos;}}" />
<TextView
style="@style/AboutPageTitle"
android:text="hello world"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;SQLBitsX&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;AboutSQLBitsX&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_link"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;SQLBitsLinkText&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;ShowSqlBitsCommand&apos;}}" />
<TextView
style="@style/AboutPageTitle"
android:text="hello world"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;SQLBits&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;AboutSQLBits&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_link"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;SQLBitsLinkText&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;ShowSqlBitsCommand&apos;}}" />
<TextView
style="@style/AboutPageTitle"
android:text="hello world"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;MvvmCross&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;AboutMvvmCross&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_email"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;StuartLinkText&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;ContactSlodgeCommand&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;ForMvvmSource&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_link"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;MvvmCrossLinkText&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;MvvmCrossOnGithubCommand&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;ForXamarin&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_link"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;MonoTouch&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;MonoTouchCommand&apos;}}" />
<Button
style="@style/IconButton"
android:drawableLeft="@drawable/appbar_link"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;MonoForAndroid&apos;},&apos;Click&apos;:{&apos;Path&apos;:&apos;MonoDroidCommand&apos;}}" />
<TextView
android:text="hello"
style="@style/AboutPageBodyText"
local:MvxBind="{&apos;Text&apos;:{&apos;Path&apos;:&apos;TextSource&apos;,&apos;Converter&apos;:&apos;Language&apos;,&apos;ConverterParameter&apos;:&apos;DisclaimerMono&apos;}}" />
</LinearLayout>
</ScrollView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:src="@drawable/tweetShare"
local:MvxBind="{&apos;Click&apos;:{&apos;Path&apos;:&apos;ShareGeneralCommand&apos;}}" />
</RelativeLayout>
@Alphapage
Copy link
Author

Support for this json format:
{
"Text":["string1","string2","string3"],
"":["string1 en","string2 en","string3 en"],
"fr":["string1 fr","string2 fr","string3 in French"],
"fr-be":["string1 fr-be","string2 fr-be","string3 fr-be"]
}
With little effort, one can open excel,add a column language, modify the values then export to json.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment