mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-04-25 07:24:14 +00:00
Add localization (#1482)
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
176
DiscordChatExporter.Gui/Converters/MarkdownToInlinesConverter.cs
Normal file
176
DiscordChatExporter.Gui/Converters/MarkdownToInlinesConverter.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using DiscordChatExporter.Gui.Utils.Extensions;
|
||||
using DiscordChatExporter.Gui.Views.Controls;
|
||||
using Markdig;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using MarkdownInline = Markdig.Syntax.Inlines.Inline;
|
||||
|
||||
namespace DiscordChatExporter.Gui.Converters;
|
||||
|
||||
public class MarkdownToInlinesConverter : IValueConverter
|
||||
{
|
||||
public static readonly MarkdownToInlinesConverter Instance = new();
|
||||
|
||||
private static readonly MarkdownPipeline MarkdownPipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmphasisExtras()
|
||||
.Build();
|
||||
|
||||
private static void ProcessInline(
|
||||
InlineCollection inlines,
|
||||
MarkdownInline markdownInline,
|
||||
FontWeight? fontWeight = null,
|
||||
FontStyle? fontStyle = null,
|
||||
TextDecorationCollection? textDecorations = null
|
||||
)
|
||||
{
|
||||
switch (markdownInline)
|
||||
{
|
||||
case LiteralInline literal:
|
||||
{
|
||||
var run = new Run(literal.Content.ToString());
|
||||
|
||||
if (fontWeight is not null)
|
||||
run.FontWeight = fontWeight.Value;
|
||||
if (fontStyle is not null)
|
||||
run.FontStyle = fontStyle.Value;
|
||||
if (textDecorations is not null)
|
||||
run.TextDecorations = textDecorations;
|
||||
|
||||
inlines.Add(run);
|
||||
break;
|
||||
}
|
||||
|
||||
case LineBreakInline:
|
||||
{
|
||||
inlines.Add(new LineBreak());
|
||||
break;
|
||||
}
|
||||
|
||||
case EmphasisInline emphasis:
|
||||
{
|
||||
var newWeight = fontWeight;
|
||||
var newStyle = fontStyle;
|
||||
var newDecorations = textDecorations;
|
||||
|
||||
switch (emphasis.DelimiterChar)
|
||||
{
|
||||
case '*' or '_' when emphasis.DelimiterCount == 2:
|
||||
newWeight = FontWeight.SemiBold;
|
||||
break;
|
||||
case '*' or '_':
|
||||
newStyle = FontStyle.Italic;
|
||||
break;
|
||||
case '~':
|
||||
newDecorations = TextDecorations.Strikethrough;
|
||||
break;
|
||||
case '+':
|
||||
newDecorations = TextDecorations.Underline;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var child in emphasis)
|
||||
ProcessInline(inlines, child, newWeight, newStyle, newDecorations);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LinkInline link:
|
||||
{
|
||||
inlines.Add(
|
||||
new InlineUIContainer(
|
||||
new HyperLink
|
||||
{
|
||||
Text = link.GetInnerText(),
|
||||
Url = link.Url,
|
||||
VerticalAlignment = VerticalAlignment.Bottom,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ContainerInline container:
|
||||
{
|
||||
foreach (var child in container)
|
||||
ProcessInline(inlines, child, fontWeight, fontStyle, textDecorations);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
var inlines = new InlineCollection();
|
||||
if (value is not string { Length: > 0 } text)
|
||||
return inlines;
|
||||
|
||||
var isFirst = true;
|
||||
|
||||
foreach (var block in Markdown.Parse(text, MarkdownPipeline))
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case ParagraphBlock { Inline: not null } paragraph:
|
||||
{
|
||||
if (!isFirst)
|
||||
{
|
||||
// Insert a blank line between paragraphs
|
||||
inlines.Add(new LineBreak());
|
||||
inlines.Add(new LineBreak());
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
|
||||
foreach (var markdownInline in paragraph.Inline!)
|
||||
ProcessInline(inlines, markdownInline);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ListBlock list:
|
||||
{
|
||||
var itemOrder = 1;
|
||||
if (list.IsOrdered && int.TryParse(list.OrderedStart, out var startNum))
|
||||
itemOrder = startNum;
|
||||
|
||||
foreach (var listItem in list.OfType<ListItemBlock>())
|
||||
{
|
||||
if (!isFirst)
|
||||
inlines.Add(new LineBreak());
|
||||
isFirst = false;
|
||||
|
||||
var prefix = list.IsOrdered ? $"{itemOrder++}. " : $"{list.BulletType} ";
|
||||
inlines.Add(new Run(prefix));
|
||||
|
||||
foreach (var subBlock in listItem.OfType<ParagraphBlock>())
|
||||
{
|
||||
if (subBlock is { Inline: not null } p)
|
||||
foreach (var markdownInline in p.Inline)
|
||||
ProcessInline(inlines, markdownInline);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inlines;
|
||||
}
|
||||
|
||||
public object? ConvertBack(
|
||||
object? value,
|
||||
Type targetType,
|
||||
object? parameter,
|
||||
CultureInfo culture
|
||||
) => throw new NotSupportedException();
|
||||
}
|
||||
Reference in New Issue
Block a user