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

Change most ruleset-accessible string types to Localisable strings #19695

Merged
merged 16 commits into from
Aug 22, 2022
Merged

Change most ruleset-accessible string types to Localisable strings #19695

merged 16 commits into from
Aug 22, 2022

Conversation

naoei
Copy link
Contributor

@naoei naoei commented Aug 10, 2022

Commits of interest:

Breaking changes

Mod.Description type has been changed

    // ...
-   public override string Description => "...";
+   public override LocalisableString Description => "...";
    // ...

ResumeOverlay.Message type has been changed

    // ...
-   protected override string Message => "...";
+   protected override LocalisableString Message => "...";
    // ...

Ruleset.GetDisplayNameForHitResult(HitResult) type has been changed

    // ...
-   public override string GetDisplayNameForHitResult(HitResult result)
+   public override LocalisableString GetDisplayNameForHitResult(HitResult result)
    // ...

Ruleset.GetVariantName(int variant) type has been changed

    // ...
-   public override string GetVariantName(int variant)
+   public override LocalisableString GetVariantName(int variant)
    // ...

@bdach
Copy link
Collaborator

bdach commented Aug 14, 2022

Revert localisation for GetDisplayNameForHitResult (1e356f6) -- Read description

The argumentation for this revert is weak. Support for text case transformations on localisable strings is already in place. Please use these methods.

Enable localisation for SettingSourceAttribute (7cbe2fa)

This has zero testing. The new ctor that accepts declaringType has no usage in this PR. Please add tests.

(As an aside I'm a bit skeptical that the API there is going to fly. As I understand it, the design is such that both the label and the description must come from the same class with localisables, which I see becoming a problem down the line. But I guess this can be addressed in due time.)

@@ -14,7 +15,7 @@ public class CatchModFloatingFruits : Mod, IApplicableToDrawableRuleset<CatchHit
{
public override string Name => "Floating Fruits";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the reason why mod names aren't also being localised here is their widespread usage, correct?

Copy link
Contributor Author

@naoei naoei Aug 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct. I can change it into a localisable string if preferred.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not in this diff. There are many concerns to figure out with regard to that, including but not limited to how to share translations with web, or how to adapt several dependent projects like osu-tools.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the issue there? I think we can still reference osu.Resources from the rulesets.

I think if we're going to break Description, we should also break Name, otherwise we'll end up breaking rulesets twice over: https://github.com/taulazer/tau/blob/master/osu.Game.Rulesets.Tau/Mods/TauModDual.cs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should be localising mod names. They historically haven't been, as they are treated as proper nouns.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you can transliterate proper nouns, as is done in Korean: https://osu.ppy.sh/wiki/ko/Gameplay/Game_modifier/Double_Time (lit. deobeul taim). I'm also seeing whisperings on the Japanese net about ハードロック and ダブルタイム.

Are you sure you want to keep these as latin characters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid direction, but we need to decide and make sure everyone is on board with it. That shouldn't be in the PR, as I think there will be diverging opinions on it.

Currently they aren't translated in stable (or osu-web to my knowledge).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

@@ -48,7 +49,7 @@ public StatisticItem([NotNull] string name, [NotNull] Drawable content, [CanBeNu
/// <param name="createContent">A function returning the <see cref="Drawable"/> content to be displayed.</param>
/// <param name="requiresHitEvents">Whether this item requires hit events. If true, <see cref="CreateContent"/> will not be called if no hit events are available.</param>
/// <param name="dimension">The <see cref="Dimension"/> of this item. This can be thought of as the column dimension of an encompassing <see cref="GridContainer"/>.</param>
public StatisticItem([NotNull] string name, [NotNull] Func<Drawable> createContent, bool requiresHitEvents = false, [CanBeNull] Dimension dimension = null)
public StatisticItem(LocalisableString name, [NotNull] Func<Drawable> createContent, bool requiresHitEvents = false, [CanBeNull] Dimension dimension = null)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this

Suggested change
public StatisticItem(LocalisableString name, [NotNull] Func<Drawable> createContent, bool requiresHitEvents = false, [CanBeNull] Dimension dimension = null)
public StatisticItem(LocalisableString? name, [NotNull] Func<Drawable> createContent, bool requiresHitEvents = false, [CanBeNull] Dimension dimension = null)

And no, this isn't a NRT issue or anything, because LocalisableString is a struct. It's really weird to have a nullable name field, then one obsoleted ctor, and then another that receives the name, but the name isn't actually optional. It looks like a bug/misannotation.

Also, you will want to chase down all StatisticItems that pass string.Empty as the name and update them to pass null instead, because otherwise, this breakage happens:

master this PR
osu_2022-08-14_16-45-28 osu_2022-08-14_16-46-52

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix looks ok, but this needs to be listed in the OP as a breaking change so that consumers know they also need to change empty strings to null.

@naoei
Copy link
Contributor Author

naoei commented Aug 14, 2022

I've applied the changes requested, however, I don't understand what you mean here:

(As an aside I'm a bit skeptical that the API there is going to fly. As I understand it, the design is such that both the label and the description must come from the same class with localisables, which I see becoming a problem down the line. But I guess this can be addressed in due time.)

Do you mean that mod settings label and descriptions should be separated? Why can't it be unified in something like a ModSettingStrings localization class?

@bdach
Copy link
Collaborator

bdach commented Aug 14, 2022

SettingSourceAttribute is not mod-specific. It started as a thing for mods, but now it's used more extensively - skin editor also uses it, and I wouldn't be surprised to see new usages in other contexts.

As I said though, not a big deal. If a case ever arises where we want to pull the title from one localisation class and the description from another, the current setup can be trivially extended to support it.

@bdach
Copy link
Collaborator

bdach commented Aug 17, 2022

Pushed one menial xmldoc cleanup (d06959e).

Diff looks fine to me now, but given it contains breaking changes, I'm not merging without a second approval. I will say that the number of files touched is a bit ridiculous (nearing on a hundred), but given they're 95% menial type changes and the diffstat altogether still has an acceptable size, I'm personally willing to look away this time.

bdach
bdach previously approved these changes Aug 17, 2022
smoogipoo
smoogipoo previously approved these changes Aug 18, 2022
Comment on lines +47 to +74
public SettingSourceAttribute(Type declaringType, string label, string? description = null)
{
Label = getLocalisableStringFromMember(label) ?? string.Empty;
Description = getLocalisableStringFromMember(description) ?? string.Empty;

LocalisableString? getLocalisableStringFromMember(string? member)
{
if (member == null)
return null;

var property = declaringType.GetMember(member, BindingFlags.Static | BindingFlags.Public).FirstOrDefault();

if (property == null)
return null;

switch (property)
{
case FieldInfo f:
return (LocalisableString)f.GetValue(null).AsNonNull();

case PropertyInfo p:
return (LocalisableString)p.GetValue(null).AsNonNull();

default:
throw new InvalidOperationException($"Member \"{member}\" was not found in type {declaringType} (must be a static field or property)");
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I fancy this custom implementation, but it's probably okay for now...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was personally thinking it would be better to share this logic with osu!framework somehow.

@smoogipoo smoogipoo requested a review from peppy August 18, 2022 06:02
@@ -59,7 +59,7 @@ public StatisticContainer([NotNull] StatisticItem item)

private static Drawable createHeader(StatisticItem item)
{
if (string.IsNullOrEmpty(item.Name))
if (item.Name == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this going to handle string.Empty incorrectly now?

@@ -18,7 +19,7 @@ public class StatisticItem
/// <summary>
/// The name of this item.
/// </summary>
public readonly string Name;
public readonly LocalisableString? Name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be nullable? Shouldn't a user just be providing string.Empty as before rather than null?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was going to suggest this as well. I think there's a difference between default(LocalisableString) and (LocalisableString)string.Empty, though, and we don't provide a LocalisableString.IsNullOrEmpty() or similar method.

I would agree with this being non-null, but I think we need that method to do so.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure. I think a method like this should be added here or framework before this change is made.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be fine if I were to add the LocalisableString.IsNullOrEmpty() within osu! then later on move it to the framework side of things?

Copy link
Contributor

@smoogipoo smoogipoo Aug 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm already in the process of adding it. I think this change can wait on it.

@naoei naoei dismissed stale reviews from smoogipoo and bdach via 29ef1c8 August 21, 2022 01:49
smoogipoo
smoogipoo previously approved these changes Aug 22, 2022
@peppy peppy self-requested a review August 22, 2022 04:45
@peppy
Copy link
Member

peppy commented Aug 22, 2022

I think these changes look okay, but how does this work in practice? ie. how does a ruleset source localisation resources? Would it have to add its localisations to the global localisation stores?

Also noticed that GetVariantName wasn't updated. I'm not sure these would be localised in practice, but if there's a chance they may be then probably best to make the change to that while we're here.

public virtual string GetVariantName(int variant) => string.Empty;

@smoogipoo
Copy link
Contributor

smoogipoo commented Aug 22, 2022

ie. how does a ruleset source localisation resources? Would it have to add its localisations to the global localisation stores?

I believe it doesn't need to, since ResourceManagerLocalisationStore looks through all loaded assemblies for the namespace in the prefix. It should just work. Have a look at https://github.com/LumpBloom7/sentakki/blob/master/osu.Game.Rulesets.Sentakki/Localisation/Mods/SentakkiModAutoTouchStrings.cs for reference

@peppy
Copy link
Member

peppy commented Aug 22, 2022

Crazy.

Thoughts on the variant name method? Should we just change while we're here?

@smoogipoo
Copy link
Contributor

I'd agree with changing GetVariantName(). It's documented as used in settings.

@peppy peppy merged commit 058d67f into ppy:master Aug 22, 2022
@naoei naoei deleted the ruleset-localization branch August 22, 2022 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants