From 277e00c74ed0525a75cc128e454d13a9a398d9e7 Mon Sep 17 00:00:00 2001 From: Denis Titovets Date: Tue, 23 Dec 2025 03:21:08 +0300 Subject: [PATCH 1/3] Localise various strings on `Play` screen (again) --- .../BeatmapOffsetControlStrings.cs | 5 +++ .../Localisation/RankingStatisticsStrings.cs | 39 +++++++++++++++++++ .../PlayerSettings/BeatmapOffsetControl.cs | 2 +- .../Ranking/Statistics/AverageHitError.cs | 8 +++- .../Ranking/Statistics/SimpleStatisticItem.cs | 13 ++++--- .../Ranking/Statistics/UnstableRate.cs | 6 ++- 6 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Localisation/RankingStatisticsStrings.cs diff --git a/osu.Game/Localisation/BeatmapOffsetControlStrings.cs b/osu.Game/Localisation/BeatmapOffsetControlStrings.cs index b905b7ae1c..37412b6a7a 100644 --- a/osu.Game/Localisation/BeatmapOffsetControlStrings.cs +++ b/osu.Game/Localisation/BeatmapOffsetControlStrings.cs @@ -39,6 +39,11 @@ namespace osu.Game.Localisation /// public static LocalisableString HitObjectsAppearEarlier => new TranslatableString(getKey(@"hit_objects_appear_earlier"), @"(hit objects appear earlier)"); + /// + /// "Beatmap offset was adjusted to {0} ms." + /// + public static LocalisableString BeatmapOffsetWasAdjustedTo(string offset) => new TranslatableString(getKey(@"beatmap_offset_was_adjusted_to"), @"Beatmap offset was adjusted to {0} ms.", offset); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/RankingStatisticsStrings.cs b/osu.Game/Localisation/RankingStatisticsStrings.cs new file mode 100644 index 0000000000..e6bd8bdc49 --- /dev/null +++ b/osu.Game/Localisation/RankingStatisticsStrings.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class RankingStatisticsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.RankingStatisticsStrings"; + + /// + /// "Average Hit Error" + /// + public static LocalisableString AverageHitErrorTitle => new TranslatableString(getKey(@"average_hit_error_title"), @"Average Hit Error"); + + /// + /// "Unstable Rate" + /// + public static LocalisableString UnstableRateTitle => new TranslatableString(getKey(@"unstable_rate_title"), @"Unstable Rate"); + + /// + /// "early" + /// + public static LocalisableString Early => new TranslatableString(getKey(@"early"), @"early"); + + /// + /// "late" + /// + public static LocalisableString Late => new TranslatableString(getKey(@"late"), @"late"); + + /// + /// "(not available)" + /// + public static LocalisableString NotAvailable => new TranslatableString(getKey(@"not_available"), @"(not available)"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index e2337a4e0e..634f1f85d2 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -330,7 +330,7 @@ namespace osu.Game.Screens.Play.PlayerSettings if (offsetChanged) { - offsetText.AddText($"Beatmap offset was adjusted to {Current.Value.ToStandardFormattedString(1)} ms.", t => t.Font = OsuFont.Style.Caption1); + offsetText.AddText(BeatmapOffsetControlStrings.BeatmapOffsetWasAdjustedTo(Current.Value.ToStandardFormattedString(1)), t => t.Font = OsuFont.Style.Caption1); offsetText.NewParagraph(); } } diff --git a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs index fb7107cc88..2670a68f03 100644 --- a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs +++ b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using osu.Framework.Localisation; using osu.Game.Rulesets.Scoring; +using osu.Game.Localisation; namespace osu.Game.Screens.Ranking.Statistics { @@ -17,11 +19,13 @@ namespace osu.Game.Screens.Ranking.Statistics /// /// Sequence of s to calculate the unstable rate based on. public AverageHitError(IEnumerable hitEvents) - : base("Average Hit Error") + : base(RankingStatisticsStrings.AverageHitErrorTitle) { Value = hitEvents.CalculateAverageHitError(); } - protected override string DisplayValue(double? value) => value == null ? "(not available)" : $"{Math.Abs(value.Value):N2} ms {(value.Value < 0 ? "early" : "late")}"; + protected override LocalisableString DisplayValue(double? value) => value == null + ? RankingStatisticsStrings.NotAvailable + : $"{Math.Abs(value.Value):N2} ms {(value.Value < 0 ? RankingStatisticsStrings.Early : RankingStatisticsStrings.Late)}"; } } diff --git a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs index 280227baea..7acb1573c3 100644 --- a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs +++ b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -17,7 +18,7 @@ namespace osu.Game.Screens.Ranking.Statistics /// /// The text to display as the statistic's value. /// - protected string Value + protected LocalisableString Value { set => valueText.Text = value; } @@ -41,9 +42,9 @@ namespace osu.Game.Screens.Ranking.Statistics /// Creates a new simple statistic item. /// /// The name of the statistic. - protected SimpleStatisticItem(string name) + protected SimpleStatisticItem(LocalisableString name) { - Name = name; + Name = name.ToString(); RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -52,7 +53,7 @@ namespace osu.Game.Screens.Ranking.Statistics { nameText = new OsuSpriteText { - Text = Name, + Text = name, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE) @@ -91,9 +92,9 @@ namespace osu.Game.Screens.Ranking.Statistics /// Used to convert to a text representation. /// Defaults to using . /// - protected virtual string DisplayValue(TValue value) => value!.ToString() ?? string.Empty; + protected virtual LocalisableString DisplayValue(TValue value) => value!.ToString() ?? string.Empty; - public SimpleStatisticItem(string name) + public SimpleStatisticItem(LocalisableString name) : base(name) { } diff --git a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs index c89e48e78d..0aaf7fd063 100644 --- a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs +++ b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Ranking.Statistics @@ -16,11 +18,11 @@ namespace osu.Game.Screens.Ranking.Statistics /// /// Sequence of s to calculate the unstable rate based on. public UnstableRate(IReadOnlyList hitEvents) - : base("Unstable Rate") + : base(RankingStatisticsStrings.UnstableRateTitle) { Value = hitEvents.CalculateUnstableRate()?.Result; } - protected override string DisplayValue(double? value) => value?.ToString(@"N2") ?? "(not available)"; + protected override LocalisableString DisplayValue(double? value) => value?.ToString(@"N2") ?? RankingStatisticsStrings.NotAvailable; } } From 0bfff2bf88808ae7fbbdfd7e4fc4d70a3771d9d5 Mon Sep 17 00:00:00 2001 From: Denis Titovets Date: Tue, 23 Dec 2025 14:27:34 +0300 Subject: [PATCH 2/3] Make edits based on reviews --- osu.Game/Localisation/RankingStatisticsStrings.cs | 8 ++++---- .../Screens/Ranking/Statistics/AverageHitError.cs | 12 +++++++++--- .../Ranking/Statistics/SimpleStatisticItem.cs | 10 +++++++++- osu.Game/Screens/Ranking/Statistics/UnstableRate.cs | 3 ++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/osu.Game/Localisation/RankingStatisticsStrings.cs b/osu.Game/Localisation/RankingStatisticsStrings.cs index e6bd8bdc49..5d26390c79 100644 --- a/osu.Game/Localisation/RankingStatisticsStrings.cs +++ b/osu.Game/Localisation/RankingStatisticsStrings.cs @@ -20,14 +20,14 @@ namespace osu.Game.Localisation public static LocalisableString UnstableRateTitle => new TranslatableString(getKey(@"unstable_rate_title"), @"Unstable Rate"); /// - /// "early" + /// "{0} ms early" /// - public static LocalisableString Early => new TranslatableString(getKey(@"early"), @"early"); + public static LocalisableString Early(string offset) => new TranslatableString(getKey(@"early"), @"{0} ms early", offset); /// - /// "late" + /// "{0} ms late" /// - public static LocalisableString Late => new TranslatableString(getKey(@"late"), @"late"); + public static LocalisableString Late(string offset) => new TranslatableString(getKey(@"late"), @"{0} ms late", offset); /// /// "(not available)" diff --git a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs index 2670a68f03..1d20479e18 100644 --- a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs +++ b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs @@ -24,8 +24,14 @@ namespace osu.Game.Screens.Ranking.Statistics Value = hitEvents.CalculateAverageHitError(); } - protected override LocalisableString DisplayValue(double? value) => value == null - ? RankingStatisticsStrings.NotAvailable - : $"{Math.Abs(value.Value):N2} ms {(value.Value < 0 ? RankingStatisticsStrings.Early : RankingStatisticsStrings.Late)}"; + protected override LocalisableString DisplayValue(double? value) + { + return value == null ? RankingStatisticsStrings.NotAvailable : getEarlyLateText(value.Value); + + LocalisableString getEarlyLateText(double offset) => + offset < 0 + ? RankingStatisticsStrings.Early(Math.Abs(offset).ToString(@"N2")) + : RankingStatisticsStrings.Late(Math.Abs(offset).ToString(@"N2")); + } } } diff --git a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs index 7acb1573c3..f56b09cfc0 100644 --- a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs +++ b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; @@ -92,7 +94,13 @@ namespace osu.Game.Screens.Ranking.Statistics /// Used to convert to a text representation. /// Defaults to using . /// - protected virtual LocalisableString DisplayValue(TValue value) => value!.ToString() ?? string.Empty; + protected virtual LocalisableString DisplayValue(TValue value) + { + if (value is IFormattable formattable) + return formattable.ToLocalisableString(); + + return value!.ToString() ?? string.Empty; + } public SimpleStatisticItem(LocalisableString name) : base(name) diff --git a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs index 0aaf7fd063..3cdc29e268 100644 --- a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs +++ b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Localisation; using osu.Game.Rulesets.Scoring; @@ -23,6 +24,6 @@ namespace osu.Game.Screens.Ranking.Statistics Value = hitEvents.CalculateUnstableRate()?.Result; } - protected override LocalisableString DisplayValue(double? value) => value?.ToString(@"N2") ?? RankingStatisticsStrings.NotAvailable; + protected override LocalisableString DisplayValue(double? value) => value?.ToLocalisableString(@"N2") ?? RankingStatisticsStrings.NotAvailable; } } From 43e217120d349c03e5e50ef79d82a1f6221b90bd Mon Sep 17 00:00:00 2001 From: Denis Titovets Date: Tue, 23 Dec 2025 14:51:31 +0300 Subject: [PATCH 3/3] Fix offset not changing using current culture --- osu.Game/Localisation/RankingStatisticsStrings.cs | 8 ++++---- osu.Game/Screens/Ranking/Statistics/AverageHitError.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/RankingStatisticsStrings.cs b/osu.Game/Localisation/RankingStatisticsStrings.cs index 5d26390c79..b69d96d1a1 100644 --- a/osu.Game/Localisation/RankingStatisticsStrings.cs +++ b/osu.Game/Localisation/RankingStatisticsStrings.cs @@ -20,14 +20,14 @@ namespace osu.Game.Localisation public static LocalisableString UnstableRateTitle => new TranslatableString(getKey(@"unstable_rate_title"), @"Unstable Rate"); /// - /// "{0} ms early" + /// "{0:N2} ms early" /// - public static LocalisableString Early(string offset) => new TranslatableString(getKey(@"early"), @"{0} ms early", offset); + public static LocalisableString Early(double offset) => new TranslatableString(getKey(@"early"), @"{0:N2} ms early", offset); /// - /// "{0} ms late" + /// "{0:N2} ms late" /// - public static LocalisableString Late(string offset) => new TranslatableString(getKey(@"late"), @"{0} ms late", offset); + public static LocalisableString Late(double offset) => new TranslatableString(getKey(@"late"), @"{0:N2} ms late", offset); /// /// "(not available)" diff --git a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs index 1d20479e18..08a67b8fdc 100644 --- a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs +++ b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs @@ -30,8 +30,8 @@ namespace osu.Game.Screens.Ranking.Statistics LocalisableString getEarlyLateText(double offset) => offset < 0 - ? RankingStatisticsStrings.Early(Math.Abs(offset).ToString(@"N2")) - : RankingStatisticsStrings.Late(Math.Abs(offset).ToString(@"N2")); + ? RankingStatisticsStrings.Early(Math.Abs(offset)) + : RankingStatisticsStrings.Late(Math.Abs(offset)); } } }