Skip to content

Commit e442bb7

Browse files
XTorLukasyaira2
andauthored
Code Quality: Adding the possibility of plural translation using ICU format (#15433)
Co-authored-by: Yair <39923744+yaira2@users.noreply.github.com>
1 parent ffb6a53 commit e442bb7

File tree

5 files changed

+77
-17
lines changed

5 files changed

+77
-17
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) 2024 Files Community
2+
// Licensed under the MIT License. See the LICENSE.
3+
4+
using Jeffijoe.MessageFormat;
5+
using Microsoft.Windows.ApplicationModel.Resources;
6+
using Windows.Globalization;
7+
using System.Globalization;
8+
using Microsoft.Extensions.Logging;
9+
10+
namespace Files.App.Extensions
11+
{
12+
public static class MessageFormatExtensions
13+
{
14+
// Resource map for accessing localized strings
15+
private static readonly ResourceMap resourcesTree = new ResourceManager().MainResourceMap.TryGetSubtree("Resources");
16+
17+
// CultureInfo based on the application's primary language override
18+
private static readonly CultureInfo locale = new(ApplicationLanguages.PrimaryLanguageOverride);
19+
20+
// Message formatter with caching enabled, using the current UI culture's two-letter ISO language name
21+
private static readonly MessageFormatter formatter = new(useCache: true, locale: locale.TwoLetterISOLanguageName);
22+
23+
// Extension method to create a dictionary for format pairs with a string key
24+
public static IReadOnlyDictionary<string, object?> ToFormatPairs(this string key, object value) => new Dictionary<string, object?> { [key] = value };
25+
26+
// Extension method to create a dictionary for format pairs with an integer key
27+
public static IReadOnlyDictionary<string, object?> ToFormatPairs(this int key, object value) => new Dictionary<string, object?> { [key.ToString()] = value };
28+
29+
// Retrieves a localized resource string, formatting it with the provided pairs
30+
public static string GetLocalizedFormatResource(this string resourceKey, IReadOnlyDictionary<string, object?> pairs)
31+
{
32+
var value = resourcesTree?.TryGetValue(resourceKey)?.ValueAsString;
33+
34+
if (value is null)
35+
return string.Empty;
36+
37+
try
38+
{
39+
value = formatter.FormatMessage(value, pairs);
40+
}
41+
catch
42+
{
43+
value = string.Empty;
44+
App.Logger.LogWarning($"Formatter could not get a valid result value for: '{resourceKey}'");
45+
}
46+
47+
return value;
48+
}
49+
50+
// Overloaded method to accept multiple dictionaries of pairs and merge them
51+
public static string GetLocalizedFormatResource(this string resourceKey, params IReadOnlyDictionary<string, object?>[] pairs)
52+
{
53+
var mergedPairs = pairs.SelectMany(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);
54+
return GetLocalizedFormatResource(resourceKey, mergedPairs);
55+
}
56+
57+
// Overloaded method to accept multiple values and convert them to a dictionary with their indices as keys
58+
public static string GetLocalizedFormatResource(this string resourceKey, params object[] values)
59+
{
60+
var pairs = values.Select((value, index) => new KeyValuePair<string, object?>(index.ToString(), value))
61+
.ToDictionary(pair => pair.Key, pair => pair.Value);
62+
return GetLocalizedFormatResource(resourceKey, pairs);
63+
}
64+
65+
//TODO: Could replace `GetLocalizedResource()` in the future
66+
public static string GetLocalizedFormatResource(this string resourceKey) => GetLocalizedFormatResource(resourceKey, new Dictionary<string, object?>());
67+
}
68+
}

src/Files.App/Files.App.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<PackageReference Include="LibGit2Sharp" Version="0.30.0" />
7676
<PackageReference Include="LiteDB" Version="5.0.19" />
7777
<PackageReference Include="LiveChartsCore.SkiaSharpView.WinUI" Version="2.0.0-rc1.2" />
78+
<PackageReference Include="MessageFormat" Version="7.1.0" />
7879
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="5.0.3" />
7980
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="5.0.3" />
8081
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.4" />

src/Files.App/Strings/en-US/Resources.resw

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -426,17 +426,13 @@
426426
<data name="InvalidFilename.Text" xml:space="preserve">
427427
<value>The item name specified is invalid</value>
428428
</data>
429-
<data name="ItemSelected.Text" xml:space="preserve">
430-
<value>item selected</value>
429+
<data name="SelectedItems" xml:space="preserve">
430+
<value>{0, plural, one {# item selected} other {# items selected}}</value>
431+
<comment>ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural</comment>
431432
</data>
432-
<data name="ItemsSelected.Text" xml:space="preserve">
433-
<value>items selected</value>
434-
</data>
435-
<data name="ItemCount.Text" xml:space="preserve">
436-
<value>item</value>
437-
</data>
438-
<data name="ItemsCount.Text" xml:space="preserve">
439-
<value>items</value>
433+
<data name="Items" xml:space="preserve">
434+
<value>{0, plural, one {item} other {items}}</value>
435+
<comment>ICU format for plurals, more information here: https://support.crowdin.com/icu-message-syntax/#plural</comment>
440436
</data>
441437
<data name="Yes" xml:space="preserve">
442438
<value>Yes</value>

src/Files.App/Views/Layouts/BaseLayoutPage.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@ internal set
244244
UpdateSelectionSize();
245245

246246
SelectedItemsPropertiesViewModel.SelectedItemsCount = selectedItems.Count;
247+
SelectedItemsPropertiesViewModel.SelectedItemsCountString = "SelectedItems".GetLocalizedFormatResource(selectedItems!.Count);
247248

248249
if (selectedItems.Count == 1)
249250
{
250-
SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{selectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}";
251251
DispatcherQueue.EnqueueOrInvokeAsync(async () =>
252252
{
253253
// Tapped event must be executed first
@@ -256,10 +256,7 @@ internal set
256256
});
257257
}
258258
else
259-
{
260-
SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{selectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}";
261259
ResetRenameDoubleClick();
262-
}
263260
}
264261

265262
NotifyPropertyChanged(nameof(SelectedItems));

src/Files.App/Views/Shells/BaseShellPage.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,7 @@ protected async void FilesystemViewModel_DirectoryInfoUpdated(object sender, Eve
239239
if (ContentPage is null)
240240
return;
241241

242-
var directoryItemCountLocalization = (FilesystemViewModel.FilesAndFolders.Count == 1)
243-
? "ItemCount/Text".GetLocalizedResource()
244-
: "ItemsCount/Text".GetLocalizedResource();
242+
var directoryItemCountLocalization = "Items".GetLocalizedFormatResource(FilesystemViewModel.FilesAndFolders.Count);
245243

246244
BranchItem? headBranch = headBranch = InstanceViewModel.IsGitRepository
247245
? await GitHelpers.GetRepositoryHead(InstanceViewModel.GitRepositoryPath)

0 commit comments

Comments
 (0)