Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Quality: Adding the possibility of plural translation using ICU format #15433

Merged
merged 9 commits into from
May 21, 2024
68 changes: 68 additions & 0 deletions src/Files.App/Extensions/MessageFormatExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Jeffijoe.MessageFormat;
using Microsoft.Windows.ApplicationModel.Resources;
using Windows.Globalization;
using System.Globalization;
using Microsoft.Extensions.Logging;

namespace Files.App.Extensions
{
public static class MessageFormatExtensions
{
// Resource map for accessing localized strings
private static readonly ResourceMap resourcesTree = new ResourceManager().MainResourceMap.TryGetSubtree("Resources");

// CultureInfo based on the application's primary language override
private static readonly CultureInfo locale = new(ApplicationLanguages.PrimaryLanguageOverride);

// Message formatter with caching enabled, using the current UI culture's two-letter ISO language name
private static readonly MessageFormatter formatter = new(useCache: true, locale: locale.TwoLetterISOLanguageName);

// Extension method to create a dictionary for format pairs with a string key
public static IReadOnlyDictionary<string, object?> ToFormatPairs(this string key, object value) => new Dictionary<string, object?> { [key] = value };

// Extension method to create a dictionary for format pairs with an integer key
public static IReadOnlyDictionary<string, object?> ToFormatPairs(this int key, object value) => new Dictionary<string, object?> { [key.ToString()] = value };

// Retrieves a localized resource string, formatting it with the provided pairs
public static string GetLocalizedFormatResource(this string resourceKey, IReadOnlyDictionary<string, object?> pairs)
{
var value = resourcesTree?.TryGetValue(resourceKey)?.ValueAsString;

if (value is null)
return string.Empty;

try
{
value = formatter.FormatMessage(value, pairs);
}
catch
{
value = string.Empty;
App.Logger.LogWarning($"Formatter could not get a valid result value for: '{resourceKey}'");
}

return value;
}

// Overloaded method to accept multiple dictionaries of pairs and merge them
public static string GetLocalizedFormatResource(this string resourceKey, params IReadOnlyDictionary<string, object?>[] pairs)
{
var mergedPairs = pairs.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
return GetLocalizedFormatResource(resourceKey, mergedPairs);
}

// Overloaded method to accept multiple values and convert them to a dictionary with their indices as keys
public static string GetLocalizedFormatResource(this string resourceKey, params object[] values)
{
var pairs = values.Select((value, index) => new KeyValuePair<string, object?>(index.ToString(), value))
.ToDictionary(pair => pair.Key, pair => pair.Value);
return GetLocalizedFormatResource(resourceKey, pairs);
}

//TODO: Could replace `GetLocalizedResource()` in the future
public static string GetLocalizedFormatResource(this string resourceKey) => GetLocalizedFormatResource(resourceKey, new Dictionary<string, object?>());
}
}
1 change: 1 addition & 0 deletions src/Files.App/Files.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
<PackageReference Include="LibGit2Sharp" Version="0.30.0" />
<PackageReference Include="LiteDB" Version="5.0.19" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WinUI" Version="2.0.0-rc1.2" />
<PackageReference Include="MessageFormat" Version="7.1.0" />
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="5.0.3" />
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.3" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.4" />
Expand Down
16 changes: 6 additions & 10 deletions src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -426,17 +426,13 @@
<data name="InvalidFilename.Text" xml:space="preserve">
<value>The item name specified is invalid</value>
</data>
<data name="ItemSelected.Text" xml:space="preserve">
<value>item selected</value>
<data name="SelectedItems" xml:space="preserve">
<value>{0, plural, one {# item selected} other {# items selected}}</value>
<comment>ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural</comment>
</data>
<data name="ItemsSelected.Text" xml:space="preserve">
<value>items selected</value>
</data>
<data name="ItemCount.Text" xml:space="preserve">
<value>item</value>
</data>
<data name="ItemsCount.Text" xml:space="preserve">
<value>items</value>
<data name="Items" xml:space="preserve">
<value>{0, plural, one {item} other {items}}</value>
<comment>ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural</comment>
</data>
<data name="Yes" xml:space="preserve">
<value>Yes</value>
Expand Down
5 changes: 1 addition & 4 deletions src/Files.App/Views/Layouts/BaseLayoutPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,10 @@ internal set
UpdateSelectionSize();

SelectedItemsPropertiesViewModel.SelectedItemsCount = selectedItems.Count;
SelectedItemsPropertiesViewModel.SelectedItemsCountString = "SelectedItems".GetLocalizedFormatResource(selectedItems!.Count);

if (selectedItems.Count == 1)
{
SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{selectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}";
DispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// Tapped event must be executed first
Expand All @@ -256,10 +256,7 @@ internal set
});
}
else
{
SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{selectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}";
ResetRenameDoubleClick();
}
}

NotifyPropertyChanged(nameof(SelectedItems));
Expand Down
4 changes: 1 addition & 3 deletions src/Files.App/Views/Shells/BaseShellPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,7 @@ protected async void FilesystemViewModel_DirectoryInfoUpdated(object sender, Eve
if (ContentPage is null)
return;

var directoryItemCountLocalization = (FilesystemViewModel.FilesAndFolders.Count == 1)
? "ItemCount/Text".GetLocalizedResource()
: "ItemsCount/Text".GetLocalizedResource();
var directoryItemCountLocalization = "Items".GetLocalizedFormatResource(FilesystemViewModel.FilesAndFolders.Count);

BranchItem? headBranch = headBranch = InstanceViewModel.IsGitRepository
? await GitHelpers.GetRepositoryHead(InstanceViewModel.GitRepositoryPath)
Expand Down
Loading