Improve localization for dates and numbers in the app UI

This commit is contained in:
chylex 2021-11-29 17:05:02 +01:00
parent c262e5aaa4
commit 07af4ae00f
No known key found for this signature in database
GPG Key ID: 4DE42C8F19A80548
11 changed files with 70 additions and 19 deletions

View File

@ -1,5 +1,6 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="clr-namespace:DHT.Desktop.Common"
x:Class="DHT.Desktop.App">
<Application.Styles>
@ -111,9 +112,13 @@
</Application.Styles>
<Application.Resources>
<common:NumberValueConverter x:Key="NumberValueConverter" />
<Thickness x:Key="ExpanderHeaderPadding">15,0</Thickness>
<Thickness x:Key="ExpanderContentPadding">15</Thickness>
<SolidColorBrush x:Key="ExpanderDropDownBackground" Color="#FCFCFC" />
</Application.Resources>
</Application>

View File

@ -0,0 +1,15 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace DHT.Desktop.Common {
public class NumberValueConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return string.Format(Program.Culture, "{0:n0}", value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,19 @@
namespace DHT.Desktop.Common {
public static class TextFormat {
public static string Format(this int number) {
return number.ToString("N0", Program.Culture);
}
public static string Format(this long number) {
return number.ToString("N0", Program.Culture);
}
public static string Pluralize(this int number, string singular) {
return number.Format() + "\u00A0" + (number == 1 ? singular : singular + "s");
}
public static string Pluralize(this long number, string singular) {
return number.Format() + "\u00A0" + (number == 1 ? singular : singular + "s");
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Avalonia.Threading;
using DHT.Desktop.Common;
using DHT.Desktop.Models;
namespace DHT.Desktop.Dialogs {
@ -55,7 +56,7 @@ namespace DHT.Desktop.Dialogs {
async Task IProgressCallback.Update(string message, int finishedItems, int totalItems) {
await Dispatcher.UIThread.InvokeAsync(() => {
model.Message = message;
model.Items = finishedItems + " / " + totalItems;
model.Items = finishedItems.Format() + " / " + totalItems.Format();
model.Progress = 100 * finishedItems / totalItems;
});
}

View File

@ -35,7 +35,7 @@
<WrapPanel>
<StackPanel>
<CheckBox IsChecked="{Binding FilterByDate}">Filter by Date</CheckBox>
<Grid ColumnDefinitions="Auto, 4, 140" RowDefinitions="Auto, 4, Auto" Margin="4 0">
<Grid ColumnDefinitions="Auto, 4, 125" RowDefinitions="Auto, 4, Auto" Margin="4 0">
<Label Grid.Row="0" Grid.Column="0">From:</Label>
<CalendarDatePicker Grid.Row="0" Grid.Column="2" x:Name="StartDatePicker" IsEnabled="{Binding FilterByDate}" SelectedDateChanged="CalendarDatePicker_OnSelectedDateChanged" />
<Label Grid.Row="2" Grid.Column="0">To:</Label>

View File

@ -3,18 +3,29 @@ using Avalonia.Markup.Xaml;
namespace DHT.Desktop.Main.Controls {
public class FilterPanel : UserControl {
private CalendarDatePicker StartDatePicker => this.FindControl<CalendarDatePicker>("StartDatePicker");
private CalendarDatePicker EndDatePicker => this.FindControl<CalendarDatePicker>("EndDatePicker");
public FilterPanel() {
InitializeComponent();
}
private void InitializeComponent() {
AvaloniaXamlLoader.Load(this);
var culture = Program.Culture;
foreach (var picker in new CalendarDatePicker[] { StartDatePicker, EndDatePicker }) {
picker.FirstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek;
picker.SelectedDateFormat = CalendarDatePickerFormat.Custom;
picker.CustomDateFormatString = culture.DateTimeFormat.ShortDatePattern;
picker.Watermark = culture.DateTimeFormat.ShortDatePattern;
}
}
public void CalendarDatePicker_OnSelectedDateChanged(object? sender, SelectionChangedEventArgs e) {
if (DataContext is FilterPanelModel model) {
model.StartDate = this.FindControl<CalendarDatePicker>("StartDatePicker").SelectedDate;
model.EndDate = this.FindControl<CalendarDatePicker>("EndDatePicker").SelectedDate;
model.StartDate = StartDatePicker.SelectedDate;
model.EndDate = EndDatePicker.SelectedDate;
}
}
}

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Common;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Models;
using DHT.Server.Data;
@ -190,13 +191,13 @@ namespace DHT.Desktop.Main.Controls {
private void UpdateChannelFilterLabel() {
long total = db.Statistics.TotalChannels;
long included = FilterByChannel ? IncludedChannels.Count : total;
ChannelFilterLabel = "Selected " + included + " / " + total + (total == 1 ? " channel." : " channels.");
ChannelFilterLabel = "Selected " + included.Format() + " / " + total.Pluralize("channel") + ".";
}
private void UpdateUserFilterLabel() {
long total = db.Statistics.TotalUsers;
long included = FilterByUser ? IncludedUsers.Count : total;
UserFilterLabel = "Selected " + included + " / " + total + (total == 1 ? " user." : " users.");
UserFilterLabel = "Selected " + included.Format() + " / " + total.Pluralize("user") + ".";
}
public MessageFilter CreateFilter() {

View File

@ -41,17 +41,17 @@
<Rectangle />
<StackPanel Orientation="Vertical">
<TextBlock Classes="label">Servers</TextBlock>
<TextBlock Classes="value" Text="{Binding DatabaseStatistics.TotalServers, StringFormat={}{0:n0}}" />
<TextBlock Classes="value" Text="{Binding DatabaseStatistics.TotalServers, Converter={StaticResource NumberValueConverter}}" />
</StackPanel>
<Rectangle />
<StackPanel Orientation="Vertical">
<TextBlock Classes="label">Channels</TextBlock>
<TextBlock Classes="value" Text="{Binding DatabaseStatistics.TotalChannels, StringFormat={}{0:n0}}" />
<TextBlock Classes="value" Text="{Binding DatabaseStatistics.TotalChannels, Converter={StaticResource NumberValueConverter}}" />
</StackPanel>
<Rectangle />
<StackPanel Orientation="Vertical">
<TextBlock Classes="label">Messages</TextBlock>
<TextBlock Classes="value" Text="{Binding DatabaseStatistics.TotalMessages, StringFormat={}{0:n0}}" />
<TextBlock Classes="value" Text="{Binding DatabaseStatistics.TotalMessages, Converter={StaticResource NumberValueConverter}}" />
</StackPanel>
</StackPanel>

View File

@ -133,24 +133,20 @@ namespace DHT.Desktop.Main.Pages {
long newChannels = newStatistics.TotalChannels - oldStatistics.TotalChannels;
long newMessages = newStatistics.TotalMessages - oldStatistics.TotalMessages;
string Pluralize(long count, string text) {
return count + "\u00A0" + (count == 1 ? text : text + "s");
}
StringBuilder message = new StringBuilder();
message.Append("Processed ");
if (successful == total) {
message.Append(Pluralize(successful, "database file"));
message.Append(successful.Pluralize("database file"));
}
else {
message.Append(successful).Append(" out of ").Append(Pluralize(total, "database file"));
message.Append(successful.Format()).Append(" out of ").Append(total.Pluralize("database file"));
}
message.Append(" and added:\n\n \u2022 ");
message.Append(Pluralize(newServers, "server")).Append("\n \u2022 ");
message.Append(Pluralize(newChannels, "channel")).Append("\n \u2022 ");
message.Append(Pluralize(newMessages, "message"));
message.Append(newServers.Pluralize("server")).Append("\n \u2022 ");
message.Append(newChannels.Pluralize("channel")).Append("\n \u2022 ");
message.Append(newMessages.Pluralize("message"));
await Dialog.ShowOk(dialog, "Database Merge", message.ToString());
}

View File

@ -6,6 +6,7 @@ using System.IO;
using System.Threading.Tasks;
using System.Web;
using Avalonia.Controls;
using DHT.Desktop.Common;
using DHT.Desktop.Main.Controls;
using DHT.Desktop.Models;
using DHT.Desktop.Resources;
@ -45,7 +46,7 @@ namespace DHT.Desktop.Main.Pages {
}
private void UpdateStatistics() {
ExportedMessageText = "Will export " + db.CountMessages(FilterModel.CreateFilter()) + " out of " + db.Statistics.TotalMessages + " message(s).";
ExportedMessageText = "Will export " + db.CountMessages(FilterModel.CreateFilter()).Format() + " out of " + db.Statistics.TotalMessages.Format() + " message(s).";
OnPropertyChanged(nameof(ExportedMessageText));
}

View File

@ -5,6 +5,7 @@ using Avalonia;
namespace DHT.Desktop {
internal static class Program {
public static string Version { get; }
public static CultureInfo Culture { get; }
static Program() {
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "";
@ -12,6 +13,7 @@ namespace DHT.Desktop {
Version = Version[..^2];
}
Culture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;