[backend/federation] Add LDLocalizedString

This commit is contained in:
pancakes 2025-01-27 01:37:36 +10:00 committed by Laura Hausmann
parent 3bfb133661
commit 9e713419ce
No known key found for this signature in database
GPG key ID: D044E84C5BE01605

View file

@ -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();
}
} }