[backend/federation] Add LDLocalizedString
This commit is contained in:
parent
3bfb133661
commit
9e713419ce
1 changed files with 155 additions and 2 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Globalization;
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -6,10 +7,113 @@ using J = Newtonsoft.Json.JsonPropertyAttribute;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
|
public class LDLocalizedString
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// key: BCP 47 language code, with empty string meaning "unknown"
|
||||||
|
/// value: content, in that language
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string?> Values { get; set; }
|
||||||
|
|
||||||
|
public LDLocalizedString()
|
||||||
|
{
|
||||||
|
Values = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public LDLocalizedString(string? language, string? value)
|
||||||
|
{
|
||||||
|
Values = [];
|
||||||
|
|
||||||
|
// this is required to create a non-Map field for non-JsonLD remotes.
|
||||||
|
Values.Add("", value);
|
||||||
|
|
||||||
|
if (language != null)
|
||||||
|
{
|
||||||
|
language = NormalizeLanguageCode(language);
|
||||||
|
|
||||||
|
if (language != null && language != "")
|
||||||
|
Values.Add(language, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the remote sends different content to multiple languages, try and guess which one they prefer "by default".
|
||||||
|
///
|
||||||
|
/// This idea was taken from Sharkey's implementation.
|
||||||
|
/// https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/401#note_1174
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language">The guessed language</param>
|
||||||
|
/// <returns>The value, in the guessed language</returns>
|
||||||
|
public string? GuessPreferredValue(out string? language)
|
||||||
|
{
|
||||||
|
if (Values.Count > 1)
|
||||||
|
{
|
||||||
|
var unknownValue = GetUnknownValue();
|
||||||
|
if (unknownValue != null)
|
||||||
|
{
|
||||||
|
var preferred = Values.FirstOrDefault(i => !IsUnknownLanguage(i.Key) && i.Value == unknownValue);
|
||||||
|
if (preferred.Value != null)
|
||||||
|
{
|
||||||
|
language = NormalizeLanguageCode(preferred.Key);
|
||||||
|
return preferred.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Values.Count == 0)
|
||||||
|
{
|
||||||
|
language = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var first = Values.FirstOrDefault(i => !IsUnknownLanguage(i.Key));
|
||||||
|
if (first.Value == null)
|
||||||
|
{
|
||||||
|
first = Values.FirstOrDefault();
|
||||||
|
if (first.Value == null)
|
||||||
|
{
|
||||||
|
language = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
language = IsUnknownLanguage(first.Key) ? null : NormalizeLanguageCode(first.Key);
|
||||||
|
return first.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? NormalizeLanguageCode(string lang)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return CultureInfo.GetCultureInfo(lang).ToString();
|
||||||
|
}
|
||||||
|
catch (CultureNotFoundException)
|
||||||
|
{
|
||||||
|
// invalid language code
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Akkoma forces all non-localized text to be in the "und" language by adding { "@language":"und" } to it's context
|
||||||
|
public static bool IsUnknownLanguage(string? lang) => lang == null || lang == "" || lang == "und";
|
||||||
|
public string? GetUnknownValue()
|
||||||
|
{
|
||||||
|
string? value;
|
||||||
|
|
||||||
|
if (Values.TryGetValue("", out value))
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (Values.TryGetValue("und", out value))
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class LDValueObject<T>
|
public class LDValueObject<T>
|
||||||
{
|
{
|
||||||
[J("@type")] public string? Type { get; set; }
|
[J("@type")] public string? Type { get; set; }
|
||||||
[J("@value")] public required T Value { get; set; }
|
[J("@value")] public required T Value { get; set; }
|
||||||
|
[J("@language")] public string? Language { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ValueObjectConverter : JsonConverter
|
public class ValueObjectConverter : JsonConverter
|
||||||
|
@ -157,4 +261,53 @@ public sealed class XsdString(string? str)
|
||||||
public static implicit operator string?(XsdString? s) => s?.Str;
|
public static implicit operator string?(XsdString? s) => s?.Str;
|
||||||
|
|
||||||
public override string? ToString() => str;
|
public override string? ToString() => str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocalizedValueObjectConverter : JsonConverter<LDLocalizedString>
|
||||||
|
{
|
||||||
|
public override LDLocalizedString? ReadJson(JsonReader reader, Type objectType, LDLocalizedString? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var obj = JArray.Load(reader);
|
||||||
|
var list = obj.ToObject<List<LDValueObject<string?>>>();
|
||||||
|
if (list == null || list.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var localized = new LDLocalizedString();
|
||||||
|
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
localized.Values.Add(item.Language ?? "", item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return localized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, LDLocalizedString? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
writer.WriteNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteStartArray();
|
||||||
|
|
||||||
|
foreach (var item in value.Values)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
|
||||||
|
if (!LDLocalizedString.IsUnknownLanguage(item.Key))
|
||||||
|
{
|
||||||
|
writer.WritePropertyName("@language");
|
||||||
|
writer.WriteValue(item.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WritePropertyName("@value");
|
||||||
|
writer.WriteValue(item.Value);
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue