osu icon indicating copy to clipboard operation
osu copied to clipboard

In-game 'Recent' profile section is not localised

Open Gabixel opened this issue 1 year ago • 2 comments

Type

Localisation

Bug description

Doesn't seem to change even when reloading the profile after changing both in-game and web language: it stays in English.

Screenshots or videos

An example of my profile with the supporter message (but it's not limited to that one). Notice the Italian title:

image

Version

2024.312.1

Logs

n/a

Gabixel avatar Mar 20 '24 14:03 Gabixel

Almost forgot about the Kudosu! section. That one's also always in English. (Simple guess is that there's no indication to retrieve the data in a different language in those API calls..?)

Gabixel avatar Mar 20 '24 14:03 Gabixel

Probably needs https://github.com/ppy/osu-framework/pull/5632 (non-working right now).

But I managed to make a diff (has some copypaste issues, but is POC) that resolves this (hardcoding markdown formatting with MessageFormatter):

diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index aa72996fff..7347d9f702 100644
--- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
@@ -33,7 +33,7 @@ public LinkFlowContainer(Action<SpriteText> defaultCreationParameters = null)
         [Resolved]
         private GameHost host { get; set; }
 
-        public void AddLinks(string text, List<Link> links)
+        public void AddLinks(string text, List<Link> links, Action<SpriteText> linkCreationParameters = null)
         {
             if (string.IsNullOrEmpty(text) || links == null)
                 return;
@@ -61,7 +61,7 @@ public void AddLinks(string text, List<Link> links)
                 object linkArgument = link.Argument;
                 string tooltip = displayText == link.Url ? null : link.Url;
 
-                AddLink(displayText, link.Action, linkArgument, tooltip);
+                AddLink(displayText, link.Action, linkArgument, tooltip, linkCreationParameters);
                 previousLinkEnd = link.Index + link.Length;
             }
 
diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs
index 8a0003b4ea..8a5035eca7 100644
--- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs
+++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs
@@ -2,11 +2,13 @@
 // See the LICENCE file in the repository root for full licence text.
 
 using System.Linq;
+using System.Text.RegularExpressions;
 using osu.Framework.Allocation;
-using osu.Framework.Extensions.ObjectExtensions;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Sprites;
+using osu.Framework.Localisation;
+using osu.Framework.Logging;
 using osu.Game.Graphics;
 using osu.Game.Graphics.Containers;
 using osu.Game.Online.API;
@@ -14,6 +16,7 @@
 using osu.Game.Online.API.Requests.Responses;
 using osu.Game.Online.Chat;
 using osu.Game.Online.Leaderboards;
+using osu.Game.Resources.Localisation.Web;
 using osu.Game.Rulesets;
 
 namespace osu.Game.Overlays.Profile.Sections.Recent
@@ -28,6 +31,9 @@ public partial class DrawableRecentActivity : CompositeDrawable
         [Resolved]
         private IRulesetStore rulesets { get; set; } = null!;
 
+        [Resolved]
+        private LocalisationManager localisation { get; set; } = null!;
+
         private readonly APIRecentActivity activity;
 
         private LinkFlowContainer content = null!;
@@ -131,44 +137,36 @@ private Drawable createIcon()
 
         private void createMessage()
         {
+            LocalisableString text = string.Empty;
+
             switch (activity.Type)
             {
                 case RecentActivityType.Achievement:
-                    addUserLink();
-                    addText($" unlocked the \"{activity.Achievement.Name}\" medal!");
+                    text = EventsStrings.Achievement(getUserLink(), activity.Achievement.Name);
                     break;
 
                 case RecentActivityType.BeatmapPlaycount:
-                    addBeatmapLink();
-                    addText($" has been played {activity.Count} times!");
+                    text = EventsStrings.BeatmapPlaycount(getBeatmapLink(), activity.Count.ToString());
                     break;
 
                 case RecentActivityType.BeatmapsetApprove:
-                    addBeatmapsetLink();
-                    addText($" has been {activity.Approval.ToString().ToLowerInvariant()}!");
+                    text = EventsStrings.BeatmapsetApprove(getBeatmapsetLink(), activity.User.Username, activity.Approval.ToString().ToLowerInvariant());
                     break;
 
                 case RecentActivityType.BeatmapsetDelete:
-                    addBeatmapsetLink();
-                    addText(" has been deleted.");
+                    text = EventsStrings.BeatmapsetDelete(getBeatmapsetLink());
                     break;
 
                 case RecentActivityType.BeatmapsetRevive:
-                    addBeatmapsetLink();
-                    addText(" has been revived from eternal slumber by ");
-                    addUserLink();
+                    text = EventsStrings.BeatmapsetRevive(getBeatmapsetLink(), activity.User.Username);
                     break;
 
                 case RecentActivityType.BeatmapsetUpdate:
-                    addUserLink();
-                    addText(" has updated the beatmap ");
-                    addBeatmapsetLink();
+                    text = EventsStrings.BeatmapsetUpdate(activity.User.Username, getBeatmapsetLink());
                     break;
 
                 case RecentActivityType.BeatmapsetUpload:
-                    addUserLink();
-                    addText(" has submitted a new beatmap ");
-                    addBeatmapsetLink();
+                    text = EventsStrings.BeatmapsetUpdate(activity.User.Username, getBeatmapsetLink());
                     break;
 
                 case RecentActivityType.Medal:
@@ -176,59 +174,60 @@ private void createMessage()
                     break;
 
                 case RecentActivityType.Rank:
-                    addUserLink();
-                    addText($" achieved rank #{activity.Rank} on ");
-                    addBeatmapLink();
-                    addText($" ({getRulesetName()})");
+                    text = EventsStrings.Rank(getUserLink(), $"#{activity.Rank}", getBeatmapLink(), getRulesetName());
                     break;
 
                 case RecentActivityType.RankLost:
-                    addUserLink();
-                    addText(" has lost first place on ");
-                    addBeatmapLink();
-                    addText($" ({getRulesetName()})");
+                    text = EventsStrings.RankLost(getUserLink(), getBeatmapLink(), getRulesetName());
                     break;
 
                 case RecentActivityType.UserSupportAgain:
-                    addUserLink();
-                    addText(" has once again chosen to support osu! - thanks for your generosity!");
+                    text = EventsStrings.UserSupportAgain(getUserLink());
                     break;
 
                 case RecentActivityType.UserSupportFirst:
-                    addUserLink();
-                    addText(" has become an osu!supporter - thanks for your generosity!");
+                    text = EventsStrings.UserSupportFirst(getUserLink());
                     break;
 
                 case RecentActivityType.UserSupportGift:
-                    addUserLink();
-                    addText(" has received the gift of osu!supporter!");
+                    text = EventsStrings.UserSupportGift(getUserLink());
                     break;
 
                 case RecentActivityType.UsernameChange:
-                    addText($"{activity.User?.PreviousUsername} has changed their username to ");
-                    addUserLink();
+                    text = EventsStrings.UsernameChange(activity.User.PreviousUsername, getUserLink());
                     break;
             }
+
+            if (!LocalisableString.IsNullOrEmpty(text))
+            {
+                var localisedText = localisation.GetLocalisedBindableString(text);
+
+                localisation.CurrentParameters.BindValueChanged(_ =>
+                {
+                    content.Clear();
+
+                    // TODO: should probably use markdown container and replace html formatting with the markdown ones, will fix bottom TODO also
+                    var formattedSource = MessageFormatter.FormatText(Regex.Replace(localisedText.Value, @"<(.|\n)*?>", string.Empty));
+
+                    // TODO: link creation parameters should only affect user link
+                    content.AddLinks(formattedSource.Text, formattedSource.Links, t => t.Font = getLinkFont(FontWeight.Bold));
+                }, true);
+            }
         }
 
         private string getRulesetName() =>
             rulesets.AvailableRulesets.FirstOrDefault(r => r.ShortName == activity.Mode)?.Name ?? activity.Mode;
 
-        private void addUserLink()
-            => content.AddLink(activity.User.AsNonNull().Username, LinkAction.OpenUserProfile, getLinkArgument(activity.User.AsNonNull().Url), creationParameters: t => t.Font = getLinkFont(FontWeight.Bold));
-
-        private void addBeatmapLink()
-            => content.AddLink(activity.Beatmap.AsNonNull().Title, LinkAction.OpenBeatmap, getLinkArgument(activity.Beatmap.AsNonNull().Url), creationParameters: t => t.Font = getLinkFont());
+        private LocalisableString getUserLink()
+            => LocalisableString.Interpolate($"[{activity.User.Username}]({api.WebsiteRootUrl}{activity.User.Url})");
 
-        private void addBeatmapsetLink()
-            => content.AddLink(activity.Beatmapset.AsNonNull().Title, LinkAction.OpenBeatmapSet, getLinkArgument(activity.Beatmapset.AsNonNull().Url), creationParameters: t => t.Font = getLinkFont());
+        private LocalisableString getBeatmapLink()
+            => LocalisableString.Interpolate($"[{activity.Beatmap.Title}]({api.WebsiteRootUrl}{activity.Beatmap.Url})");
 
-        private object getLinkArgument(string url) => MessageFormatter.GetLinkDetails($"{api.WebsiteRootUrl}{url}").Argument.AsNonNull();
+        private LocalisableString getBeatmapsetLink()
+            => LocalisableString.Interpolate($"[{activity.Beatmapset.Title}]({api.WebsiteRootUrl}{activity.Beatmapset.Url})");
 
         private FontUsage getLinkFont(FontWeight fontWeight = FontWeight.Regular)
             => OsuFont.GetFont(size: font_size, weight: fontWeight, italics: true);
-
-        private void addText(string text)
-            => content.AddText(text, t => t.Font = OsuFont.GetFont(size: font_size, weight: FontWeight.SemiBold));
     }
 }

Not sure if this is any more cursed than what is done in this file right now.

I mean there's a precedent (a little different as the string itself already has markdown). Also this doesn't update after language change cuz no BindValueChanged: https://github.com/ppy/osu/blob/8ee391530f8799399690b677c898ee0102c2d07f/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs#L116-L121

Joehuu avatar Mar 20 '24 17:03 Joehuu