Skip to content
Merged

I18n v2 #2709

Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion BetterGenshinImpact/App.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Application x:Class="BetterGenshinImpact.App"
<Application x:Class="BetterGenshinImpact.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behavior="clr-namespace:BetterGenshinImpact.View.Behavior"
Expand All @@ -24,6 +24,7 @@
<bgivc:NotNullConverter x:Key="NotNullConverter" />
<bgivc:EnumToKVPConverter x:Key="EnumToKVPConverter" />
<bgivc:CultureInfoNameToKVPConverter x:Key="CultureInfoNameToKVPConverter" />
<bgivc:TrConverter x:Key="TrConverter" />
<Style BasedOn="{StaticResource DefaultTextBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="behavior:ClipboardInterceptor.EnableSafeClipboard" Value="True" />
</Style>
Expand Down
7 changes: 6 additions & 1 deletion BetterGenshinImpact/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@ public partial class App : Application
}

Log.Logger = loggerConfiguration.CreateLogger();
services.AddLogging(c => c.AddSerilog());
services.AddSingleton<ITranslationService, JsonTranslationService>();
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.Services.AddSingleton<ILoggerProvider, TranslatingSerilogLoggerProvider>();
});

services.AddLocalization();

Expand Down
3 changes: 3 additions & 0 deletions BetterGenshinImpact/BetterGenshinImpact.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
<None Update="GameTask\Common\Element\Assets\1920x1080\in_domain.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="User\I18n\en.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
130 changes: 130 additions & 0 deletions BetterGenshinImpact/Helpers/TranslatingSerilogLoggerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BetterGenshinImpact.Service.Interface;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;

namespace BetterGenshinImpact.Helpers;

public sealed class TranslatingSerilogLoggerProvider : ILoggerProvider
{
private readonly ITranslationService _translationService;

public TranslatingSerilogLoggerProvider(ITranslationService translationService)
{
_translationService = translationService;
}

public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName)
{
return new TranslatingSerilogLogger(categoryName, _translationService);
}

public void Dispose()
{
}

private sealed class TranslatingSerilogLogger : Microsoft.Extensions.Logging.ILogger
{
private readonly ITranslationService _translationService;
private readonly Serilog.ILogger _logger;

public TranslatingSerilogLogger(string categoryName, ITranslationService translationService)
{
_translationService = translationService;
_logger = Serilog.Log.Logger.ForContext("SourceContext", categoryName);
}

public IDisposable BeginScope<TState>(TState state) where TState : notnull
{
return NullScope.Instance;
}

public bool IsEnabled(LogLevel logLevel)
{
return logLevel != LogLevel.None;
}

public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}

var serilogLevel = ConvertLevel(logLevel);
if (serilogLevel == null)
{
return;
}

var (template, values) = ExtractTemplateAndValues(state, formatter, exception);
var translatedTemplate = _translationService.Translate(template, TranslationSourceInfo.From(MissingTextSource.Log));

if (values.Length == 0)
{
_logger.Write(serilogLevel.Value, exception, translatedTemplate);
return;
}

_logger.Write(serilogLevel.Value, exception, translatedTemplate, values);
}

private (string Template, object?[] Values) ExtractTemplateAndValues<TState>(
TState state,
Func<TState, Exception?, string> formatter,
Exception? exception)
{
if (state is IReadOnlyList<KeyValuePair<string, object?>> kvps)
{
var original = kvps.FirstOrDefault(kv => string.Equals(kv.Key, "{OriginalFormat}", StringComparison.Ordinal));
var template = original.Value as string;
if (string.IsNullOrEmpty(template))
{
template = formatter(state, exception);
}

var values = kvps
.Where(kv =>
!string.Equals(kv.Key, "{OriginalFormat}", StringComparison.Ordinal) &&
!string.Equals(kv.Key, "EventId", StringComparison.Ordinal))
.Select(kv => kv.Value)
.ToArray();

return (template ?? string.Empty, values);
}

return (formatter(state, exception), Array.Empty<object?>());
}

private static LogEventLevel? ConvertLevel(LogLevel level)
{
return level switch
{
LogLevel.Trace => LogEventLevel.Verbose,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Critical => LogEventLevel.Fatal,
_ => null
};
}

private sealed class NullScope : IDisposable
{
public static NullScope Instance { get; } = new();

public void Dispose()
{
}
}
}
}
39 changes: 39 additions & 0 deletions BetterGenshinImpact/Service/Interface/ITranslationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Globalization;

namespace BetterGenshinImpact.Service.Interface;

public enum MissingTextSource
{
Log,
UiStaticLiteral,
UiDynamicBinding,
Unknown
}

public sealed class TranslationSourceInfo
{
public MissingTextSource Source { get; set; } = MissingTextSource.Unknown;
public string? ViewXamlPath { get; set; }
public string? ViewType { get; set; }
public string? ElementType { get; set; }
public string? ElementName { get; set; }
public string? PropertyName { get; set; }
public string? BindingPath { get; set; }
public string? Notes { get; set; }

public static TranslationSourceInfo From(MissingTextSource source)
{
return new TranslationSourceInfo
{
Source = source
};
}
}

public interface ITranslationService
{
string Translate(string text);
string Translate(string text, TranslationSourceInfo sourceInfo);
CultureInfo GetCurrentCulture();
}

Loading