I want to translate keys used in JSON to different languages.
I'm aware this seems like nonsense from a technical perspective when designing interfaces, APIs and so on. Why not use English only in the first place? Well, I didn't write this requirement ;)
/// <summary>serialization language</summary>
public enum Language
{
/// <summary>English</summary>
EN,
/// <summary>German</summary>
DE
// some more ...
}
The easiest way to achieve this is probably an attribute:
/// <summary>An Attribute to add different "translations"</summary>
public class TranslatedFieldName : Attribute
{
public string Name { get; }
public Language Lang{ get; }
// actually there might be a dictionary with lang-name pairs later on; but let's keep it simple
public TranslatedFieldName(string translatedName, Language lang)
{
this.Lang = lang;
this.Name = translatedName;
}
}
Then I add this attribute to the class I'd like to serialize:
/// <summary>I want to serialize classes of this kind to json in different languages </summary>
public class TranslatableObject
{
[TranslatedFieldName("deutscher_schluessel", Language.DE)]
public string english_key;
}
and call JsonConvert.SerializeObject(...)
public void SerializationMethod()
{
TranslatableObject to = new TranslatableObject()
{
english_key = "foo bar"
};
string englishJson = JsonConvert.SerializeObject(to); // == { "english_key": "foo bar" }
string germanJson = JsonConvert.SerializeObject(to, new MultiLangConverter(Language.DE); // == { "deutscher_schluessel": "foo bar" }
}
I don't have a clue how the used MultiLangConverter
should actually look like:
public class MultiLangConverter : JsonConverter
{
private readonly Language _lang,
public MultiLangConverter() : this(Language.EN) { }
public MultiLangConverter(Language lang)
{
_lang = lang;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Out of scope for now");
}
public override bool CanRead
{
get { return false; } // out of scope for now
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// what goes here?
}
}
All the attempts I tried seemed overly complex. Do I actually need a JsonConverter? Or is a ContractResolver better suited?
I think you are on the right track with using attributes to represent the translated field names, but I think you will ultimately want to use a custom ContractResolver
to handle the translation rather than a JsonConverter
, especially if you will need to do translation on multiple model classes (seems likely).
Here is what the resolver would look like:
public class MultiLangResolver : DefaultContractResolver
{
public Language Language { get; set; }
public MultiLangResolver(Language lang)
{
Language = lang;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
// See if there is a [TranslatedFieldName] attribute applied to the property
// for the requested language
var att = prop.AttributeProvider.GetAttributes(true)
.OfType<TranslatedFieldNameAttribute>()
.FirstOrDefault(a => a.Lang == Language);
// if so, change the property name to the one from the attribute
if (att != null)
{
prop.PropertyName = att.Name;
}
return prop;
}
}
And here is how you would use the resolver to serialize:
public static string Serialize(object obj, Language lang)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new MultiLangResolver(lang),
Formatting = Formatting.Indented
};
var json = JsonConvert.SerializeObject(obj, settings);
return json;
}
One other note: you will want to mark your TranslatedFieldNameAttribute
class with an [AttributeUsage]
attribute which indicates that it can be used on properties and can be applied multiple times (once for each different language you want to support):
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class TranslatedFieldNameAttribute : Attribute
{
...
}
Working demo: https://dotnetfiddle.net/lIQaJc
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With