Merge pull request #14003 from Game4all/localise-profile-overlay-header-stats

Localise profile overlay numeric statistics
This commit is contained in:
Dan Balasescu 2021-07-25 12:20:38 +09:00 committed by GitHub
commit ce30bd8cc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 59 additions and 46 deletions

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Globalization;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Utils; using osu.Game.Utils;
@ -20,7 +19,7 @@ namespace osu.Game.Tests.NonVisual
[TestCase(1, "100.00%")] [TestCase(1, "100.00%")]
public void TestAccuracyFormatting(double input, string expectedOutput) public void TestAccuracyFormatting(double input, string expectedOutput)
{ {
Assert.AreEqual(expectedOutput, input.FormatAccuracy(CultureInfo.InvariantCulture)); Assert.AreEqual(expectedOutput, input.FormatAccuracy().ToString());
} }
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Utils; using osu.Game.Utils;
@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface
Current.Value = DisplayedCount = 1.0f; Current.Value = DisplayedCount = 1.0f;
} }
protected override string FormatCount(double count) => count.FormatAccuracy(); protected override LocalisableString FormatCount(double count) => count.FormatAccuracy();
protected override double GetProportionalDuration(double currentValue, double newValue) protected override double GetProportionalDuration(double currentValue, double newValue)
{ {

View File

@ -10,6 +10,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
@ -137,8 +138,8 @@ namespace osu.Game.Graphics.UserInterface
/// Used to format counts. /// Used to format counts.
/// </summary> /// </summary>
/// <param name="count">Count to format.</param> /// <param name="count">Count to format.</param>
/// <returns>Count formatted as a string.</returns> /// <returns>Count formatted as a localisable string.</returns>
protected virtual string FormatCount(T count) protected virtual LocalisableString FormatCount(T count)
{ {
return count.ToString(); return count.ToString();
} }

View File

@ -3,6 +3,7 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
@ -37,7 +38,7 @@ namespace osu.Game.Graphics.UserInterface
return currentValue > newValue ? currentValue - newValue : newValue - currentValue; return currentValue > newValue ? currentValue - newValue : newValue - currentValue;
} }
protected override string FormatCount(double count) protected override LocalisableString FormatCount(double count)
{ {
string format = new string('0', RequiredDisplayDigits.Value); string format = new string('0', RequiredDisplayDigits.Value);

View File

@ -372,10 +372,10 @@ namespace osu.Game.Online.Leaderboards
public class LeaderboardScoreStatistic public class LeaderboardScoreStatistic
{ {
public IconUsage Icon; public IconUsage Icon;
public string Value; public LocalisableString Value;
public string Name; public string Name;
public LeaderboardScoreStatistic(IconUsage icon, string name, string value) public LeaderboardScoreStatistic(IconUsage icon, string name, LocalisableString value)
{ {
Icon = icon; Icon = icon;
Name = name; Name = name;

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -204,7 +205,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
this.text = text; this.text = text;
} }
public string Text public LocalisableString Text
{ {
set => text.Text = value; set => text.Text = value;
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Resources.Localisation.Web; using osu.Game.Resources.Localisation.Web;
using osu.Game.Users; using osu.Game.Users;
@ -145,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header
private void updateDisplay(User user) private void updateDisplay(User user)
{ {
hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-"; hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-"; hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
} }
} }
} }

View File

@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
private void updateProgress(User user) private void updateProgress(User user)
{ {
levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0;
levelProgressText.Text = user?.Statistics?.Level.Progress.ToString("0'%'"); levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'");
} }
} }
} }

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
set => title.Text = value; set => title.Text = value;
} }
public string Content public LocalisableString Content
{ {
set => content.Text = value; set => content.Text = value;
} }

View File

@ -4,6 +4,7 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osuTK; using osuTK;
@ -46,6 +47,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
protected abstract IconUsage Icon { get; } protected abstract IconUsage Icon { get; }
protected void SetValue(int value) => drawableText.Text = value.ToString("#,##0"); protected void SetValue(int value) => drawableText.Text = value.ToLocalisableString("#,##0");
} }
} }

View File

@ -7,6 +7,7 @@ using System.Linq;
using Humanizer; using Humanizer;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Resources.Localisation.Web; using osu.Game.Resources.Localisation.Web;
@ -65,7 +66,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
return new TooltipDisplayContent return new TooltipDisplayContent
{ {
Rank = $"#{rank:N0}", Rank = rank.ToLocalisableString("\\##,##0"),
Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago" Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago"
}; };
} }
@ -92,7 +93,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
private class TooltipDisplayContent private class TooltipDisplayContent
{ {
public string Rank; public LocalisableString Rank;
public string Time; public string Time;
} }
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards; using osu.Game.Online.Leaderboards;
@ -172,13 +173,13 @@ namespace osu.Game.Overlays.Profile.Header
private void updateDisplay(User user) private void updateDisplay(User user)
{ {
medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0";
ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0"; ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0";
foreach (var scoreRankInfo in scoreRankInfos) foreach (var scoreRankInfo in scoreRankInfos)
scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-"; detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
detailCountryRank.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-"; detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
rankGraph.Statistics.Value = user?.Statistics; rankGraph.Statistics.Value = user?.Statistics;
} }
@ -189,7 +190,7 @@ namespace osu.Game.Overlays.Profile.Header
public int RankCount public int RankCount
{ {
set => rankCount.Text = value.ToString("#,##0"); set => rankCount.Text = value.ToLocalisableString("#,##0");
} }
public ScoreRankInfo(ScoreRank rank) public ScoreRankInfo(ScoreRank rank)

View File

@ -181,19 +181,19 @@ namespace osu.Game.Overlays.Profile.Header
if (user?.Statistics != null) if (user?.Statistics != null)
{ {
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy)); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToLocalisableString("#,##0")));
userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToLocalisableString("#,##0")));
} }
} }
private class UserStatsLine : Container private class UserStatsLine : Container
{ {
public UserStatsLine(LocalisableString left, string right) public UserStatsLine(LocalisableString left, LocalisableString right)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;

View File

@ -8,6 +8,7 @@ using osu.Game.Graphics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Localisation;
namespace osu.Game.Overlays.Profile.Sections namespace osu.Game.Overlays.Profile.Sections
{ {
@ -48,7 +49,7 @@ namespace osu.Game.Overlays.Profile.Sections
private void onCurrentChanged(ValueChangedEvent<int> value) private void onCurrentChanged(ValueChangedEvent<int> value)
{ {
counter.Text = value.NewValue.ToString("N0"); counter.Text = value.NewValue.ToLocalisableString("N0");
} }
} }
} }

View File

@ -170,7 +170,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
RelativePositionAxes = Axes.Y, RelativePositionAxes = Axes.Y,
Margin = new MarginPadding { Right = 3 }, Margin = new MarginPadding { Right = 3 },
Text = value.ToString("N0"), Text = value.ToLocalisableString("N0"),
Font = OsuFont.GetFont(size: 12), Font = OsuFont.GetFont(size: 12),
Y = y Y = y
}); });
@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{ {
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
Text = value.ToString("MMM yyyy"), Text = value.ToLocalisableString("MMM yyyy"),
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Rotation = 45, Rotation = 45,
X = x X = x

View File

@ -34,8 +34,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
return new TooltipDisplayContent return new TooltipDisplayContent
{ {
Name = tooltipCounterName, Name = tooltipCounterName,
Count = playCount.ToString("N0"), Count = playCount.ToLocalisableString("N0"),
Date = date.ToString("MMMM yyyy") Date = date.ToLocalisableString("MMMM yyyy")
}; };
} }
@ -63,8 +63,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
private class TooltipDisplayContent private class TooltipDisplayContent
{ {
public LocalisableString Name; public LocalisableString Name;
public string Count; public LocalisableString Count;
public string Date; public LocalisableString Date;
} }
} }
} }

View File

@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
public new int Count public new int Count
{ {
set => valueText.Text = value.ToString("N0"); set => valueText.Text = value.ToLocalisableString("N0");
} }
public CountSection(LocalisableString header) public CountSection(LocalisableString header)

View File

@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Online.API; using osu.Game.Online.API;
@ -34,7 +35,7 @@ namespace osu.Game.Scoring
public double Accuracy { get; set; } public double Accuracy { get; set; }
[JsonIgnore] [JsonIgnore]
public string DisplayAccuracy => Accuracy.FormatAccuracy(); public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy();
[JsonProperty(@"pp")] [JsonProperty(@"pp")]
public double? PP { get; set; } public double? PP { get; set; }

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Utils; using osu.Game.Utils;
@ -63,7 +64,7 @@ namespace osu.Game.Screens.Play.Break
valueText.Text = newText; valueText.Text = newText;
} }
protected virtual string Format(T count) protected virtual LocalisableString Format(T count)
{ {
if (count is Enum countEnum) if (count is Enum countEnum)
return countEnum.GetDescription(); return countEnum.GetDescription();
@ -86,6 +87,6 @@ namespace osu.Game.Screens.Play.Break
{ {
} }
protected override string Format(double count) => count.FormatAccuracy(); protected override LocalisableString Format(double count) => count.FormatAccuracy();
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -31,7 +32,7 @@ namespace osu.Game.Screens.Play.HUD
Current.BindTo(scoreProcessor.Combo); Current.BindTo(scoreProcessor.Combo);
} }
protected override string FormatCount(int count) protected override LocalisableString FormatCount(int count)
{ {
return $@"{count}x"; return $@"{count}x";
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -44,7 +45,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING; protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
protected override string FormatCount(double count) => count.FormatAccuracy(); protected override LocalisableString FormatCount(double count) => count.FormatAccuracy();
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
{ {

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
} }
protected override string FormatCount(long count) => count.ToString("N0"); protected override LocalisableString FormatCount(long count) => count.ToString("N0");
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Localisation;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Utils; using osu.Game.Utils;
using static osu.Game.Users.User; using static osu.Game.Users.User;
@ -45,7 +46,7 @@ namespace osu.Game.Users
public double Accuracy; public double Accuracy;
[JsonIgnore] [JsonIgnore]
public string DisplayAccuracy => (Accuracy / 100).FormatAccuracy(); public LocalisableString DisplayAccuracy => (Accuracy / 100).FormatAccuracy();
[JsonProperty(@"play_count")] [JsonProperty(@"play_count")]
public int PlayCount; public int PlayCount;

View File

@ -2,8 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Globalization;
using Humanizer; using Humanizer;
using osu.Framework.Localisation;
namespace osu.Game.Utils namespace osu.Game.Utils
{ {
@ -13,9 +13,8 @@ namespace osu.Game.Utils
/// Turns the provided accuracy into a percentage with 2 decimal places. /// Turns the provided accuracy into a percentage with 2 decimal places.
/// </summary> /// </summary>
/// <param name="accuracy">The accuracy to be formatted.</param> /// <param name="accuracy">The accuracy to be formatted.</param>
/// <param name="formatProvider">An optional format provider.</param>
/// <returns>formatted accuracy in percentage</returns> /// <returns>formatted accuracy in percentage</returns>
public static string FormatAccuracy(this double accuracy, IFormatProvider formatProvider = null) public static LocalisableString FormatAccuracy(this double accuracy)
{ {
// for the sake of display purposes, we don't want to show a user a "rounded up" percentage to the next whole number. // for the sake of display purposes, we don't want to show a user a "rounded up" percentage to the next whole number.
// ie. a score which gets 89.99999% shouldn't ever show as 90%. // ie. a score which gets 89.99999% shouldn't ever show as 90%.
@ -23,7 +22,7 @@ namespace osu.Game.Utils
// percentile with a non-matching grade is confusing. // percentile with a non-matching grade is confusing.
accuracy = Math.Floor(accuracy * 10000) / 10000; accuracy = Math.Floor(accuracy * 10000) / 10000;
return accuracy.ToString("0.00%", formatProvider ?? CultureInfo.CurrentCulture); return accuracy.ToLocalisableString("0.00%");
} }
/// <summary> /// <summary>