diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index 09c2a60..e494583 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -118,8 +117,12 @@ public abstract class ExportCommandBase : DiscordCommandBase )] public string DateFormat { get; init; } = "MM/dd/yyyy h:mm tt"; - [CommandOption("locale", Description = "Locale to use when formatting dates and numbers.")] - public string Locale { get; init; } = CultureInfo.CurrentCulture.Name; + [CommandOption( + "locale", + Description = "Locale to use when formatting dates and numbers. " + + "If not specified, the default system locale will be used." + )] + public string? Locale { get; init; } [CommandOption("utc", Description = "Normalize all timestamps to UTC+0.")] public bool IsUtcNormalizationEnabled { get; init; } = false; diff --git a/DiscordChatExporter.Core/Exporting/ExportRequest.cs b/DiscordChatExporter.Core/Exporting/ExportRequest.cs index 8ddab72..30a7cb2 100644 --- a/DiscordChatExporter.Core/Exporting/ExportRequest.cs +++ b/DiscordChatExporter.Core/Exporting/ExportRequest.cs @@ -8,6 +8,7 @@ using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exporting.Filtering; using DiscordChatExporter.Core.Exporting.Partitioning; using DiscordChatExporter.Core.Utils; +using DiscordChatExporter.Core.Utils.Extensions; namespace DiscordChatExporter.Core.Exporting; @@ -39,9 +40,9 @@ public partial class ExportRequest public bool ShouldReuseAssets { get; } - public string Locale { get; } + public string? Locale { get; } - public CultureInfo CultureInfo { get; } + public CultureInfo? CultureInfo { get; } public bool IsUtcNormalizationEnabled { get; } @@ -58,7 +59,7 @@ public partial class ExportRequest bool shouldFormatMarkdown, bool shouldDownloadAssets, bool shouldReuseAssets, - string locale, + string? locale, bool isUtcNormalizationEnabled ) { @@ -83,7 +84,7 @@ public partial class ExportRequest ? FormatPath(assetsDirPath, Guild, Channel, After, Before) : $"{OutputFilePath}_Files{Path.DirectorySeparatorChar}"; - CultureInfo = CultureInfo.GetCultureInfo(Locale); + CultureInfo = Locale?.Pipe(CultureInfo.GetCultureInfo); } } diff --git a/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs b/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs index bfae746..f7cff19 100644 --- a/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs +++ b/DiscordChatExporter.Gui/Converters/LocaleToDisplayNameConverter.cs @@ -9,13 +9,15 @@ public class LocaleToDisplayNameConverter : IValueConverter { public static LocaleToDisplayNameConverter Instance { get; } = new(); - public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) => - value is string locale ? CultureInfo.GetCultureInfo(locale).DisplayName : null; + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => + value is string locale && !string.IsNullOrWhiteSpace(locale) + ? CultureInfo.GetCultureInfo(locale).DisplayName + : "System default"; public object ConvertBack( - object value, + object? value, Type targetType, - object parameter, + object? parameter, CultureInfo culture ) => throw new NotSupportedException(); } diff --git a/DiscordChatExporter.Gui/Services/SettingsService.cs b/DiscordChatExporter.Gui/Services/SettingsService.cs index 2d11f1c..58310ef 100644 --- a/DiscordChatExporter.Gui/Services/SettingsService.cs +++ b/DiscordChatExporter.Gui/Services/SettingsService.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.IO; using Cogwheel; using DiscordChatExporter.Core.Exporting; @@ -21,7 +20,7 @@ public partial class SettingsService() public ThreadInclusionMode ThreadInclusionMode { get; set; } = ThreadInclusionMode.None; - public string Locale { get; set; } = CultureInfo.CurrentCulture.Name; + public string? Locale { get; set; } public bool IsUtcNormalizationEnabled { get; set; } diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs index 10370b1..d15e217 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; +using DiscordChatExporter.Core.Utils.Extensions; using DiscordChatExporter.Gui.Models; using DiscordChatExporter.Gui.Services; using DiscordChatExporter.Gui.ViewModels.Framework; @@ -37,10 +37,11 @@ public class SettingsViewModel(SettingsService settingsService) : DialogScreen set => settingsService.ThreadInclusionMode = value; } + // These items have to be non-nullable because WPF ComboBox doesn't allow a null value to be selected public IReadOnlyList AvailableLocales { get; } = new[] { - // Current locale - CultureInfo.CurrentCulture.Name, + // Current locale (maps to null downstream) + "", // Locales supported by the Discord app "da-DK", "de-DE", @@ -73,10 +74,12 @@ public class SettingsViewModel(SettingsService settingsService) : DialogScreen "ko-KR" }.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); + // This has to be non-nullable because WPF ComboBox doesn't allow a null value to be selected public string Locale { - get => settingsService.Locale; - set => settingsService.Locale = value; + get => settingsService.Locale ?? ""; + // Important to reduce empty strings to nulls, because empty strings don't correspond to valid cultures + set => settingsService.Locale = value.NullIfWhiteSpace(); } public bool IsUtcNormalizationEnabled