Skip to content

Conversation

@sven-n
Copy link
Member

@sven-n sven-n commented Jan 2, 2026

  • Added language setting to Account
  • Added resources for player messages
  • Sending localized messages to players
  • Added resources for data model
  • Added resources for the admin panel
  • Added component for language selection
  • Added resources for plugins (name, description)
  • Localize Exceptions
  • Add more translations

@gemini-code-assist
Copy link

Summary of Changes

Hello @sven-n, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers comprehensive multi-language support, allowing the application to present information and messages in a player's preferred language. It establishes the necessary infrastructure within the data model and game logic to handle localized strings, moving away from hardcoded text. This change significantly improves the accessibility and user experience for a global player base, making the game more adaptable to different linguistic preferences.

Highlights

  • Multilanguage Support Core: Introduced a LanguageIsoCode property to the Account entity, allowing players to select their preferred language. This forms the foundation for all subsequent localization efforts.
  • Localized Player Messages: Replaced numerous hardcoded player messages throughout the game logic and chat commands with localized strings fetched from a new PlayerMessage.resx resource file, ensuring a consistent and translatable user experience.
  • Dynamic Language Switching: Added a new chat command, /language, which enables players to view available languages and dynamically switch their in-game language without needing to restart the client.
  • Data Model Localization: Implemented a ModelResourceProvider to retrieve localized captions and descriptions for data model types, properties, and enum values, enhancing the translatability of administrative interfaces and game data.
  • Plugin Metadata Localization: Updated PlugIn attributes across a wide range of plugins to utilize Display(Name, Description) attributes, preparing plugin metadata for localization in administrative tools.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive localization system across the application, primarily affecting game logic and chat commands. Key changes include adding a LanguageIsoCode property to the Account entity, implementing a ModelResourceProvider for localized captions and descriptions, and integrating PlayerMessage.resx for storing player-facing messages. The Player class now includes Culture information and new methods (GetLocalizedMessage, ShowLocalizedBlueMessageAsync) to display localized messages. Many hardcoded strings in game logic (e.g., mini-game messages, MU Helper messages, duel messages, item consumption feedback) and chat commands have been replaced with calls to these new localization methods, ensuring that messages are displayed in the player's preferred language. Additionally, a new /language chat command has been added to allow players to change their language. The PlugIn attributes across numerous files have been refactored to use the Display attribute for plugin metadata, improving consistency and potentially enabling better UI integration. Error handling in chat commands has been updated to leverage LocalizableExceptionBase for localized error messages. Review comments highlighted issues such as an incorrect method signature for GetLocalizedMessage (fixed to use params object?[]), an illogical condition in SetStatChatCommandPlugIn (attribute.MaximumValue < 0 changed to arguments.Amount < 0), a copy-paste error in a PlugIn attribute description (EndDuelWhenLeavingDuelMapPlugIn), and the need to localize a quest progress message in QuestMonsterKillCountPlugIn.

Comment on lines +636 to +644
public string GetLocalizedMessage(string resourceName, params ReadOnlySpan<object?> formatArguments)
{
if (formatArguments.Length > 0)
{
return string.Format(PlayerMessage.ResourceManager.GetString(resourceName, this.Culture) ?? string.Empty, formatArguments);
}

return PlayerMessage.ResourceManager.GetString(resourceName, this.Culture) ?? string.Empty;
}

Choose a reason for hiding this comment

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

critical

The method signature public string GetLocalizedMessage(string resourceName, params ReadOnlySpan<object?> formatArguments) is invalid. The params keyword cannot be used with ReadOnlySpan<T>. Additionally, string.Format does not have an overload that accepts a ReadOnlySpan<T>. This will cause a compilation error. You should change the signature to use params object?[] instead.

    public string GetLocalizedMessage(string resourceName, params object?[] formatArguments)
    {
        if (formatArguments.Length > 0)
        {
             return string.Format(PlayerMessage.ResourceManager.GetString(resourceName, this.Culture) ?? string.Empty, formatArguments);
        }

        return PlayerMessage.ResourceManager.GetString(resourceName, this.Culture) ?? string.Empty;
    }

Comment on lines +64 to 68
if (attribute.MaximumValue < 0 || arguments.Amount > attribute.MaximumValue)
{
await player.ShowMessageAsync(e.Message).ConfigureAwait(false);
await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.InvalidStatValueRange), arguments.StatType, attribute.MaximumValue).ConfigureAwait(false);
return;
}

Choose a reason for hiding this comment

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

high

The condition attribute.MaximumValue < 0 seems incorrect. It's likely a typo and should be arguments.Amount < 0 to check if the provided amount is negative, which would be invalid. The current check against attribute.MaximumValue doesn't make sense in this context, especially since the error message refers to a valid range starting from 0.

        if (arguments.Amount < 0 || arguments.Amount > attribute.MaximumValue)
        {
            await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.InvalidStatValueRange), arguments.StatType, attribute.MaximumValue).ConfigureAwait(false);
            return;
        }

/// </summary>
[PlugIn(nameof(EndDuelWhenLeavingDuelMapPlugIn), "Updates the state of the weather of each hosted map in a random way.")]
[PlugIn]
[Display(Name = nameof(EndDuelWhenLeavingDuelMapPlugIn), Description = "Updates the state of the weather of each hosted map in a random way.")]

Choose a reason for hiding this comment

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

medium

The description in the Display attribute seems to be incorrect. It says "Updates the state of the weather of each hosted map in a random way.", but the plugin is for ending a duel when a player leaves the duel map. This seems to be a copy-paste error.

[Display(Name = nameof(EndDuelWhenLeavingDuelMapPlugIn), Description = "Ends the duel when a player leaves the duel map.")]


await player.ShowMessageAsync(message).ConfigureAwait(false);
// TODO: Localize
await player.ShowBlueMessageAsync(message).ConfigureAwait(false);

Choose a reason for hiding this comment

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

medium

The quest progress message is constructed manually and not localized, as noted by the TODO. You should move this string to the PlayerMessage.resx resource file and use ShowLocalizedBlueMessageAsync to display it. This will improve maintainability and make the message localizable.

For example, you could add a QuestMonsterKillProgress resource string like "{0}: {1} killed ({2}/{3})" and then change the code to use it.

                    await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.QuestMonsterKillProgress), questState.ActiveQuest.Name, monster.Definition.Designation, requirementState.KillCount, killRequirement.MinimumNumber).ConfigureAwait(false);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants